@delorenj/pjangler 1.2.4 → 1.2.8

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.
Files changed (30) hide show
  1. package/dist/index.js +797 -737
  2. package/dist/mcp-server.js +753 -683
  3. package/package.json +1 -1
  4. package/templates/commonproject/AGENTS.md +3 -3
  5. package/templates/commonproject/README.md +12 -11
  6. package/templates/commonproject/copier.yml +58 -10
  7. package/templates/commonproject/mise.toml +3 -3
  8. package/templates/commonproject/template/.agents/hooks/sync.py +12 -1
  9. package/templates/commonproject/template/.agents/local.example.json +3 -1
  10. package/templates/commonproject/template/.project.json.jinja +13 -6
  11. package/templates/commonproject/template/mise.toml.jinja +8 -4
  12. package/templates/hermes-agent/README.md +8 -8
  13. package/templates/hermes-agent/copier.yml +3 -4
  14. package/templates/hermes-agent/docs/architecture.md +4 -4
  15. package/templates/hermes-agent/docs/operations.md +2 -2
  16. package/templates/hermes-agent/docs/sentinel/README.md +6 -8
  17. package/templates/hermes-agent/docs/sentinel/architecture.md +2 -1
  18. package/templates/hermes-agent/docs/sentinel/development.md +13 -12
  19. package/templates/hermes-agent/docs/sentinel/providers.md +12 -32
  20. package/templates/hermes-agent/install-local.sh +6 -13
  21. package/templates/hermes-agent/template/.scripts/42-ticket-provider.sh +14 -37
  22. package/templates/hermes-agent/template/.scripts/70-systemd.sh +2 -0
  23. package/templates/hermes-agent/template/.scripts/lib/ticket-provider.sh +6 -2
  24. package/templates/hermes-agent/template/.scripts/sentinel/docs/autonomous-delegated-review.md +2 -2
  25. package/templates/hermes-agent/template/.scripts/sentinel/docs/continuous-ticket-orchestration.md +1 -1
  26. package/templates/hermes-agent/template/.scripts/sentinel.prompt.md.jinja +6 -1
  27. package/templates/hermes-agent/template/SOUL.md.jinja +9 -3
  28. package/templates/hermes-agent/template/role.yaml.jinja +3 -6
  29. package/templates/hermes-agent/template/.scripts/40-plane.sh +0 -51
  30. package/templates/hermes-agent/template/.scripts/providers/linear.sh +0 -176
@@ -1,7 +1,7 @@
1
1
  # Provider adapters
2
2
 
3
3
  This guide is the reference for the ticket-provider abstraction: the contract
4
- every adapter implements, the three adapters that ship today, and a
4
+ every adapter implements, the two adapters that ship today, and a
5
5
  step-by-step process for adding a new provider or verifying an existing one
6
6
  against a live board. Read [Architecture](architecture.md) first for how the
7
7
  adapter fits into the engine.
@@ -43,12 +43,8 @@ maps each to its back end's concrete state.
43
43
 
44
44
  ## The adapters that ship today
45
45
 
46
- The repository includes three adapters with different verification status.
46
+ The repository includes two supported adapters with different verification status.
47
47
 
48
- - **Linear** (`providers/linear.sh`) is the reference implementation. It uses
49
- the Linear GraphQL API. It's verified live against a real board. Linear's
50
- `issue(id:)` field accepts both the UUID and the human identifier (for
51
- example, `DEL-42`), so the engine can pass identifiers through.
52
48
  - **Plane** (`providers/plane.sh`) uses the Plane REST API with `X-API-Key`
53
49
  authentication. A Plane project maps to the board, a cycle maps to the
54
50
  milestone, and `state.group` maps to the state type. It's verified live. Note
@@ -64,21 +60,6 @@ The repository includes three adapters with different verification status.
64
60
 
65
61
  Set credentials in the environment and the board binding in `role.yaml`.
66
62
 
67
- <details>
68
- <summary>Linear</summary>
69
-
70
- - Credentials: `LINEAR_API_KEY`.
71
- - Binding:
72
-
73
- ```yaml
74
- ticket_provider:
75
- name: linear
76
- team: DEL # Linear team key
77
- project: "" # optional, scopes milestone and issue queries
78
- ```
79
-
80
- </details>
81
-
82
63
  <details>
83
64
  <summary>Plane</summary>
84
65
 
@@ -114,8 +95,9 @@ Set credentials in the environment and the board binding in `role.yaml`.
114
95
 
115
96
  To add a fourth provider, for example GitHub Issues or Jira, follow these steps.
116
97
 
117
- 1. Create `template/.scripts/providers/<name>.sh`. Start from `linear.sh` if the
118
- back end uses GraphQL, or `plane.sh` if it uses REST with a single API key.
98
+ 1. Create `template/.scripts/providers/<name>.sh`. Start from `plane.sh` if it
99
+ uses REST with a single API key, or `trello.sh` if it has a board/list/card
100
+ model.
119
101
  2. Implement every operation in [the contract](#the-contract). Return the exact
120
102
  JSON shapes shown in the table. Map your back end's states to the five
121
103
  normalized states.
@@ -127,8 +109,7 @@ To add a fourth provider, for example GitHub Issues or Jira, follow these steps.
127
109
  5. Extend `template/role.yaml.jinja` with a provider-specific binding block
128
110
  under the `{% if ticket_provider == '<name>' %}` branch.
129
111
  6. Teach `42-ticket-provider.sh` how to resolve or create the board for your
130
- provider, if it differs from the existing `linear`, `plane`, and `trello`
131
- cases.
112
+ provider, if it differs from the existing `plane` and `trello` cases.
132
113
  7. Validate and verify with the steps below.
133
114
 
134
115
  <!-- prettier-ignore -->
@@ -158,7 +139,7 @@ Before you touch a live board, confirm the adapter parses and fails cleanly.
158
139
  exit, with no Python traceback:
159
140
 
160
141
  ```bash
161
- ( unset LINEAR_API_KEY; sh template/.scripts/providers/linear.sh resolve )
142
+ ( unset PLANE_API_KEY; sh template/.scripts/providers/plane.sh resolve )
162
143
  ```
163
144
 
164
145
  ## Verifying an adapter against a live board
@@ -169,15 +150,15 @@ never closes or modifies a ticket, so it's safe to run.
169
150
 
170
151
  ```bash
171
152
  # 1. Load the credential into the environment only (never print it).
172
- export LINEAR_API_KEY="$(grep -E '^(export )?LINEAR_API_KEY=' \
173
- ~/.hermes/<agent>.env | head -1 | sed -E 's/^(export )?LINEAR_API_KEY=//; s/^"//; s/"$//')"
153
+ export PLANE_API_KEY="$(grep -E '^(export )?PLANE_API_KEY=' \
154
+ ~/.hermes/<agent>.env | head -1 | sed -E 's/^(export )?PLANE_API_KEY=//; s/^"//; s/"$//')"
174
155
 
175
156
  # 2. Stage a role directory bound to the real board.
176
157
  T=$(mktemp -d); RD="$T/agents/hermes/pm"
177
158
  mkdir -p "$RD/.scripts/lib" "$RD/.scripts/providers"
178
159
  cp template/.scripts/lib/ticket-provider.sh "$RD/.scripts/lib/"
179
- cp template/.scripts/providers/linear.sh "$RD/.scripts/providers/"
180
- printf 'repo: demo\nrole: pm\nticket_provider:\n name: linear\n team: DEL\n' \
160
+ cp template/.scripts/providers/plane.sh "$RD/.scripts/providers/"
161
+ printf 'repo: demo\nrole: pm\nticket_provider:\n name: plane\n workspace: 33god\n project: <project-uuid>\n' \
181
162
  > "$RD/role.yaml"
182
163
 
183
164
  # 3. Run the read operations.
@@ -199,5 +180,4 @@ verification.
199
180
  ## Read next
200
181
 
201
182
  - [Development guide](development.md): the full edit, validate, and propagate
202
- workflow, plus the open roadmap, which includes live-verifying Plane and
203
- Trello.
183
+ workflow, plus the open roadmap, which includes live-verifying Trello.
@@ -15,10 +15,10 @@
15
15
  #
16
16
  # Environment overrides (skip the prompts):
17
17
  # HAT_REPO=<name> project/repo name (default: basename of CWD)
18
- # HAT_PROVIDER=linear|plane|trello (default: plane)
18
+ # HAT_PROVIDER=plane|trello (default: plane)
19
19
  # HAT_ROLES="pm" roles to install (default: pm)
20
20
  # HAT_DRY_RUN=1 print actions, change nothing
21
- # Provider creds: LINEAR_API_KEY | PLANE_API_KEY+PLANE_BASE | TRELLO_KEY+TRELLO_TOKEN
21
+ # Provider creds: PLANE_API_KEY+PLANE_BASE | TRELLO_KEY+TRELLO_TOKEN
22
22
  set -eu
23
23
 
24
24
  say() { printf '\033[36m%s\033[0m\n' "$*"; }
@@ -100,7 +100,7 @@ TOML
100
100
  fi
101
101
 
102
102
  # --- 4. Provider credentials + board binding ---------------------------------
103
- ask HAT_PROVIDER "Ticket provider (linear|plane|trello)" "plane"
103
+ ask HAT_PROVIDER "Ticket provider (plane|trello)" "plane"
104
104
  PROVIDER="$HAT_PROVIDER"
105
105
  ask HAT_REPO "Project/repo name" "$(basename "$PROJECT_DIR" | tr '[:upper:]' '[:lower:]')"
106
106
  REPO="$HAT_REPO"
@@ -109,7 +109,7 @@ PM_ENV="$HOME/.hermes/${REPO}-pm.env"
109
109
  # These get written into the pm role.yaml binding after render.
110
110
  # Pre-seed from optional env knobs so the install can run non-interactively.
111
111
  TP_WORKSPACE="${HAT_PLANE_WORKSPACE:-}"; TP_PROJECT="${HAT_PLANE_PROJECT:-}"
112
- TP_TEAM="${HAT_LINEAR_TEAM:-}"; TP_BOARD="${HAT_TRELLO_BOARD:-}"
112
+ TP_BOARD="${HAT_TRELLO_BOARD:-}"
113
113
  case "$PROVIDER" in
114
114
  plane)
115
115
  ask PLANE_BASE "Plane base URL" "https://app.plane.so"
@@ -136,12 +136,6 @@ PY
136
136
  CRED_LINES="PLANE_API_KEY=$PLANE_API_KEY
137
137
  PLANE_BASE=$PLANE_BASE"
138
138
  ;;
139
- linear)
140
- : "${LINEAR_API_KEY:?Set LINEAR_API_KEY in your environment, then re-run}"
141
- ask TP_TEAM "Linear team key (for example DEL)" ""
142
- [ -n "$TP_TEAM" ] || die "team key is required"
143
- CRED_LINES="LINEAR_API_KEY=$LINEAR_API_KEY"
144
- ;;
145
139
  trello)
146
140
  : "${TRELLO_KEY:?Set TRELLO_KEY in your environment, then re-run}"
147
141
  : "${TRELLO_TOKEN:?Set TRELLO_TOKEN in your environment, then re-run}"
@@ -170,7 +164,7 @@ for ROLE in $ROLES; do
170
164
  DEST="$PROJECT_DIR/agents/hermes/$ROLE"
171
165
  # Scrub provider creds from copier's environment so 42-ticket-provider skips
172
166
  # board CREATION; we bind to the existing board in step 6 instead.
173
- run "env -u PLANE_API_KEY -u PLANE_33GOD_API_KEY -u LINEAR_API_KEY \
167
+ run "env -u PLANE_API_KEY -u PLANE_33GOD_API_KEY \
174
168
  -u TRELLO_KEY -u TRELLO_TOKEN \
175
169
  copier copy '$TEMPLATE_SRC' '$DEST' --trust --defaults --overwrite \
176
170
  --data target_repo='$REPO' --data role='$ROLE' --data ticket_provider='$PROVIDER'"
@@ -180,7 +174,7 @@ done
180
174
  PM_ROLE="$PROJECT_DIR/agents/hermes/pm/role.yaml"
181
175
  if [ -f "$PM_ROLE" ] && [ "${HAT_DRY_RUN:-0}" != "1" ]; then
182
176
  say "6. binding pm to your $PROVIDER board"
183
- TP_WORKSPACE="$TP_WORKSPACE" TP_PROJECT="$TP_PROJECT" TP_TEAM="$TP_TEAM" \
177
+ TP_WORKSPACE="$TP_WORKSPACE" TP_PROJECT="$TP_PROJECT" \
184
178
  TP_BOARD="$TP_BOARD" PROVIDER="$PROVIDER" python3 - "$PM_ROLE" <<'PY'
185
179
  import os, re, sys, pathlib
186
180
  p = pathlib.Path(sys.argv[1]); t = p.read_text()
@@ -190,7 +184,6 @@ def setleaf(text, key, val):
190
184
  return new if n else text
191
185
  t = setleaf(t, "workspace", os.environ.get("TP_WORKSPACE",""))
192
186
  t = setleaf(t, "project", os.environ.get("TP_PROJECT",""))
193
- t = setleaf(t, "team", os.environ.get("TP_TEAM",""))
194
187
  t = setleaf(t, "board", os.environ.get("TP_BOARD",""))
195
188
  p.write_text(t)
196
189
  print(" bound:", {k:v for k,v in os.environ.items() if k.startswith("TP_") and v})
@@ -46,13 +46,13 @@ PY
46
46
  }
47
47
 
48
48
  # pj_write — merge board binding (optional) + this agent into .project.json.
49
- # args: set_provider(0|1) provider board_id board_url workspace identifier team
49
+ # args: set_provider(0|1) provider board_id board_url workspace identifier
50
50
  pj_write() {
51
51
  REPO="$REPO" REPO_ROOT="$REPO_ROOT" AGENT_ID="$AGENT_ID" ROLE="$ROLE" \
52
52
  ROLE_DIR_REL="$ROLE_DIR_REL" PROJECT_DESC="${PROJECT_DESC:-}" \
53
53
  python3 - "$PROJECT_JSON" "$@" <<'PY'
54
54
  import sys, os, json, pathlib
55
- (path, set_provider, provider, board_id, board_url, workspace, identifier, team) = sys.argv[1:9]
55
+ (path, set_provider, provider, board_id, board_url, workspace, identifier) = sys.argv[1:8]
56
56
  p = pathlib.Path(path)
57
57
  try:
58
58
  d = json.loads(p.read_text())
@@ -72,7 +72,6 @@ if set_provider == "1":
72
72
  if identifier: tp["identifier"] = identifier
73
73
  if board_id: tp["board_id"] = board_id
74
74
  if board_url: tp["board_url"] = board_url
75
- if team: tp["team"] = team
76
75
  ag = d.setdefault("agents", {})
77
76
  ag[os.environ["AGENT_ID"]] = {"role": os.environ["ROLE"], "role_dir": os.environ["ROLE_DIR_REL"]}
78
77
  p.write_text(json.dumps(d, indent=2) + "\n")
@@ -82,8 +81,8 @@ PY
82
81
 
83
82
  # Mirror the binding into role.yaml so legacy consumers keep working.
84
83
  mirror_to_role_yaml() {
85
- # mirror_to_role_yaml <provider> <board_id> <board_url> <workspace> <identifier> <team>
86
- local provider="$1" bid="$2" burl="$3" ws="$4" ident="$5" team="$6"
84
+ # mirror_to_role_yaml <provider> <board_id> <board_url> <workspace> <identifier>
85
+ local provider="$1" bid="$2" burl="$3" ws="$4" ident="$5"
87
86
  yaml_set ticket_provider.name "$provider" 2>/dev/null || true
88
87
  [ -n "$bid" ] && yaml_set ticket_provider.board_id "$bid" 2>/dev/null || true
89
88
  [ -n "$burl" ] && yaml_set ticket_provider.board_url "$burl" 2>/dev/null || true
@@ -98,8 +97,8 @@ mirror_to_role_yaml() {
98
97
  trello)
99
98
  [ -n "$bid" ] && yaml_set ticket_provider.board "$bid" 2>/dev/null || true
100
99
  ;;
101
- linear)
102
- [ -n "$team" ] && yaml_set ticket_provider.team "$team" 2>/dev/null || true
100
+ *)
101
+ die "unsupported ticket provider: $provider (expected plane|trello)"
103
102
  ;;
104
103
  esac
105
104
  }
@@ -111,7 +110,6 @@ SOT_BOARD_ID="$(pj ticket_provider.board_id)"
111
110
  SOT_URL="$(pj ticket_provider.board_url)"
112
111
  SOT_WS="$(pj ticket_provider.workspace)"
113
112
  SOT_IDENT="$(pj ticket_provider.identifier)"
114
- SOT_TEAM="$(pj ticket_provider.team)"
115
113
 
116
114
  # role.yaml provider comes from copier --data (the operator's pjangler choice).
117
115
  ROLE_PROVIDER="$(yaml_get ticket_provider.name)"
@@ -122,9 +120,10 @@ if [ -n "$SOT_BOARD_ID" ]; then
122
120
  if [ -n "$ROLE_PROVIDER" ] && [ "$ROLE_PROVIDER" != "$PROVIDER" ]; then
123
121
  warn "[42] requested provider '$ROLE_PROVIDER' but repo board is '$PROVIDER' (.project.json wins); binding to existing board"
124
122
  fi
123
+ case "$PROVIDER" in plane|trello) ;; *) die "unsupported ticket provider in .project.json: $PROVIDER (expected plane|trello)" ;; esac
125
124
  log "[42] binding $AGENT_ID to existing repo board (provider=$PROVIDER, id=$SOT_BOARD_ID)"
126
- mirror_to_role_yaml "$PROVIDER" "$SOT_BOARD_ID" "$SOT_URL" "$SOT_WS" "$SOT_IDENT" "$SOT_TEAM"
127
- pj_write 0 "$PROVIDER" "" "" "" "" "" # register agent only; board already recorded
125
+ mirror_to_role_yaml "$PROVIDER" "$SOT_BOARD_ID" "$SOT_URL" "$SOT_WS" "$SOT_IDENT"
126
+ pj_write 0 "$PROVIDER" "" "" "" "" # register agent only; board already recorded
128
127
  mark_done 42-ticket-provider
129
128
  exit 0
130
129
  fi
@@ -143,45 +142,23 @@ NAME="$(printf '%s' "$REPO" | tr '_-' ' ' | python3 -c 'import sys; print(" ".j
143
142
  DESC="Ticket board for $REPO"
144
143
 
145
144
  case "$PROVIDER" in
146
- linear)
147
- if [[ -z "${LINEAR_API_KEY:-}" ]]; then
148
- warn "[42] LINEAR_API_KEY not set; set role.yaml/.project.json ticket_provider.team and re-run ./.scripts/42-ticket-provider.sh"
149
- pj_write 1 linear "" "" "" "$IDENT" "$SOT_TEAM"
150
- mark_done 42-ticket-provider; exit 0
151
- fi
152
- OUT="$(tp resolve 2>/dev/null || true)"
153
- BID="$(printf '%s' "$OUT" | python3 -c 'import sys,json
154
- try: print(json.load(sys.stdin).get("board_id",""))
155
- except Exception: print("")')"
156
- BURL="$(printf '%s' "$OUT" | python3 -c 'import sys,json
157
- try: print(json.load(sys.stdin).get("board_url",""))
158
- except Exception: print("")')"
159
- if [ -n "$BID" ]; then
160
- mirror_to_role_yaml linear "$BID" "$BURL" "" "$IDENT" "$SOT_TEAM"
161
- pj_write 1 linear "$BID" "$BURL" "" "$IDENT" "$SOT_TEAM"
162
- else
163
- warn "[42] linear resolve returned no board; set ticket_provider.team and re-run"
164
- pj_write 1 linear "" "" "" "$IDENT" "$SOT_TEAM"
165
- fi
166
- ;;
167
-
168
145
  plane|trello)
169
146
  KEYVAR=PLANE_API_KEY; [ "$PROVIDER" = trello ] && KEYVAR=TRELLO_KEY
170
147
  if [[ -z "${!KEYVAR:-}" ]]; then
171
148
  warn "[42] $KEYVAR not set; skipping board creation. Set creds and re-run ./.scripts/42-ticket-provider.sh"
172
- pj_write 1 "$PROVIDER" "" "" "${SOT_WS:-$PLANE_WORKSPACE}" "$IDENT" ""
149
+ pj_write 1 "$PROVIDER" "" "" "${SOT_WS:-${PLANE_WORKSPACE:-}}" "$IDENT"
173
150
  mark_done 42-ticket-provider; exit 0
174
151
  fi
175
152
  OUT="$(tp create_board "$NAME" "$IDENT" "$DESC")" || die "create_board failed for $PROVIDER"
176
153
  BID="$(printf '%s' "$OUT" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("board_id",""))')"
177
154
  BURL="$(printf '%s' "$OUT" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("board_url",""))')"
178
- WS="${SOT_WS:-$PLANE_WORKSPACE}"
155
+ WS="${SOT_WS:-${PLANE_WORKSPACE:-}}"
179
156
  [ "$PROVIDER" = trello ] && WS=""
180
- mirror_to_role_yaml "$PROVIDER" "$BID" "$BURL" "$WS" "$IDENT" ""
181
- pj_write 1 "$PROVIDER" "$BID" "$BURL" "$WS" "$IDENT" ""
157
+ mirror_to_role_yaml "$PROVIDER" "$BID" "$BURL" "$WS" "$IDENT"
158
+ pj_write 1 "$PROVIDER" "$BID" "$BURL" "$WS" "$IDENT"
182
159
  ;;
183
160
 
184
- *) die "unknown ticket provider: $PROVIDER (expected linear|plane|trello)" ;;
161
+ *) die "unknown ticket provider: $PROVIDER (expected plane|trello)" ;;
185
162
  esac
186
163
 
187
164
  mark_done 42-ticket-provider
@@ -32,6 +32,8 @@ Type=simple
32
32
  Environment=HERMES_HOME=$RUNTIME
33
33
  Environment=HERMES_OAUTH_FILE=$HERMES_OAUTH_FILE
34
34
  Environment=CODEX_HOME=$CODEX_HOME
35
+ Environment=HERMES_KANBAN_DISPATCH_IN_GATEWAY=0
36
+ Environment=HERMES_KANBAN_SLASH_ENABLED=0
35
37
  EnvironmentFile=-$RUNTIME/.env
36
38
  ExecStart=$HERMES_BIN gateway run --replace
37
39
  Restart=on-failure
@@ -1,6 +1,6 @@
1
1
  # shellcheck shell=bash
2
2
  # Ticket-provider adapter dispatcher — the single seam between the heartbeat
3
- # sentinel engine and a concrete ticket system (Linear | Plane | Trello).
3
+ # sentinel engine and a concrete ticket system (Plane | Trello).
4
4
  #
5
5
  # The engine NEVER calls a provider directly. It calls `tp <op> [args...]`,
6
6
  # which dispatches to providers/<provider>.sh. Swapping providers is a one-line
@@ -64,7 +64,7 @@ PY
64
64
  )"
65
65
  [ -n "$name" ] && { printf '%s\n' "$name"; return 0; }
66
66
  fi
67
- printf 'linear\n'
67
+ printf 'plane\n'
68
68
  }
69
69
 
70
70
  # Directory holding provider implementations (sibling of this lib).
@@ -81,6 +81,10 @@ tp() {
81
81
 
82
82
  local name impl
83
83
  name="$(tp_provider_name)"
84
+ case "$name" in
85
+ plane|trello) ;;
86
+ *) echo "tp: unsupported ticket provider '$name' (expected plane|trello)" >&2; return 2 ;;
87
+ esac
84
88
  impl="$(tp_providers_dir)/${name}.sh"
85
89
 
86
90
  if [ ! -f "$impl" ]; then
@@ -13,7 +13,7 @@ actively tries to break the work, surface unmet acceptance criteria, hidden
13
13
  regressions, and drift. On a clean adversarial verdict the loop treats the
14
14
  ticket as done and moves on — through the ticket-provider adapter
15
15
  (`tp transition <id> <state>`) — and emits a BloodBank decision event carrying
16
- the full report. This works identically on Linear, Plane, or Trello.
16
+ the full report. This works identically on Plane or Trello.
17
17
 
18
18
  The operator's verification is **deferred** to end-of-product QA over the review
19
19
  lane, backed by a queryable decision trail. A downstream regression rollback is
@@ -144,7 +144,7 @@ Write `<ISSUE>.review.md`; the script validates it:
144
144
  ```markdown
145
145
  # Autonomous Review Report: <ISSUE>
146
146
  ## Issue
147
- - Linear/Plane/Trello issue: <ISSUE>
147
+ - Provider issue: <ISSUE>
148
148
  - Review lane reason:
149
149
  ## Reviewer
150
150
  - Reviewer agent: <independent-agent-id>
@@ -19,7 +19,7 @@ approve merges.
19
19
  All board access goes through the adapter (`tp`, from
20
20
  `.scripts/lib/ticket-provider.sh`) and reasons in normalized states:
21
21
  `backlog | unstarted | started | in_review | completed`. Never call the provider
22
- directly — the engine is identical across Linear, Plane, and Trello.
22
+ directly — the engine is identical across Plane and Trello.
23
23
 
24
24
  ## Work-state feed
25
25
 
@@ -6,6 +6,11 @@ A cheap systemd heartbeat already decided this full pass is needed.
6
6
  Working repo: the git root containing this role at `agents/hermes/{{ role }}/`.
7
7
  Ticket provider: **{{ ticket_provider }}** (reached only through the adapter — see below).
8
8
 
9
+ The configured ticket provider is the authoritative repo ticket board/SOT for
10
+ this PM. Hermes local Kanban must NOT be an active durable task lifecycle
11
+ surface. Local Kanban may only run as an explicitly configured ephemeral child
12
+ execution queue and must never close/move authoritative repo tickets.
13
+
9
14
  You are the **{{ target_repo }} PM**, running your continuous board-reconciliation
10
15
  pass. Act autonomously, but stay inside the project contracts. Read
11
16
  `agents/hermes/{{ role }}/SOUL.md` and the engine docs under
@@ -27,7 +32,7 @@ tp transition <id> <normalized-state> # backlog|unstarted|started|in_review|com
27
32
  ```
28
33
 
29
34
  Reason in **normalized states**, not provider terms. This pass works identically
30
- on Linear, Plane, or Trello.
35
+ on Plane or Trello.
31
36
 
32
37
  ## Pass
33
38
 
@@ -55,12 +55,18 @@ read it before publishing a type you haven't published before.
55
55
  {% if role == "pm" -%}
56
56
  You are the **project manager**. You triage incoming requests from Telegram /
57
57
  Bloodbank command lanes, decompose them into discrete tasks on the
58
- Plane board, and route work to other agents in the fleet (e.g. the dev role
58
+ configured ticket board, and route work to other agents in the fleet (e.g. the dev role
59
59
  on `bloodbank.cmd.v1.agent.{{ target_repo }}-dev.task.assign`). You do not
60
60
  write application code. You do not approve merges.
61
61
 
62
+ The configured ticket provider (`{{ ticket_provider }}`) is the authoritative
63
+ repo ticket board/SOT for this PM. Hermes local Kanban must NOT be an active
64
+ durable task lifecycle surface. The kanban toolset is removed from the PM's cli
65
+ surface. Local Kanban may only run as an explicitly configured ephemeral child
66
+ execution queue and must never close/move authoritative repo tickets.
67
+
62
68
  Default execution workflow for implementation delivery: use
63
- `subagent-driven-development` in kanban-orchestrated codex mode
69
+ `subagent-driven-development` in provider-ticket-orchestrated implementation mode
64
70
  (WIP=1, spec review gate, quality review gate).
65
71
 
66
72
  Decision events you commonly emit:
@@ -106,7 +112,7 @@ declared` event so the fleet knows what to route to you.
106
112
  - **Hostnames**: Use `*.delo.sh` for external/cross-machine access (resolved
107
113
  via Cloudflare Tunnel), `localhost` for same-host, Docker network service
108
114
  names for container-to-container, Tailscale for private machine-to-machine.
109
- - **Plane**: Always include a Plane ticket reference in commit messages.
115
+ - **Ticketing**: Always include the configured provider's ticket reference in commit messages.
110
116
 
111
117
  ## Memory hygiene
112
118
 
@@ -21,17 +21,14 @@ profile: {{ agent_id }}
21
21
  telegram:
22
22
  bot_username: "{{ bot_handle }}"
23
23
 
24
- # Ticket-provider binding (linear | plane | trello).
24
+ # Ticket-provider binding (plane | trello).
25
25
  # The heartbeat reconciliation pass talks ONLY to this via .scripts/lib/ticket-provider.sh;
26
26
  # swapping providers is a one-line change here. Filled in by 42-ticket-provider.sh.
27
27
  ticket_provider:
28
28
  name: {{ ticket_provider }}
29
29
  board_id: ""
30
30
  board_url: ""
31
- {%- if ticket_provider == 'linear' %}
32
- team: "" # Linear team key, e.g. DEL (set before first sentinel run)
33
- project: "" # optional Linear project name to scope milestones/issues
34
- {%- elif ticket_provider == 'plane' %}
31
+ {%- if ticket_provider == 'plane' %}
35
32
  workspace: "{{ plane_workspace }}"
36
33
  project: "" # Plane project uuid (set by 42-ticket-provider.sh)
37
34
  {%- elif ticket_provider == 'trello' %}
@@ -47,7 +44,7 @@ reconcile:
47
44
  grace_hours: 0 # 0 = no human-approval wait (adversarially-reviewed review-lane = done); set >0 to require a wait
48
45
  auto_review: true # autonomous adversarial review (act, do not wait) on/off
49
46
 
50
- # Plane project (1:1 with this agent) — retained for fleet-registry back-compat.
47
+ # Plane metadata — retained for fleet-registry back-compat when provider=plane.
51
48
  plane:
52
49
  # Empty -> resolved from ~/.config/hermes-agent-template/config.toml [plane].workspace
53
50
  workspace: "{{ plane_workspace }}"
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Create a Plane project in the configured workspace (one project per agent).
3
- # Workspace + base URL come from role.yaml / config.toml (see _lib.sh).
4
- # shellcheck source=_lib.sh
5
- source "$(dirname "$0")/_lib.sh"
6
- load_role_env
7
-
8
- already_done 40-plane && { log "[40] plane already set up — skipping"; exit 0; }
9
- [[ "${SKIP_PLANE:-0}" == "1" ]] && { log "[40] plane — SKIPPED"; mark_done 40-plane; exit 0; }
10
-
11
- [[ -n "$PLANE_API_KEY" ]] || { warn "[40] PLANE_API_KEY not set; skipping. set PLANE_33GOD_API_KEY and re-run ./.scripts/40-plane.sh"; exit 0; }
12
-
13
- # Build the 5-char identifier: <repo[0..2]><role[0..1]> uppercased
14
- RAW=$(printf '%s%s' "${REPO:0:3}" "${ROLE:0:2}" | tr -cd '[:alnum:]' | tr '[:lower:]' '[:upper:]')
15
- while (( ${#RAW} < 3 )); do RAW="${RAW}X"; done
16
- IDENT="${RAW:0:5}"
17
-
18
- # Project name = display_name (already smart-cased: "Bloodbank PM", "Hermes-Agent Dev").
19
- # Plane forbids hyphens in names — substitute spaces.
20
- NAME="${DISPLAY_NAME//-/ }"
21
-
22
- log "[40] creating plane project '$NAME' [$IDENT]"
23
-
24
- # Check for existing project with this identifier
25
- EXISTING=$(curl -sS "$PLANE_BASE/api/v1/workspaces/$PLANE_WORKSPACE/projects/?per_page=200" \
26
- -H "X-API-Key: $PLANE_API_KEY" \
27
- | python3 -c "
28
- import sys, json
29
- d = json.load(sys.stdin)
30
- ident = '$IDENT'
31
- for p in d.get('results', []):
32
- if (p.get('identifier') or '').upper() == ident:
33
- print(p['id']); break")
34
-
35
- if [[ -n "$EXISTING" ]]; then
36
- log " plane project $IDENT already exists (id=$EXISTING) — reusing"
37
- PROJECT_ID="$EXISTING"
38
- else
39
- RESP=$(curl -sS -X POST "$PLANE_BASE/api/v1/workspaces/$PLANE_WORKSPACE/projects/" \
40
- -H "X-API-Key: $PLANE_API_KEY" \
41
- -H "Content-Type: application/json" \
42
- -d "$(python3 -c "import json,sys; print(json.dumps({'name': sys.argv[1], 'identifier': sys.argv[2], 'description': sys.argv[3]}))" \
43
- "$NAME" "$IDENT" "Hermes agent board for $AGENT_ID")")
44
- PROJECT_ID=$(echo "$RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id') or '')")
45
- [[ -n "$PROJECT_ID" ]] || die "plane create failed: $RESP"
46
- log " plane project created id=$PROJECT_ID"
47
- fi
48
-
49
- yaml_set plane.identifier "$IDENT"
50
- echo "$PROJECT_ID" > "$ROLE_DIR/.scripts/.plane-project-id"
51
- mark_done 40-plane