@catalyst-cloud/cli 0.13.0 → 0.14.0
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 +10 -0
- package/README.md +27 -30
- package/dist/args.js +299 -61
- package/dist/ask.js +30 -18
- package/dist/capabilities.js +7 -0
- package/dist/cli.js +125 -20
- package/dist/config.js +3 -1
- package/dist/env/draft.js +134 -0
- package/dist/env/legacy.js +54 -0
- package/dist/env/parse-env.js +50 -0
- package/dist/env/settings-toml.js +66 -0
- package/dist/env.js +126 -26
- package/dist/environment.js +1 -1
- package/dist/legacy.js +224 -0
- package/dist/onboard-consent.js +65 -0
- package/dist/onboard.js +836 -0
- package/dist/project.js +191 -0
- package/dist/ready.js +11 -1
- package/dist/release.js +1 -1
- package/dist/repo.js +297 -0
- package/dist/runtime-verb.js +1 -1
- package/dist/secret.js +25 -4
- package/dist/skill-shape.js +21 -13
- package/dist/skills.js +44 -4
- package/dist/transport.js +26 -1
- package/dist/var.js +118 -0
- package/package.json +6 -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 +18 -21
- 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/lib/project-coverage.mjs +53 -0
- package/skills/catalyst-onboard/scripts/where-am-i.mjs +929 -184
- 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 +13 -14
- 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 +20 -30
- package/skills/whats-happening/agents/openai.yaml +1 -1
- package/skills/whats-happening/references/coding-accounts.md +11 -0
- package/skills/whats-happening/references/routing-work.md +4 -33
- package/skills/whats-happening/references/settings-and-where-they-live.md +17 -0
- package/skills/whats-happening/references/stages-and-mapping.md +19 -0
- package/skills/whats-happening/references/status-reply.md +11 -31
- package/skills/whats-happening/references/the-ladder.md +20 -0
- package/skills/whats-happening/references/what-runs-next.md +13 -0
- package/skills/whats-happening/references/when-a-phase-fails.md +21 -0
- package/skills/whats-happening/references/why-is-it-stuck.md +38 -62
- 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/dist/env/declaration-rules.js +0 -163
- 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
|
@@ -1,70 +1,46 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `blocked` | a live blocking relation holds it | whoever owns the blocker closes it; when the blocker is an ask, that is the person, with their own login |
|
|
34
|
-
| `not_at_dispatch_stage` | the card is not in the team's dispatch column | the person, with their own login, moves the card to the dispatch slot (`references/reprioritising.md`) |
|
|
35
|
-
| `not_at_pr_stage` | merge is next but the card is not in the PR column | the person, with their own login, returns the card to the pr slot; an automation that bounced it should be found |
|
|
36
|
-
| `no_change_hold` | a repair round changed nothing | the person, with their own login, comments on the ticket or pushes to the branch |
|
|
37
|
-
| `waiting_on` | a merge-gate failure with no cause the cloud can repair | the person, with their own login, reads the merge-wait comment on the ticket; usually a hold label or a review they must give (`catalyst-github`) |
|
|
38
|
-
| `externally_claimed` | a worker outside the cloud holds it | that worker releases it, or the person, with their own login, removes the local-lane label |
|
|
39
|
-
| `environment_check_required` | the repository's environment check has not run | a tenant owner or admin, in settings, runs the environment check |
|
|
40
|
-
| `environment_check_failed` | the repository's environment check failed | a tenant owner or admin, in settings, fixes and reruns the environment check |
|
|
41
|
-
| `environment_check_expired` | the environment check's verdict aged out | a tenant owner or admin, in settings, reruns the environment check |
|
|
42
|
-
| `environment_check_hash_mismatch` | the environment changed since its recorded verdict | a tenant owner or admin, in settings, reruns the environment check |
|
|
43
|
-
| `repo_paused` | an operator paused the repository | an operator resumes it |
|
|
44
|
-
| `scope_overlap` | its declared file scope intersects a ticket already in flight | the person, with their own login, decides which goes first, or waits for the other ticket |
|
|
45
|
-
| `human_owned_pr` | a person's own pull request holds the ticket | the person, with their own login, closes or merges that pull request, or hands it to Catalyst; never close someone's pull request for them. No release clears this one — the release command refuses it terminally — so do not route it to `unstick` expecting one |
|
|
46
|
-
| `review_not_converging` | review and repair kept finding new problems without converging | the person, with their own login, reads the findings (`catalyst-github`) and comments on the ticket to resume; raise an ask for that read. No release clears this one — the release command refuses it terminally — so do not route it to `unstick` expecting one |
|
|
47
|
-
| `round_threshold` | the ticket spent its lifetime repair budget | the person, with their own login, answers the ask the cloud already raised, or pushes a fix; point at that ask rather than raising a second one. No release clears this one — the release command refuses it terminally — so do not route it to `unstick` expecting one |
|
|
1
|
+
# Eligibility reasons
|
|
2
|
+
|
|
3
|
+
Use explain.mjs; unknown reasons stay verbatim. Holds: when-a-phase-fails.md.
|
|
4
|
+
|
|
5
|
+
## Automatic release
|
|
6
|
+
|
|
7
|
+
Automatic waits, where no one acts: `retry_backoff`, `lease_held`, `intake_lease_held`, `later_phase_lease_held`, `environment_check_running`, `claim_storm`, `repo_at_capacity`, `stale_failure_episode`, `no_branch_to_remediate` or `branch_missing`. These cover backoff, leases, environment checks, hourly claim throttling, occupied seats, obsolete repair and budgeted missing-branch release.
|
|
8
|
+
|
|
9
|
+
| reason | release and actor |
|
|
10
|
+
| -- | -- |
|
|
11
|
+
| `runner_image_breaker` | an operator moves faulty startup image pin; one fleet alert |
|
|
12
|
+
| `routing_unavailable` | kickoff route/slot/provider failure; recovery clears it; persistence needs a workspace owner or admin, in settings |
|
|
13
|
+
| `cooling_down` with callback | cloud watches detail's condition; no one acts unless stalled, then own-login release through unstick |
|
|
14
|
+
| `wip_limit` | no one acts on this ticket; project at limit; started work must finish; inspect those tickets' own reasons |
|
|
15
|
+
|
|
16
|
+
## Human action
|
|
17
|
+
|
|
18
|
+
| reason | meaning and action |
|
|
19
|
+
| -- | -- |
|
|
20
|
+
| `ask_ticket` | the person, with their own login: labelled question, never work; answer through what-needs-me |
|
|
21
|
+
| `ask_shape_suspected` | the person, with their own login: unlabelled decision text; answer or apply contract release label |
|
|
22
|
+
| `blocked` | blocker owner closes it; for an ask, the person, with their own login, answers |
|
|
23
|
+
| `not_at_dispatch_stage` / `not_at_pr_stage` | the person, with their own login: restore dispatch/PR slot respectively; for PR, fix bouncing automation |
|
|
24
|
+
| `no_change_hold` | the person, with their own login: unchanged repair; comment/push/unstick |
|
|
25
|
+
| `waiting_on` | the person, with their own login: unrepairable PR merge gate; read merge-wait comment for hold/review owed, using catalyst-github |
|
|
26
|
+
| `externally_claimed` | the person, with their own login: outside worker owns it; worker/person removes local-lane label |
|
|
27
|
+
| `environment_check_required` / `environment_check_failed` / `environment_check_expired` / `environment_check_hash_mismatch` | absent/failed/aged/changed environment check; a workspace owner or admin, in settings, runs or fixes it |
|
|
28
|
+
| `repo_paused` | an operator resumes repository |
|
|
29
|
+
| `scope_overlap` | the person, with their own login: declared files overlap running work; choose order or wait |
|
|
30
|
+
| `human_owned_pr` | the person, with their own login: only its human owner closes, merges or hands it over; release refused |
|
|
31
|
+
| `review_not_converging` | the person, with their own login: read findings through catalyst-github, comment; release refused |
|
|
32
|
+
| `round_threshold` | the person, with their own login: lifetime repair budget spent; answer existing ask or push fix, never duplicate ask; release refused |
|
|
48
33
|
|
|
49
34
|
## Reasons a release clears once the cause is fixed
|
|
50
35
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
| reason | what it means | note | who acts |
|
|
54
|
-
| -- | -- | -- | -- |
|
|
55
|
-
| `phase_parked` / `cooling_down` (parked after repeated failures, or the round cap was spent) | the phase is parked and does not release itself | `unstick`; `explain --history` names the park and the failure class it recorded | the person, with their own login |
|
|
56
|
-
| `remediate_parked` | the repair phase itself is parked, so the failing phase has nowhere to be repaired | as above | the person, with their own login |
|
|
57
|
-
| `validate_class_spent` | this validate failure already spent its one repair round in this episode | a push or a comment saying what to change releases it on its own; otherwise `unstick` | the person, with their own login |
|
|
58
|
-
| `branch_gone` | a branch that existed was deleted | the person, with their own login, decides whether to recreate it or drop the ticket | the person, with their own login |
|
|
59
|
-
|
|
60
|
-
## Reasons that are not problems
|
|
36
|
+
After a fix, the person, with their own login, routes `phase_parked`, `cooling_down` from failures/round cap, `remediate_parked`, `validate_class_spent` and `branch_gone` to `unstick`. Read history, preview release; recreate deleted branches or drop tickets. Validate-budget also clears by push/comment/ask answer.
|
|
61
37
|
|
|
62
|
-
|
|
38
|
+
## Finished
|
|
63
39
|
|
|
64
|
-
|
|
40
|
+
Finished, so no one acts: `ticket_terminal`, `pipeline_complete` and `pr_merged`; report them as closed.
|
|
65
41
|
|
|
66
|
-
|
|
42
|
+
## Inconclusive
|
|
67
43
|
|
|
68
|
-
|
|
44
|
+
unknown with `ordering_never_published`, `ordering_stale`, `workflow_mapping_unknown`, `ticket_unknown`, `dependency_snapshot_unknown`, `blocker_unknown`, `label_snapshot_unknown`, `prior_artifact_unknown`, `scope_unknown`, `scope_occupancy_unknown` or `branch_snapshot_unknown` means unreadable, never absent. Usually recovered next pass. `workflow_mapping_unknown` does not clear without a saved mapping; nor does accompanying `ordering_never_published`. Remap with `catalyst team map <KEY>` as owner/admin; other persistent unknowns need catalyst-onboard.
|
|
69
45
|
|
|
70
|
-
`human_addressed_unlabeled_ask_suspect`
|
|
46
|
+
`human_addressed_unlabeled_ask_suspect` is advisory: list the human-assigned possible ask under waiting-on-human with a question mark.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// explain.mjs — "why is this ticket stuck?" in one paragraph. A thin wrapper over
|
|
3
|
-
// `catalyst
|
|
3
|
+
// `catalyst explain <ticket>`, which reads the cloud's eligibility explainer for the ticket's
|
|
4
4
|
// team and translates the exclusion reason, the failure block and any advisories into plain English.
|
|
5
5
|
import { mustRun, parseFlags, printHelp } from "./lib/cli.mjs";
|
|
6
6
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// lib/cli.mjs — the one way a skill script reaches Catalyst Cloud: spawn the catalyst
|
|
2
|
+
// lib/cli.mjs — the one way a skill script reaches Catalyst Cloud: spawn the catalyst CLI.
|
|
3
3
|
// The CLI holds the SDK and the key; this file holds neither. It reads customer.json only
|
|
4
4
|
// to learn where the CLI lives, and it exits 2 with one line when the machine is not connected.
|
|
5
5
|
// Run any script beside this one with --help; this file is a library and is never run directly.
|
|
@@ -8,7 +8,9 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { CONNECT_COMMAND, hasCredential } from "./credential.mjs";
|
|
10
10
|
|
|
11
|
-
export const PACKAGE = "@catalyst-cloud/
|
|
11
|
+
export const PACKAGE = "@catalyst-cloud/cli";
|
|
12
|
+
/** The package ships two commands, so npx needs the one to run named: `npx -p <package> catalyst`. */
|
|
13
|
+
export const NPX_ARGS = ["-p", PACKAGE, "catalyst"];
|
|
12
14
|
export const NOT_CONFIGURED_EXIT = 2;
|
|
13
15
|
|
|
14
16
|
export function configPath() {
|
|
@@ -36,7 +38,7 @@ function notConfigured(why) {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/**
|
|
39
|
-
* Run one catalyst
|
|
41
|
+
* Run one catalyst verb and return {code, stdout, stderr}. Uses the CLI path the login
|
|
40
42
|
* recorded when it still exists, else `npx <package>`. A CLI exit of 2 with a "not joined" or
|
|
41
43
|
* "not connected" line is turned into this script's own exit 2, so every caller sees one contract.
|
|
42
44
|
*/
|
|
@@ -44,7 +46,7 @@ export function runCli(args, opts = {}) {
|
|
|
44
46
|
const cfg = loadConfig();
|
|
45
47
|
const useRecorded = typeof cfg.cliPath === "string" && existsSync(cfg.cliPath);
|
|
46
48
|
const cmd = useRecorded ? process.execPath : "npx";
|
|
47
|
-
const argv = useRecorded ? [cfg.cliPath, ...args] : [
|
|
49
|
+
const argv = useRecorded ? [cfg.cliPath, ...args] : [...NPX_ARGS, ...args];
|
|
48
50
|
const res = spawnSync(cmd, argv, {
|
|
49
51
|
input: opts.stdin,
|
|
50
52
|
encoding: "utf8",
|
|
@@ -130,6 +132,6 @@ export function printHelp(usage, spec, notes = []) {
|
|
|
130
132
|
for (const n of names) lines.push(` --${n}${spec[n].value ? " <value>" : ""} ${spec[n].help}`);
|
|
131
133
|
lines.push("");
|
|
132
134
|
}
|
|
133
|
-
lines.push(...notes, "", "Exit codes: 0 ok · 1 the check failed or the arguments were wrong · 2 this machine is not connected
|
|
135
|
+
lines.push(...notes, "", "Exit codes: 0 ok · 1 the check failed or the arguments were wrong · 2 this machine is not connected");
|
|
134
136
|
process.stdout.write(lines.join("\n") + "\n");
|
|
135
137
|
}
|
|
@@ -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) {
|
|
@@ -1,45 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// show-my-map.mjs — this
|
|
2
|
+
// show-my-map.mjs — this account's stage map, ladder and thresholds, straight from the contract:
|
|
3
3
|
// per team, every slot with the Linear stage it maps to, its type, whether that state still exists
|
|
4
4
|
// and how the mapping was chosen; then the ladder and the live failure thresholds.
|
|
5
|
-
import {
|
|
5
|
+
import { mustRun, parseFlags, parseJson, printHelp } from "./lib/cli.mjs";
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
--team <key> only this team
|
|
13
|
-
--json one JSON document: { slots, teams, ladder, thresholds }
|
|
14
|
-
--help this text
|
|
15
|
-
|
|
16
|
-
A slot marked * is load-bearing: dispatch, intake, pr, done and canceled must be mapped for the
|
|
17
|
-
ladder to move at all; the others are informational. "(unmapped)" means the team has no stage for
|
|
18
|
-
that slot; "(state gone)" means the mapped Linear state no longer exists and needs re-mapping in
|
|
19
|
-
settings.
|
|
20
|
-
|
|
21
|
-
Exit 0, 1 on a usage error or an unknown team, 2 when this machine is not connected to a tenant or
|
|
22
|
-
the contract could not be read (the line says which).`;
|
|
7
|
+
const SPEC = {
|
|
8
|
+
team: { value: true, help: "only this team" },
|
|
9
|
+
json: { value: false, help: "one JSON document: { slots, teams, ladder, thresholds }" },
|
|
10
|
+
};
|
|
23
11
|
|
|
24
12
|
const LOAD_BEARING = new Set(["dispatch", "intake", "pr", "done", "canceled"]);
|
|
25
13
|
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
|
|
14
|
+
const { help, flags } = parseFlags(process.argv.slice(2), SPEC);
|
|
15
|
+
if (help) {
|
|
16
|
+
printHelp("node scripts/show-my-map.mjs [--team <key>] [--json] [--help]", SPEC, [
|
|
17
|
+
"Prints the contract's stage map, ladder and thresholds. Every value is read from the contract",
|
|
18
|
+
"Catalyst Cloud serves for your account. Wraps: catalyst contract --path.",
|
|
19
|
+
"A slot marked * is load-bearing: dispatch, intake, pr, done and canceled must be mapped for the",
|
|
20
|
+
'ladder to move at all. "(unmapped)" means the team has no stage for that slot; "(state gone)"',
|
|
21
|
+
"means the mapped Linear state no longer exists and the team needs re-mapping.",
|
|
22
|
+
]);
|
|
29
23
|
process.exit(0);
|
|
30
24
|
}
|
|
31
|
-
const { flags } = parseFlags(argv, { bool: ["json"], value: ["team"] });
|
|
32
25
|
|
|
33
26
|
function contractPath(path) {
|
|
34
|
-
const r =
|
|
35
|
-
|
|
36
|
-
const v = parseJson(r.stdout);
|
|
37
|
-
if (v === null || v === undefined) {
|
|
38
|
-
relayStderr(r);
|
|
39
|
-
console.error(`contract --path ${path} returned nothing readable`);
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
return { value: v, stderr: r.stderr };
|
|
27
|
+
const r = mustRun(["contract", "--path", path, "--json"]);
|
|
28
|
+
return { value: parseJson(r.stdout, `contract --path ${path}`), stderr: r.stderr };
|
|
43
29
|
}
|
|
44
30
|
|
|
45
31
|
const slotsRead = contractPath("slots");
|
|
@@ -53,7 +39,7 @@ if (flags.team) {
|
|
|
53
39
|
const want = flags.team.toUpperCase();
|
|
54
40
|
teams = teams.filter((t) => String(t.key ?? "").toUpperCase() === want);
|
|
55
41
|
if (teams.length === 0) {
|
|
56
|
-
console.error(`no team with key ${flags.team} on this
|
|
42
|
+
console.error(`no team with key ${flags.team} on this account's contract`);
|
|
57
43
|
process.exit(1);
|
|
58
44
|
}
|
|
59
45
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// snapshot.mjs — one call that answers "where are we?": the
|
|
2
|
+
// snapshot.mjs — one call that answers "where are we?": the account's contract (trimmed to what a
|
|
3
3
|
// status reply needs), what is running, what is queued, what is waiting on a human, and whether the
|
|
4
4
|
// local replica is fresh. Prints ONE JSON document on stdout and the source line on stderr.
|
|
5
5
|
import { mustRun, parseFlags, parseJson, printHelp, runCli } from "./lib/cli.mjs";
|
|
@@ -8,16 +8,18 @@ const SPEC = {
|
|
|
8
8
|
team: { value: true, help: "limit the queue and the board to this team key" },
|
|
9
9
|
board: { value: false, help: "also include open tickets grouped by stage, in the contract's slot order" },
|
|
10
10
|
limit: { value: true, help: "board: max tickets to read (default 200)" },
|
|
11
|
-
"full-contract": { value: false, help: "include the whole contract instead of the trimmed tenant block" },
|
|
11
|
+
"full-contract": { value: false, help: "include the whole contract instead of the trimmed `tenant` block" },
|
|
12
|
+
accounts: { value: false, help: "also include the coding accounts: provider, state, usage windows, walls, quarantine (never a credential)" },
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
const { help, flags } = parseFlags(process.argv.slice(2), SPEC);
|
|
15
16
|
if (help) {
|
|
16
|
-
printHelp("node scripts/snapshot.mjs [--team K] [--board] [--limit N] [--full-contract] [--help]", SPEC, [
|
|
17
|
-
"Runs, in order: catalyst
|
|
18
|
-
"
|
|
17
|
+
printHelp("node scripts/snapshot.mjs [--team K] [--board] [--accounts] [--limit N] [--full-contract] [--help]", SPEC, [
|
|
18
|
+
"Runs, in order: catalyst contract, running, queue, ask list, replica status (query issues with --board,",
|
|
19
|
+
"accounts with --accounts).",
|
|
20
|
+
"Output: one JSON document {takenAt, source, tenant, running, queue, waitingOnHuman, board?, accounts?, errors?}.",
|
|
19
21
|
"A section the cloud refused is reported under errors and the rest still prints; exit 1 in that case.",
|
|
20
|
-
"Every
|
|
22
|
+
"Every account fact (stage names, thresholds, teams) comes from the contract in this output, never from prose.",
|
|
21
23
|
]);
|
|
22
24
|
process.exit(0);
|
|
23
25
|
}
|
|
@@ -62,6 +64,8 @@ const out = {
|
|
|
62
64
|
waitingOnHuman: asks,
|
|
63
65
|
};
|
|
64
66
|
|
|
67
|
+
if (flags.accounts) out.accounts = section("accounts", ["accounts", "--json"]);
|
|
68
|
+
|
|
65
69
|
if (flags.board) {
|
|
66
70
|
const args = ["query", "issues", "--limit", String(flags.limit ?? 200), "--json"];
|
|
67
71
|
if (flags.team) args.push("--team", flags.team);
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
// declaration-rules.ts — a vendored, dated copy of catalyst-cloud's rules for a repo-scope
|
|
2
|
-
// `catalyst.env.json`, applied by `validateRepoEnvironmentDeclaration`
|
|
3
|
-
// (packages/environment/src/contract.ts:416-493 on catalyst-cloud `main`; the ticket cites line 488,
|
|
4
|
-
// which falls inside that range). That file lives in the PRIVATE `coalesce-labs/catalyst-cloud`
|
|
5
|
-
// monorepo — no sibling checkout, no npm publication (its one customer, `apps/cli`, is itself
|
|
6
|
-
// "private": true) and no network reachability from this session. Everything below is vendored
|
|
7
|
-
// secondhand from two independent, dated thoughts-pool research passes (2026-09-10 and 2026-09-17)
|
|
8
|
-
// that agreed with each other, following the same idiom this repository's own
|
|
9
|
-
// `test/skills-content.test.ts` already uses to vendor catalyst-cloud's `READINESS_CHECK_IDS` (see
|
|
10
|
-
// that file's `TEAM_CHECK_IDS` and its provenance comment): dated, cited, paired with a test, never
|
|
11
|
-
// hand-copied and trusted on its own.
|
|
12
|
-
//
|
|
13
|
-
// What a parity test built against THIS copy can, and cannot, catch: it can catch this copy DRIFTING
|
|
14
|
-
// FROM ITSELF — a rule dropped, loosened or tightened by a future edit here. It CANNOT notice a new
|
|
15
|
-
// rule landing upstream, because nothing in this repository imports the real function — there is no
|
|
16
|
-
// publish channel for it. The one fixture pinned by a real, run command transcript rather than by
|
|
17
|
-
// prose (`valid-empty-provenance.json`, in `test/fixtures/env-declaration/`) is the strongest anchor
|
|
18
|
-
// this vendored copy has; the rest are pinned by the two agreeing research passes.
|
|
19
|
-
const MAX_ENV_REFS = 128;
|
|
20
|
-
const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
21
|
-
// ⛔ A PREFIX ONLY COUNTS AT A TOKEN BOUNDARY (validate attempt 29, CR-6). These were matched with
|
|
22
|
-
// `text.includes(p)`, and "sk-" is a SUBSTRING of "task-", "risk-" and "disk-" — so a declaration
|
|
23
|
-
// whose setup step was `["npm","run","task-build"]` was refused as containing secret material. Every
|
|
24
|
-
// prefix here is identifier-shaped, so a match is real only at the start of the text or after a
|
|
25
|
-
// character that cannot be part of a token; PEM armour, which is not identifier-shaped and carries
|
|
26
|
-
// its own unmistakable delimiter, keeps a plain substring match of its own.
|
|
27
|
-
// `gho_`, `sk_live_` and `AIza` are in the 2026-09-10 pass's list of upstream's prefixes
|
|
28
|
-
// (thoughts: repos/catalyst/shared/research/2026-09-10-CTC-2020-laptop-environment-inventory.md:65),
|
|
29
|
-
// and `ghs_` is pinned as an upstream entry by the 2026-09-27 CTC-3378 plan v2 (:238). Validate
|
|
30
|
-
// attempt 31 found the first three missing here (and noted `ghs_`), so `env check` said `valid`
|
|
31
|
-
// where upstream's validator refuses.
|
|
32
|
-
// That upstream list ends "and others": this copy still cannot claim to hold every entry.
|
|
33
|
-
const KNOWN_SECRET_PREFIXES = ["ghp_", "gho_", "ghs_", "github_pat_", "sk-", "sk_live_", "AKIA", "AIza", "xoxb-", "xoxp-"];
|
|
34
|
-
const PEM_ARMOUR = "-----BEGIN";
|
|
35
|
-
const KNOWN_SECRET_PREFIX_RE = new RegExp(`(?:^|[^A-Za-z0-9_])(?:${KNOWN_SECRET_PREFIXES.map(escapeForRegExp).join("|")})`);
|
|
36
|
-
function escapeForRegExp(literal) {
|
|
37
|
-
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
38
|
-
}
|
|
39
|
-
const SECRET_ASSIGNMENT_RE = /\b([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(\S{4,})/g;
|
|
40
|
-
const SECRET_NAME_RE = /(_TOKEN|_SECRET|_KEY|_PASSWORD|_CREDENTIAL)$|^(TOKEN|SECRET|PASSWORD)$/i;
|
|
41
|
-
const REQUIRED_ARRAY_FIELDS = ["toolchains", "systemPackages", "setup", "verify", "services", "environment", "agentAssets", "provenance"];
|
|
42
|
-
const TOP_LEVEL_FIELDS = new Set(["version", ...REQUIRED_ARRAY_FIELDS]);
|
|
43
|
-
/** Empty array = valid. Never reads, and never returns, a value — only field names and paths. */
|
|
44
|
-
export function validateDeclaration(doc) {
|
|
45
|
-
if (typeof doc !== "object" || doc === null || Array.isArray(doc)) {
|
|
46
|
-
return ["the declaration must be a JSON object"];
|
|
47
|
-
}
|
|
48
|
-
const rec = doc;
|
|
49
|
-
const errors = [];
|
|
50
|
-
for (const key of Object.keys(rec))
|
|
51
|
-
if (!TOP_LEVEL_FIELDS.has(key))
|
|
52
|
-
errors.push(`unknown key "${key}"`);
|
|
53
|
-
if (rec.version !== 1)
|
|
54
|
-
errors.push("version must be 1");
|
|
55
|
-
for (const field of REQUIRED_ARRAY_FIELDS)
|
|
56
|
-
if (!Array.isArray(rec[field]))
|
|
57
|
-
errors.push(`${field} must be an array`);
|
|
58
|
-
// The rules below assume the shape above holds; a missing/wrong-typed field is reported on its own.
|
|
59
|
-
if (errors.length > 0)
|
|
60
|
-
return errors;
|
|
61
|
-
const environment = rec.environment;
|
|
62
|
-
if (environment.length > MAX_ENV_REFS)
|
|
63
|
-
errors.push(`environment has ${environment.length} entries; at most ${MAX_ENV_REFS}`);
|
|
64
|
-
for (const [i, e] of environment.entries()) {
|
|
65
|
-
if (typeof e !== "object" || e === null) {
|
|
66
|
-
errors.push(`environment[${i}] must be an object`);
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const rec2 = e;
|
|
70
|
-
if (typeof rec2.name !== "string" || !ENV_NAME_RE.test(rec2.name))
|
|
71
|
-
errors.push(`environment[${i}].name is not a valid name`);
|
|
72
|
-
if (typeof rec2.required !== "boolean")
|
|
73
|
-
errors.push(`environment[${i}].required must be a boolean`);
|
|
74
|
-
if (!Array.isArray(rec2.provenanceIds))
|
|
75
|
-
errors.push(`environment[${i}].provenanceIds must be an array`);
|
|
76
|
-
}
|
|
77
|
-
for (const field of ["setup", "verify"]) {
|
|
78
|
-
for (const [i, s] of rec[field].entries()) {
|
|
79
|
-
if (typeof s !== "object" || s === null) {
|
|
80
|
-
errors.push(`${field}[${i}] must be an object`);
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
const rec2 = s;
|
|
84
|
-
const command = rec2.command;
|
|
85
|
-
if (!Array.isArray(command) || command.length === 0 || !command.every((c) => typeof c === "string")) {
|
|
86
|
-
errors.push(`${field}[${i}].command must be an argv array (a bare shell string is refused; ["sh","-lc", …] is the explicit escape)`);
|
|
87
|
-
}
|
|
88
|
-
if (typeof rec2.timeoutSeconds !== "number")
|
|
89
|
-
errors.push(`${field}[${i}].timeoutSeconds must be a number`);
|
|
90
|
-
if (!Array.isArray(rec2.provenanceIds))
|
|
91
|
-
errors.push(`${field}[${i}].provenanceIds must be an array`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// C-2: services / provenance / agentAssets had no element check at all, so a non-object element
|
|
95
|
-
// reached the free-text sweep below and threw instead of being reported. The rule is the same one
|
|
96
|
-
// `environment` and `setup`/`verify` already apply: an element that is not an object is an error.
|
|
97
|
-
for (const field of ["services", "provenance", "agentAssets"]) {
|
|
98
|
-
for (const [i, e] of rec[field].entries()) {
|
|
99
|
-
if (typeof e !== "object" || e === null || Array.isArray(e))
|
|
100
|
-
errors.push(`${field}[${i}] must be an object`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
// assertNoSecretMaterial, LAST — a value-SHAPE refusal over every free-text field (argv strings,
|
|
104
|
-
// service image names, provenance explanations, agent-asset references), never a classifier over
|
|
105
|
-
// environment[].name: there is nothing to classify there. The refusal names the FIELD, never the
|
|
106
|
-
// matched text, so this function never prints what it found.
|
|
107
|
-
for (const { path, text } of collectFreeText(rec)) {
|
|
108
|
-
if (text.includes(PEM_ARMOUR) || KNOWN_SECRET_PREFIX_RE.test(text)) {
|
|
109
|
-
errors.push(`${path} appears to contain secret material (matches a known credential prefix)`);
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
SECRET_ASSIGNMENT_RE.lastIndex = 0;
|
|
113
|
-
let m;
|
|
114
|
-
while ((m = SECRET_ASSIGNMENT_RE.exec(text))) {
|
|
115
|
-
if (SECRET_NAME_RE.test(m[1])) {
|
|
116
|
-
errors.push(`${path} appears to contain secret material (an assignment shaped like a secret name)`);
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return errors;
|
|
122
|
-
}
|
|
123
|
-
function collectFreeText(rec) {
|
|
124
|
-
const out = [];
|
|
125
|
-
for (const field of ["setup", "verify"]) {
|
|
126
|
-
for (const [i, s] of rec[field].entries()) {
|
|
127
|
-
const rec2 = asRecord(s);
|
|
128
|
-
if (rec2 === null)
|
|
129
|
-
continue;
|
|
130
|
-
if (Array.isArray(rec2.command)) {
|
|
131
|
-
for (const c of rec2.command)
|
|
132
|
-
if (typeof c === "string")
|
|
133
|
-
out.push({ path: `${field}[${i}].command`, text: c });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
for (const [i, s] of rec.services.entries()) {
|
|
138
|
-
const rec2 = asRecord(s);
|
|
139
|
-
if (rec2 === null)
|
|
140
|
-
continue;
|
|
141
|
-
if (typeof rec2.image === "string")
|
|
142
|
-
out.push({ path: `services[${i}].image`, text: rec2.image });
|
|
143
|
-
}
|
|
144
|
-
for (const [i, p] of rec.provenance.entries()) {
|
|
145
|
-
const rec2 = asRecord(p);
|
|
146
|
-
if (rec2 === null)
|
|
147
|
-
continue;
|
|
148
|
-
if (typeof rec2.explanation === "string")
|
|
149
|
-
out.push({ path: `provenance[${i}].explanation`, text: rec2.explanation });
|
|
150
|
-
}
|
|
151
|
-
for (const [i, a] of rec.agentAssets.entries()) {
|
|
152
|
-
const rec2 = asRecord(a);
|
|
153
|
-
if (rec2 === null)
|
|
154
|
-
continue;
|
|
155
|
-
if (typeof rec2.reference === "string")
|
|
156
|
-
out.push({ path: `agentAssets[${i}].reference`, text: rec2.reference });
|
|
157
|
-
}
|
|
158
|
-
return out;
|
|
159
|
-
}
|
|
160
|
-
/** An element the caller may safely read properties off, or null — a non-object is reported above. */
|
|
161
|
-
function asRecord(value) {
|
|
162
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
163
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# Choosing a coding account
|
|
2
|
-
|
|
3
|
-
A coding account is the AI provider login the work runs on. It is the first thing to settle, because nothing else in setup produces any work without one, and because it needs nothing else in setup to be done. Read the part with `node scripts/where-am-i.mjs`; it says whether one is enrolled, whether each enrolled one can still take work, and who may enrol one.
|
|
4
|
-
|
|
5
|
-
## The first question
|
|
6
|
-
|
|
7
|
-
Ask it plainly: "Do you already have a coding account you want Catalyst to run on?" Then wait.
|
|
8
|
-
|
|
9
|
-
Say why in one line: Catalyst runs each stage of a ticket on an account you own, so you pay your own provider at your own rate and keep the usage under your own name.
|
|
10
|
-
|
|
11
|
-
## What we support today
|
|
12
|
-
|
|
13
|
-
Four kinds. Name the one they have in their words, then in the page's words.
|
|
14
|
-
|
|
15
|
-
| they say | kind | what enrolling asks for | how they get it |
|
|
16
|
-
| -- | -- | -- | -- |
|
|
17
|
-
| "a Claude subscription", "Claude Pro or Max", "I use Claude Code" | Claude, a setup token | the token `claude setup-token` prints | on their own machine: run `claude`, check `/status` shows the account they mean, then run `claude setup-token` and copy the token |
|
|
18
|
-
| "ChatGPT", "Codex", "a ChatGPT Plus or Pro plan" | Codex, an auth file | the contents of `~/.codex/auth.json` | run `codex login`, choose "Sign in with ChatGPT"; the login writes the file. `$CODEX_HOME/auth.json` if `CODEX_HOME` is set. On macOS `pbcopy < ~/.codex/auth.json` copies it |
|
|
19
|
-
| "a GLM key", "Z.ai", "a GLM coding plan" | GLM, an API key | the provider's API key | from the provider's console |
|
|
20
|
-
| "a Qwen key", "Alibaba Model Studio", "a Qwen coding plan" | Qwen, an API key | the provider's API key | from the provider's console |
|
|
21
|
-
|
|
22
|
-
A plain Anthropic or OpenAI API key is not something the page can enrol today. If that is what they have, say so in one sentence and ask whether they also have one of the four above. Do not improvise a way in.
|
|
23
|
-
|
|
24
|
-
Codex has one more thing to say, before they paste: Catalyst takes over that login. Its refresh token is single-use, so the copy on their machine stops working once Catalyst uses it. To keep using Codex locally, they sign in again afterwards, separately.
|
|
25
|
-
|
|
26
|
-
## Enrolling one
|
|
27
|
-
|
|
28
|
-
Enrolling is a browser step, by construction: the person pastes a credential into a write-only field, and nothing you run ever sees it.
|
|
29
|
-
|
|
30
|
-
1. Hand over the page the script printed for the part (Settings → AI accounts). Say: "Choose the provider, give the account a label you will recognise, and paste the credential the page asks for. Nothing on this side sees it."
|
|
31
|
-
2. Wait. Do not run anything while they are on the page.
|
|
32
|
-
3. When they say it is saved, run `node scripts/where-am-i.mjs` and read them the `coding accounts` part. An enrolled and active account, named by its label, is proof. If the part still says none is enrolled, refresh the contract once (`catalyst contract --refresh`) and read it again.
|
|
33
|
-
|
|
34
|
-
Only a workspace owner or admin can enrol one (the page still says "tenant" in places). If the person is neither, say who can, and stop this step there.
|
|
35
|
-
|
|
36
|
-
## Naming accounts
|
|
37
|
-
|
|
38
|
-
Call an account what the page calls it: its label, else its email, else its slot. `catalyst accounts --json` carries this as `displayName`. Never call one "the first account" or "account 2", and never read out a credential.
|
|
39
|
-
|
|
40
|
-
## An account that already exists
|
|
41
|
-
|
|
42
|
-
The script reads each enrolled account. Read it back in their words:
|
|
43
|
-
|
|
44
|
-
- **Active, or attested.** "Your Claude account, Work laptop, is enrolled and can take work." Move on.
|
|
45
|
-
- **Out of rotation.** Every account is deactivated. Reactivate one on its own page. Never enrol another.
|
|
46
|
-
- **Needs a new credential.** Quarantined, expired or revoked, or its last polls failed on the credential itself. Replace the credential on that account's own page, with a credential from that same login. The steps, and the login check that has to come first, are in `references/replacing-a-credential.md`. Never enrol a second account for this.
|
|
47
|
-
- **Cancelled or ended.** The subscription was cancelled, or the provider ended the account. Say so in one clause and move on: it stays for reporting, it is not used, and it is not counted against setup. Never paste a token from another login into it, which would put a live account's credential onto a dead slot, and never tell them to retire or delete it. If the subscription comes back, they reactivate it on its own page.
|
|
48
|
-
- **Could not be read.** Say the accounts could not be read this time and read again later. It is not "no accounts". Do not tell them to enrol one on this reading.
|
|
49
|
-
|
|
50
|
-
## When more than one is right
|
|
51
|
-
|
|
52
|
-
One account is enough to start. A second one, on a different provider or plan, gives the router somewhere to go when the first is at its limit. Offer it as a later improvement, not as a step now.
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# Who fixes what
|
|
2
|
-
|
|
3
|
-
Setup is eight parts. Each has one instrument, and each instrument answers about its own part and nothing else. Read this before you tell a person that something is not ready, and before you tell them to do anything about it. The product's words for what they own are Workspace, Project, Repository, Integration, Connected account and Member; the parts below are the instruments' names for the same things.
|
|
4
|
-
|
|
5
|
-
## The eight parts, and the instrument that owns each
|
|
6
|
-
|
|
7
|
-
| part | instrument | what a pass proves | what it does **not** prove | who fixes a failure, and where |
|
|
8
|
-
| -- | -- | -- | -- | -- |
|
|
9
|
-
| **machine** | `catalyst status`, and the non-team checks of `catalyst ready` | Node is new enough, this machine holds a credential, the CLI is where the config says, the contract is cached, the skills are on disk | anything at all about the tenant | the person at this keyboard, here |
|
|
10
|
-
| **person** | `catalyst me`, and `catalyst connections personal <linear|github> status` | the credential resolves to this person, shows their role and Linear identity match, and reports each personal grant's state | that their seat is active, or that they may change tenant settings | the member approves missing or expired personal grants in a browser; an owner or admin handles seat or identity conflicts in Settings → Members |
|
|
11
|
-
| **account** | `catalyst contract --path account` | a resolved Linear workspace means the tenant's Linear grant landed | that the GitHub App is installed — the contract does not carry it | a tenant owner or admin, `<their cloud>/settings/connections` |
|
|
12
|
-
| **project** | `catalyst team list`, then `team check <KEY>` and the `team:` checks of `ready` for the selected team | the live team list and the selected team's latest readiness verdict | that an unchecked team is ready | a tenant owner or admin, through `catalyst team` or `<their cloud>/settings/linear-teams` |
|
|
13
|
-
| **repository** | `catalyst contract --path merge.repositories` | the repository is registered to the account | that it is active, that a project can dispatch into it, or that its environment is declared | a tenant owner or admin, `<their cloud>/settings/repositories` |
|
|
14
|
-
| **coding accounts** | `codingAccounts` in `catalyst contract` (`catalyst accounts` on an older cloud) | an account is enrolled and active | that it has headroom left for the next phase | the enroller and page the contract names; see `references/what-a-phase-needs.md` |
|
|
15
|
-
| **repository declarations** | the `environment_declared` check in `catalyst contract --path teams`, and its per-repository notes | the project's default repository has a committed `.catalyst/catalyst.toml` in effect: ingested, valid, and its latest revision approved | that the values the names refer to exist; that any other repository is declared, unless its note says so | the person, with you, in the repository for the file; a tenant owner or admin for the approval, at the repository's Environment page under `<their cloud>/settings/repositories` |
|
|
16
|
-
| **host** | the `hosts_current` check in `catalyst contract --path teams` | no host is missing or behind, or the tenant runs none | anything before a project has been checked | the owner the contract names for `hosts_current`; see `references/what-a-phase-needs.md` |
|
|
17
|
-
|
|
18
|
-
`node scripts/where-am-i.mjs` runs all eight, in the order a person can act on them, and labels each finding with its part. Use it rather than composing this by hand.
|
|
19
|
-
|
|
20
|
-
## The two silences that are not absences
|
|
21
|
-
|
|
22
|
-
⛔ **An empty contract project list means nothing is mapped yet — it does not mean they have no projects.** The tenant contract carries a project only once someone has saved a stage mapping for it. Read the available team list with `catalyst team list`, choose one, then go to step 4. `team check --all` performs readiness checks and may create asks for every team.
|
|
23
|
-
|
|
24
|
-
⛔ **A registered repository is not a dispatchable one.** The contract's repository list carries no status and no project attachment, so a paused repository and an active one look identical there, and a repository registered without a project attached looks exactly like a correctly attached one. Registration is all it proves. If a card does not start, `catalyst explain <ticket>` names the real reason; do not conclude anything about the repository from its presence in a list.
|
|
25
|
-
|
|
26
|
-
⭐ And the general form of both: **an instrument that answers about a part it does not own will answer confidently and wrongly.** When you are unsure which part something belongs to, look it up in the table above rather than guessing from the wording of an error.
|
|
27
|
-
|
|
28
|
-
## Triaging NOT READY
|
|
29
|
-
|
|
30
|
-
`catalyst ready` prints one verdict over every check, machine and project together. That single verdict is correct for "can work run?" and useless for "what should I do now?", because it folds two parts into one word. Split it before you act:
|
|
31
|
-
|
|
32
|
-
1. Run `catalyst ready --json`. Every check has an `id`.
|
|
33
|
-
2. A check whose id begins with `team:` is a **project** finding. Everything else is a **machine** finding.
|
|
34
|
-
3. Report the two groups separately, in that order, each with its own owner.
|
|
35
|
-
|
|
36
|
-
- **A machine check failed.** This is the person's, here, now. Each failing check carries its own `fix` line; read it and do it. A missing or partial skill set is `catalyst install`; a stale contract is `catalyst contract --refresh`; a missing CLI path is one more `catalyst login`.
|
|
37
|
-
- **A project check failed.** Name the check, the project, and the owner; the `who` field carries the workspace's own owners and admins. An admin or owner can run the check, preview a stage mapping, or adopt the workflow through `catalyst team`; some failures still need provider consent or a browser action, and `<their cloud>/settings/linear-teams` is where those live. Use the check's reason to choose the next action. Re-running `ready` alone does not fix it.
|
|
38
|
-
- **A check is a note.** Notes never move the verdict. A stale or absent replica or event cache is optional; an unknown freshness probe means the cloud comparison could not be proved. A check that has never been run is waiting, not failing; a check the engine could not run is unknown, which is not a pass and not a failure. Say which it is. The API-backed skills still work; ask before starting the optional writer.
|
|
39
|
-
|
|
40
|
-
## When to stop rather than continue
|
|
41
|
-
|
|
42
|
-
- **They have no account yet.** There is no self-serve sign-up. Coalesce Labs provisions each account, and a person joins one by invitation from its admin. Say so and stop; the next step is theirs.
|
|
43
|
-
- **The account is suspended.** Setup cannot proceed and no retry changes that. Say so and name the conversation they need to have.
|
|
44
|
-
- **Their seat is not active, or they are not an owner or admin where one is required.** Say who is, and what to ask for. Do not offer a workaround.
|
|
45
|
-
- **A page said it worked and the instrument still disagrees.** Refresh the contract once (`catalyst contract --refresh`) and read it again. If it still disagrees, report both facts — what the page said and what the instrument says — and let the person decide. Do not pick one for them.
|
|
46
|
-
|
|
47
|
-
## What still happens in a browser, and why
|
|
48
|
-
|
|
49
|
-
Every step a key can do, the guide does. These are the ones it hands over, each with the reason; when a reason stops being true, the step moves to the CLI and this table changes in the same release.
|
|
50
|
-
|
|
51
|
-
| step | today | why it is still the person's |
|
|
52
|
-
| -- | -- | -- |
|
|
53
|
-
| install Node, or a coding agent | the person, on their machine | outside the CLI |
|
|
54
|
-
| approve the login | the code and URL `login` prints, in a browser | a grant only the person can give |
|
|
55
|
-
| connect the Linear integration | Settings → Connections | an OAuth grant, by construction |
|
|
56
|
-
| install the GitHub App | Settings → Connections | an OAuth grant, by construction |
|
|
57
|
-
| enrol a coding account, or replace its credential | Settings → AI accounts | the credential is theirs to paste; a CLI verb for the paste is being built |
|
|
58
|
-
| register a repository to a project | Settings → Repositories | the route exists; the CLI verb is being built |
|
|
59
|
-
| approve one repository's settings file | the repository's Environment page | the route for a key is being built |
|
|
60
|
-
| enter a secret's value | the repository's Environment page, or `secret set` | a value never passes through the agent |
|
|
61
|
-
| the person's own Linear and GitHub connected accounts | the link `connections personal … start` prints | a grant only the person can give |
|
|
62
|
-
| move the first ticket | Linear, or `write state` when they ask | their call which ticket runs first |
|
|
63
|
-
|
|
64
|
-
A project's readiness check, its stage mapping and adopting the workflow are CLI steps (`team check`, `team map`, `team adopt`) when `catalyst capabilities` says the installed CLI can run them for the person's role; the script names the command, and the page only for a person whose role cannot.
|