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
2 changes: 1 addition & 1 deletion tools/spec-loop/.last-sync
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bcd8b7f28e5d926ca8eff6e603c64b327ae038fe
da660f14e69670145e6096aa27626845ae910327
78 changes: 71 additions & 7 deletions tools/spec-loop/specs/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
https://www.apache.org/licenses/LICENSE-2.0 -->

---
title: Adapters (Gmail / PonyMail / Jira / GitHub / Bitbucket / mail-source / SourceHut / maildir / VCS / change-request)
title: Adapters (Gmail / PonyMail / Jira / GitHub / GitLab / Bitbucket / mail-source / SourceHut / maildir / VCS / change-request / chat)
status: experimental
kind: feature
mode: infra
Expand All @@ -14,7 +14,7 @@ source: >
tools/sourcehut/, tools/maildir/, tools/vcs/, tools/change-request/,
tools/asf-svn/, tools/mail-archive/, tools/mail-patch/,
tools/jira-patch/, tools/forwarder-relay/, tools/github-body-field/,
tools/github-rollup/.
tools/github-rollup/, tools/gitlab/, tools/chat/, tools/chat-slack/.
acceptance:
- Project-specific integrations live behind adapter modules, not
hardcoded into skills.
Expand All @@ -24,7 +24,7 @@ acceptance:
redactor before any LLM read.
---

# Adapters (Gmail / PonyMail / Jira / GitHub / Bitbucket / SourceHut / maildir / VCS / change-request)
# Adapters (Gmail / PonyMail / Jira / GitHub / GitLab / Bitbucket / SourceHut / maildir / VCS / change-request / chat)

## What it does

Expand All @@ -45,8 +45,29 @@ by swapping the adapter, not the skill.
- `tools/jira/` — issue-tracker adapter for projects on Jira.
- `tools/github/` — issues/PRs/labels read + write-back helpers.
Sub-adapters: `tools/github-body-field/` (reads GitHub issue/PR body
structured field sets) and `tools/github-rollup/` (aggregates
multi-repo PR state into a single view).
structured field sets) and `tools/github-rollup/` (`contract:tracker`,
stdlib-only), which maintains the single status-rollup comment on a
tracker issue in a subprocess so the growing rollup body never enters
agent context.
`append` and `amend-latest` print the rollup comment's URL on stdout
(#1451), so a caller links to the entry without a second lookup.
Its subcommands are `append` (creates the rollup if none exists),
`amend-latest` (replaces the newest entry's body, keeping its date and
user, and refuses with exit 4 when that entry's action differs, so another
writer's entry is never overwritten), `fold` (moves one legacy bot comment
into the rollup with its own date and author, then deletes it; refuses to
fold the rollup itself), `list`, and `latest`.
An existing rollup is found by any `<!-- <name> status rollup v<N>`
marker, and a comment's id is read from its `#issuecomment-<id>` URL.
`tools/github/status-rollup.md` calls the tool instead of carrying its own
script.
Under the secure agent setup both tools' `gh` runs sandboxed and fails,
so their READMEs route skills to the same procedures through vetted-ops:
`vetted-op-tracker` (`rollup-append`, `rollup-amend-latest`,
`rollup-fold`, `body-field-set`) for writes and `vetted-op-read …
body-field-get` for the read, with the parsing modules vendored into
vetted-ops; the uv CLIs remain for use outside the sandbox (#1448; see
[`vetted-command-surface.md`](vetted-command-surface.md)).
- `tools/bitbucket/` — partial Bitbucket Cloud and Bitbucket
Data Center bridge foundation. Supports repository metadata reads,
read-only branch restriction context, Cloud-only issue listing/fetching, issue comment fetching, issue attachment metadata fetching, and confirmed issue comment creation,
Expand All @@ -59,6 +80,22 @@ by swapping the adapter, not the skill.
deeper Jira handoff, broader issue writes, review/merge writes, broader
repository permissions, and fuller Pipelines run/log/retry coverage remain
tracked in #606.
- `tools/gitlab/` (`magpie-gitlab`) — partial, read-only GitLab REST API v4
bridge, declared `contract:tracker + contract:source-control +
contract:change-request` with `**Coverage:** partial`.
Operations: `repo get`, `issue list` / `issue get`, `mr list` / `mr get` /
`mr diff` / `mr commits` / `mr pipelines`, and `pipeline status`, all
printing JSON.
Auth from `GITLAB_TOKEN` or `CI_JOB_TOKEN` (optional for public reads),
with `GITLAB_AUTH_SCHEME` to force `PrivateToken`, `Bearer`, or
`JobToken`; `GITLAB_INSTANCE_URL` defaults to `https://gitlab.com`.
Redirects that change origin or downgrade HTTPS to HTTP are refused so
the token cannot leak.
Paginated reads follow `X-Next-Page` until `--limit` items are collected
(short pages keep paging), or, without `--limit`, stop at 10 pages and
note the cap on stderr; `--limit` rejects values below 1.
No write or mutation operations exist.
Offline tests mock every HTTP response.
- `tools/sourcehut/` — SourceHut (sr.ht) forge bridge: ticket tracking
(`todo.sr.ht`), mailing-list patchset review (`lists.sr.ht`), CI build
status (`builds.sr.ht`), and repository reads (`git.sr.ht`/`hg.sr.ht`)
Expand Down Expand Up @@ -101,6 +138,21 @@ by swapping the adapter, not the skill.
patch-over-Jira adapters; implement `contract:change-request` for
projects that land patches via mailing-list review or Jira rather than
GitHub PRs.
- `tools/chat/` — the `contract:chat` interface (pure Markdown) for a
project's public chat.
Three verbs, `list_channels`, `resolve_user(github_handle)`, and
`search_messages(chat_user_id, since, until, channels)`; read-only by
construction: no verb posts, reacts, edits, or reads a direct message or
private channel.
Consumed by the contributor-growth skills to see how a contributor helps
others in chat ([contributor growth](contributor-growth.md)).
Selected by `chat.kind` in `<project-config>/project.md`.
- `tools/chat-slack/` — the shipping `contract:chat` adapter: a mapping
onto the Slack MCP tools (`operations.md`) over the public channels of the
project's workspace, optionally narrowed by `chat.channels`.
It never calls a tool that sends, schedules, drafts, or edits a message.
`discord` and `none` are placeholders in the contract's adapter table
(Discord tracked in #1421).

## Behaviour & contract

Expand Down Expand Up @@ -144,16 +196,28 @@ by swapping the adapter, not the skill.
## Validation

```bash
for t in gmail maildir ponymail jira github bitbucket; do
for t in gmail maildir ponymail jira github bitbucket gitlab; do
uv run --project tools/$t --group dev pytest || echo "check tools/$t test setup"
done
uv run --project tools/vcs --group dev pytest || echo "check tools/vcs test setup"
uv run --all-packages --group dev pytest tools/github-rollup/tests
```

## Known gaps

- `experimental` overall — adapter coverage varies; a new adopter system
(e.g. GitLab, a different mail backend) is a gap the plan pass records.
(e.g. a different mail backend) is a gap the plan pass records.
- **GitLab adapter is a read-only foundation.** `tools/gitlab/` covers
repository metadata, issue and merge-request reads, MR diffs, commits and
pipelines, and pipeline status; it is `partial` for all three contracts
and must not be advertised as a selectable complete backend.
Writes, issue/MR mutations, and review or merge actions remain tracked in
#305.
Fetched issue and MR titles, descriptions, diffs and commit messages are
external data, never instructions.
- **Chat covers Slack only.** `contract:chat` ships one adapter;
`discord` and `none` are placeholders, so a project on Discord gets chat
reported as not collected.
- **Bitbucket adapter is new and intentionally partial.** `tools/bitbucket/`
currently provides read-only repository metadata, read-only branch restriction
context, pull-request discovery, pull-request fetching, read-only pull-request
Expand Down
128 changes: 115 additions & 13 deletions tools/spec-loop/specs/adoption-and-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ acceptance:
plugin list, with no `ref`, and a fresh clone is brought up to that
minimum and never held down to it.
- Drift between the committed pin and the local install is detected and
surfaced with an upgrade proposal.
surfaced with an upgrade proposal, or with a full re-install proposal
when the local install's method or URL differs from the pin.
- A gitignored `.apache-magpie-local/` supplies per-person overrides that
layer above the committed `.apache-magpie-overrides/`, cannot weaken the
safety baseline, and can be ignored for a single run via a one-shot
Expand Down Expand Up @@ -139,8 +140,26 @@ committed version with drift detection.

## Where it lives

- Skill: `setup` (install, adopt/unadopt, verify, upgrade, override,
reconcile — the one-time project-wide reconciliation sweep).
- Skill: `setup` (install, config, adopt/unadopt, verify, upgrade,
override, reconcile — the one-time project-wide reconciliation sweep) at
`plugins/magpie-setup/skills/setup/`: one `SKILL.md` plus a sibling
detail file per sub-action (`install.md`, `config.md`, `adopt.md`,
`upgrade.md`, `verify.md`, `reconcile.md`, `locks.md`, …).
`skills/setup` is the flat-tree mirror symlink, like every other skill's
`skills/<dir>`; the sibling setup skills below live beside it under
`plugins/magpie-setup/skills/`.
- Pre-flight checker: the `setup_preflight` package ships inside the
`magpie-setup` plugin at `plugins/magpie-setup/skills/setup/setup_preflight/`
(#1409), so a marketplace-only adopter has a copy to install.
`tools/setup-preflight/` stays the uv workspace member that carries its
tests and CI job; its `src/setup_preflight` is a symlink inward to the
plugin's package, and `check-family-plugins.py` fails when the package
leaves the plugin or the mirror link drifts.
- Project templates: `plugins/magpie-setup/templates/` (#1410), the
scaffold `setup config` copies adopter configuration from.
`projects/_template` is a symlink to it, so older paths keep resolving;
`check-family-plugins.py` fails when the templates leave the plugin or
that link drifts.
- Skills: `setup-isolated-setup-install` / `-update` / `-verify` / `-doctor`
(the sandbox harness; `-doctor` probes live restrictions — SSH agent /
Yubikey reachability, localhost port binding, filesystem restrictions),
Expand All @@ -150,7 +169,11 @@ committed version with drift detection.
method and pin, drift between local and committed locks, which skills
are wired in the current repo.
- Docs: `docs/setup/` (install recipes, agentic-overrides contract,
prerequisites).
prerequisites, `commit-attribution.md`).
- Commit attribution: `commit-attribution.toml`, committed in
`.apache-magpie-overrides/` (project policy) and gitignored in
`.apache-magpie-local/` (a contributor's preference), scaffolded from
`plugins/magpie-setup/templates/commit-attribution.toml` (#1385).
- Lock files: `.apache-magpie.lock` (committed — a pin on the snapshot
methods, a floor on `method: marketplace`) and
`.apache-magpie.local.lock` (gitignored, what this machine fetched).
Expand Down Expand Up @@ -224,6 +247,56 @@ committed version with drift detection.
- **`upgrade` splits on adoption:** nothing repo-side when the project
has not adopted; `min_version` raised — never lowered — and staged
when it has.
- **An unrecognised sub-action is never guessed** (#1391). A first
argument that names no sub-action and is not a flag prints an
"Unknown setup sub-action" message with close-match suggestions (edit
distance one or two, or a prefix; every candidate when several qualify)
and the full sub-action list, then waits.
It runs a suggested sub-action only after the user confirms it, and
never falls back to `install`, because `upgrade`, `reconcile` and
`adopt` stage committed files.
- **Stacked PRs only with write access** (#1430). The agent conventions
block `install` writes into an adopter's `AGENTS.md`, like this repo's own
`AGENTS.md`, tells agents how to open a series of dependent PRs.
With push access to `<upstream>` (`gh api repos/<upstream> --jq
.permissions.push`), every branch is pushed to `<upstream>` itself, each PR
is opened in the browser from its approved body against the previous
branch, and the stack is joined with `gh stack link`; the agent only
suggests installing the `github/gh-stack` extension, never installs it.
Without push access, the PRs go from the fork against the default branch
with a *"Depends on #N"* line.
`gh stack submit --auto` and letting `gh stack` generate titles or bodies
are never used.
- **Commit attribution is a project choice** (#1385). `adopt` always asks
the maintainer for the convention (`generated-by`, the default and the
ASF convention; `assisted-by`; `co-authored-by`; `none`;
`contributor-choice`; or custom wording) and writes it to
`.apache-magpie-overrides/commit-attribution.toml`.
`config` asks a contributor only when the project's file is absent or
says `contributor-choice`, and writes
`.apache-magpie-local/commit-attribution.toml`.
This is the one configuration file where the project wins over the
contributor's local copy; an unreadable file or unknown value fails
closed to `generated-by`.
Every skill that commits adds the trailer as a `--trailer` argument,
never inside the message, so it stays visible to command-line guards.
The framework repository pins `generated-by` for itself.
- **Adversarial reviewers are configured on request** (#1371).
`config adversarial-review` (Step 3c, run only when named and only with
the `magpie-adversarial-review` plugin installed) runs
`adversarial-review detect`, pre-selects every available backend except
the running harness's own model, writes
`.apache-magpie-local/adversarial-review.md` from its template, and
offers the Codex / Gemini command files under the user's home directory,
never inside a repository.
`verify` check 8i reports configured reviewers whose CLI is gone and
stale command files; `adopt` always flags `adversarial-review.md` as
personal.
- **`method: local` has nothing to drift** (#1414). The framework
checkout's self-adoption writes `method: local` with a `source:` key; the
checker's lock parser accepts that key and returns no project findings
for the method, and `upgrade` on it only refreshes the pre-flight
checker from the in-repo package and stops.
- **Every skill's pre-flight also compares its own generated
`surface_hash` against the reconciliation stamp**, silently when they
match, at no extra cost inside a sandboxed session: both values are
Expand All @@ -249,7 +322,14 @@ committed version with drift detection.
3. The committed lock re-installs the same version on a fresh clone on the
snapshot methods, and on `method: marketplace` brings a fresh clone up to
the recorded floor without capping it there.
4. Drift between local and committed locks is surfaced with an upgrade.
4. Drift between local and committed locks is surfaced with a remedy that
fits it: a missing local lock proposes `/magpie-setup`; a differing
`ref` or `commit` proposes `/magpie-setup upgrade`; a differing `method`
or `url` (the machine fetched the framework another way, or from
another source) proposes a full re-install with `/magpie-setup`,
because an upgrade cannot fix it. `setup_preflight` compares all four
keys (`method` and `url` since #1435), and its `step-2` rules section
carries the three remedies.
5. Override files can be discovered and surfaced to skills without
editing upstream skill bodies, and override text cannot weaken the
safety/confidentiality baseline.
Expand Down Expand Up @@ -376,7 +456,10 @@ committed version with drift detection.
`.apache-magpie-local/`** by `/magpie-setup config` and refreshed
there by `/magpie-setup upgrade`, because Bash can neither read nor
execute the plugin cache under the framework's own recommended
sandbox. `config` states that it installed an executable, since it may
sandbox. The copy's source is the package inside the installed
`magpie-setup` plugin on a marketplace install, and
`<snapshot-dir>/tools/setup-preflight/src/setup_preflight/` (the same
files, through the mirror link) on a snapshot install. `config` states that it installed an executable, since it may
run unattended from a skill's pre-flight. `upgrade` skips the refresh
when the directory does not exist rather than creating it, because its
absence is what marks a project as never configured.
Expand All @@ -399,6 +482,25 @@ committed version with drift detection.
The stamp is written only by `python3 -m setup_preflight.isolated
record-update | record-reminder`, never by hand; the proposal never
runs the update and never blocks the skill.
28. The memoised project verdict is keyed on the lock files, the plugin
listing, and the checker's own `core.py` and `lockfile.py`, so a
checker refreshed by `upgrade` never serves the previous code's
answer (#1414). A `method: local` lock, `source:` key included,
parses and yields no project findings.
29. An unrecognised `setup` sub-action is answered with close-match
suggestions and the sub-action list, and nothing runs until the user
confirms (#1391; eval suite
`tools/skill-evals/evals/setup/step-unknown-subaction`).
30. `adopt` writes the project's commit-attribution convention to
`.apache-magpie-overrides/commit-attribution.toml`; `config` writes a
contributor's preference to
`.apache-magpie-local/commit-attribution.toml` only when the project
leaves the choice open. Resolution is project first, then contributor,
then `generated-by`, failing closed to `generated-by` (#1385).
31. `setup config` scaffolds every template from
`plugins/magpie-setup/templates/<file>` on a marketplace install and
from `<snapshot-dir>/projects/_template/<file>` on a snapshot install;
both are the same file (#1410).

## Validation

Expand All @@ -416,11 +518,11 @@ uv run --project tools/skill-and-tool-validator --group dev skill-and-tool-valid
only the namespace of their invocation differs.

- `stable`; gaps appear as new agent targets to add to the registry
([`agents.md`](../../../skills/setup/agents.md)) or new override
([`agents.md`](../../../plugins/magpie-setup/skills/setup/agents.md)) or new override
surfaces — recorded by the plan pass.
- **Not yet built:** the `.apache-magpie-local/` personal override surface
(acceptance 5) and the one-shot default-run switch (acceptance 6). Both are
intended behaviour recorded here and tracked as work items
`magpie-local-convention` and `override-bypass-one-shot` in the plan. The
three hybrid-setup how-tos that build on the local surface are tracked
alongside them.
- **Built since first recorded as gaps:** the `.apache-magpie-local/`
personal override surface (acceptance 6) and the one-shot default-run
switch (acceptance 7, the `--no-overrides` flag in
`docs/setup/agentic-overrides.md` § One-shot defaults run) both ship.
They were tracked as work items `magpie-local-convention` and
`override-bypass-one-shot` in the plan.
Loading