@codyswann/lisa 4.83.2 → 4.84.1
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.
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +6 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +17 -13
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +28 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +39 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +6 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +10 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +145 -18
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +13 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +1 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +70 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +2 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +8 -1
- package/plugins/lisa/rules/reference/config-resolution.md +1 -0
- package/plugins/lisa/scripts/ready-queue-order.mjs +109 -6
- package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +28 -0
- package/plugins/lisa/skills/lisa-session-manager/SKILL.md +39 -0
- package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +6 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +10 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +145 -18
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +13 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +1 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +70 -0
- package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +2 -0
- package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +8 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/ready-queue-order.mjs +109 -6
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +28 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +39 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +6 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +10 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +145 -18
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +13 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +1 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +70 -0
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +2 -0
- package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +8 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +1 -0
- package/plugins/lisa-copilot/scripts/ready-queue-order.mjs +109 -6
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +28 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +39 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +6 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +10 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +145 -18
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +13 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +1 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +70 -0
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +2 -0
- package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +8 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +1 -0
- package/plugins/lisa-cursor/scripts/ready-queue-order.mjs +109 -6
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +28 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +39 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +6 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +10 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +145 -18
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +13 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +1 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +70 -0
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +2 -0
- package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +8 -1
- package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/rules/reference/config-resolution.md +1 -0
- package/plugins/lisa-devin/scripts/ready-queue-order.mjs +109 -6
- package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +28 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +39 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +6 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +10 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +145 -18
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +13 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +1 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +70 -0
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +2 -0
- package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +8 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/src/base/rules/reference/config-resolution.md +1 -0
- package/plugins/src/base/scripts/ready-queue-order.mjs +109 -6
- package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +28 -0
- package/plugins/src/base/skills/lisa-session-manager/SKILL.md +39 -0
- package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +6 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +10 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +145 -18
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +13 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +1 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +70 -0
- package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +2 -0
- package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +8 -1
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +276 -6
package/package.json
CHANGED
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
"qs": "^6.16.0"
|
|
190
190
|
},
|
|
191
191
|
"name": "@codyswann/lisa",
|
|
192
|
-
"version": "4.
|
|
192
|
+
"version": "4.84.1",
|
|
193
193
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
194
194
|
"main": "dist/index.js",
|
|
195
195
|
"exports": {
|
|
@@ -347,7 +347,7 @@
|
|
|
347
347
|
"test": "tests"
|
|
348
348
|
},
|
|
349
349
|
"types": "./dist/index.d.ts",
|
|
350
|
-
"lisaReleaseCommit": "
|
|
351
|
-
"gitHead": "
|
|
352
|
-
"lisaReleaseTag": "v4.
|
|
350
|
+
"lisaReleaseCommit": "f2b00d3c6cbe98a9095d41b62cdd0993b3b8ae67",
|
|
351
|
+
"gitHead": "f2b00d3c6cbe98a9095d41b62cdd0993b3b8ae67",
|
|
352
|
+
"lisaReleaseTag": "v4.84.1"
|
|
353
353
|
}
|
|
@@ -819,10 +819,38 @@ export function validateSessionManager(block) {
|
|
|
819
819
|
]
|
|
820
820
|
: []),
|
|
821
821
|
...validateOperatorIds(block.operatorIds),
|
|
822
|
+
...validateBacklogPickup(block.backlogPickup, block.operatorIds),
|
|
822
823
|
...findCredentials(block, "sessionManager"),
|
|
823
824
|
];
|
|
824
825
|
}
|
|
825
826
|
|
|
827
|
+
/**
|
|
828
|
+
* Validate `sessionManager.backlogPickup`: opt-in promotion of validated
|
|
829
|
+
* backlog items to Ready, assigned to `operatorIds[0]`. Without an operator
|
|
830
|
+
* there is nobody to assign the promoted item to, so it is refused rather
|
|
831
|
+
* than promoting work into the agent's own name.
|
|
832
|
+
* @param {unknown} value The value, if present.
|
|
833
|
+
* @param {unknown} operatorIds `sessionManager.operatorIds`, if present.
|
|
834
|
+
* @returns {string[]} Problems found.
|
|
835
|
+
*/
|
|
836
|
+
function validateBacklogPickup(value, operatorIds) {
|
|
837
|
+
if (value === undefined) return [];
|
|
838
|
+
if (typeof value !== "boolean") {
|
|
839
|
+
return [
|
|
840
|
+
`sessionManager.backlogPickup must be true or false (absent means false).`,
|
|
841
|
+
];
|
|
842
|
+
}
|
|
843
|
+
const hasOperator =
|
|
844
|
+
Array.isArray(operatorIds) &&
|
|
845
|
+
operatorIds.some(id => typeof id === "string" && id.trim() !== "");
|
|
846
|
+
return value && !hasOperator
|
|
847
|
+
? [
|
|
848
|
+
`sessionManager.backlogPickup needs sessionManager.operatorIds: a ` +
|
|
849
|
+
`promoted backlog item is assigned to the first operator.`,
|
|
850
|
+
]
|
|
851
|
+
: [];
|
|
852
|
+
}
|
|
853
|
+
|
|
826
854
|
/**
|
|
827
855
|
* Validate every block this plan introduced.
|
|
828
856
|
* @param {object} cfg Parsed `.lisa.config.json`.
|
|
@@ -216,6 +216,17 @@ Skip while `capacity` reports `paused`. Otherwise:
|
|
|
216
216
|
createdAt, assignee, labels, description, creator and children with their state type.
|
|
217
217
|
- `ordered` is the dispatch order: assigned to an operator, then to "me", then unassigned; each
|
|
218
218
|
group by priority, then age. `excluded` needs no action.
|
|
219
|
+
- **Backlog tier** (only when `sessionManager.backlogPickup` is `true`). GitHub: `queue` fetches
|
|
220
|
+
it itself (open issues with no build or PRD lifecycle label). Jira / Linear: also fetch the
|
|
221
|
+
open leaf items in **no** lifecycle role — Linear workflow state type `backlog` or `unstarted`
|
|
222
|
+
other than the configured `ready` state; Jira status category "To Do" other than the configured
|
|
223
|
+
`ready` status — paged to completion, including each item's `updatedAt` / `updated` and its
|
|
224
|
+
blockers (Linear `inverseRelations` with `type` and the blocking issue's `state.type`; Jira
|
|
225
|
+
`issuelinks`). Put them in the same input file as
|
|
226
|
+
`"backlog":{"complete":true,"pages":<n>,"items":[...]}`; `queue` refuses a missing or partial
|
|
227
|
+
backlog envelope while the key is on. It returns `backlogOrdered` (same grouping and order,
|
|
228
|
+
minus items with an unfinished blocker and items whose cached validation failure still matches
|
|
229
|
+
their `updatedAt`) and `backlogExcluded`.
|
|
219
230
|
2. **While `capacity` says `can_spawn`** and items remain, take the next item that has no pending
|
|
220
231
|
question in the ledger:
|
|
221
232
|
1. Read it fully (`lisa-tracker-read`): title, description, labels, project, parent, comments.
|
|
@@ -246,6 +257,34 @@ Skip while `capacity` reports `paused`. Otherwise:
|
|
|
246
257
|
`recovery-required` with that message as the summary. It is not a failed attempt; do not
|
|
247
258
|
dispatch to that repo again until the operator fixes it.
|
|
248
259
|
- any other exit: move the item back to the `ready` role, notify Telegram, continue.
|
|
260
|
+
3. **Backlog tier, lowest priority.** Only when `backlogPickup` is on, every `ordered` item has been
|
|
261
|
+
taken or skipped, and `capacity` still says `can_spawn`. Ready work always wins: never promote a
|
|
262
|
+
backlog item while a dispatchable Ready item remains. For the next `backlogOrdered` item with no
|
|
263
|
+
pending question in the ledger:
|
|
264
|
+
1. Read it fully (`lisa-tracker-read`). If it gained an assignee who is neither "me" nor an
|
|
265
|
+
operator, a lifecycle role, open child work, a human hold or an unfinished blocker since the
|
|
266
|
+
queue was listed, skip it.
|
|
267
|
+
2. **Validate it** with `lisa-tracker-validate` against the live item (identifier mode, all
|
|
268
|
+
gates, including the stateless-pickup dry-run). This is what "can be taken to completion"
|
|
269
|
+
means. On a definitive `FAIL`, record
|
|
270
|
+
`ledger validation-failed <ref> --updated-at <the item's updatedAt>` with a one-line summary
|
|
271
|
+
of the failing gates on STDIN, and skip it; the queue leaves it out until the item changes.
|
|
272
|
+
On could-not-validate (an outage or unreadable item, not a verdict), skip it WITHOUT
|
|
273
|
+
recording, so the next cycle retries it. Either way, post nothing to Telegram: backlog
|
|
274
|
+
grooming is not the operator's inbox.
|
|
275
|
+
3. **Route the repo** as in step 2.2, but never ask: an ambiguous or multi-repo backlog item is
|
|
276
|
+
skipped.
|
|
277
|
+
4. **Promote** in ONE `lisa-tracker-write` update: assignee = the first `operatorIds` entry, move
|
|
278
|
+
to the `ready` role, comment
|
|
279
|
+
`[lisa-session-manager] Promoted from backlog: validation passed; assigned to the operator.`
|
|
280
|
+
Read it back with `lisa-tracker-read` and continue only when the role is `ready` and the
|
|
281
|
+
assignee is that operator. Then `ledger validation-cleared <ref>`.
|
|
282
|
+
5. Continue with steps 2.3 and 2.4 (claim in the child repo, re-read, spawn, handle the exit)
|
|
283
|
+
exactly as for a Ready item, with one change: on exit 0, post
|
|
284
|
+
`[<REF>] promoted from backlog and started in <repo> (<name>)` in place of the plain start
|
|
285
|
+
notice. Every other exit keeps its step 2.4 handling.
|
|
286
|
+
A promotion written and read back counts as a state change for the run outcome; a spawn that
|
|
287
|
+
then fails follows step 2.4's exits (the item stays in Ready, assigned to the operator).
|
|
249
288
|
|
|
250
289
|
### 3. Report and record
|
|
251
290
|
|
package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md
CHANGED
|
@@ -23,6 +23,12 @@ then to the manager's own tracker identity, then unassigned; within each, by pri
|
|
|
23
23
|
Items assigned to someone else, containers, human-held items and (when configured) items by
|
|
24
24
|
untrusted authors are skipped. As many as the machine has capacity for.
|
|
25
25
|
|
|
26
|
+
When `sessionManager.backlogPickup` is on and capacity remains after every Ready item, a lowest tier
|
|
27
|
+
follows: backlog items (open leaves in no lifecycle role) in the same order, minus those with an
|
|
28
|
+
unfinished blocker and those that already failed validation and have not changed since. Each one
|
|
29
|
+
must pass the full tracker validation before it is assigned to the first operator, moved to Ready
|
|
30
|
+
and dispatched like any other item.
|
|
31
|
+
|
|
26
32
|
## Scope/bounds
|
|
27
33
|
|
|
28
34
|
Only the repos listed in `sessionManager.repos` (or this repo). It never writes code itself, never
|
|
@@ -24,9 +24,11 @@ import {
|
|
|
24
24
|
import { parseJson, readJsonFile } from "./cli-queue.mjs";
|
|
25
25
|
import {
|
|
26
26
|
clearPendingQuestion,
|
|
27
|
+
clearValidationFailure,
|
|
27
28
|
readLedger,
|
|
28
29
|
recordFinish,
|
|
29
30
|
recordSpawn,
|
|
31
|
+
recordValidationFailure,
|
|
30
32
|
setControl,
|
|
31
33
|
setPendingQuestion,
|
|
32
34
|
} from "./ledger.mjs";
|
|
@@ -130,6 +132,14 @@ const LEDGER_ACTIONS = {
|
|
|
130
132
|
reason: stdinText(ctx, "reason", { required: false }),
|
|
131
133
|
});
|
|
132
134
|
},
|
|
135
|
+
"validation-failed": ctx =>
|
|
136
|
+
recordValidationFailure(ctx.root, {
|
|
137
|
+
ref: requirePositional(ctx, "ref"),
|
|
138
|
+
updatedAt: requireValue(ctx, "updated-at"),
|
|
139
|
+
reason: stdinText(ctx, "reason", { required: false }),
|
|
140
|
+
}),
|
|
141
|
+
"validation-cleared": ctx =>
|
|
142
|
+
clearValidationFailure(ctx.root, { ref: requirePositional(ctx, "ref") }),
|
|
133
143
|
control: ctx =>
|
|
134
144
|
setControl(
|
|
135
145
|
ctx.root,
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* `{complete, pages, items}`; anything but a literal `complete: true` is
|
|
10
10
|
* refused with exit 2, because ordering a partial queue would dispatch the
|
|
11
11
|
* wrong item with full confidence.
|
|
12
|
+
*
|
|
13
|
+
* With `sessionManager.backlogPickup` on, `queue` also ranks the backlog tier
|
|
14
|
+
* (open leaves in no lifecycle role) as `backlogOrdered`, under the same
|
|
15
|
+
* paging rule. The manager walks it only after the whole Ready queue.
|
|
12
16
|
* @module session-manager/cli-queue
|
|
13
17
|
*/
|
|
14
18
|
import { readFileSync } from "node:fs";
|
|
@@ -18,6 +22,7 @@ import {
|
|
|
18
22
|
normalizeGithubIssue,
|
|
19
23
|
normalizeJiraIssue,
|
|
20
24
|
normalizeLinearIssue,
|
|
25
|
+
orderBacklogQueue,
|
|
21
26
|
orderReadyQueue,
|
|
22
27
|
} from "../../../scripts/ready-queue-order.mjs";
|
|
23
28
|
import { resolveRole } from "../../../scripts/resolve-lifecycle-role.mjs";
|
|
@@ -61,12 +66,12 @@ export function readJsonFile(file) {
|
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
/**
|
|
64
|
-
*
|
|
69
|
+
* The GitHub repo to read: `--github-repo`, else github.org/github.repo.
|
|
65
70
|
* @param {object} ctx - CLI context
|
|
66
71
|
* @param {ReturnType<typeof loadProject>} project - Loaded project
|
|
67
|
-
* @returns {
|
|
72
|
+
* @returns {string} `owner/repo`
|
|
68
73
|
*/
|
|
69
|
-
function
|
|
74
|
+
function githubRepo(ctx, project) {
|
|
70
75
|
const merged = { ...project.global?.github, ...project.local?.github };
|
|
71
76
|
const repo =
|
|
72
77
|
ctx.values["github-repo"] ??
|
|
@@ -77,6 +82,68 @@ function fetchGithubQueue(ctx, project) {
|
|
|
77
82
|
2
|
|
78
83
|
);
|
|
79
84
|
}
|
|
85
|
+
return repo;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Every string leaf of an object (label names, including env-keyed `done`).
|
|
90
|
+
* @param {unknown} value - Config subtree
|
|
91
|
+
* @returns {string[]} Leaves
|
|
92
|
+
*/
|
|
93
|
+
function stringLeaves(value) {
|
|
94
|
+
if (typeof value === "string") return value ? [value] : [];
|
|
95
|
+
if (!value || typeof value !== "object") return [];
|
|
96
|
+
return Object.values(value).flatMap(stringLeaves);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Fetch every open GitHub issue carrying no lifecycle label (build or PRD),
|
|
101
|
+
* all pages: the GitHub backlog tier.
|
|
102
|
+
* @param {object} ctx - CLI context
|
|
103
|
+
* @param {ReturnType<typeof loadProject>} project - Loaded project
|
|
104
|
+
* @returns {{ items: object[], pages: number }} Normalized items
|
|
105
|
+
*/
|
|
106
|
+
function fetchGithubBacklog(ctx, project) {
|
|
107
|
+
const repo = githubRepo(ctx, project);
|
|
108
|
+
const lifecycle = new Set(
|
|
109
|
+
[project.global?.github?.labels, project.local?.github?.labels]
|
|
110
|
+
.flatMap(labels => [
|
|
111
|
+
...stringLeaves(labels?.build),
|
|
112
|
+
...stringLeaves(labels?.prd),
|
|
113
|
+
])
|
|
114
|
+
.map(label => label.toLowerCase())
|
|
115
|
+
);
|
|
116
|
+
const stdout = mustRun(
|
|
117
|
+
"gh",
|
|
118
|
+
[
|
|
119
|
+
"api",
|
|
120
|
+
"--paginate",
|
|
121
|
+
"--slurp",
|
|
122
|
+
`repos/${repo}/issues?state=open&per_page=100`,
|
|
123
|
+
],
|
|
124
|
+
{ env: scrubWorkerEnv(ctx.env) }
|
|
125
|
+
);
|
|
126
|
+
const pages = parseJson(stdout, "gh api output");
|
|
127
|
+
const rows = Array.isArray(pages) ? pages.flat() : [];
|
|
128
|
+
return {
|
|
129
|
+
items: rows
|
|
130
|
+
.map(row => normalizeGithubIssue(row, { repo }))
|
|
131
|
+
.filter(Boolean)
|
|
132
|
+
.filter(
|
|
133
|
+
item => !item.labels.some(label => lifecycle.has(label.toLowerCase()))
|
|
134
|
+
),
|
|
135
|
+
pages: Array.isArray(pages) ? pages.length : 0,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Fetch every open GitHub issue carrying the ready label, all pages.
|
|
141
|
+
* @param {object} ctx - CLI context
|
|
142
|
+
* @param {ReturnType<typeof loadProject>} project - Loaded project
|
|
143
|
+
* @returns {{ items: object[], pages: number }} Normalized items
|
|
144
|
+
*/
|
|
145
|
+
function fetchGithubQueue(ctx, project) {
|
|
146
|
+
const repo = githubRepo(ctx, project);
|
|
80
147
|
const role = resolveRole({
|
|
81
148
|
role: "ready",
|
|
82
149
|
vendor: "github",
|
|
@@ -101,24 +168,51 @@ function fetchGithubQueue(ctx, project) {
|
|
|
101
168
|
}
|
|
102
169
|
|
|
103
170
|
/**
|
|
104
|
-
*
|
|
105
|
-
* @param {
|
|
171
|
+
* Check one paged envelope and normalize its raw issues.
|
|
172
|
+
* @param {unknown} envelope - `{complete, pages, items}`
|
|
173
|
+
* @param {string} where - Where it came from, for messages
|
|
106
174
|
* @param {"jira"|"linear"} tracker - Vendor
|
|
107
175
|
* @returns {{ items: object[], pages: unknown }} Normalized items
|
|
108
176
|
*/
|
|
109
|
-
function
|
|
177
|
+
function normalizeEnvelope(envelope, where, tracker) {
|
|
178
|
+
asSetupError(() => assertPagedToCompletion(envelope));
|
|
179
|
+
if (!Array.isArray(envelope.items)) {
|
|
180
|
+
throw new CliError(`${where}: items must be an array.`, 2);
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
items: envelope.items.map(NORMALIZERS[tracker]),
|
|
184
|
+
pages: envelope.pages ?? null,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Read a vendor-skill envelope and normalize its raw issues. With
|
|
190
|
+
* `withBacklog`, the envelope must also carry `backlog`, a second
|
|
191
|
+
* `{complete, pages, items}` envelope held to the same paging rule.
|
|
192
|
+
* @param {object} ctx - CLI context
|
|
193
|
+
* @param {"jira"|"linear"} tracker - Vendor
|
|
194
|
+
* @param {boolean} withBacklog - Whether the backlog tier is required
|
|
195
|
+
* @returns {{ items: object[], pages: unknown, backlog: { items: object[], pages: unknown } | null }} Normalized items
|
|
196
|
+
*/
|
|
197
|
+
function readEnvelopeQueue(ctx, tracker, withBacklog) {
|
|
110
198
|
const file = ctx.values.input;
|
|
111
199
|
if (!file) {
|
|
112
200
|
throw new CliError(`queue --tracker ${tracker} needs --input <file>.`, 2);
|
|
113
201
|
}
|
|
114
202
|
const envelope = readJsonFile(file);
|
|
115
|
-
|
|
116
|
-
if (!
|
|
117
|
-
|
|
203
|
+
const ready = normalizeEnvelope(envelope, file, tracker);
|
|
204
|
+
if (!withBacklog) return { ...ready, backlog: null };
|
|
205
|
+
if (!envelope.backlog) {
|
|
206
|
+
throw new CliError(
|
|
207
|
+
`${file}: sessionManager.backlogPickup is on, so the input needs a ` +
|
|
208
|
+
`"backlog" envelope {complete:true,pages,items} of open leaf items ` +
|
|
209
|
+
`in no lifecycle role.`,
|
|
210
|
+
2
|
|
211
|
+
);
|
|
118
212
|
}
|
|
119
213
|
return {
|
|
120
|
-
|
|
121
|
-
|
|
214
|
+
...ready,
|
|
215
|
+
backlog: normalizeEnvelope(envelope.backlog, `${file} backlog`, tracker),
|
|
122
216
|
};
|
|
123
217
|
}
|
|
124
218
|
|
|
@@ -126,8 +220,12 @@ function readEnvelopeQueue(ctx, tracker) {
|
|
|
126
220
|
* `queue --tracker <github|jira|linear>`: the Ready queue, the operator's
|
|
127
221
|
* items first (`sessionManager.operatorIds`), then mine, then unassigned.
|
|
128
222
|
* @param {object} ctx - CLI context
|
|
223
|
+
* With `sessionManager.backlogPickup` on it also returns the backlog tier,
|
|
224
|
+
* ordered the same way and always dispatched after every Ready item.
|
|
225
|
+
* @param {object} ctx - CLI context
|
|
129
226
|
* @returns {Promise<{ json: object }>}
|
|
130
|
-
* `{tracker,me,operators,total,pages,ordered,excluded
|
|
227
|
+
* `{tracker,me,operators,total,pages,ordered,excluded,backlogPickup[,
|
|
228
|
+
* backlogTotal,backlogPages,backlogOrdered,backlogExcluded]}`
|
|
131
229
|
*/
|
|
132
230
|
export async function queue(ctx) {
|
|
133
231
|
const tracker = ctx.values.tracker;
|
|
@@ -136,19 +234,48 @@ export async function queue(ctx) {
|
|
|
136
234
|
}
|
|
137
235
|
const project = loadProject(ctx.root);
|
|
138
236
|
const config = validOrAbsent(project);
|
|
139
|
-
const
|
|
237
|
+
const backlogPickup = config?.backlogPickup === true;
|
|
238
|
+
const { items, pages, backlog } =
|
|
140
239
|
tracker === "github"
|
|
141
|
-
?
|
|
142
|
-
|
|
143
|
-
|
|
240
|
+
? {
|
|
241
|
+
...fetchGithubQueue(ctx, project),
|
|
242
|
+
backlog: backlogPickup ? fetchGithubBacklog(ctx, project) : null,
|
|
243
|
+
}
|
|
244
|
+
: readEnvelopeQueue(ctx, tracker, backlogPickup);
|
|
245
|
+
const ledger = await readLedger(ctx.root);
|
|
246
|
+
const me = ctx.values.me ?? ledger.control.me ?? null;
|
|
144
247
|
const operators = config?.operatorIds ?? [];
|
|
145
|
-
const
|
|
248
|
+
const policy = {
|
|
146
249
|
me,
|
|
147
250
|
operators,
|
|
148
251
|
trustedAuthorIds: config?.trustedAuthorIds ?? undefined,
|
|
149
252
|
humanNeededLabel: humanNeededLabel(project, tracker),
|
|
253
|
+
};
|
|
254
|
+
const order = orderReadyQueue(items, policy);
|
|
255
|
+
const json = {
|
|
256
|
+
tracker,
|
|
257
|
+
me,
|
|
258
|
+
operators,
|
|
259
|
+
total: items.length,
|
|
260
|
+
pages,
|
|
261
|
+
...order,
|
|
262
|
+
backlogPickup,
|
|
263
|
+
};
|
|
264
|
+
if (!backlog) return { json };
|
|
265
|
+
// An item both Ready and in the backlog fetch is Ready work; never twice.
|
|
266
|
+
const readyRefs = new Set(items.map(item => item.ref));
|
|
267
|
+
const backlogItems = backlog.items.filter(item => !readyRefs.has(item.ref));
|
|
268
|
+
const tier = orderBacklogQueue(backlogItems, {
|
|
269
|
+
...policy,
|
|
270
|
+
validationFailures: ledger.validationFailures,
|
|
150
271
|
});
|
|
151
272
|
return {
|
|
152
|
-
json: {
|
|
273
|
+
json: {
|
|
274
|
+
...json,
|
|
275
|
+
backlogTotal: backlogItems.length,
|
|
276
|
+
backlogPages: backlog.pages,
|
|
277
|
+
backlogOrdered: tier.ordered,
|
|
278
|
+
backlogExcluded: tier.excluded,
|
|
279
|
+
},
|
|
153
280
|
};
|
|
154
281
|
}
|
|
@@ -117,6 +117,13 @@ Per cycle
|
|
|
117
117
|
first, then to --me, then unassigned; each group by priority, then age.
|
|
118
118
|
Items assigned to anyone else are excluded (assigned-to-other).
|
|
119
119
|
--me defaults to the ledger's control.me.
|
|
120
|
+
With sessionManager.backlogPickup true, also prints backlogTotal,
|
|
121
|
+
backlogPages, backlogOrdered and backlogExcluded: open leaves in no
|
|
122
|
+
lifecycle role, same order, always after every ready item; excluded
|
|
123
|
+
also for blocked-by-open-item and failed-validation-unchanged (the
|
|
124
|
+
ledger's validationFailures entry matches the item's updatedAt).
|
|
125
|
+
GitHub fetches them itself; jira|linear need <file>.backlog =
|
|
126
|
+
{complete:true,pages,items}, else exit 2.
|
|
120
127
|
ledger get [<ref>]
|
|
121
128
|
ledger record-spawn <ref> --worker <name> [--session-id <id>] [--repo <key>]
|
|
122
129
|
ledger set-question <ref> [--telegram-message-id <n>] [--sender <name>]
|
|
@@ -124,6 +131,12 @@ Per cycle
|
|
|
124
131
|
ledger clear-question <ref>
|
|
125
132
|
ledger finish <ref> --status DONE|FAILED [--sender <name>] STDIN: reason
|
|
126
133
|
--sender: exit 2 unless the ledger's worker for <ref> is exactly <name>.
|
|
134
|
+
ledger validation-failed <ref> --updated-at <ts> STDIN: failing gates
|
|
135
|
+
Caches a backlog item's failed validation; queue skips it until the
|
|
136
|
+
item's updatedAt changes.
|
|
137
|
+
ledger validation-cleared <ref>
|
|
138
|
+
Forgets that failure: after a promotion, or by hand to have the next
|
|
139
|
+
cycle re-validate an item without editing it. stdout {cleared}.
|
|
127
140
|
ledger control [--paused true|false] [--max-workers <n>|none] [--me <id>|none]
|
|
128
141
|
route [--reply-to <telegram message id>] [--owner <name>] STDIN: message
|
|
129
142
|
stdout {kind:"answer",ref,body[,message]} | {kind:"command",name,...}
|
|
@@ -81,6 +81,17 @@ const FINISH_STATUSES = new Set(["DONE", "FAILED"]);
|
|
|
81
81
|
* @property {number} version - Shape version
|
|
82
82
|
* @property {Record<string, LedgerItem>} items - Items keyed by ref
|
|
83
83
|
* @property {{paused: boolean, maxWorkers: number|null, me: string|null}} control - Operator controls
|
|
84
|
+
* @property {Record<string, ValidationFailure>} validationFailures - Backlog
|
|
85
|
+
* items that failed validation, keyed by ref, so an unchanged item is not
|
|
86
|
+
* re-validated every cycle
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A backlog item's last failed validation.
|
|
91
|
+
* @typedef {object} ValidationFailure
|
|
92
|
+
* @property {string} updatedAt - The item's tracker `updatedAt` when it failed
|
|
93
|
+
* @property {string} at - When the failure was recorded (ISO)
|
|
94
|
+
* @property {string|null} reason - Short summary of the failing gates
|
|
84
95
|
*/
|
|
85
96
|
|
|
86
97
|
/**
|
|
@@ -119,6 +130,7 @@ function emptyLedger() {
|
|
|
119
130
|
version: LEDGER_VERSION,
|
|
120
131
|
items: {},
|
|
121
132
|
control: { paused: false, maxWorkers: null, me: null },
|
|
133
|
+
validationFailures: {},
|
|
122
134
|
};
|
|
123
135
|
}
|
|
124
136
|
|
|
@@ -159,6 +171,7 @@ export async function readLedger(projectRoot, seams) {
|
|
|
159
171
|
version: parsed.version ?? empty.version,
|
|
160
172
|
items: parsed.items ?? {},
|
|
161
173
|
control: { ...empty.control, ...parsed.control },
|
|
174
|
+
validationFailures: parsed.validationFailures ?? {},
|
|
162
175
|
};
|
|
163
176
|
}
|
|
164
177
|
|
|
@@ -384,6 +397,63 @@ export function recordFinish(projectRoot, { ref, status, reason }, seams) {
|
|
|
384
397
|
});
|
|
385
398
|
}
|
|
386
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Remember that a backlog item failed validation at its current tracker
|
|
402
|
+
* `updatedAt`. The backlog queue skips it until the item changes, so an
|
|
403
|
+
* unready item costs one validation per edit, not one per cycle.
|
|
404
|
+
* @param {string} projectRoot - Absolute project root
|
|
405
|
+
* @param {{ref: string, updatedAt: string, reason?: string|null}} failure - What failed
|
|
406
|
+
* @param {LedgerSeams} [seams] - Injected seams
|
|
407
|
+
* @returns {Promise<ValidationFailure>} The stored record
|
|
408
|
+
*/
|
|
409
|
+
export function recordValidationFailure(
|
|
410
|
+
projectRoot,
|
|
411
|
+
{ ref, updatedAt, reason = null },
|
|
412
|
+
seams
|
|
413
|
+
) {
|
|
414
|
+
if (typeof ref !== "string" || ref === "") {
|
|
415
|
+
return Promise.reject(new Error("validation failure needs a ref"));
|
|
416
|
+
}
|
|
417
|
+
if (typeof updatedAt !== "string" || updatedAt === "") {
|
|
418
|
+
return Promise.reject(
|
|
419
|
+
new Error(
|
|
420
|
+
"validation failure needs the item's updatedAt; without it the cache could never expire"
|
|
421
|
+
)
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
return updateLedger(projectRoot, seams, (ledger, at) => {
|
|
425
|
+
const entry = { updatedAt, at, reason: reason || null };
|
|
426
|
+
return {
|
|
427
|
+
ledger: {
|
|
428
|
+
...ledger,
|
|
429
|
+
validationFailures: { ...ledger.validationFailures, [ref]: entry },
|
|
430
|
+
},
|
|
431
|
+
result: entry,
|
|
432
|
+
};
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Forget a backlog item's validation failure (it was promoted, or a human
|
|
438
|
+
* asked for it to be judged again).
|
|
439
|
+
* @param {string} projectRoot - Absolute project root
|
|
440
|
+
* @param {{ref: string}} target - Item to forget
|
|
441
|
+
* @param {LedgerSeams} [seams] - Injected seams
|
|
442
|
+
* @returns {Promise<{cleared: boolean}>} Whether a record existed
|
|
443
|
+
*/
|
|
444
|
+
export function clearValidationFailure(projectRoot, { ref }, seams) {
|
|
445
|
+
return updateLedger(projectRoot, seams, ledger => {
|
|
446
|
+
if (!Object.hasOwn(ledger.validationFailures, ref)) {
|
|
447
|
+
return { ledger, result: { cleared: false } };
|
|
448
|
+
}
|
|
449
|
+
const { [ref]: _removed, ...rest } = ledger.validationFailures;
|
|
450
|
+
return {
|
|
451
|
+
ledger: { ...ledger, validationFailures: rest },
|
|
452
|
+
result: { cleared: true },
|
|
453
|
+
};
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
387
457
|
/**
|
|
388
458
|
* Check a control patch before it is written.
|
|
389
459
|
* @param {{paused?: unknown, maxWorkers?: unknown, me?: unknown}} patch - Patch
|
|
@@ -130,6 +130,8 @@ const LEDGER_SPECS = {
|
|
|
130
130
|
},
|
|
131
131
|
"clear-question": { options: {}, positionals: 1 },
|
|
132
132
|
finish: { options: { status: text, sender: text }, positionals: 1 },
|
|
133
|
+
"validation-failed": { options: { "updated-at": text }, positionals: 1 },
|
|
134
|
+
"validation-cleared": { options: {}, positionals: 1 },
|
|
133
135
|
control: {
|
|
134
136
|
options: { paused: text, "max-workers": text, me: text },
|
|
135
137
|
positionals: 0,
|
|
@@ -70,7 +70,14 @@ writes `$SM`, and pass `--project-root "$ROOT"` (the absolute project root) on e
|
|
|
70
70
|
"me", spelled as the tracker API returns it: the GitHub **login** (`gh api users/<login> --jq
|
|
71
71
|
.login`), the Jira `accountId`, or the Linear user id (the operator's entry in the team's member
|
|
72
72
|
list, or `viewer.id` only when the operator runs the lookup with their own credential).
|
|
73
|
-
5.
|
|
73
|
+
5. Ask whether to set `sessionManager.backlogPickup` to `true` (default off). When the Ready queue
|
|
74
|
+
is exhausted and a worker slot is still free, the manager then looks at backlog items (open
|
|
75
|
+
leaves in no lifecycle role) that nobody else is assigned to, that are not human-held and have
|
|
76
|
+
no unfinished blocker. Each one that passes the full tracker validation, including the
|
|
77
|
+
stateless-pickup check, is assigned to the first `operatorIds` entry, moved to Ready and
|
|
78
|
+
dispatched. Say plainly that this lets the manager start work nobody marked Ready, so it needs
|
|
79
|
+
`operatorIds` and should go with `trustedAuthorIds` on any tracker outsiders can write to.
|
|
80
|
+
6. `node "$SM/session-manager.mjs" validate --project-root "$ROOT"` must print `valid: true`.
|
|
74
81
|
|
|
75
82
|
## Step 3: Store the bot token
|
|
76
83
|
|
|
@@ -735,6 +735,7 @@ have a `.lisa.config.json` and their own `.git` (written as `"root": "."`).
|
|
|
735
735
|
| `sessionManager.notify.stateDir` | **yes** | — | This project's own Telegram state dir. Never the shared default `~/.claude/channels/telegram`, because one bot per state dir is how projects stay isolated. Setup writes `~/.claude/channels/telegram-<project>`. |
|
|
736
736
|
| `sessionManager.trustedAuthorIds` | no | — | Optional list of **stable tracker user ids**, as strings: the GitHub numeric user id (`gh api users/<login> --jq .id`), the Jira `accountId`, or the Linear user id. Not logins or display names: matching compares ids, so a login silently matches no one and excludes every item. When set, the manager takes only items authored by these people. |
|
|
737
737
|
| `sessionManager.operatorIds` | no | — | Optional, non-empty list of the people the manager works for, as tracker user ids matched against the **assignee** the same way "me" is: the GitHub **login**, the Jira `accountId`, or the Linear user id, spelled exactly as the tracker API returns it (matching is case-sensitive, so look the OPERATOR up rather than typing from memory: `gh api users/<operator-login> --jq .login` returns the canonical GitHub spelling. Do not use `gh api user`, which returns whoever the credential belongs to — the agent account, when that is the case this key exists for). Dispatch order is assigned to an operator, then to "me" (the tracker credential's identity), then unassigned; items assigned to anyone else are skipped. Set it whenever the tracker credential is not the operator's own (an agent account), or the operator's Ready items are never dispatched. |
|
|
738
|
+
| `sessionManager.backlogPickup` | no | `false` | Opt-in lowest dispatch tier. When the Ready queue is exhausted and capacity remains, the manager considers backlog items (open leaves in no lifecycle role) with the Ready exclusions plus any unfinished blocker; each one that passes the tracker validator (all gates, including the stateless-pickup dry-run) is assigned to the first `operatorIds` entry, moved to Ready in one write, read back and dispatched. A failed validation is cached against the item's `updatedAt`, so it is re-judged only after the item changes. Requires `operatorIds`. |
|
|
738
739
|
|
|
739
740
|
- **Non-secret only.** The bot token lives in `<stateDir>/.env` (directory 0700, file 0600),
|
|
740
741
|
written by setup. A credential-named field or a token-shaped value anywhere in this block is a
|