Skip to content

Add a bundled contributor role (onboarding + issue-filing + fork/PR coaching) and make it the helper-session default; owner overrides locally #620

Description

@dotdevdotdev

Goal

Add a reusable contributor role — the persona the agentwire helper session carries by default for non-owner users. It knows the agentwire-dev repo and helps a newcomer:

  • get set up: install, configure, orchestrate their own project sessions, and have the system explained to them;
  • submit ideas easily via a clean, correctly-labeled GitHub issue;
  • if they want, fork the repo and build their own version as a first-class supported path.

This generalizes "what the owner uses the agentwire session for" so it works for people who are not the repo owner.

Decision: one universal role, owner overrides locally

There is no separate owner role file. contributor is the published default for everyone. The owner layers a small local override role on their own machine that says "you own this repo — no fork needed, you can push to main / write labels / merge directly." Because both the owner's role file and .agentwire.yml stay untracked, the override never ships to anyone else.

  • Everyonecontributor (bundled, default).
  • Ownercontributor + a local owner-override role (untracked, see below).

How the role system actually works (verified — read before implementing)

The original draft mis-described two mechanisms. Ground truth:

  1. There is no "universal default role for all sessions." The only role auto-injected into every session is soul (inject_soul, agentwire/roles/__init__.py:168). Persona roles are NOT auto-injected. The worktree-session analogy in the original draft is wrong: worktree-session is intrinsic etiquette tied to a derived session kind (INTRINSIC_ETIQUETTE at agentwire/roles/__init__.py:209, applied by resolve_roles at :243). contributor is a plain persona role and has no such hook — a session carries it only because a command hardcodes it or .agentwire.yml lists it.

  2. Roles are markdown files, discovered by name (discover_role, agentwire/roles/__init__.py:296), first match wins:

    • project: <repo>/.agentwire/roles/<name>.md (note: .agentwire/ is gitignored)
    • user: ~/.agentwire/roles/<name>.md
    • bundled: agentwire/roles/<name>.md (ships in the package)
  3. .agentwire.yml only lists role names in its roles: field — it does not contain role bodies. agentwire new reads it (agentwire/session_cli.py:135-141resolve_roles). .agentwire.yml is personal/untracked in this repo: .gitignore lists both .agentwire.yml (line 47) and .agentwire/ (line 53), and ensure_gitignored (agentwire/project_config.py:274) re-appends .agentwire.yml to .gitignore whenever config is saved through the CLI — unless the file is already tracked, in which case it's deliberately left alone (:299-305, "already tracked = deliberate team choice; don't fight it"). So git doesn't literally forbid a commit (git add -f could force one), but in normal use the owner's .agentwire.yml stays untracked and never ships — which is exactly what the override design relies on.

  4. agentwire dev already exists (cmd_dev, agentwire/system_cli.py:42, registered at :569-573). It creates the agentwire tmux session in the source dir and hardcodes role_names = inject_soul(["agentwire"], …) at :57, at {agent}-bypass (:64-65). It ignores .agentwire.yml entirelycmd_dev never calls load_project_config, so it reads neither project roles: nor type:. So today the helper session carries the agentwire role, not contributor.

Scope

In scope:

  • Author the bundled contributor role (agentwire/roles/contributor.md).
  • Make the helper session (agentwire dev) carry it by default.
  • Ship a committed config example so a fresh clone of agentwire-dev spawns a contributor session via agentwire new, while the owner's untracked .agentwire.yml still wins.
  • Document the owner-override role.

Out of scope:

Approach

1. The contributor roleagentwire/roles/contributor.md, same frontmatter+markdown format as agentwire/roles/agentwire.md. Encode:

  • Onboarding: help install/configure agentwire, wire up the user's own projects, explain sessions / panes / orchestration.
  • Repo awareness: CLAUDE.md, docs/wiki/INDEX.md, issue conventions (feature:* / area:* / priority:* taxonomy; issue body = full plan; Closes #N in the PR body).
  • Easy idea submission: coach a clean, correctly-labeled issue draft; search for duplicates first.
  • Fork-based PR flow (load-bearing — the contributor's gh is their OWN account, with no upstream push/label/merge rights):
    • gh repo fork dotdevdotdev/agentwire-dev (or reuse an existing fork)
    • branch + push on the fork; open a PR into dotdevdotdev/agentwire-dev:main
    • never assume upstream push, label-write, or merge rights.
  • Build-your-own: present forking-to-maintain-a-variant as a first-class supported path.
  • Tone: friendly maintainer-proxy; never impersonate the owner.

The role becomes discoverable automatically once the file exists (it's picked up by discover_role / agentwire roles list).

2. Make the helper session carry it. Edit cmd_dev (agentwire/system_cli.py:57) to inject contributor instead of (or in addition to) agentwire for the default helper session. NOTE: because cmd_dev hardcodes its role list and ignores .agentwire.yml, the committed example in step 3 will not affect agentwire dev — this code edit is required for the #619 helper session to carry contributor. Decide whether the dev session should be ["contributor"], ["agentwire", "contributor"], or keep agentwire for owner-on-own-repo and switch the default. (Coordinate with #619, which owns the agentwire dev UX.)

3. Committed config example for agentwire new so a fresh clone spawns a contributor session out of the box, with the owner's local file winning:

  • Commit the template under a distinct filename (e.g. .agentwire.yml.example), not .agentwire.yml. Two reasons: the CLI keeps .agentwire.yml gitignored (ensure_gitignored), and — more fundamentally — a committed default and the owner's local override would otherwise be the same path, so they can't coexist with the local file winning. A separate template filename lets the committed default and a local untracked .agentwire.yml both exist. The template's roles: lists contributor.
  • New resolution logic requiredfind_project_config / load_project_config (agentwire/project_config.py:195 and :224) today read only .agentwire.yml and have no awareness of any .example. Add a fallback so that, when no live .agentwire.yml exists, the committed example is used; when a local untracked .agentwire.yml exists, it wins. (copy_files already carries .agentwire.yml into worktrees — agentwire/config.py:91 — so worktree dispatch is unaffected.)

4. Owner-override role — document that the owner drops an override role markdown file at ~/.agentwire/roles/<name>.md (or <repo>/.agentwire/roles/<name>.md, which is gitignored) and references it in their untracked .agentwire.yml roles: (e.g. [contributor, owner-override]). merge_roles (agentwire/roles/__init__.py:120) concatenates instructions in list order, so the override (listed last) rides on top of contributor with recency weight. No code change needed for this — it's documentation of an existing capability. Caveat: this .agentwire.yml roles: path only takes effect for agentwire new (which reads the yml); the agentwire dev helper ignores .agentwire.yml (see point 4 above), so if the owner wants the override on the dev session specifically, that comes from the step-2 cmd_dev decision, not from the yml.

Verification

  • Non-owner gh account: agentwire dev (or agentwire new in a fresh clone) → session offers fork/branch/PR flow, never pushes upstream; from "I have an idea about X" it produces a correctly-labeled issue draft.
  • Owner install with the override role: same session, but it knows not to fork and can act directly (push to main, write labels, merge).
  • Role discovery: agentwire roles show contributor resolves the bundled file.
  • Resolution precedence: with a local .agentwire.yml present, the committed example is ignored; with it absent, the example's contributor role is used.
  • Regression: uv run pytest (role-resolution and project-config tests live in tests/unit/test_roles.py and tests/unit/test_project_config.py).

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions