@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.
- package/CHANGELOG.md +6 -0
- package/README.md +26 -29
- package/dist/args.js +16 -0
- package/dist/ask.js +30 -18
- package/dist/capabilities.js +5 -0
- package/dist/cli.js +18 -5
- package/dist/environment.js +1 -1
- package/dist/legacy.js +224 -0
- package/dist/project.js +129 -0
- package/dist/release.js +1 -1
- package/dist/repo.js +297 -0
- package/dist/runtime-verb.js +1 -1
- package/dist/secret.js +1 -1
- package/dist/skill-shape.js +21 -13
- package/dist/skills.js +44 -4
- package/dist/transport.js +26 -1
- package/package.json +5 -4
- package/skills/catalyst-github/SKILL.md +7 -7
- package/skills/catalyst-github/references/is-it-mergeable.md +8 -2
- package/skills/catalyst-github/references/what-a-pr-accumulates.md +3 -3
- package/skills/catalyst-github/scripts/is-it-mergeable.mjs +3 -3
- package/skills/catalyst-github/scripts/lib/cli.mjs +9 -7
- package/skills/catalyst-github/scripts/lib/credential.mjs +2 -2
- package/skills/catalyst-github/scripts/read-pr.mjs +2 -2
- package/skills/catalyst-linear/SKILL.md +17 -19
- package/skills/catalyst-linear/agents/openai.yaml +1 -1
- package/skills/catalyst-linear/agents/portability.yaml +1 -1
- package/skills/catalyst-linear/references/reading-a-ticket.md +9 -26
- package/skills/catalyst-linear/references/what-a-ticket-accumulates.md +2 -2
- package/skills/catalyst-linear/references/writing-to-linear.md +7 -9
- package/skills/catalyst-linear/scripts/comment.mjs +1 -1
- package/skills/catalyst-linear/scripts/create-ticket.mjs +1 -1
- package/skills/catalyst-linear/scripts/label.mjs +1 -1
- package/skills/catalyst-linear/scripts/lib/cli.mjs +9 -7
- package/skills/catalyst-linear/scripts/lib/credential.mjs +2 -2
- package/skills/catalyst-linear/scripts/move.mjs +1 -1
- package/skills/catalyst-linear/scripts/read-ticket.mjs +1 -1
- package/skills/catalyst-linear/scripts/search.mjs +1 -1
- package/skills/catalyst-onboard/SKILL.md +37 -57
- package/skills/catalyst-onboard/agents/openai.yaml +2 -2
- package/skills/catalyst-onboard/agents/portability.yaml +1 -1
- package/skills/catalyst-onboard/references/connecting-this-machine.md +35 -0
- package/skills/catalyst-onboard/references/declaring-a-repository.md +7 -17
- package/skills/catalyst-onboard/references/local-sync.md +19 -7
- package/skills/catalyst-onboard/references/reading-ready.md +79 -0
- package/skills/catalyst-onboard/references/replacing-a-credential.md +8 -33
- package/skills/catalyst-onboard/references/repository-agent-setup.md +23 -0
- package/skills/catalyst-onboard/references/skill-sources.md +13 -19
- package/skills/catalyst-onboard/references/the-one-path.md +18 -74
- package/skills/catalyst-onboard/references/what-a-phase-needs.md +21 -28
- package/skills/catalyst-onboard/references/what-the-browser-owns.md +20 -37
- package/skills/{catalyst-setup → catalyst-onboard}/scripts/check.mjs +10 -9
- package/skills/catalyst-onboard/scripts/lib/cli.mjs +6 -4
- package/skills/catalyst-onboard/scripts/lib/credential.mjs +2 -2
- package/skills/catalyst-onboard/scripts/where-am-i.mjs +60 -4
- package/skills/run-this-project/SKILL.md +23 -25
- package/skills/run-this-project/agents/openai.yaml +1 -1
- package/skills/run-this-project/agents/portability.yaml +1 -1
- package/skills/run-this-project/references/making-work-ready.md +20 -37
- package/skills/run-this-project/references/reacting-to-events.md +25 -48
- package/skills/run-this-project/references/stalls-and-escalation.md +20 -42
- package/skills/run-this-project/scripts/lib/cli.mjs +7 -5
- package/skills/run-this-project/scripts/lib/credential.mjs +2 -2
- package/skills/run-this-project/scripts/scope-status.mjs +0 -0
- package/skills/run-this-project/scripts/watch-scope.mjs +1 -1
- package/skills/unstick/SKILL.md +12 -12
- package/skills/unstick/agents/openai.yaml +1 -1
- package/skills/unstick/agents/portability.yaml +1 -1
- package/skills/unstick/references/playbook.md +13 -13
- package/skills/unstick/scripts/lib/cli.mjs +6 -4
- package/skills/unstick/scripts/lib/credential.mjs +2 -2
- package/skills/unstick/scripts/unstick.mjs +1 -1
- package/skills/what-needs-me/SKILL.md +12 -14
- package/skills/what-needs-me/agents/openai.yaml +2 -2
- package/skills/what-needs-me/agents/portability.yaml +1 -1
- package/skills/what-needs-me/references/raising-a-decision.md +8 -16
- package/skills/what-needs-me/references/reading-the-inbox.md +6 -13
- package/skills/what-needs-me/references/settling-an-answer.md +6 -12
- package/skills/what-needs-me/scripts/inbox.mjs +14 -13
- package/skills/what-needs-me/scripts/lib/cli.mjs +6 -4
- package/skills/what-needs-me/scripts/lib/credential.mjs +2 -2
- package/skills/what-needs-me/scripts/raise.mjs +1 -1
- package/skills/what-needs-me/scripts/settle.mjs +1 -1
- package/skills/what-this-repo-needs/SKILL.md +10 -19
- package/skills/what-this-repo-needs/references/what-each-group-means.md +14 -44
- package/skills/what-this-repo-needs/scripts/check.mjs +2 -2
- package/skills/what-this-repo-needs/scripts/inventory.mjs +2 -2
- package/skills/what-this-repo-needs/scripts/lib/cli.mjs +11 -9
- package/skills/what-this-repo-needs/scripts/lib/credential.mjs +2 -2
- package/skills/whats-happening/SKILL.md +27 -21
- package/skills/whats-happening/agents/openai.yaml +1 -1
- package/skills/whats-happening/references/coding-accounts.md +24 -0
- package/skills/whats-happening/references/routing-work.md +11 -29
- package/skills/whats-happening/references/settings-and-where-they-live.md +20 -0
- package/skills/whats-happening/references/stages-and-mapping.md +25 -0
- package/skills/whats-happening/references/status-reply.md +21 -23
- package/skills/whats-happening/references/the-ladder.md +30 -0
- package/skills/whats-happening/references/what-runs-next.md +32 -0
- package/skills/whats-happening/references/when-a-phase-fails.md +27 -0
- package/skills/whats-happening/references/why-is-it-stuck.md +30 -44
- package/skills/whats-happening/scripts/explain.mjs +1 -1
- package/skills/whats-happening/scripts/lib/cli.mjs +7 -5
- package/skills/whats-happening/scripts/lib/credential.mjs +2 -2
- package/skills/{how-catalyst-works → whats-happening}/scripts/show-my-map.mjs +18 -32
- package/skills/whats-happening/scripts/snapshot.mjs +10 -6
- package/skills/catalyst-onboard/references/choosing-a-coding-account.md +0 -52
- package/skills/catalyst-onboard/references/who-fixes-what.md +0 -64
- package/skills/catalyst-setup/SKILL.md +0 -36
- package/skills/catalyst-setup/agents/openai.yaml +0 -6
- package/skills/catalyst-setup/agents/portability.yaml +0 -4
- package/skills/catalyst-setup/references/what-each-check-means.md +0 -88
- package/skills/catalyst-setup/scripts/lib/cli.mjs +0 -103
- package/skills/catalyst-setup/scripts/lib/credential.mjs +0 -29
- package/skills/catalyst-setup/scripts/replica-status.mjs +0 -46
- package/skills/connect-me/SKILL.md +0 -63
- package/skills/connect-me/agents/openai.yaml +0 -6
- package/skills/connect-me/agents/portability.yaml +0 -5
- package/skills/connect-me/references/keeping-the-replica-running.md +0 -88
- package/skills/connect-me/scripts/lib/cli.mjs +0 -185
- package/skills/connect-me/scripts/lib/credential.mjs +0 -29
- package/skills/connect-me/scripts/verify-connection.mjs +0 -68
- package/skills/how-catalyst-works/SKILL.md +0 -45
- package/skills/how-catalyst-works/agents/openai.yaml +0 -6
- package/skills/how-catalyst-works/agents/portability.yaml +0 -4
- package/skills/how-catalyst-works/references/coding-accounts.md +0 -51
- package/skills/how-catalyst-works/references/settings-and-where-they-live.md +0 -20
- package/skills/how-catalyst-works/references/stages-and-mapping.md +0 -56
- package/skills/how-catalyst-works/references/the-ladder.md +0 -41
- package/skills/how-catalyst-works/references/what-catalyst-is.md +0 -30
- package/skills/how-catalyst-works/references/what-runs-next.md +0 -95
- package/skills/how-catalyst-works/references/when-a-phase-fails.md +0 -57
- package/skills/how-catalyst-works/scripts/explain-ticket.mjs +0 -41
- package/skills/how-catalyst-works/scripts/lib/cli.mjs +0 -164
- package/skills/how-catalyst-works/scripts/lib/credential.mjs +0 -29
- package/skills/how-catalyst-works/scripts/whats-running.mjs +0 -65
- package/skills/whats-happening/references/reading-the-board.md +0 -43
- package/skills/whats-happening/references/reprioritising.md +0 -37
|
@@ -14,6 +14,7 @@ import { runLocalSync } from "./local-sync.mjs";
|
|
|
14
14
|
const SPEC = {
|
|
15
15
|
next: { help: "print only the single next step" },
|
|
16
16
|
json: { help: "one JSON document instead of the report" },
|
|
17
|
+
repo: { value: true, help: "a checkout of a mapped repository on this machine: its agent setup (AGENTS.md block, portable layout) is read and reported, never changed" },
|
|
17
18
|
};
|
|
18
19
|
const NOTES = [
|
|
19
20
|
"Reads, in this order: `status` (machine), `ready --json` (machine checks and project checks, kept apart),",
|
|
@@ -22,6 +23,8 @@ const NOTES = [
|
|
|
22
23
|
"`contract --path codingAccounts` and `accounts --json` (coding accounts, and which one needs a new credential), and each",
|
|
23
24
|
"project's hosts_current check with its fixedWhere (host), and each project's environment_declared check with its per-repository notes (repository declarations). The teams read is refreshed, so a mapping just saved reads back mapped.",
|
|
24
25
|
"Reads `capabilities --json` once: when the installed CLI can run a step for this person's role (`team check`, `team map`), the next step is that command; otherwise the page, with who can.",
|
|
26
|
+
"Reads `legacy --json` when the CLI has the verb: leftovers of the old local Catalyst runtime become a machine line and a note; removing them is the person's yes.",
|
|
27
|
+
"With --repo <path>, reads `repo agent-setup <path> --json` (a read; nothing changes) and adds a `repository agent setup` part that never blocks: the Catalyst block in AGENTS.md and the portable layout, with the writes named for the person to approve.",
|
|
25
28
|
"Also runs `gh repo view <owner>/thoughts` for each registered repository's owner, as a note: it shows the repository exists, never that the GitHub App can reach it.",
|
|
26
29
|
"When the tenant serves a contract version this CLI refuses, runs `npm view @catalyst-cloud/cli version` once to say whether a newer CLI is published.",
|
|
27
30
|
"Writes nothing and changes nothing. Runs before this machine is connected — that is one of the states it reports.",
|
|
@@ -29,7 +32,7 @@ const NOTES = [
|
|
|
29
32
|
|
|
30
33
|
const { help, flags, positionals } = parseFlags(process.argv.slice(2), SPEC);
|
|
31
34
|
if (help) {
|
|
32
|
-
printHelp("node scripts/where-am-i.mjs [--next] [--json]", SPEC, NOTES);
|
|
35
|
+
printHelp("node scripts/where-am-i.mjs [--next] [--json] [--repo <path>]", SPEC, NOTES);
|
|
33
36
|
process.exit(0);
|
|
34
37
|
}
|
|
35
38
|
if (positionals.length > 0) {
|
|
@@ -155,6 +158,19 @@ if (connected) {
|
|
|
155
158
|
const capDoc = tryJson(runCli(["capabilities", "--json"]).stdout);
|
|
156
159
|
capabilities = Array.isArray(capDoc?.capabilities) ? capDoc.capabilities : null;
|
|
157
160
|
}
|
|
161
|
+
// Leftovers of the old local Catalyst runtime (its plugin, jobs, commands, state), from the CLI's own
|
|
162
|
+
// fixed list. Reported on the machine part and as a note; removal is the person's yes, never this script's.
|
|
163
|
+
let legacyNote = null;
|
|
164
|
+
if (verbAvailable("legacy")) {
|
|
165
|
+
const legacyDoc = tryJson(runCli(["legacy", "--json"]).stdout);
|
|
166
|
+
const found = Array.isArray(legacyDoc?.found) ? legacyDoc.found : [];
|
|
167
|
+
if (found.length > 0) {
|
|
168
|
+
const machine = parts.find((p) => p.part === "machine");
|
|
169
|
+
const names = found.map((f) => `${f.kind} ${f.name}`);
|
|
170
|
+
machine?.lines.push(`note leftovers of the old local Catalyst runtime (${found.length}): ${names.slice(0, 6).join(", ")}${found.length > 6 ? ", …" : ""}; removing them is strongly recommended: catalyst legacy --remove (the data folders are a separate yes, --data)`);
|
|
171
|
+
legacyNote = `this machine still carries ${found.length} piece${found.length === 1 ? "" : "s"} of the old local Catalyst runtime (${names.slice(0, 4).join(", ")}${found.length > 4 ? ", …" : ""}); offer catalyst legacy --remove once, strongly recommended, then --data as a separate question`;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
158
174
|
|
|
159
175
|
// ── person ────────────────────────────────────────────────────────────────────────────────────────
|
|
160
176
|
if (!connected) {
|
|
@@ -593,6 +609,45 @@ if (!connected) {
|
|
|
593
609
|
}
|
|
594
610
|
}
|
|
595
611
|
|
|
612
|
+
// ── repository agent setup (only with --repo) ─────────────────────────────────────────────────────
|
|
613
|
+
// A checkout the person named. Read through the CLI's own verb, never by this script opening files:
|
|
614
|
+
// the verb is the one place that knows the block's words and the portable layout. Never blocking:
|
|
615
|
+
// a repository that is not portable still runs; this is an offer, and the writes wait for a yes.
|
|
616
|
+
let repoNote = null;
|
|
617
|
+
let repoNext = null;
|
|
618
|
+
if (typeof flags.repo === "string" && flags.repo !== "") {
|
|
619
|
+
const REPO_INSTRUMENT = `repo agent-setup ${flags.repo} --json`;
|
|
620
|
+
if (!connected) {
|
|
621
|
+
add("repository agent setup", REPO_INSTRUMENT, "unreadable", ["not readable until this machine is connected"], null, null, false);
|
|
622
|
+
} else if (!verbAvailable("repo agent-setup")) {
|
|
623
|
+
add("repository agent setup", REPO_INSTRUMENT, "unreadable", ["this CLI has no repo agent-setup verb; update the CLI to read a checkout's agent setup"], null, null, false).next =
|
|
624
|
+
"update the CLI (it lacks repo agent-setup); the checkout's agent setup cannot be read until then (does not block anything)";
|
|
625
|
+
} else {
|
|
626
|
+
const res = runCli(["repo", "agent-setup", flags.repo, "--json"]);
|
|
627
|
+
const doc = tryJson(res.stdout);
|
|
628
|
+
if (doc === null) {
|
|
629
|
+
add("repository agent setup", REPO_INSTRUMENT, "unreadable", [`the checkout could not be read (${(res.stderr || res.stdout).trim().split("\n")[0] || "no output"})`], null, null, false).next =
|
|
630
|
+
`run this again with the checkout's real path; ${flags.repo} could not be read`;
|
|
631
|
+
} else {
|
|
632
|
+
const block = doc.agentsMd?.block ?? "unknown";
|
|
633
|
+
const verdict = doc.verdict ?? "unknown";
|
|
634
|
+
const ok = verdict === "portable" && block === "current";
|
|
635
|
+
const lines = [
|
|
636
|
+
`AGENTS.md: ${doc.agentsMd?.present ? `present; Catalyst block ${block}` : "absent"}`,
|
|
637
|
+
`CLAUDE.md: ${doc.claudeMd?.present ? (doc.claudeMd.importsAgentsMd ? "imports AGENTS.md" : `${doc.claudeMd.otherLines ?? "?"} lines of its own guidance, no @AGENTS.md import`) : "absent"}`,
|
|
638
|
+
`layout: ${verdict}${Array.isArray(doc.plan) && doc.plan.length ? ` — ${doc.plan.join("; ")}` : ""}${Array.isArray(doc.blockers) && doc.blockers.length ? ` — by hand: ${doc.blockers.join("; ")}` : ""}`,
|
|
639
|
+
];
|
|
640
|
+
const offers = [];
|
|
641
|
+
if (block !== "current") offers.push(`the Catalyst block for AGENTS.md (catalyst repo agents-block ${flags.repo} --write)`);
|
|
642
|
+
if (verdict === "convertible") offers.push(`the portable layout (catalyst repo agent-setup ${flags.repo} --apply)`);
|
|
643
|
+
if (verdict === "needs_hand_merge") offers.push("a hand merge first (the layout line names what)");
|
|
644
|
+
repoNext = ok ? null : `say in one clause what the checkout holds, then offer ${offers.join(" and ")}, each after a yes, on a branch for the pull request the person opens`;
|
|
645
|
+
add("repository agent setup", REPO_INSTRUMENT, ok ? "ok" : "unfinished", lines, ok ? null : "you, after the person's yes, in their checkout; the pull request is theirs", ok ? null : `catalyst repo agent-setup ${flags.repo}`, false);
|
|
646
|
+
if (!ok) repoNote = `repository ${flags.repo}: Catalyst block ${block}, layout ${verdict} — offer ${offers.join(" and ")} after a yes (references/repository-agent-setup.md)`;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
596
651
|
// ── the single next step ──────────────────────────────────────────────────────────────────────────
|
|
597
652
|
// The machine's next action is not one sentence: an unconnected machine needs the login, and a
|
|
598
653
|
// connected one needs whatever check failed — and `ready` already printed that check's own fix, so
|
|
@@ -620,6 +675,7 @@ const NEXT = {
|
|
|
620
675
|
repositories: "install the GitHub App on the Connections page, granting it the repository you want worked; registering that repository on the Repositories page, attached to the project, is the step right after",
|
|
621
676
|
"coding accounts": accountsNext,
|
|
622
677
|
"repository declarations": declNext,
|
|
678
|
+
"repository agent setup": repoNext ?? "read the checkout's agent setup again",
|
|
623
679
|
host: "connect a Catalyst host",
|
|
624
680
|
};
|
|
625
681
|
// The parts are read in the order their data allows and reported in the order a person can act on
|
|
@@ -627,7 +683,7 @@ const NEXT = {
|
|
|
627
683
|
// workspace's Linear integration, the project, the GitHub App with its repository, the person's own
|
|
628
684
|
// connected accounts (personal GitHub after the repository proves the App), the repository's
|
|
629
685
|
// declaration, and last the host. `--next` is the first unfinished part in this order.
|
|
630
|
-
const ORDER = ["machine", "coding accounts", "account", "projects", "repositories", "person", "repository declarations", "host"];
|
|
686
|
+
const ORDER = ["machine", "coding accounts", "account", "projects", "repositories", "person", "repository declarations", "repository agent setup", "host"];
|
|
631
687
|
parts.sort((a, b) => ORDER.indexOf(a.part) - ORDER.indexOf(b.part));
|
|
632
688
|
// A personal Linear grant cannot start until the tenant's Linear workspace exists. Once that account
|
|
633
689
|
// connection is present, a missing personal grant becomes the next member step before project setup.
|
|
@@ -680,9 +736,9 @@ if (contractMismatch !== null && stuck !== null) {
|
|
|
680
736
|
const finished = parts.every((p) => p.verdict === "ok");
|
|
681
737
|
|
|
682
738
|
if (flags.json) {
|
|
683
|
-
console.log(JSON.stringify({ cli: via, connected, cloud, personalConnections, parts, localSync, next, finished, notes: retireNote
|
|
739
|
+
console.log(JSON.stringify({ cli: via, connected, cloud, personalConnections, parts, localSync, next, finished, notes: [retireNote, repoNote, legacyNote].filter((n) => n !== null) }));
|
|
684
740
|
} else if (flags.next) {
|
|
685
|
-
process.on("exit", () => {
|
|
741
|
+
process.on("exit", () => { for (const n of [retireNote, repoNote, legacyNote]) if (n !== null) console.log(`note: ${n}`); });
|
|
686
742
|
if (next === null) console.log("nothing left: every part is finished, a coding account is enrolled and the host check passes. Move one card into the project's start stage (usually Todo).");
|
|
687
743
|
else console.log(`${next.part}: ${next.action}${next.blocking ? "" : " (does not block the steps below)"}${next.owner ? ` — who: ${next.owner}` : ""}${next.where ? ` — ${next.where.startsWith("http") ? "where" : "do"}: ${next.where}` : ""}`);
|
|
688
744
|
} else {
|
|
@@ -1,45 +1,43 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: run-this-project
|
|
3
3
|
description: >-
|
|
4
|
-
Own one Catalyst Cloud project end to end until it closes. Use when the person says "run this project for me", "own this until it ships", "keep this moving", or hands you a project id or a set of tickets to drive. Subscribes to the
|
|
5
|
-
|
|
6
|
-
allowed-tools: Bash(catalyst-skills:*) Bash(npx @catalyst-cloud/catalyst-skills:*)
|
|
4
|
+
Own one Catalyst Cloud project end to end until it closes. Use when the person says "run this project for me", "own this until it ships", "keep this moving", or hands you a project id or a set of tickets to drive. Subscribes to the account's event stream for the scope through the catalyst CLI, reacts to each change in the same turn, makes tickets ready and moves them to dispatch, parks what should stop, chases stalls, escalates inward, and keeps one status summary current. Writes to Linear as the app actor; never polls.
|
|
5
|
+
allowed-tools: Bash(catalyst:*) Bash(npx -p @catalyst-cloud/cli catalyst:*)
|
|
7
6
|
---
|
|
8
|
-
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.13.
|
|
7
|
+
<!-- vendored-from: @catalyst-cloud/catalyst-skills@0.13.1 — written in this repository for customer accounts -->
|
|
9
8
|
|
|
10
9
|
# Run this project
|
|
11
10
|
|
|
12
|
-
You are the steward: the single-threaded owner of ONE project or ticket set
|
|
11
|
+
You are the steward: the single-threaded owner of ONE project or ticket set until it closes. Catalyst runs the phases; you make work ready and visible, react to what happens, unblock, and keep one status summary the person can read. `whats-happening` is the desk; other stewards are peers.
|
|
13
12
|
|
|
14
13
|
## Run first
|
|
15
14
|
|
|
16
|
-
Scripts are run, never read. Each prints its own
|
|
15
|
+
Scripts are run, never read. Each prints `--help`, and exits 2 when this machine is not connected (`catalyst login`), 1 when its own check fails.
|
|
17
16
|
|
|
18
|
-
1. `node scripts/scope-status.mjs --project <id>` (or `--team <key>`): tickets by stage
|
|
19
|
-
2. `node scripts/watch-scope.mjs --project <id>`: the subscription. In Claude Code, arm a monitor on it and react to each printed line in the same turn
|
|
20
|
-
3. `node scripts/make-ready.mjs <ticket>`
|
|
21
|
-
4. `catalyst
|
|
22
|
-
|
|
23
|
-
Every script exits 2 when this machine is not connected (run `catalyst-skills login`), 1 when its own check fails.
|
|
17
|
+
1. `node scripts/scope-status.mjs --project <id>` (or `--team <key>`): tickets by stage, what is running and queued in scope, and stalls against the local policy. Run it to take the scope, and again after a resync.
|
|
18
|
+
2. `node scripts/watch-scope.mjs --project <id>`: the subscription. In Claude Code, arm a monitor on it and react to each printed line in the same turn; elsewhere add `--exec <command>` so a reaction runs per frame.
|
|
19
|
+
3. `node scripts/make-ready.mjs <ticket>` dispatches; `--park` stops; `--note <why>` records it.
|
|
20
|
+
4. `catalyst explain <ticket>` whenever a ticket is not moving.
|
|
24
21
|
|
|
25
22
|
## Load on demand
|
|
26
23
|
|
|
27
24
|
| when | read |
|
|
28
25
|
| -- | -- |
|
|
29
|
-
| arming the watch,
|
|
30
|
-
| dispatching or parking
|
|
31
|
-
| something is not moving, tuning the stall policy,
|
|
32
|
-
|
|
|
26
|
+
| arming the watch, reading a frame, a failed reaction, a resync | `references/reacting-to-events.md` |
|
|
27
|
+
| dispatching or parking, judging whether a phase ran, writing a ticket for Catalyst | `references/making-work-ready.md` |
|
|
28
|
+
| something is not moving, tuning the stall policy, whether a human needs to hear | `references/stalls-and-escalation.md` |
|
|
29
|
+
| what an exclusion reason means, the ladder, the stage map | the `whats-happening` skill |
|
|
33
30
|
| raising or settling a decision | the `what-needs-me` skill |
|
|
34
|
-
| a PR's checks, review and merge
|
|
31
|
+
| a PR's checks, review and merge | the `catalyst-github` skill |
|
|
35
32
|
|
|
36
33
|
## Rules
|
|
37
34
|
|
|
38
|
-
- **React
|
|
39
|
-
- **Dispatch is a card move.**
|
|
40
|
-
- **
|
|
41
|
-
- **
|
|
42
|
-
- **Escalate inward
|
|
43
|
-
- **
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
35
|
+
- **React to the stream.** The watch and its cursor file are the mechanism, and a reaction that throws leaves the cursor so the frame is offered again. Re-reading the API or the replica in a loop is a defect.
|
|
36
|
+
- **Dispatch is a card move.** Your two moves are into dispatch and into the backlog; the cloud writes every ladder stage itself.
|
|
37
|
+
- **A phase ran when its outcome comment, attached document and agent session say so,** not when the clock or the column suggests it.
|
|
38
|
+
- **Account facts come from the contract, live** (`catalyst contract --path <a.b.c>`), never from prose.
|
|
39
|
+
- **Escalate inward:** instrument, you, the desk, then the human as an ask through `what-needs-me` naming what it blocks. Decide technical calls yourself and record the default; a fleet or provider condition is one note, never one ask per ticket.
|
|
40
|
+
- **Say what a write costs.** Every move and comment spends the contract's daily write budget as the app actor, and a move into dispatch starts paid work on a coding account. Read the verdict `make-ready.mjs` prints before the next move.
|
|
41
|
+
- **Reply where the message arrived,** in-thread, tagged, as the app actor. The human speaks for themselves, and each ask belongs to its own addressee. Bookkeeping records take the contract's marker (`catalyst write comment --bookkeeping`).
|
|
42
|
+
- **One status summary,** current after every reaction: in flight, blocked and on whom, closed, next, each line with a ticket id, and a pending decision's line with its ask id.
|
|
43
|
+
- **Releases belong to `unstick`.** Once the cause is fixed, the person's own login releases a park or most holds through the `unstick` skill (`catalyst release`); a round-threshold hold and a review that will not converge refuse it. PR labels and reactions are not mirrored; execution history (`explain --history`) and coding accounts (`catalyst accounts`) are readable.
|
|
@@ -3,4 +3,4 @@ interface:
|
|
|
3
3
|
short_description: "Own one Catalyst project end to end: react to its events, make tickets ready, chase stalls, escalate inward"
|
|
4
4
|
default_prompt: "Use $run-this-project to run project <id> for me until it closes."
|
|
5
5
|
policy:
|
|
6
|
-
allow_implicit_invocation:
|
|
6
|
+
allow_implicit_invocation: true
|
|
@@ -1,60 +1,43 @@
|
|
|
1
1
|
# Making work ready
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## The steward's two moves
|
|
6
|
-
|
|
7
|
-
Catalyst does not take work by being asked. It takes work by finding a card in the team's dispatch column and offering that card's next phase to a runner. So the steward's dispatch verb is a card move, and its stop verb is a card move:
|
|
3
|
+
Catalyst takes work by finding a card in the team's dispatch column and offering its next phase to a runner. So the steward's dispatch and stop verbs are both card moves:
|
|
8
4
|
|
|
9
5
|
```sh
|
|
10
|
-
node scripts/make-ready.mjs ENG-41 #
|
|
11
|
-
node scripts/make-ready.mjs ENG-41 --park #
|
|
6
|
+
node scripts/make-ready.mjs ENG-41 # into the dispatch column
|
|
7
|
+
node scripts/make-ready.mjs ENG-41 --park # into the team's backlog-type state
|
|
12
8
|
node scripts/make-ready.mjs ENG-41 --park --note "waiting on the vendor's API change"
|
|
13
9
|
```
|
|
14
10
|
|
|
15
|
-
Dispatch resolves the dispatch slot
|
|
11
|
+
Dispatch resolves the team's dispatch slot from the contract; parking resolves the team's first backlog-type state from its live workflow, since the backlog is not a slot. Every other stage on a ticket in the ladder is the cloud's record of what ran: a hand move forward runs nothing, and a hand move back undoes nothing.
|
|
16
12
|
|
|
17
13
|
## What a dispatchable ticket carries
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
A phase agent reads the ticket, not your chat. Read it in full, then check it has:
|
|
20
16
|
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
- No ask on it. A ticket that carries the ask label, or whose own text reads as a decision request (an ask-shaped title, lettered options, a "default if silent" line), is excluded as a question rather than work. If a real ticket trips the shape detector, a human applies the release label named in the contract's vocabulary; you can also rewrite the text so it reads as work.
|
|
27
|
-
- Declared scope when the team enforces it: a ticket whose declared files overlap a ticket already in flight is held as `scope_overlap` for the implement phase.
|
|
17
|
+
- a title stating the outcome, and a description of what done looks like (behaviour, constraints, files or surfaces, acceptance criteria validate can check);
|
|
18
|
+
- the right team (the prefix; its stage map and labels are what the cloud uses) and a priority (unset sorts last);
|
|
19
|
+
- no live blocking relation, which excludes it as `blocked`;
|
|
20
|
+
- no ask: an ask label, or text that reads as a decision request, excludes it as a question. A human applies the contract's release label to a false positive; you can rewrite the text so it reads as work;
|
|
21
|
+
- declared scope when the team enforces it, and no file overlap with a ticket in flight (`scope_overlap`). Serialise two tickets that touch the same files.
|
|
28
22
|
|
|
29
|
-
|
|
23
|
+
It needs no branch, PR or artifact; the ladder makes those, starting at intake when the account has it on, else research.
|
|
30
24
|
|
|
31
25
|
## After the move
|
|
32
26
|
|
|
33
|
-
`make-ready.mjs`
|
|
34
|
-
|
|
35
|
-
- `offered` or `eligible` with a queue position: done; a runner will pick it up in the next dispatch pass, ordered by priority, then age, then identifier, with tickets already mid-ladder ahead of fresh ones.
|
|
36
|
-
- An ordering that is stale or never published: the cloud re-derives the team's queue within a pass of the move; ask again in a minute with `catalyst-skills explain <ticket>`.
|
|
37
|
-
- Any other exclusion reason: the paragraph names it and what releases it. The reasons and what unblocks each are in `how-catalyst-works` and in `whats-happening`'s "why is it stuck" reference.
|
|
27
|
+
`make-ready.mjs` prints the explainer's verdict. A queue position means a runner will pick it up in order. A stale or unpublished ordering is re-derived within a pass; ask again in a minute with `catalyst explain <ticket>`. Any other reason is in the `whats-happening` skill's `references/why-is-it-stuck.md`.
|
|
38
28
|
|
|
39
29
|
## Evidence a phase ran
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
1. **The outcome comment.** The cloud posts a card per phase: a completed phase names the phase, the attempt, and its artifact; a failed phase names the phase, the attempt, and the failure class, and may carry a park or hold block. A remediate round posts its own attempt card with the round number and the class it is repairing. These arrive as comment frames on the watch.
|
|
44
|
-
2. **The attachment and document.** Each artifact-bearing phase (research, plan, implement, validate, pr, remediate) is projected to a Linear document titled with the ticket, the phase, the attempt and the date, attached to the ticket, with a short link comment. Research and plan on a ticket with a project also appear as a project link.
|
|
45
|
-
3. **The agent session.** The ticket's agent session carries the ladder as its plan, with the phases before the current one completed, the current one in progress, and the rest pending; its activities are the phase start, the gate, artifacts written, the PR opened, and the report.
|
|
46
|
-
|
|
47
|
-
The card's stage is the fourth, weakest signal: a completed phase moves the card to the stage the advance table names, a failed phase writes no stage at all, and Done is written only when the pull request actually merges, by the merge webhook, never by a phase. A card sitting in a stage tells you which phase last finished, not whether the next one is running; `scripts/scope-status.mjs` shows running and leased phases beside the stage for exactly that reason.
|
|
31
|
+
Time passing proves nothing. A phase leaves three kinds of evidence on the ticket:
|
|
48
32
|
|
|
49
|
-
|
|
33
|
+
1. **The outcome comment.** Per phase: the phase, the attempt, and its artifact, or on failure the failure class and any park or hold block. A remediate round posts its own card with the round number. These arrive as comment frames.
|
|
34
|
+
2. **The document.** Each artifact-bearing phase attaches a Linear document with a short link comment.
|
|
35
|
+
3. **The agent session.** Its plan is the ladder, with the current phase in progress; its activities are the phase start, the gate, the artifacts, the PR, and the report.
|
|
50
36
|
|
|
51
|
-
|
|
37
|
+
The card's stage is the weakest signal: a completed phase moves it, a failed one writes nothing, and Done is written only when the pull request merges. `scope-status.mjs` shows running and leased phases beside each stage for that reason.
|
|
52
38
|
|
|
53
|
-
|
|
39
|
+
## Parking
|
|
54
40
|
|
|
55
|
-
|
|
41
|
+
Parking stops the cloud offering further rounds: the card is excluded at the next offer. A phase already running under a lease finishes and posts its outcome first. Un-parking is the dispatch move again, and counted attempts and rounds carry over. A cloud park (three consecutive failures, or the round cap) is not a card move: once its cause is fixed, the `unstick` skill releases it with `catalyst release <ticket>`, and only a refusal that names a person's action becomes an ask. When you park a ticket the cloud already parked, record why in a bookkeeping note.
|
|
56
42
|
|
|
57
|
-
|
|
58
|
-
- Never remove an ask label or apply the release label yourself; a human decides whether a ticket is a question.
|
|
59
|
-
- Never dispatch two tickets that touch the same files at once; serialise them or let the second one wait as `scope_overlap`.
|
|
60
|
-
- Never dispatch a ticket you have not read in full.
|
|
43
|
+
Ask labels and the release label are a human's call; leave them as you find them.
|
|
@@ -1,76 +1,53 @@
|
|
|
1
1
|
# Reacting to events
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## The mechanism
|
|
6
|
-
|
|
7
|
-
A steward never polls. It subscribes once to the tenant stream through the SDK's live client and reacts to each change as it arrives. The verb is `catalyst-skills watch`, and this skill's `scripts/watch-scope.mjs` is that verb with the scope checked up front:
|
|
3
|
+
A steward subscribes once to the account's event stream and reacts to each change as it arrives. The verb is `catalyst watch`; `scripts/watch-scope.mjs` is that verb with the scope checked up front:
|
|
8
4
|
|
|
9
5
|
```sh
|
|
10
|
-
node scripts/watch-scope.mjs --project <id>
|
|
11
|
-
node scripts/watch-scope.mjs --team <key>
|
|
6
|
+
node scripts/watch-scope.mjs --project <id>
|
|
7
|
+
node scripts/watch-scope.mjs --team <key>
|
|
12
8
|
node scripts/watch-scope.mjs --ticket ENG-41 --ticket ENG-42
|
|
9
|
+
node scripts/watch-scope.mjs --project <id> --exec 'node my-reaction.mjs' # a harness with no monitor
|
|
13
10
|
```
|
|
14
11
|
|
|
15
|
-
The stream
|
|
16
|
-
|
|
17
|
-
**In Claude Code**, arm a monitor on the command above. Each line it prints is one applied change inside the scope, delivered into your session as an event. React to it in the same turn: read what changed, decide, act, and only then let the turn end.
|
|
18
|
-
|
|
19
|
-
**In Codex, OpenCode, or any harness without a monitor**, pass `--exec`:
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
node scripts/watch-scope.mjs --project <id> --exec 'node my-reaction.mjs'
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
The command runs once per frame with the frame on stdin. A non-zero exit is a failed reaction (see the cursor rule). Nothing polls in either shape.
|
|
12
|
+
The stream is account-wide and scope is filtered on this machine, resolving comments, sessions and pull requests to their ticket's project; an ignored frame costs one JSON parse, so a wide scope is fine. In Claude Code, a monitor on the command delivers each printed line into your session. With `--exec`, the command runs once per frame with the frame on stdin, and a non-zero exit is a failed reaction.
|
|
26
13
|
|
|
27
14
|
## The frame
|
|
28
15
|
|
|
29
|
-
One JSON object per line,
|
|
16
|
+
One JSON object per line, as the SDK delivers it:
|
|
30
17
|
|
|
31
18
|
```json
|
|
32
|
-
{"type":"change","accountId":"<
|
|
19
|
+
{"type":"change","accountId":"<account>","seq":4182,"entity":"comments","entityId":"<id>","op":"upsert","row":{...}}
|
|
33
20
|
```
|
|
34
21
|
|
|
35
|
-
`seq` is the
|
|
22
|
+
`seq` is the account-wide position, `entity` a feed table, `op` is `upsert` (with the row) or `delete` (the id only).
|
|
36
23
|
|
|
37
24
|
## Entities that matter to a scope
|
|
38
25
|
|
|
39
26
|
| entity | what a change means | first reaction |
|
|
40
27
|
| -- | -- | -- |
|
|
41
|
-
| `issues` | a card moved, was
|
|
42
|
-
| `comments` | someone or something wrote on a ticket | a human comment in
|
|
43
|
-
| `issue_labels`, `labels` | a label
|
|
44
|
-
| `relations` | a
|
|
45
|
-
| `pull_requests`, `pr_events`, `pushes` | the branch
|
|
46
|
-
| `check_runs`, `check_suites`, `commit_statuses` | CI moved | red
|
|
47
|
-
| `reviews`, `pr_review_threads`, `pr_review_comments` | the reviewer spoke | unresolved threads block the merge; a clean pass is a reaction or a terse comment
|
|
48
|
-
| `agent_sessions`, `agent_activities` | a phase started, wrote
|
|
49
|
-
| `fleet_activity`, `fleet_host_liveness` | a runner picked up or dropped a phase | a phase
|
|
50
|
-
| `fleet_anomalies` |
|
|
51
|
-
| `workflow_states`, `team_workflow_mapping` | the
|
|
52
|
-
| `projects`, `cycles`, `initiatives` |
|
|
53
|
-
|
|
54
|
-
Entities not listed still arrive when they are in scope; ignore what you do not need.
|
|
28
|
+
| `issues` | a card moved, was edited, re-prioritised, assigned or created | a move into a cloud-owned stage is an advance; a hand move out of the ladder, ask why before touching it |
|
|
29
|
+
| `comments` | someone or something wrote on a ticket | answer a human comment in-thread, tagged; read a cloud outcome card (phase complete or failed, remediate attempt, board health, merge wait) as your execution signal |
|
|
30
|
+
| `issue_labels`, `labels` | a label changed | an ask label means a question; a hold label means the cloud wants a human; the release label means a human cleared a false positive |
|
|
31
|
+
| `relations` | a blocker appeared or went away | chase a new blocker; a closed one may make a card dispatchable |
|
|
32
|
+
| `pull_requests`, `pr_events`, `pushes` | the branch or PR changed | ready-for-review and merged are milestones; a force-push after a resolved thread is worth a look |
|
|
33
|
+
| `check_runs`, `check_suites`, `commit_statuses` | CI moved | a red required check after the queue label wakes an automatic remediation; watch it |
|
|
34
|
+
| `reviews`, `pr_review_threads`, `pr_review_comments` | the reviewer spoke | unresolved threads block the merge; a clean pass is a reaction or a terse comment |
|
|
35
|
+
| `agent_sessions`, `agent_activities` | a phase started, wrote, opened a PR, reported | the session's plan is the ladder |
|
|
36
|
+
| `fleet_activity`, `fleet_host_liveness` | a runner picked up or dropped a phase | a running phase is not a stall |
|
|
37
|
+
| `fleet_anomalies` | a fleet-level alert | one alert covers every ticket it touches |
|
|
38
|
+
| `workflow_states`, `team_workflow_mapping` | the stage map changed | `catalyst contract --refresh` before the next move |
|
|
39
|
+
| `projects`, `cycles`, `initiatives` | the scope's container changed | update the summary |
|
|
55
40
|
|
|
56
41
|
## The same-turn rule
|
|
57
42
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
## The cursor rule
|
|
61
|
-
|
|
62
|
-
The cursor file (`~/.config/catalyst-cloud/watch-cursor.json`, stamped with the tenant) advances only after the reaction returns. A reaction that throws, or an `--exec` command that exits non-zero, leaves the cursor at the last good frame; the CLI closes the socket and the SDK's reconnect replays from that cursor, so the failed frame is offered again. Delivery is therefore at-least-once: make reactions safe to repeat (check before you write, prefer idempotent writes such as a label add over a fresh comment).
|
|
63
|
-
|
|
64
|
-
Two consequences. First, a crash mid-reaction replays, never loses. Second, a reaction that keeps failing keeps the watch pinned on one frame; fix the reaction rather than skipping the frame, or restart with `--from head` and re-read the scope with `scripts/scope-status.mjs` to catch up on what you missed.
|
|
65
|
-
|
|
66
|
-
## What to do on resync
|
|
43
|
+
React to a frame in the turn it arrives, and end the turn with the status summary current. A reaction that needs a human decision files the ask through `what-needs-me` in that same turn, before proceeding on the default.
|
|
67
44
|
|
|
68
|
-
|
|
45
|
+
## The cursor
|
|
69
46
|
|
|
70
|
-
|
|
47
|
+
The cursor file (`~/.config/catalyst-cloud/watch-cursor.json`) advances only after a reaction returns. A reaction that throws, or an `--exec` that exits non-zero, leaves it at the last good frame, and the reconnect replays from there. Delivery is at-least-once, so make reactions safe to repeat: check before writing, and prefer idempotent writes such as a label add. A reaction that keeps failing pins the watch on one frame; fix the reaction, or restart with `--from head` and re-read the scope with `scripts/scope-status.mjs`.
|
|
71
48
|
|
|
72
|
-
|
|
49
|
+
A resync prints `[watch] resync: cursor moved to head <n>` on standard error and continues from the head without replaying the gap: run `scripts/scope-status.mjs` once and reconcile. Start with `--from head` when taking over a project that ran without a steward; resume from the saved cursor (the default) after your own break.
|
|
73
50
|
|
|
74
51
|
## The honest limit
|
|
75
52
|
|
|
76
|
-
The stream carries the mirror's entity changes
|
|
53
|
+
The stream carries the mirror's entity changes, not the relay ledger's failures, parks, rounds or backoff timers as frames. Those arrive as the cloud's outcome comments, and `catalyst explain <ticket>` gives the current verdict; `--history` adds the attempts, rounds against the cap, and any park with what releases it.
|
|
@@ -1,63 +1,41 @@
|
|
|
1
1
|
# Stalls and escalation
|
|
2
2
|
|
|
3
|
-
The stall thresholds
|
|
3
|
+
The stall thresholds are **local judgment**, set once by the project's owner and read by `scripts/scope-status.mjs`. The cloud publishes no stall policy and enforces none of these numbers; what it does about a failing ticket is in the contract's `thresholds`.
|
|
4
4
|
|
|
5
5
|
## The policy file
|
|
6
6
|
|
|
7
|
-
`assets/stall-policy.json` ships the defaults: minutes a ticket may sit in each slot with no update and nothing running or leased
|
|
8
|
-
|
|
9
|
-
Two slots deserve thought when tuning. The dispatch slot's threshold is how long a card may wait to be picked up before you ask why nothing took it; on a busy tenant with a full queue that is capacity, not a stall, and the answer is to wait or re-prioritise. The PR slot's threshold is how long a card may wait for merge evidence; reviews and CI take real time, so set it generously.
|
|
7
|
+
`assets/stall-policy.json` ships the defaults: minutes a ticket may sit in each slot, with no update and nothing running or leased, before it counts as stalled. To change them, copy it to `~/.config/catalyst-cloud/stall-policy.json` and edit the copy; the script prefers it and prints which file it used (`--stall-policy <file>` picks another). On a busy account a waiting dispatch card is capacity, not a stall, and reviews and CI take real time, so set the dispatch and PR thresholds generously.
|
|
10
8
|
|
|
11
9
|
## What counts as stuck
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
A running phase is never a stall, however long it has run; the cloud's own timeout will fail it and the outcome card will say so. A ticket in retry backoff is waiting out a rung of two, five or fifteen minutes and is not a stall either.
|
|
11
|
+
All at once: an active slot, nothing running or leased, a last update older than the slot's threshold, and an explainer reason that is not a clock. `scope-status.mjs` checks the first three and hands you the fourth as a command. A running phase is never a stall; its own timeout fails it and the outcome card says so.
|
|
16
12
|
|
|
17
13
|
## The chase order
|
|
18
14
|
|
|
19
|
-
Run `catalyst
|
|
20
|
-
|
|
21
|
-
| the explainer says | what it means | your move |
|
|
22
|
-
| -- | -- | -- |
|
|
23
|
-
| offered, with a position | the queue has it; a runner is the bottleneck | wait; if every ticket waits, it is capacity, see below |
|
|
24
|
-
| lease held, intake lease held | a container has it | not a stall; wait for the outcome card |
|
|
25
|
-
| retry backoff | a pre-branch or infrastructure failure is retrying in place | wait out the rung; three in a row parks it, and then it is a real stall |
|
|
26
|
-
| blocked | a live blocks relation | chase the blocker: is it dispatchable, is it an ask nobody answered, is it done but still open |
|
|
27
|
-
| ask ticket, ask shape suspected | the ticket is a question | route it to `what-needs-me`; if it is really work, a human applies the release label |
|
|
28
|
-
| not at dispatch stage | somebody moved the card, or it never entered | read the card's history; re-dispatch with `make-ready.mjs` if it should run |
|
|
29
|
-
| phase parked, cooling down, remediate parked | the cloud parked it after repeated failure or the round cap | read the last outcome card for the class; once its cause is fixed, the `unstick` skill releases it (`catalyst-skills release <ticket> --because <what changed>`); if the fix is a decision, file an ask |
|
|
30
|
-
| no change hold | a remediate round changed nothing | a human comment on the ticket, or a new push to the branch, releases it; say what should change |
|
|
31
|
-
| validate class spent, stale failure episode | the repair budget for this failure is used, or the ladder moved on | read the outcome cards; usually a decision about the approach, so an ask |
|
|
32
|
-
| waiting on | a merge-gate failure with no automatic repair | read the merge-wait comment and the PR's three legs through `catalyst-github` |
|
|
33
|
-
| branch missing, branch gone, no branch to remediate | the branch the phase needs does not exist | a hand-deleted branch is a human question; a never-created one means implement has not run, so check the earlier phases |
|
|
34
|
-
| environment check required, running, failed, expired, hash mismatch | the repository's environment gate | a repository setting; the tenant admin resolves it in settings, one ask for the repository, not per ticket |
|
|
35
|
-
| scope overlap | another in-flight ticket owns the files | wait for it, or re-order by priority |
|
|
36
|
-
| routing unavailable, no eligible slot, runner image breaker, repo paused | a fleet or provider condition | one fleet note, never per ticket; see below |
|
|
37
|
-
| pr merged, pipeline complete, ticket terminal | it is finished | close the loop in the summary; if the card is not Done a minute after the merge, that is a finding, not a chore |
|
|
38
|
-
|
|
39
|
-
Where the explainer names a reason not in this table, it prints the raw reason; read it as spelled and consult `how-catalyst-works`.
|
|
40
|
-
|
|
41
|
-
## Capacity and fleet conditions are one note, not many asks
|
|
15
|
+
Run `catalyst explain <ticket>` and look the reason up in the `whats-happening` skill's `references/why-is-it-stuck.md`. For you as the owner:
|
|
42
16
|
|
|
43
|
-
|
|
17
|
+
- **It releases itself:** wait for the outcome card.
|
|
18
|
+
- **The person, with their own login, acts:** do it when the move is yours (re-dispatch, a comment that clears a no-change or validate-budget hold, a question routed to `what-needs-me`), and file an ask when it is theirs.
|
|
19
|
+
- **A park or hold a release clears:** once its cause is fixed, the `unstick` skill releases it (`catalyst release <ticket> --because <what changed>`). A round-threshold hold and a review that will not converge refuse that release; the table says what clears each.
|
|
20
|
+
- **An owner or admin, in settings, or an operator:** one ask per repository or setting.
|
|
21
|
+
- **Finished:** close the loop in the summary.
|
|
44
22
|
|
|
45
|
-
|
|
23
|
+
Quote a reason the table does not know as the explainer spelled it.
|
|
46
24
|
|
|
47
|
-
|
|
25
|
+
## Shared causes are one note
|
|
48
26
|
|
|
49
|
-
|
|
27
|
+
When several tickets are offered and nothing takes them, or the explainer names a routing, slot, provider or image condition, the cause is shared (coding-account headroom, a provider outage, a paused repository, a bad runner image). Write one summary line naming the condition and the tickets it holds. `catalyst accounts` reads coding-account status; enrolling or pausing one is the settings page.
|
|
50
28
|
|
|
51
|
-
|
|
52
|
-
2. **Does this need to block at all?** If a sane default exists, take it, record it, and file the ask anyway so the human can overrule; the work does not wait.
|
|
53
|
-
3. **Who else can move this?** Another steward, the desk, a repository owner. Pulling in a peer is preferred over pulling in the human.
|
|
29
|
+
## Escalate inward
|
|
54
30
|
|
|
55
|
-
|
|
31
|
+
The order is instrument, you, the desk (`whats-happening`), then the human, who only ever sees an ask. Before anything reaches the human:
|
|
56
32
|
|
|
57
|
-
|
|
33
|
+
1. **Can I decide this myself?** Approach, retry or abandon, rebase or re-cut, ordering: yours. Decide, record it in a bookkeeping note, move on.
|
|
34
|
+
2. **Does it need to block?** With a sane default, take it, record it, and file the ask anyway so the human can overrule.
|
|
35
|
+
3. **Who else can move it?** A peer steward, the desk or a repository owner comes before the human.
|
|
58
36
|
|
|
59
|
-
|
|
37
|
+
What survives is a product, priority or approval decision, or an action only a human can take: close their own pull request, read a review that will not converge, change a repository setting, apply the release label, answer a merge-policy question.
|
|
60
38
|
|
|
61
|
-
##
|
|
39
|
+
## Filing the ask
|
|
62
40
|
|
|
63
|
-
|
|
41
|
+
File it through `what-needs-me`, as the app actor: the question as its title, the options, the default if silent, and the stalled ticket in its blocks list so the inbox ranks it by what it holds. Search for an existing ask on the same decision first and attach to it, because two asks split one decision's urgency. Then proceed on the default and say so on the ticket.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// lib/cli.mjs — the one way a skill script reaches Catalyst Cloud: by spawning the catalyst
|
|
2
|
+
// lib/cli.mjs — the one way a skill script reaches Catalyst Cloud: by spawning the catalyst
|
|
3
3
|
// CLI. The CLI holds the SDK and the key; this file holds neither. It reads
|
|
4
4
|
// ~/.config/catalyst-cloud/customer.json (under CATALYST_SKILLS_HOME when set, else HOME) for the
|
|
5
|
-
// CLI path that login recorded and falls back to `npx @catalyst-cloud/catalyst
|
|
5
|
+
// CLI path that login recorded and falls back to `npx -p @catalyst-cloud/cli catalyst`.
|
|
6
6
|
//
|
|
7
7
|
// Exit codes every script built on this file shares: 2 = this machine is not connected, 1 = the
|
|
8
8
|
// script's own check failed, 0 = fine.
|
|
@@ -12,7 +12,9 @@ import { join } from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { CONNECT_COMMAND, hasCredential } from "./credential.mjs";
|
|
14
14
|
|
|
15
|
-
export const PACKAGE_NAME = "@catalyst-cloud/
|
|
15
|
+
export const PACKAGE_NAME = "@catalyst-cloud/cli";
|
|
16
|
+
/** The package ships two commands, so npx needs the one to run named: `npx -p <package> catalyst`. */
|
|
17
|
+
export const NPX_ARGS = ["-p", PACKAGE_NAME, "catalyst"];
|
|
16
18
|
export const NOT_CONFIGURED_EXIT = 2;
|
|
17
19
|
export const CHECK_FAILED_EXIT = 1;
|
|
18
20
|
|
|
@@ -55,7 +57,7 @@ export function cliCommand(cfg = loadCustomerConfig()) {
|
|
|
55
57
|
return { command: process.execPath, prefix: [cfg.cliPath], via: `node ${cfg.cliPath}` };
|
|
56
58
|
}
|
|
57
59
|
const npx = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
58
|
-
return { command: npx, prefix:
|
|
60
|
+
return { command: npx, prefix: NPX_ARGS, via: `npx -p ${PACKAGE_NAME} catalyst` };
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
/**
|
|
@@ -176,7 +178,7 @@ export function wantsHelp(argv) {
|
|
|
176
178
|
|
|
177
179
|
const HELP = `lib/cli.mjs — shared helper; not a command.
|
|
178
180
|
|
|
179
|
-
Resolves the catalyst
|
|
181
|
+
Resolves the catalyst CLI (the path login recorded in ${configPath()}, else npx -p ${PACKAGE_NAME} catalyst)
|
|
180
182
|
and runs one verb for the script that imports it. Run any sibling script with --help instead.`;
|
|
181
183
|
|
|
182
184
|
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
//
|
|
7
7
|
// customer.json carries exactly one credential: a personal key (`key`), or the keyless login's
|
|
8
8
|
// session (`auth`, the recommended rail). A script never reads either for its value: it spawns the
|
|
9
|
-
// catalyst
|
|
9
|
+
// catalyst CLI, which authenticates with whichever is present and refreshes a login's token
|
|
10
10
|
// itself. A new credential kind lands here, once.
|
|
11
11
|
|
|
12
12
|
/** The command that connects this machine, as every not-connected line names it. */
|
|
13
13
|
export const CONNECT_COMMAND =
|
|
14
|
-
"npx @catalyst-cloud/catalyst
|
|
14
|
+
"npx -p @catalyst-cloud/cli catalyst login (or, with a personal key: CATALYST_CLOUD_TOKEN=<your personal key> npx -p @catalyst-cloud/cli catalyst login)";
|
|
15
15
|
|
|
16
16
|
/** True when `cfg` (parsed customer.json) holds a usable credential of either kind. Never throws. */
|
|
17
17
|
export function hasCredential(cfg) {
|
|
Binary file
|