@catalyst-cloud/cli 0.13.0 → 0.13.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.
Files changed (137) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +26 -29
  3. package/dist/args.js +16 -0
  4. package/dist/ask.js +30 -18
  5. package/dist/capabilities.js +5 -0
  6. package/dist/cli.js +18 -5
  7. package/dist/environment.js +1 -1
  8. package/dist/legacy.js +224 -0
  9. package/dist/project.js +129 -0
  10. package/dist/release.js +1 -1
  11. package/dist/repo.js +297 -0
  12. package/dist/runtime-verb.js +1 -1
  13. package/dist/secret.js +1 -1
  14. package/dist/skill-shape.js +21 -13
  15. package/dist/skills.js +44 -4
  16. package/dist/transport.js +26 -1
  17. package/package.json +5 -4
  18. package/skills/catalyst-github/SKILL.md +7 -7
  19. package/skills/catalyst-github/references/is-it-mergeable.md +8 -2
  20. package/skills/catalyst-github/references/what-a-pr-accumulates.md +3 -3
  21. package/skills/catalyst-github/scripts/is-it-mergeable.mjs +3 -3
  22. package/skills/catalyst-github/scripts/lib/cli.mjs +9 -7
  23. package/skills/catalyst-github/scripts/lib/credential.mjs +2 -2
  24. package/skills/catalyst-github/scripts/read-pr.mjs +2 -2
  25. package/skills/catalyst-linear/SKILL.md +17 -19
  26. package/skills/catalyst-linear/agents/openai.yaml +1 -1
  27. package/skills/catalyst-linear/agents/portability.yaml +1 -1
  28. package/skills/catalyst-linear/references/reading-a-ticket.md +9 -26
  29. package/skills/catalyst-linear/references/what-a-ticket-accumulates.md +2 -2
  30. package/skills/catalyst-linear/references/writing-to-linear.md +7 -9
  31. package/skills/catalyst-linear/scripts/comment.mjs +1 -1
  32. package/skills/catalyst-linear/scripts/create-ticket.mjs +1 -1
  33. package/skills/catalyst-linear/scripts/label.mjs +1 -1
  34. package/skills/catalyst-linear/scripts/lib/cli.mjs +9 -7
  35. package/skills/catalyst-linear/scripts/lib/credential.mjs +2 -2
  36. package/skills/catalyst-linear/scripts/move.mjs +1 -1
  37. package/skills/catalyst-linear/scripts/read-ticket.mjs +1 -1
  38. package/skills/catalyst-linear/scripts/search.mjs +1 -1
  39. package/skills/catalyst-onboard/SKILL.md +37 -57
  40. package/skills/catalyst-onboard/agents/openai.yaml +2 -2
  41. package/skills/catalyst-onboard/agents/portability.yaml +1 -1
  42. package/skills/catalyst-onboard/references/connecting-this-machine.md +35 -0
  43. package/skills/catalyst-onboard/references/declaring-a-repository.md +7 -17
  44. package/skills/catalyst-onboard/references/local-sync.md +19 -7
  45. package/skills/catalyst-onboard/references/reading-ready.md +79 -0
  46. package/skills/catalyst-onboard/references/replacing-a-credential.md +8 -33
  47. package/skills/catalyst-onboard/references/repository-agent-setup.md +23 -0
  48. package/skills/catalyst-onboard/references/skill-sources.md +13 -19
  49. package/skills/catalyst-onboard/references/the-one-path.md +18 -74
  50. package/skills/catalyst-onboard/references/what-a-phase-needs.md +21 -28
  51. package/skills/catalyst-onboard/references/what-the-browser-owns.md +20 -37
  52. package/skills/{catalyst-setup → catalyst-onboard}/scripts/check.mjs +10 -9
  53. package/skills/catalyst-onboard/scripts/lib/cli.mjs +6 -4
  54. package/skills/catalyst-onboard/scripts/lib/credential.mjs +2 -2
  55. package/skills/catalyst-onboard/scripts/where-am-i.mjs +60 -4
  56. package/skills/run-this-project/SKILL.md +23 -25
  57. package/skills/run-this-project/agents/openai.yaml +1 -1
  58. package/skills/run-this-project/agents/portability.yaml +1 -1
  59. package/skills/run-this-project/references/making-work-ready.md +20 -37
  60. package/skills/run-this-project/references/reacting-to-events.md +25 -48
  61. package/skills/run-this-project/references/stalls-and-escalation.md +20 -42
  62. package/skills/run-this-project/scripts/lib/cli.mjs +7 -5
  63. package/skills/run-this-project/scripts/lib/credential.mjs +2 -2
  64. package/skills/run-this-project/scripts/scope-status.mjs +0 -0
  65. package/skills/run-this-project/scripts/watch-scope.mjs +1 -1
  66. package/skills/unstick/SKILL.md +12 -12
  67. package/skills/unstick/agents/openai.yaml +1 -1
  68. package/skills/unstick/agents/portability.yaml +1 -1
  69. package/skills/unstick/references/playbook.md +13 -13
  70. package/skills/unstick/scripts/lib/cli.mjs +6 -4
  71. package/skills/unstick/scripts/lib/credential.mjs +2 -2
  72. package/skills/unstick/scripts/unstick.mjs +1 -1
  73. package/skills/what-needs-me/SKILL.md +12 -14
  74. package/skills/what-needs-me/agents/openai.yaml +2 -2
  75. package/skills/what-needs-me/agents/portability.yaml +1 -1
  76. package/skills/what-needs-me/references/raising-a-decision.md +8 -16
  77. package/skills/what-needs-me/references/reading-the-inbox.md +6 -13
  78. package/skills/what-needs-me/references/settling-an-answer.md +6 -12
  79. package/skills/what-needs-me/scripts/inbox.mjs +14 -13
  80. package/skills/what-needs-me/scripts/lib/cli.mjs +6 -4
  81. package/skills/what-needs-me/scripts/lib/credential.mjs +2 -2
  82. package/skills/what-needs-me/scripts/raise.mjs +1 -1
  83. package/skills/what-needs-me/scripts/settle.mjs +1 -1
  84. package/skills/what-this-repo-needs/SKILL.md +10 -19
  85. package/skills/what-this-repo-needs/references/what-each-group-means.md +14 -44
  86. package/skills/what-this-repo-needs/scripts/check.mjs +2 -2
  87. package/skills/what-this-repo-needs/scripts/inventory.mjs +2 -2
  88. package/skills/what-this-repo-needs/scripts/lib/cli.mjs +11 -9
  89. package/skills/what-this-repo-needs/scripts/lib/credential.mjs +2 -2
  90. package/skills/whats-happening/SKILL.md +27 -21
  91. package/skills/whats-happening/agents/openai.yaml +1 -1
  92. package/skills/whats-happening/references/coding-accounts.md +24 -0
  93. package/skills/whats-happening/references/routing-work.md +11 -29
  94. package/skills/whats-happening/references/settings-and-where-they-live.md +20 -0
  95. package/skills/whats-happening/references/stages-and-mapping.md +25 -0
  96. package/skills/whats-happening/references/status-reply.md +21 -23
  97. package/skills/whats-happening/references/the-ladder.md +30 -0
  98. package/skills/whats-happening/references/what-runs-next.md +32 -0
  99. package/skills/whats-happening/references/when-a-phase-fails.md +27 -0
  100. package/skills/whats-happening/references/why-is-it-stuck.md +30 -44
  101. package/skills/whats-happening/scripts/explain.mjs +1 -1
  102. package/skills/whats-happening/scripts/lib/cli.mjs +7 -5
  103. package/skills/whats-happening/scripts/lib/credential.mjs +2 -2
  104. package/skills/{how-catalyst-works → whats-happening}/scripts/show-my-map.mjs +18 -32
  105. package/skills/whats-happening/scripts/snapshot.mjs +10 -6
  106. package/skills/catalyst-onboard/references/choosing-a-coding-account.md +0 -52
  107. package/skills/catalyst-onboard/references/who-fixes-what.md +0 -64
  108. package/skills/catalyst-setup/SKILL.md +0 -36
  109. package/skills/catalyst-setup/agents/openai.yaml +0 -6
  110. package/skills/catalyst-setup/agents/portability.yaml +0 -4
  111. package/skills/catalyst-setup/references/what-each-check-means.md +0 -88
  112. package/skills/catalyst-setup/scripts/lib/cli.mjs +0 -103
  113. package/skills/catalyst-setup/scripts/lib/credential.mjs +0 -29
  114. package/skills/catalyst-setup/scripts/replica-status.mjs +0 -46
  115. package/skills/connect-me/SKILL.md +0 -63
  116. package/skills/connect-me/agents/openai.yaml +0 -6
  117. package/skills/connect-me/agents/portability.yaml +0 -5
  118. package/skills/connect-me/references/keeping-the-replica-running.md +0 -88
  119. package/skills/connect-me/scripts/lib/cli.mjs +0 -185
  120. package/skills/connect-me/scripts/lib/credential.mjs +0 -29
  121. package/skills/connect-me/scripts/verify-connection.mjs +0 -68
  122. package/skills/how-catalyst-works/SKILL.md +0 -45
  123. package/skills/how-catalyst-works/agents/openai.yaml +0 -6
  124. package/skills/how-catalyst-works/agents/portability.yaml +0 -4
  125. package/skills/how-catalyst-works/references/coding-accounts.md +0 -51
  126. package/skills/how-catalyst-works/references/settings-and-where-they-live.md +0 -20
  127. package/skills/how-catalyst-works/references/stages-and-mapping.md +0 -56
  128. package/skills/how-catalyst-works/references/the-ladder.md +0 -41
  129. package/skills/how-catalyst-works/references/what-catalyst-is.md +0 -30
  130. package/skills/how-catalyst-works/references/what-runs-next.md +0 -95
  131. package/skills/how-catalyst-works/references/when-a-phase-fails.md +0 -57
  132. package/skills/how-catalyst-works/scripts/explain-ticket.mjs +0 -41
  133. package/skills/how-catalyst-works/scripts/lib/cli.mjs +0 -164
  134. package/skills/how-catalyst-works/scripts/lib/credential.mjs +0 -29
  135. package/skills/how-catalyst-works/scripts/whats-running.mjs +0 -65
  136. package/skills/whats-happening/references/reading-the-board.md +0 -43
  137. package/skills/whats-happening/references/reprioritising.md +0 -37
@@ -1,164 +0,0 @@
1
- #!/usr/bin/env node
2
- // lib/cli.mjs — the one way a skill script reaches Catalyst Cloud: it spawns the catalyst-skills CLI
3
- // this machine connected with (the path recorded in customer.json, else npx) and hands back its
4
- // output. Scripts import it; a person runs it with --help to see what it does. No dependencies.
5
- import { spawnSync } from "node:child_process";
6
- import { existsSync, readFileSync } from "node:fs";
7
- import { join } from "node:path";
8
- import { pathToFileURL } from "node:url";
9
- import { CONNECT_COMMAND, hasCredential } from "./credential.mjs";
10
-
11
- export const PACKAGE = "@catalyst-cloud/catalyst-skills";
12
- export const CONNECT_HINT = CONNECT_COMMAND;
13
-
14
- /** ~/.config/catalyst-cloud/customer.json, honouring CATALYST_SKILLS_HOME (used by tests) over HOME. */
15
- export function configPath() {
16
- const home = process.env.CATALYST_SKILLS_HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? "";
17
- return join(home, ".config", "catalyst-cloud", "customer.json");
18
- }
19
-
20
- /** The config, or a one-line reason it could not be read. Never throws. */
21
- export function loadConfig() {
22
- const path = configPath();
23
- if (!existsSync(path)) return { ok: false, reason: `no config at ${path}` };
24
- try {
25
- const cfg = JSON.parse(readFileSync(path, "utf8"));
26
- if (!hasCredential(cfg) || typeof cfg.account !== "string") {
27
- return { ok: false, reason: `${path} is missing a key or login, or the account` };
28
- }
29
- return { ok: true, cfg, path };
30
- } catch (err) {
31
- return { ok: false, reason: `${path} is unreadable: ${err instanceof Error ? err.message : String(err)}` };
32
- }
33
- }
34
-
35
- /** Exit 2 with the one line every script prints when this machine is not connected to a tenant. */
36
- export function notConfigured(reason) {
37
- console.error(`not connected to a Catalyst Cloud tenant (${reason}) — run: ${CONNECT_HINT}`);
38
- process.exit(2);
39
- }
40
-
41
- /**
42
- * Run one catalyst-skills verb. Returns { code, stdout, stderr }. The CLI is `node <cliPath>` when
43
- * the config recorded one that still exists, else `npx @catalyst-cloud/catalyst-skills`.
44
- * Exits 2 (not configured) before spawning anything when the config is absent or unreadable.
45
- */
46
- export function runCli(args, { stdin } = {}) {
47
- const loaded = loadConfig();
48
- if (!loaded.ok) notConfigured(loaded.reason);
49
- const { cfg } = loaded;
50
- let cmd;
51
- let argv;
52
- let shell = false;
53
- if (typeof cfg.cliPath === "string" && existsSync(cfg.cliPath)) {
54
- cmd = process.execPath;
55
- argv = [cfg.cliPath, ...args];
56
- } else {
57
- cmd = process.platform === "win32" ? "npx.cmd" : "npx";
58
- argv = [PACKAGE, ...args];
59
- shell = process.platform === "win32";
60
- }
61
- const r = spawnSync(cmd, argv, {
62
- encoding: "utf8",
63
- input: stdin,
64
- env: process.env,
65
- shell,
66
- maxBuffer: 64 * 1024 * 1024,
67
- });
68
- if (r.error) {
69
- console.error(`could not run ${cmd}: ${r.error.message} — re-run ${CONNECT_HINT} to record the CLI path`);
70
- process.exit(2);
71
- }
72
- return { code: r.status ?? 1, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
73
- }
74
-
75
- /** Print the CLI's stderr (the source line, the contract line, any refusal) on our stderr. */
76
- export function relayStderr(r) {
77
- const text = r.stderr.trimEnd();
78
- if (text) console.error(text);
79
- }
80
-
81
- /** A non-zero CLI result ends the script with the same code, its stdout shown so nothing is lost. */
82
- export function exitOnFailure(r) {
83
- if (r.code === 0) return;
84
- relayStderr(r);
85
- const out = r.stdout.trimEnd();
86
- if (out) console.log(out);
87
- process.exit(r.code);
88
- }
89
-
90
- export function parseJson(text) {
91
- try {
92
- return JSON.parse(text.trim());
93
- } catch {
94
- return null;
95
- }
96
- }
97
-
98
- /**
99
- * A small flag parser: `spec.value` names flags that take a value, `spec.bool` boolean flags,
100
- * `spec.repeat` value flags that may repeat (collected into arrays). Unknown flags are a usage error.
101
- */
102
- export function parseFlags(argv, spec = {}) {
103
- const value = new Set(spec.value ?? []);
104
- const bool = new Set(spec.bool ?? []);
105
- const repeat = new Set(spec.repeat ?? []);
106
- const flags = {};
107
- const positionals = [];
108
- for (let i = 0; i < argv.length; i++) {
109
- const a = argv[i];
110
- if (a === "--") {
111
- positionals.push(...argv.slice(i + 1));
112
- break;
113
- }
114
- if (!a.startsWith("--")) {
115
- positionals.push(a);
116
- continue;
117
- }
118
- let name = a.slice(2);
119
- let inline;
120
- const eq = name.indexOf("=");
121
- if (eq !== -1) {
122
- inline = name.slice(eq + 1);
123
- name = name.slice(0, eq);
124
- }
125
- if (bool.has(name)) {
126
- flags[name] = true;
127
- } else if (value.has(name) || repeat.has(name)) {
128
- const v = inline ?? argv[++i];
129
- if (v === undefined || v === "") usage(`--${name} needs a value`);
130
- if (repeat.has(name)) (flags[name] ??= []).push(v);
131
- else flags[name] = v;
132
- } else {
133
- usage(`unknown option --${name}`);
134
- }
135
- }
136
- return { flags, positionals };
137
- }
138
-
139
- export function usage(message) {
140
- console.error(message);
141
- process.exit(1);
142
- }
143
-
144
- export function wantsHelp(argv) {
145
- return argv.includes("--help") || argv.includes("-h");
146
- }
147
-
148
- const runDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
149
- if (runDirectly) {
150
- if (wantsHelp(process.argv.slice(2)) || process.argv.length <= 2) {
151
- console.log(
152
- [
153
- "lib/cli.mjs — shared helper for this skill's scripts (not a command of its own)",
154
- "",
155
- "Reads ~/.config/catalyst-cloud/customer.json and runs the catalyst-skills CLI recorded there",
156
- `(or npx ${PACKAGE} when no path is recorded). Exit 2 when the machine is not connected.`,
157
- "",
158
- `Connect first with: ${CONNECT_HINT}`,
159
- `Config in use: ${configPath()}`,
160
- ].join("\n"),
161
- );
162
- process.exit(0);
163
- }
164
- }
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env node
2
- // lib/credential.mjs — is this machine connected? The ONE place a skill script decides it, vendored
3
- // byte-identical into every skill's scripts/lib/ from skill-lib/credential.mjs at the package root
4
- // (`npm run skill-lib:sync`; a test fails on any drift). Skills install one directory at a time, so
5
- // each carries its own copy. This file is a library — run a sibling script with --help for usage.
6
- //
7
- // customer.json carries exactly one credential: a personal key (`key`), or the keyless login's
8
- // session (`auth`, the recommended rail). A script never reads either for its value: it spawns the
9
- // catalyst-skills CLI, which authenticates with whichever is present and refreshes a login's token
10
- // itself. A new credential kind lands here, once.
11
-
12
- /** The command that connects this machine, as every not-connected line names it. */
13
- export const CONNECT_COMMAND =
14
- "npx @catalyst-cloud/catalyst-skills login (or, with a personal key: CATALYST_CLOUD_TOKEN=<your personal key> npx @catalyst-cloud/catalyst-skills login)";
15
-
16
- /** True when `cfg` (parsed customer.json) holds a usable credential of either kind. Never throws. */
17
- export function hasCredential(cfg) {
18
- if (cfg === null || typeof cfg !== "object") return false;
19
- const key = cfg["key"];
20
- if (typeof key === "string" && key !== "") return true;
21
- const login = cfg["auth"];
22
- return (
23
- login !== null &&
24
- typeof login === "object" &&
25
- login["kind"] === "oauth" &&
26
- typeof login["refreshToken"] === "string" &&
27
- login["refreshToken"] !== ""
28
- );
29
- }
@@ -1,65 +0,0 @@
1
- #!/usr/bin/env node
2
- // whats-running.mjs — what Catalyst is executing right now (fleet activity, the agent roster, lease
3
- // attributions), optionally the dispatch queue and the coding-account line.
4
- import { exitOnFailure, parseFlags, parseJson, relayStderr, runCli, wantsHelp } from "./lib/cli.mjs";
5
-
6
- const HELP = `Usage: node scripts/whats-running.mjs [--queue] [--accounts] [--team <key>] [--json]
7
-
8
- Prints what is executing on your tenant. Wraps: catalyst-skills running, queue, accounts.
9
-
10
- --queue also print the dispatch queue (what runs next, in order)
11
- --team <key> with --queue: one team's queue
12
- --accounts also print the coding-account line (provider, state, windows — never a credential)
13
- --json one JSON document: { running, queue?, accounts? }
14
- --help this text
15
-
16
- Exit 0, 1 on a usage error, 2 when this machine is not connected to a tenant or the cloud refused
17
- a read (the line says which).`;
18
-
19
- const argv = process.argv.slice(2);
20
- if (wantsHelp(argv)) {
21
- console.log(HELP);
22
- process.exit(0);
23
- }
24
- const { flags } = parseFlags(argv, { bool: ["queue", "accounts", "json"], value: ["team"] });
25
-
26
- const out = {};
27
- const running = runCli(["running", "--json"]);
28
- exitOnFailure(running);
29
- relayStderr(running);
30
- out.running = parseJson(running.stdout) ?? running.stdout.trimEnd();
31
-
32
- if (flags.queue) {
33
- const args = ["queue", "--json"];
34
- if (flags.team) args.push("--team", flags.team);
35
- const queue = runCli(args);
36
- exitOnFailure(queue);
37
- relayStderr(queue);
38
- out.queue = parseJson(queue.stdout) ?? queue.stdout.trimEnd();
39
- }
40
-
41
- if (flags.accounts) {
42
- const accounts = runCli(["accounts"]);
43
- exitOnFailure(accounts);
44
- relayStderr(accounts);
45
- out.accounts = accounts.stdout.trimEnd();
46
- }
47
-
48
- if (flags.json) {
49
- console.log(JSON.stringify(out));
50
- process.exit(0);
51
- }
52
-
53
- const section = (title, value) => {
54
- console.log(`== ${title}`);
55
- console.log(typeof value === "string" ? value : JSON.stringify(value, null, 2));
56
- };
57
- if (out.running && typeof out.running === "object") {
58
- section("fleet activity", out.running.fleetActivity ?? out.running);
59
- if (out.running.agentRoster !== undefined) section("agent roster", out.running.agentRoster);
60
- if (out.running.leaseAttributions !== undefined) section("lease attributions", out.running.leaseAttributions);
61
- } else {
62
- section("running", out.running);
63
- }
64
- if (out.queue !== undefined) section("dispatch queue", out.queue);
65
- if (out.accounts !== undefined) section("coding accounts", out.accounts);
@@ -1,43 +0,0 @@
1
- # Reading the board
2
-
3
- This reference restates invariants: what the stages are, what order they come in, and what "stuck" means. The names your team uses for each stage, and which state ids they map to, are tenant facts read live from the contract; `node scripts/snapshot.mjs --board` prints the board already grouped and ordered by them.
4
-
5
- ## Stages are slots, names are display
6
-
7
- Catalyst thinks in eleven **slots** in pipeline order: dispatch, intake, research, plan, implement, remediate, verify, review, pr, done, canceled. Each of your teams maps some or all of those slots onto its own Linear workflow states. The contract carries, per team and per slot, the state id (the authority), the live state name and type (display), and whether the state still exists in Linear. A team may have adopted Catalyst's recommended states, mapped its existing ones, or mixed the two; the contract's `workflowMode` says which.
8
-
9
- Five slots are load-bearing and a missing one is a distinct silent failure: **dispatch** (where a card goes to be picked up), **intake** (an optional first pass for tickets that have never entered the ladder), **pr** (where a card sits while its pull request is reviewed and merged), **done** and **canceled**. The other six are informational: a wrong mapping there costs a warning, not a stall.
10
-
11
- Read a board by slot, never by name. Two teams can call the same slot different things, and a state renamed in Linear keeps its id and its slot.
12
-
13
- ## Work in progress by stage
14
-
15
- The snapshot's `board` block groups open tickets by state name in slot order, drops terminal states, and lists any state name that maps to no slot at the end. Read it top to bottom:
16
-
17
- - **dispatch** holds what is ready and waiting for a container. A long dispatch column with nothing in flight is a capacity or eligibility question, not a work question: run `explain` on the first row. If it says `wip_limit`, the project is at its WIP limit (tickets in progress, counting blocked, parked and waiting ones, not containers): the work in progress is what is waiting, so read that column, not this one.
18
- - **intake** through **pr** are the ladder. A card advances one slot when the matching phase completes; a failed phase writes no board state, so a card that has not moved is either still running, retrying in place, or moved sideways to remediate.
19
- - **remediate** is an interrupt, not a step. A card there was moved by a failure and returns to its exact previous stage when a repair round succeeds. Count remediate cards separately; they are not progress.
20
- - Backlog-type states are not slots. A ticket there is parked or not yet chosen; nothing is offered for it and nothing is wrong with it.
21
-
22
- ## Aging
23
-
24
- Age is time since `updated_at`, or since the lease started for a ticket in flight. Report it in human units. Compare it to the phase, not to a fixed number: a research phase and a merge wait have different natural durations, and the cloud's own retry backoff (the contract's `thresholds.retryBackoffMs`) explains many short waits.
25
-
26
- ## What counts as stuck
27
-
28
- A ticket is **stuck** only when all three hold:
29
-
30
- 1. Nothing is offered for it: the queue lists it excluded, or `explain` gives a reason rather than a position.
31
- 2. The reason does not release itself. Backoffs, cooldowns that name a callback, and fleet-wide holds that clear when the cloud repins are waits, not stalls. The table in `references/why-is-it-stuck.md` marks which is which.
32
- 3. No one is acting on the release: no open ask names it, no human comment landed after the failure, no push arrived.
33
-
34
- Everything else is **waiting**, and the reply says what it is waiting on. Calling a wait "stuck" sends the human to fix something the cloud is already handling.
35
-
36
- ## The signals the cloud posts on the ticket itself
37
-
38
- Before declaring anything, read the ticket's comments (`catalyst-skills query issue <id>`; the `catalyst-linear` skill explains each shape). The cloud posts a phase-outcome card on every completion and failure, a remediate-attempt card per repair round, a board-health note when it detects a stall itself, and a merge-wait note when a merge is held. Those comments are the narrative; the per-ticket ledger behind them — attempts, rounds against the cap, park state — is `catalyst-skills explain --history <ticket>`, and the two must agree.
39
-
40
- ## Two things that look like work and are not
41
-
42
- - A ticket carrying the ask marker label, or whose text reads as a decision request, is a question. It is excluded from dispatch by design and belongs in the waiting-on-human block.
43
- - A ticket claimed by a worker outside the cloud (the local-lane label) is being worked, but not by Catalyst; report it as in flight elsewhere and do not chase the cloud for it.
@@ -1,37 +0,0 @@
1
- # Reprioritising
2
-
3
- This reference restates invariants: how Catalyst orders work and which levers a person actually has. The live queue is `catalyst-skills queue [--team K]` (inside `node scripts/snapshot.mjs`); the thresholds behind backoffs and parks are the contract's `thresholds` block, printed in the snapshot's `tenant` section.
4
-
5
- ## How the cloud orders work
6
-
7
- Each team's queue is derived by the cloud itself from the team's own tickets, so there is nothing a session publishes and nothing that goes stale between sessions. The order is:
8
-
9
- 1. **Priority** ascending, as set on the Linear ticket (urgent first).
10
- 2. **Created time** ascending: older tickets first among equals.
11
- 3. **Identifier** ascending, as the final tiebreak.
12
-
13
- Tickets that are already **mid-ladder** join the same candidate set and sort ahead by how many phases they have completed, so a ticket that is nearly done finishes before a fresh one starts. That is an ordering rule only; it never makes an ineligible ticket eligible.
14
-
15
- Within the order, dispatch is bounded per repository (a concurrency cap of 20 running phases by default that an operator raises or lowers — the tenant's own settings page displays it and does not change it — and a paused repository resolves to zero without losing the stored value) and split across teams so one team cannot starve another. Comment-wake work, where the cloud answers a human comment on a ticket, shares the same cap as relay work.
16
-
17
- The queue is recomputed on every ingest that touches the team and on each alarm pass, so a change you make shows up within seconds, not on a schedule.
18
-
19
- ## The three levers a person has
20
-
21
- **1. Priority on the ticket.** Change the Linear priority and the queue reorders itself. This is the lever for "do this one first". It does not jump a running phase; it changes what is picked up next.
22
-
23
- **2. The dispatch column.** A card is offered only when it sits in the team's dispatch slot (and, for a ticket that has never entered the ladder, the intake slot when intake is enabled). Moving a card into that slot is how work is started; moving it out to a backlog-type state is how work is stopped without cancelling it. The `run-this-project` skill has the script; through this skill you describe the move and let the person or the project owner make it.
24
-
25
- **3. Holds.** A PR label from the contract's `merge.prLabels` set keeps an otherwise-mergeable pull request out of the merge queue until a person removes it. A blocking relation from an ask holds every ticket the ask names. Applying the release label the contract names frees a ticket the ask-shape detector flagged by mistake.
26
-
27
- Everything else that looks like a lever is not one from a key: unparking a phase, clearing a repair hold, resetting a validate budget and re-pinning a base are operator actions today, and the reply says so and names the settings page rather than promising them.
28
-
29
- ## What not to promise
30
-
31
- - **A running phase is not interrupted** by any of the three levers. It finishes or fails on its own; then the new order applies.
32
- - **A failed phase does not need re-queueing.** A pre-branch failure or an infrastructure failure retries in place after a backoff; a later failure moves the card to the remediate slot and queues a repair round, capped by the contract's `thresholds.remediateRoundCap`; repeated failures park it after `thresholds.parkAfterConsecutiveFailures`. Moving the card by hand during that sequence usually restarts the count rather than shortening it.
33
- - **Priority is not urgency to the human.** An urgent ticket that is blocked on an ask still waits for the ask. The what-needs-me ranking (by what an answer releases) is the right list for the human; the queue is the right list for the fleet.
34
-
35
- ## Answering "why is that one not next?"
36
-
37
- Run `explain` on it. If the answer is a position, it is next in order and the cloud is bounded by capacity: say how many are ahead and whether any run at all (if nothing runs anywhere, the likely cause is coding-account capacity, which a key cannot read yet, so name the settings page). If the answer is a reason, use `references/why-is-it-stuck.md`. If the answer is that the ticket is not in the explainer, it is on another team, terminal, or unknown to the mirror: check the identifier before anything else.