@wirecat/cli-base 0.40.4 → 0.41.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/README.md +1 -0
- package/docs/standards/README.md +111 -0
- package/docs/standards/accounts.md +148 -0
- package/docs/standards/architecture.md +214 -0
- package/docs/standards/ci.md +159 -0
- package/docs/standards/commands.md +302 -0
- package/docs/standards/docs.md +214 -0
- package/docs/standards/layout.md +128 -0
- package/docs/standards/lifecycle.md +143 -0
- package/docs/standards/mcp.md +207 -0
- package/docs/standards/new-cli.md +111 -0
- package/docs/standards/output.md +256 -0
- package/docs/standards/permissions.md +295 -0
- package/docs/standards/releases.md +191 -0
- package/docs/standards/security.md +260 -0
- package/docs/standards/store.md +177 -0
- package/docs/standards/style.md +119 -0
- package/docs/standards/testing.md +222 -0
- package/docs/standards/toolchain.md +232 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ the command tree as data — the parts every CLI needs and nobody should write t
|
|
|
24
24
|
| [Shared tooling](docs/TOOLING.md) | The TypeScript, Biome, lefthook, docs and CI setup a CLI takes from here, and `cli-dev` |
|
|
25
25
|
| [Testing](docs/dev/TESTING.md) | The checks, and what each one is for |
|
|
26
26
|
| [Conventions](docs/dev/CONVENTIONS.md) | How code here is written |
|
|
27
|
+
| [Standards handbook](docs/standards/README.md) | The rules every WireCat CLI follows, one page per area (a skeleton for now) |
|
|
27
28
|
| [CLI standard](https://github.com/WireCatLabs/cli-core/blob/main/docs/dev/STANDARD.md) | The rules every WireCat CLI follows; this package holds them as data |
|
|
28
29
|
| [Changelog](CHANGELOG.md) | What each version changed, and what may break callers |
|
|
29
30
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# WireCat CLI standards
|
|
2
|
+
|
|
3
|
+
This handbook holds the rules every WireCat CLI follows: tools, layout, commands, output, MCP, accounts,
|
|
4
|
+
permissions, architecture, tests, security, documents, releases and CI. Read this page first. Then read the page
|
|
5
|
+
for the area you are about to change.
|
|
6
|
+
|
|
7
|
+
It ships in the `@wirecat/cli-base` package. A CLI that depends on cli-base has it at
|
|
8
|
+
`node_modules/@wirecat/cli-base/docs/standards/README.md`, at the same version as the `cli-dev lint` it runs.
|
|
9
|
+
|
|
10
|
+
## How to use this for a new CLI
|
|
11
|
+
|
|
12
|
+
Start with the [New CLI](new-cli.md) checklist. Each step names the check that proves it and the area page whose
|
|
13
|
+
rules it keeps. A new CLI is ready for its first release when every check under "Done when" passes.
|
|
14
|
+
|
|
15
|
+
## Which rule wins
|
|
16
|
+
|
|
17
|
+
1. **The handbook wins over a repository's own documents.** When a `CONVENTIONS.md`, `CLAUDE.md` or `AGENTS.md`
|
|
18
|
+
disagrees with a rule here, the rule here applies, and the repository document is the one to fix.
|
|
19
|
+
2. **A repository may be stricter, never looser.** For example, a smaller file size cap is fine. Turning off a
|
|
20
|
+
rule is not.
|
|
21
|
+
3. **Product rules stay local.** A rule that only makes sense for one CLI lives in that CLI's `CLAUDE.md`, under
|
|
22
|
+
"Local rules", below a link to this page.
|
|
23
|
+
|
|
24
|
+
## Areas
|
|
25
|
+
|
|
26
|
+
| Page | Area id | Rules | What it covers |
|
|
27
|
+
|---|---|---:|---|
|
|
28
|
+
| [Toolchain](toolchain.md) | `TOOL` | 19 | TypeScript, Biome, grit plugins, size caps, import cycles, spelling, Markdown |
|
|
29
|
+
| [Layout](layout.md) | `LAY` | 9 | Repository layout, entry points, exports, file size, one job per file, splitting a unit |
|
|
30
|
+
| [Commands](commands.md) | `CMD` | 27 | Paths, vocabulary, flags, arguments, effects, lifetime, query, help |
|
|
31
|
+
| [Output](output.md) | `OUT` | 22 | Data on stdout only, envelopes, errors, exit codes, partial answers, projection |
|
|
32
|
+
| [MCP](mcp.md) | `MCP` | 12 | The MCP tools, how they come from commands, what each permission shows |
|
|
33
|
+
| [Accounts](accounts.md) | `ACCT` | 10 | The explicit profile, reads across accounts, scope flags |
|
|
34
|
+
| [Permissions](permissions.md) | `PERM` | 21 | Levels, keys, defaults, hard limits, the write guard and its journal |
|
|
35
|
+
| [Architecture](architecture.md) | `ARCH` | 16 | Layers, dependency direction, vendor types, what goes in which package |
|
|
36
|
+
| [Lifecycle](lifecycle.md) | `LIFE` | 10 | Opening late, who closes, one-shot exits, long-running commands, the `serve` relay |
|
|
37
|
+
| [Store](store.md) | `STORE` | 14 | Store areas, account scope, migrations, test isolation |
|
|
38
|
+
| [Testing](testing.md) | `TEST` | 17 | Sandbox, no network, no real waits, leak tests, coverage, contracts, test matrix, parity |
|
|
39
|
+
| [Security](security.md) | `SECR` | 19 | Secrets, logs, keyring, argv, error text, scanners, workflow hardening, reporting |
|
|
40
|
+
| [Documents](docs.md) | `DOC` | 20 | User pages and developer pages, generated pages, current facts, changelog |
|
|
41
|
+
| [Releases](releases.md) | `REL` | 16 | `bin/release`, the release lock, versions, release reports, trusted publishing |
|
|
42
|
+
| [CI](ci.md) | `CI` | 12 | Hooks, the pull request budget, full validation, monthly runs |
|
|
43
|
+
| [Style](style.md) | `STYLE` | 10 | Comments, TypeScript habits, commit messages |
|
|
44
|
+
| [New CLI](new-cli.md) | — | — | The checklist for starting a new CLI |
|
|
45
|
+
|
|
46
|
+
Each count leaves out the ids that moved or merged (see "How ids work").
|
|
47
|
+
|
|
48
|
+
## How a page is laid out
|
|
49
|
+
|
|
50
|
+
Every area page has the same parts, in this order:
|
|
51
|
+
|
|
52
|
+
1. **Scope:** what the page covers, its rule id prefix, and links to the pages that own nearby rules. It may add
|
|
53
|
+
"Words used here" for terms the page defines, and "Who follows these rules today" for known gaps in a CLI.
|
|
54
|
+
2. **The rules:** under one `## Rules` heading, or under a few `##` headings that group them by theme.
|
|
55
|
+
3. **Planned — not built yet** (only on a page that needs it): an approved design that is not true yet. It has no
|
|
56
|
+
rule ids.
|
|
57
|
+
4. **Sources:** a short list of the documents the rules came from. Where one of them disagrees with the page, the
|
|
58
|
+
page holds.
|
|
59
|
+
|
|
60
|
+
## How a rule is written
|
|
61
|
+
|
|
62
|
+
Every rule has the same shape. A rule may add a sentence or a short list under its heading, before the bullets.
|
|
63
|
+
|
|
64
|
+
```markdown
|
|
65
|
+
### WC-<AREA>-<n> · <the rule, in one short sentence>
|
|
66
|
+
|
|
67
|
+
- **Why:** <one line>.
|
|
68
|
+
- **Enforced by:** <check names in backticks, joined by " · ">, or the words "review only".
|
|
69
|
+
- **Example:** <a command or a piece of code that keeps the rule>.
|
|
70
|
+
- **Exceptions:** <when the rule does not apply>, or "none".
|
|
71
|
+
- **Applies to:** <all CLIs, or which ones>. **Since:** cli-base <version>. **Rulings:** <ids>, or "none".
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- **Enforced by** names checks in one of these forms:
|
|
75
|
+
- a check in this repository: a `cli-dev lint` or `cli-dev audit` rule (`flag-synonym`), a workflow (`ci.yml`, or
|
|
76
|
+
`ci.yml:ci` for one job), a `pnpm` script (`pnpm standards:check`) or a file by its path (`src/keyring.test.ts`);
|
|
77
|
+
- a check in another repository, as `<repo>: <check>`, such as `cli-core: pnpm parity:check` or
|
|
78
|
+
`cli-core: biome noRestrictedImports`. `<repo>` is the repository's name on GitHub: `cli-core`, `cli-testing`,
|
|
79
|
+
`cli-docs`, `community`, `tg-cli`, `max-cli`, `cli-memo`, `drive-cli`, `zoom-cli` or `whatsapp-cli`. `<app>`
|
|
80
|
+
stands for each CLI the rule applies to, when every one of them runs that check in its own repository.
|
|
81
|
+
- **Since** is the cli-base release whose handbook first carried the rule. A feature can be older than its rule.
|
|
82
|
+
|
|
83
|
+
## How ids work
|
|
84
|
+
|
|
85
|
+
- A rule id is `WC-<AREA>-<n>`, for example `WC-OUT-3`. The area id is in the table above.
|
|
86
|
+
- The `WC-` prefix keeps rule ids apart from other ids in the WireCat repositories.
|
|
87
|
+
- Numbers go up within a page and are never reused or renumbered. A new rule takes the next number and goes last on
|
|
88
|
+
its page.
|
|
89
|
+
- A rule that moves to another page, or merges into another rule, keeps its id as a one-line entry, so a citation of
|
|
90
|
+
the old id still leads somewhere:
|
|
91
|
+
|
|
92
|
+
```markdown
|
|
93
|
+
### WC-CI-11 · Moved to [WC-SECR-20](security.md#wc-secr-20--pin-each-third-party-action-to-a-commit-and-give-each-workflow-read-only-defaults)
|
|
94
|
+
### WC-ARCH-12 · Merged into [WC-LIFE-6](lifecycle.md#wc-life-6--open-nothing-until-an-action-needs-it)
|
|
95
|
+
```
|
|
96
|
+
- A `cli-dev lint` or `cli-dev audit` rule keeps its own kebab-case name, such as `flag-synonym`. The handbook rule
|
|
97
|
+
names it under "Enforced by".
|
|
98
|
+
|
|
99
|
+
## What `pnpm docs:check` checks here
|
|
100
|
+
|
|
101
|
+
[`scripts/standards-ids.ts`](https://github.com/WireCatLabs/cli-base/blob/main/scripts/standards-ids.ts) reads every
|
|
102
|
+
page and fails when:
|
|
103
|
+
|
|
104
|
+
- a rule id appears twice, belongs to another page's area, or is out of order on its page;
|
|
105
|
+
- a rule has no "Enforced by" line;
|
|
106
|
+
- a name in an "Enforced by" line, on any of its lines, is not a known check. A check in this repository must exist.
|
|
107
|
+
A check in another repository cannot be read from here, so only its form is checked: a known repository name, a
|
|
108
|
+
colon and a space, and a check name. Anything else says "review only";
|
|
109
|
+
- a "Moved to" or "Merged into" entry points at an id that is not a rule;
|
|
110
|
+
- the rule count in the table above differs from the page;
|
|
111
|
+
- a page in this folder is not linked from this index.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Accounts
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
How a CLI chooses an account: the profile is always explicit, a read across accounts covers all of them and says so,
|
|
6
|
+
and scope flags work the same everywhere.
|
|
7
|
+
|
|
8
|
+
A **profile** is the name of one login of a CLI on one computer, for example `personal` or `work`. One profile can
|
|
9
|
+
hold a personal session, a bot token, or both.
|
|
10
|
+
|
|
11
|
+
Rule ids on this page are `WC-ACCT-<n>`. How to read a rule, how ids work and which rule wins are in the
|
|
12
|
+
[index](README.md).
|
|
13
|
+
|
|
14
|
+
### Who follows these rules today
|
|
15
|
+
|
|
16
|
+
cli-core resolves the profile for tg, max and wa. WC-ACCT-1 needs cli-core 0.319.0 or newer, and all three pin
|
|
17
|
+
0.321.0. memo has no messenger profile: its shared commands run without one, and they pick accounts with `--provider`
|
|
18
|
+
and `--account` (WC-ACCT-7). drive and zoom still have their own profile code: drive registers its own profiles, and
|
|
19
|
+
zoom keeps one saved profile in its config file. These are known gaps, not exceptions.
|
|
20
|
+
|
|
21
|
+
## Rules
|
|
22
|
+
|
|
23
|
+
### WC-ACCT-1 · Name the profile on every command that acts for an account
|
|
24
|
+
|
|
25
|
+
The profile is the first word on the command line, or the `<PREFIX>_PROFILE` variable. Nothing picks one for the
|
|
26
|
+
owner: not a config file, not the first profile in a list, not a profile called `default`, and not the only profile
|
|
27
|
+
on the computer.
|
|
28
|
+
|
|
29
|
+
- **Why:** which account a command speaks for is the owner's choice; a guess sends from the wrong account.
|
|
30
|
+
- **Enforced by:** `cli-core: src/cli/settings.test.ts`.
|
|
31
|
+
- **Example:** `tg personal chats list`, or `TG_PROFILE=personal tg chats list`. When no profile is named and the
|
|
32
|
+
config file still has `default_profile`, cli-core stops and says which profile to type. `config set default_profile`
|
|
33
|
+
is refused.
|
|
34
|
+
- **Exceptions:** a command that touches no account runs without a profile, for example `account list`,
|
|
35
|
+
`config show` and `commands`.
|
|
36
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-84.
|
|
37
|
+
|
|
38
|
+
### WC-ACCT-2 · The profile is a word, never a flag
|
|
39
|
+
|
|
40
|
+
There is no `--profile` option. The first word is the profile unless it names a command, and an option is never a
|
|
41
|
+
profile.
|
|
42
|
+
|
|
43
|
+
- **Why:** one place for the profile keeps every command line readable the same way, in every CLI.
|
|
44
|
+
- **Enforced by:** `cli-core: src/cli/profile.test.ts`.
|
|
45
|
+
- **Example:** `max work bot messages send …` uses the same first word as `max work chats list`. `tg --json chats list`
|
|
46
|
+
still works, because `--json` is an option.
|
|
47
|
+
- **Exceptions:** none.
|
|
48
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-84, NEED-294.
|
|
49
|
+
|
|
50
|
+
### WC-ACCT-3 · A missing profile is an error that lists the choices
|
|
51
|
+
|
|
52
|
+
When a command needs a profile and none is named, it fails with `validation_error`. The message and
|
|
53
|
+
`details.profiles` list the profiles on this computer. With no profile at all, the message says how to create one by
|
|
54
|
+
name.
|
|
55
|
+
|
|
56
|
+
- **Why:** the owner, or an agent, can fix the command from the error alone.
|
|
57
|
+
- **Enforced by:** `cli-core: src/cli/settings.test.ts`.
|
|
58
|
+
- **Example:** ``name a profile: `tg <profile> …` — profiles here: personal, work (or set TG_PROFILE)``. With none,
|
|
59
|
+
the message says to ``create one with `tg personal setup` (any name in place of personal)``.
|
|
60
|
+
- **Exceptions:** none.
|
|
61
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
62
|
+
|
|
63
|
+
### WC-ACCT-4 · A profile name is a plain word and never a command word
|
|
64
|
+
|
|
65
|
+
A name has letters, digits, dot, dash and underscore only, and starts with a letter or a digit. A name that is also a
|
|
66
|
+
top-level command, such as `chats` or `bot`, is refused when the profile is created. `default` is an ordinary name.
|
|
67
|
+
|
|
68
|
+
- **Why:** the name becomes a file name and a keyring account, and a profile named after a command could never be
|
|
69
|
+
selected.
|
|
70
|
+
- **Enforced by:** `cli-core: src/cli/profile.test.ts` · `cli-core: src/cli/settings.test.ts`.
|
|
71
|
+
- **Example:** `tg work setup` works. `tg chats setup` is refused with the list of command words to avoid.
|
|
72
|
+
`tg a/../b setup` is refused as a name.
|
|
73
|
+
- **Exceptions:** none.
|
|
74
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-294.
|
|
75
|
+
|
|
76
|
+
### WC-ACCT-5 · A profile lock pins a process to one profile
|
|
77
|
+
|
|
78
|
+
When `<PREFIX>_PROFILE_LOCK` is set, a first word or `<PREFIX>_PROFILE` that names any other profile is refused with
|
|
79
|
+
`permission_error`.
|
|
80
|
+
|
|
81
|
+
- **Why:** an agent must not escape its guards by naming a profile with fewer of them.
|
|
82
|
+
- **Enforced by:** `cli-core: src/cli/settings.test.ts`.
|
|
83
|
+
- **Example:** with `MAX_PROFILE_LOCK=agent`, `max personal messages send …` is refused, and `max messages list` runs
|
|
84
|
+
as `agent`.
|
|
85
|
+
- **Exceptions:** none.
|
|
86
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
87
|
+
|
|
88
|
+
### WC-ACCT-6 · The most specific setting wins
|
|
89
|
+
|
|
90
|
+
A setting is read from the nearest of these sections: this profile's `personal` or `bot` entry, then the profile,
|
|
91
|
+
then every personal account or every bot (`personal.defaults`, `bot.defaults`), then `defaults`.
|
|
92
|
+
|
|
93
|
+
- **Why:** naming one account says more than naming all of them, so it must not be undone by a broader section.
|
|
94
|
+
- **Enforced by:** review only.
|
|
95
|
+
- **Example:** `defaults.sends_per_hour: 30` and `profiles.work.sends_per_hour: 10` give `work` a limit of 10.
|
|
96
|
+
- **Exceptions:** permissions layer the same way, with one more rule: see [WC-PERM-7](permissions.md).
|
|
97
|
+
- **Applies to:** messenger CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-355.
|
|
98
|
+
|
|
99
|
+
### WC-ACCT-7 · Never pick an account inside a provider for the owner
|
|
100
|
+
|
|
101
|
+
When a provider has several accounts, the command takes `--account <account>` or reads all of them. It never takes the
|
|
102
|
+
first account in a list.
|
|
103
|
+
|
|
104
|
+
- **Why:** the same reason as WC-ACCT-1, one level down.
|
|
105
|
+
- **Enforced by:** review only.
|
|
106
|
+
- **Example:** `memo search mail "invoice"` reads every mailbox; `--source email:work` reads one.
|
|
107
|
+
- **Exceptions:** none.
|
|
108
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
109
|
+
|
|
110
|
+
### WC-ACCT-8 · Scope flags have one meaning in every CLI
|
|
111
|
+
|
|
112
|
+
`--account` names one account. `--provider` reads every account of one provider. `--source` takes a comma-separated
|
|
113
|
+
list of `provider:account`, a provider, a kind, or `group:<name>`. `--all-bots` also reads the other bots' copies that
|
|
114
|
+
the config allows.
|
|
115
|
+
|
|
116
|
+
- **Why:** an agent that learned a flag in one CLI must not be surprised by it in another.
|
|
117
|
+
- **Enforced by:** `flag-two-meanings` · `flag-synonym` · `option-role` · `same-flag-meaning`.
|
|
118
|
+
- **Example:** `memo search messages "invoice" --source telegram,max`.
|
|
119
|
+
- **Exceptions:** `same-flag-meaning` runs weekly and does not block a pull request.
|
|
120
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
121
|
+
|
|
122
|
+
### WC-ACCT-9 · A read across accounts covers all of them and says which it read
|
|
123
|
+
|
|
124
|
+
A read that is not limited to one account reads every account in its scope, and its answer names them, for example in
|
|
125
|
+
`coverage.accounts`.
|
|
126
|
+
|
|
127
|
+
- **Why:** an answer from part of the data must not look like an answer from all of it.
|
|
128
|
+
- **Enforced by:** review only.
|
|
129
|
+
- **Example:** `search messages` answers `coverage.accounts`; `search mail` names the mailboxes it read.
|
|
130
|
+
- **Exceptions:** a meaning search never reads wider than the running account unless `--source` says so. `search all`
|
|
131
|
+
reads meetings only when they are named. Not every cross-account read reports its accounts yet.
|
|
132
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** ss:8.
|
|
133
|
+
|
|
134
|
+
### WC-ACCT-10 · A bot reads another bot's copy only when the config and the command both say so
|
|
135
|
+
|
|
136
|
+
- **Why:** each bot's local copy belongs to that bot; reading it is a choice, not a default.
|
|
137
|
+
- **Enforced by:** review only.
|
|
138
|
+
- **Example:** `read_other_bots: ["support"]` in the bot's section, and `--all-bots` or `--bots support` on the command.
|
|
139
|
+
- **Exceptions:** none.
|
|
140
|
+
- **Applies to:** messenger CLIs. **Since:** cli-base 0.41.0. **Rulings:** bots:12.
|
|
141
|
+
|
|
142
|
+
## Sources
|
|
143
|
+
|
|
144
|
+
These documents explain the rules on this page in more detail. Where one of them disagrees with this page, this page
|
|
145
|
+
holds.
|
|
146
|
+
|
|
147
|
+
- [cli-core `STANDARD.md`, "Command names"](https://github.com/WireCatLabs/cli-core/blob/main/docs/dev/STANDARD.md#command-names), rule 1
|
|
148
|
+
- [cli-core `ARCHITECTURE.md`](https://github.com/WireCatLabs/cli-core/blob/main/docs/dev/ARCHITECTURE.md), on the account-bound store
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
How the code is split: the layers and the direction of dependencies, no vendor type above the adapter, and what
|
|
6
|
+
belongs in cli-base, in cli-core or in the CLI itself.
|
|
7
|
+
|
|
8
|
+
Rule ids on this page are `WC-ARCH-<n>`. How to read a rule, how ids work and which rule wins are in the
|
|
9
|
+
[index](README.md).
|
|
10
|
+
|
|
11
|
+
Related pages: file size, one job per file and how a large unit is split are in [Layout](layout.md); import cycles
|
|
12
|
+
and the lint that holds the layers are in [Toolchain](toolchain.md); who opens and closes a resource, what may open
|
|
13
|
+
at start-up and the `serve` relay are in [Lifecycle](lifecycle.md); the store's own rules are in [Store](store.md).
|
|
14
|
+
|
|
15
|
+
### Words used here
|
|
16
|
+
|
|
17
|
+
- **Adapter:** the code that talks to one provider (Telegram, MAX, WhatsApp, Google Drive, Zoom) and translates its
|
|
18
|
+
objects into our own types.
|
|
19
|
+
- **Port:** an interface that says what a service needs, for example `MessengerAdapter` or a store area.
|
|
20
|
+
- **Service:** a use case, written once, that both a command and an MCP tool call.
|
|
21
|
+
- **Composition root:** the place in a CLI where real implementations are chosen and connected, usually
|
|
22
|
+
`program.ts` and the command context.
|
|
23
|
+
- **Vendor type:** a type from a provider library, such as `@mtcute/*` or `ws`.
|
|
24
|
+
|
|
25
|
+
## Rules
|
|
26
|
+
|
|
27
|
+
### WC-ARCH-1 · Call only the layers below you: interface, then service, then port, then adapter
|
|
28
|
+
|
|
29
|
+
- **Why:** when a command calls an adapter directly, the MCP tool for the same job does something different.
|
|
30
|
+
- **Enforced by:** review only.
|
|
31
|
+
- **Example:** cli-core has five layers: domain (`src/domain/`), adapters, ports (`port.ts`, the store), services
|
|
32
|
+
(`src/services/`) and the interface (commands and MCP tools). Its `biome.json` is meant to refuse an import of
|
|
33
|
+
`commander` or of `src/cli/` from the services and the layers below them, and an import of a command file from
|
|
34
|
+
`src/mcp/`. What a
|
|
35
|
+
command and a service share lives below the command.
|
|
36
|
+
- **Exceptions:** none. A new adapter method goes into a named group in `port.ts`, never into the domain. Today the
|
|
37
|
+
last `noRestrictedImports` override in cli-core's `biome.json` replaces the earlier ones, so cli-core's layer,
|
|
38
|
+
provider, module and store import rules (WC-ARCH-2, WC-ARCH-8, WC-STORE-4) catch nothing until it is fixed.
|
|
39
|
+
- **Applies to:** all CLIs. Each repository's `biome.json` holds its own import rules. **Since:** cli-base 0.41.0.
|
|
40
|
+
**Rulings:** none.
|
|
41
|
+
|
|
42
|
+
### WC-ARCH-2 · Dependencies point one way: a CLI depends on cli-core, cli-core on cli-base, never back
|
|
43
|
+
|
|
44
|
+
- **Why:** a shared package that imports an app cannot be shared.
|
|
45
|
+
- **Enforced by:** review only.
|
|
46
|
+
- **Example:** cli-base has no dependency on cli-core or on any CLI. cli-core's `biome.json` is meant to refuse an import
|
|
47
|
+
of a messenger library or an adapter directory ("cli-core speaks no provider"); today it does not (WC-ARCH-1).
|
|
48
|
+
- **Exceptions:** none.
|
|
49
|
+
- **Applies to:** cli-base, cli-core and all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
50
|
+
|
|
51
|
+
### WC-ARCH-3 · Only the adapter imports a vendor library; nothing above it sees a vendor type
|
|
52
|
+
|
|
53
|
+
- **Why:** the provider library underneath must be replaceable, and the layers above must not change when it is.
|
|
54
|
+
- **Enforced by:** `max-cli: biome noRestrictedImports` · `tg-cli: biome noRestrictedImports` ·
|
|
55
|
+
`whatsapp-cli: biome noRestrictedImports`.
|
|
56
|
+
- **Example:** in max-cli, `biome.json` lets only `src/client.ts` and `src/client/` reach the protocol, and only
|
|
57
|
+
`src/adapter/` maps the client onto the shared messenger port. A field that only one provider has travels in
|
|
58
|
+
`providerMetadata`.
|
|
59
|
+
- **Exceptions:** a bot slice that talks to an official HTTP API keeps its own transport, beside the personal
|
|
60
|
+
account's adapter, never above it.
|
|
61
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-12.
|
|
62
|
+
|
|
63
|
+
### WC-ARCH-4 · Put code in the lowest shared package that can hold it
|
|
64
|
+
|
|
65
|
+
- **Why:** the CLIs keep to one standard by sharing code, not by copying text.
|
|
66
|
+
- **Enforced by:** review only.
|
|
67
|
+
- **Example:**
|
|
68
|
+
- cli-base: general CLI infrastructure that needs no domain knowledge. This includes the streams, the renderer,
|
|
69
|
+
errors and exit codes, the keyring, config and paths, clocks, retry, the command registry and format-neutral code
|
|
70
|
+
generation.
|
|
71
|
+
- cli-core: provider-neutral models, ports and use cases, even when only one CLI uses them today. Meeting discovery
|
|
72
|
+
is in cli-core although Zoom was its first user.
|
|
73
|
+
- The CLI: vendor translation, profile binding, resource lifetimes and rules that only make sense for that product.
|
|
74
|
+
- **Exceptions:** share an invariant or a real capability. Lines that only look alike are not a reason for a new
|
|
75
|
+
shared abstraction.
|
|
76
|
+
- **Applies to:** cli-base, cli-core and all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-292, NEED-440.
|
|
77
|
+
|
|
78
|
+
### WC-ARCH-5 · Build a feature that another provider could have as a shared command behind a capability
|
|
79
|
+
|
|
80
|
+
- **Why:** a command written once in cli-core gives every messenger the same path, flags and output.
|
|
81
|
+
- **Enforced by:** review only.
|
|
82
|
+
- **Example:** topics and polls are port groups and shared commands in cli-core. A messenger that lacks a capability
|
|
83
|
+
refuses the command; it does not silently do less. A bot is another messenger with fewer capabilities, and tg and
|
|
84
|
+
max share the bot code.
|
|
85
|
+
- **Exceptions:** a feature stays in one CLI only when the other providers cannot have it, such as MAX's socket server
|
|
86
|
+
or Telegram's app registration.
|
|
87
|
+
- **Applies to:** messenger CLIs (tg, max, wa). **Since:** cli-base 0.41.0. **Rulings:** NEED-820, NEED-440,
|
|
88
|
+
NEED-301, NEED-444.
|
|
89
|
+
|
|
90
|
+
### WC-ARCH-6 · Replace a use case, not a command
|
|
91
|
+
|
|
92
|
+
- **Why:** when a CLI replaces the service method, its command and its MCP tool both get the change.
|
|
93
|
+
- **Enforced by:** review only.
|
|
94
|
+
- **Example:** `Messenger.services` receives the shared services and returns only the ones it changes:
|
|
95
|
+
`services: (base) => ({ messages: { ...base.messages, list: maxList } })`. To add a subcommand, a CLI calls
|
|
96
|
+
`addCommand` on the command a factory returns.
|
|
97
|
+
- **Exceptions:** none.
|
|
98
|
+
- **Applies to:** messenger CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
99
|
+
|
|
100
|
+
### WC-ARCH-7 · Keep policy in the service; a command or MCP handler only parses input and presents the result
|
|
101
|
+
|
|
102
|
+
- **Why:** two transports that each interpret a query or a write policy drift apart.
|
|
103
|
+
- **Enforced by:** review only.
|
|
104
|
+
- **Example:** query interpretation, cursor binding, evidence checks and write policy live in the service. Each
|
|
105
|
+
caller parses its own input, so an error names `--since` in a command and `since` in a tool, but both run the same
|
|
106
|
+
method and give the same error for the same input.
|
|
107
|
+
- **Exceptions:** none.
|
|
108
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
109
|
+
|
|
110
|
+
### WC-ARCH-8 · Cross a module boundary only through the module's entry point
|
|
111
|
+
|
|
112
|
+
- **Why:** a deep import ties the caller to another module's internals.
|
|
113
|
+
- **Enforced by:** `cli-memo: biome noRestrictedImports` · `drive-cli: biome noRestrictedImports` ·
|
|
114
|
+
`zoom-cli: biome noRestrictedImports`.
|
|
115
|
+
- **Example:** in cli-core, `src/tasks/` and `src/meetings/` are entered only through their `index.ts`, `cli/`, `mcp/`
|
|
116
|
+
and `testing/` entries; its `biome.json` is meant to refuse the rest, and today does not (WC-ARCH-1). memo, drive and zoom have a "public exports only" rule. A
|
|
117
|
+
package's own entry points are its `exports`
|
|
118
|
+
([WC-LAY-5](layout.md#wc-lay-5--add-an-entry-point-as-an-exports-entry-built-from-srcindexts)).
|
|
119
|
+
- **Exceptions:** tests may import what they test.
|
|
120
|
+
- **Applies to:** all CLIs. tg, max and wa do not have the import rule yet. **Since:** cli-base 0.41.0.
|
|
121
|
+
**Rulings:** none.
|
|
122
|
+
|
|
123
|
+
### WC-ARCH-9 · Pass the narrow port or callback a use case needs, not a whole store or container
|
|
124
|
+
|
|
125
|
+
- **Why:** a narrow dependency shows what the use case touches and is easy to replace in a test.
|
|
126
|
+
- **Enforced by:** review only.
|
|
127
|
+
- **Example:** a service sees the messenger as `MessengerTraits`, only the fields it reads. The CLI's `Messenger`
|
|
128
|
+
adds how commands connect.
|
|
129
|
+
- **Exceptions:** none. Add a factory, inheritance or a dependency-injection framework only for a concrete benefit.
|
|
130
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
131
|
+
|
|
132
|
+
### WC-ARCH-10 · Pass every effect on the machine as an argument, with the real one only as the default
|
|
133
|
+
|
|
134
|
+
- **Why:** a test then replaces the clock, the keyring or the network, and never touches the real machine.
|
|
135
|
+
- **Enforced by:** review only.
|
|
136
|
+
- **Example:** clock, sleep, keyring, `env`, `fetch`, streams, stdin and the TTY check are parameters or fields of the
|
|
137
|
+
command environment. Production code uses `environment.sleep ?? realSleep`. A new effect gets a new field, not a
|
|
138
|
+
module mock.
|
|
139
|
+
- **Exceptions:** none. Watch the defaults: `new Credentials({...})` without `keyring` is the owner's real keychain,
|
|
140
|
+
and `resolvePaths` without `env` reads `process.env`.
|
|
141
|
+
- **Applies to:** all CLIs and the shared packages. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
142
|
+
|
|
143
|
+
### WC-ARCH-11 · Keep one command environment per program, cli-core's
|
|
144
|
+
|
|
145
|
+
- **Why:** two registries of the same environment must both be filled on every run, and one is always forgotten.
|
|
146
|
+
- **Enforced by:** review only.
|
|
147
|
+
- **Example:** a CLI extends `BaseEnvironment`, hands it to cli-core's `provide(program, environment)`, and its
|
|
148
|
+
commands read it with `environmentOf<Environment>(command)`, never with `process.*`.
|
|
149
|
+
- **Exceptions:** a CLI that does not use cli-core's command skeleton keeps one environment of its own.
|
|
150
|
+
- **Applies to:** CLIs built on cli-core's command skeleton. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
151
|
+
|
|
152
|
+
### WC-ARCH-12 · Merged into [WC-LIFE-6](lifecycle.md#wc-life-6--open-nothing-until-an-action-needs-it)
|
|
153
|
+
|
|
154
|
+
### WC-ARCH-13 · Merged into [WC-LIFE-1](lifecycle.md#wc-life-1--the-code-that-opens-a-resource-closes-it-so-a-one-shot-command-exits-when-it-has-answered)
|
|
155
|
+
|
|
156
|
+
### WC-ARCH-14 · Merged into [WC-LIFE-11](lifecycle.md#wc-life-11--run-commands-through-serve-when-the-messenger-allows-one-connection-per-account)
|
|
157
|
+
|
|
158
|
+
### WC-ARCH-15 · Each CLI runs its own background server, built on cli-core's process library
|
|
159
|
+
|
|
160
|
+
- **Why:** a server shared between CLIs couples their releases and their failures.
|
|
161
|
+
- **Enforced by:** review only.
|
|
162
|
+
- **Example:** cli-core's `./background` export holds the lock per app and profile, the PID check and the systemd and
|
|
163
|
+
launchd units. `tg serve` and `max serve` are each built on it.
|
|
164
|
+
- **Exceptions:** none.
|
|
165
|
+
- **Applies to:** CLIs with a background process. **Since:** cli-base 0.41.0. **Rulings:** NEED-492.
|
|
166
|
+
|
|
167
|
+
### WC-ARCH-16 · Layers below the commands never name a command
|
|
168
|
+
|
|
169
|
+
- **Why:** an adapter that prints "check `tg polls show`" breaks when the command is renamed and cannot be shared.
|
|
170
|
+
- **Enforced by:** review only.
|
|
171
|
+
- **Example:** a tg adapter error carries a typed reason, such as `{ check: "polls.show" }`. The command layer turns
|
|
172
|
+
the reason into the words `tg polls show`.
|
|
173
|
+
- **Exceptions:** none.
|
|
174
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
175
|
+
|
|
176
|
+
### WC-ARCH-17 · Merged into [WC-LAY-8](layout.md#wc-lay-8--give-each-module-one-reason-to-change)
|
|
177
|
+
|
|
178
|
+
### WC-ARCH-18 · An optional capability that is missing fails out loud
|
|
179
|
+
|
|
180
|
+
- **Why:** a request that quietly becomes a smaller one gives the caller a wrong answer that looks right.
|
|
181
|
+
- **Enforced by:** review only.
|
|
182
|
+
- **Example:** a new adapter method is optional and reached with `capability()`. When it is absent, the command
|
|
183
|
+
refuses with a reason; it does not drop the filter or the scope it could not apply.
|
|
184
|
+
- **Exceptions:** none.
|
|
185
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-820.
|
|
186
|
+
|
|
187
|
+
### WC-ARCH-19 · Shared packages print nothing and log nothing on their own
|
|
188
|
+
|
|
189
|
+
- **Why:** what is written, and where, is the host's decision; a stray line breaks machine output.
|
|
190
|
+
- **Enforced by:** review only.
|
|
191
|
+
- **Example:** cli-base hands the host a `Logger` interface, a Pino adapter and a renderer. It never calls
|
|
192
|
+
`console.log`, nor `console.error`, which the lint allows elsewhere
|
|
193
|
+
([WC-TOOL-9](toolchain.md#wc-tool-9--write-to-the-console-only-with-consoleerror-except-in-scripts)).
|
|
194
|
+
- **Exceptions:** none.
|
|
195
|
+
- **Applies to:** cli-base and cli-core. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
196
|
+
|
|
197
|
+
### WC-ARCH-20 · Keep HTTP out of cli-base's root export
|
|
198
|
+
|
|
199
|
+
- **Why:** a CLI that only speaks a socket must not depend on an HTTP stack.
|
|
200
|
+
- **Enforced by:** review only.
|
|
201
|
+
- **Example:** a helper that reads a status code or a header goes to `@wirecat/cli-base/http`. How an entry point
|
|
202
|
+
is added is [WC-LAY-5](layout.md#wc-lay-5--add-an-entry-point-as-an-exports-entry-built-from-srcindexts).
|
|
203
|
+
- **Exceptions:** none.
|
|
204
|
+
- **Applies to:** cli-base. **Since:** cli-base 0.41.0. **Rulings:** NEED-15.
|
|
205
|
+
|
|
206
|
+
## Sources
|
|
207
|
+
|
|
208
|
+
These documents explain the rules on this page in more detail. Where one of them disagrees with this page, this page
|
|
209
|
+
holds.
|
|
210
|
+
|
|
211
|
+
- [cli-base `MODULARITY.md`](https://github.com/WireCatLabs/cli-base/blob/main/docs/dev/MODULARITY.md)
|
|
212
|
+
- [cli-base `ARCHITECTURE.md`](https://github.com/WireCatLabs/cli-base/blob/main/docs/dev/ARCHITECTURE.md)
|
|
213
|
+
- [cli-core `ARCHITECTURE.md`](https://github.com/WireCatLabs/cli-core/blob/main/docs/dev/ARCHITECTURE.md)
|
|
214
|
+
- [cli-core `ADAPTERS.md`](https://github.com/WireCatLabs/cli-core/blob/main/docs/dev/ADAPTERS.md)
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# CI
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
What runs where: the local hooks, the checks on every pull request and their time budget, full validation on each
|
|
6
|
+
merge to `main`, and the monthly runs.
|
|
7
|
+
|
|
8
|
+
Rule ids on this page are `WC-CI-<n>`. How to read a rule, how ids work and which rule wins are in the
|
|
9
|
+
[index](README.md). What each tool checks is on the [Toolchain page](toolchain.md); the release workflow is on the
|
|
10
|
+
[Releases page](releases.md); which scanners must exist and how workflows are hardened is on the
|
|
11
|
+
[Security page](security.md).
|
|
12
|
+
|
|
13
|
+
"The ten repositories" on this page means the six CLIs (tg, max, memo, drive, zoom, wa), cli-base, cli-core,
|
|
14
|
+
cli-testing, cli-docs, plus community itself.
|
|
15
|
+
|
|
16
|
+
## Rules
|
|
17
|
+
|
|
18
|
+
### WC-CI-1 · Run only staged lint and a staged secret scan before a commit
|
|
19
|
+
|
|
20
|
+
- **Why:** a hook that takes more than a second makes people skip it, which is worse than a small hook.
|
|
21
|
+
- **Enforced by:** `pnpm standards:check` · `standards.yml:standards`.
|
|
22
|
+
- **Example:** the shared `lefthook` config runs, in parallel, Biome on the staged files and
|
|
23
|
+
`gitleaks git --staged --redact`. A clone without gitleaks gets a warning and can still commit; CI scans anyway.
|
|
24
|
+
The `prepare` script installs the hooks; `LEFTHOOK=0 git commit` skips them for one commit.
|
|
25
|
+
- **Exceptions:** none.
|
|
26
|
+
- **Applies to:** the ten repositories. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
27
|
+
|
|
28
|
+
### WC-CI-2 · Add no push hook, and no hook that installs, builds, checks types, generates or tests
|
|
29
|
+
|
|
30
|
+
- **Why:** every check in a hook runs again in CI, so a slow hook only costs time and teaches people to bypass hooks.
|
|
31
|
+
- **Enforced by:** review only.
|
|
32
|
+
- **Example:** a repository's `lefthook.yml` holds only the `extends` line for `config/wirecat/lefthook.yml`. An agent
|
|
33
|
+
does not run `standards:check`, the full lint, spelling, a build or the test suite on each commit or push; it runs a
|
|
34
|
+
targeted check when it needs one.
|
|
35
|
+
- **Exceptions:** none. cli-base `config/lefthook.yml` still has a pre-push typecheck, but no repository extends it.
|
|
36
|
+
- **Applies to:** the ten repositories. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
37
|
+
|
|
38
|
+
### WC-CI-3 · Run the fast standards job on every pull request and every push to `main`
|
|
39
|
+
|
|
40
|
+
- **Why:** it catches broken presets, lint, Markdown, types and new secrets in well under a minute, before review.
|
|
41
|
+
- **Enforced by:** `standards.yml:standards`.
|
|
42
|
+
- **Example:** the job does one cached install with lifecycle scripts off, then runs `pnpm standards:check`,
|
|
43
|
+
`pnpm lint`, `pnpm docs:lint`, `pnpm run --if-present ci:quick` and a gitleaks scan of the new commits only. A new
|
|
44
|
+
commit cancels the run it replaces. The job times out after five minutes, a ceiling for a stalled setup, not an
|
|
45
|
+
expected time.
|
|
46
|
+
- **Exceptions:** none. Runner queues and cold downloads are outside the budget.
|
|
47
|
+
- **Applies to:** the ten repositories. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
48
|
+
|
|
49
|
+
### WC-CI-4 · Keep `ci:quick` to early bug checks that finish within two minutes
|
|
50
|
+
|
|
51
|
+
- **Why:** it is the only build-level check on a pull request, so it must stay fast enough that nobody wants it gone.
|
|
52
|
+
- **Enforced by:** `standards.yml:standards` · `pnpm ci:quick`.
|
|
53
|
+
- **Example:** what each repository runs today:
|
|
54
|
+
- tg, max, memo and wa: `pnpm typecheck && pnpm build && pnpm lint:commands`;
|
|
55
|
+
- drive: `pnpm typecheck && pnpm lint:commands`, which builds first;
|
|
56
|
+
- zoom, cli-base and cli-testing: `pnpm typecheck`;
|
|
57
|
+
- cli-core: `pnpm typecheck` and its command lint test;
|
|
58
|
+
- cli-docs: its unit tests, without the one that needs generated pages;
|
|
59
|
+
- community: its script tests.
|
|
60
|
+
- **Exceptions:** never put live-account suites, agent suites, full tests, coverage or platform jobs here.
|
|
61
|
+
- **Applies to:** the ten repositories. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
62
|
+
|
|
63
|
+
### WC-CI-5 · Keep full tests, builds of documents, parity and platform suites out of pull request CI
|
|
64
|
+
|
|
65
|
+
- **Why:** pull request CI is the step people wait on; the heavy suites run once on `main`, where a failure still
|
|
66
|
+
shows within minutes of the merge.
|
|
67
|
+
- **Enforced by:** review only.
|
|
68
|
+
- **Example:** on a pull request, only the standards job, the changelog guard and small path-scoped jobs run. Coverage,
|
|
69
|
+
contracts, the test matrix, `docs:check`, parity, Windows and macOS run in full validation (WC-CI-7).
|
|
70
|
+
- **Exceptions:** a path-scoped job that tests one platform-specific file may run on pull requests, such as cli-base
|
|
71
|
+
`macos.yml` for the path code. cli-docs and community run `security-pr.yml`, a new-commit secret scan. Third-party
|
|
72
|
+
security apps may post their own checks.
|
|
73
|
+
- **Applies to:** the ten repositories. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
74
|
+
|
|
75
|
+
### WC-CI-6 · Run the changelog guard on every pull request
|
|
76
|
+
|
|
77
|
+
- **Why:** the guard is the only check that reads the changelog on a pull request.
|
|
78
|
+
- **Enforced by:** `changelog-guard.yml:guard`.
|
|
79
|
+
- **Example:** each repository's `changelog-guard.yml` calls
|
|
80
|
+
`WireCatLabs/cli-base/.github/workflows/changelog-guard.yml` at a pinned commit, on `opened`, `edited`,
|
|
81
|
+
`synchronize` and `reopened`. The changelog rules it applies are on the [Documents page](docs.md).
|
|
82
|
+
- **Exceptions:** cli-testing, cli-docs and community have no changelog guard yet.
|
|
83
|
+
- **Applies to:** the CLIs, cli-base and cli-core. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
84
|
+
|
|
85
|
+
### WC-CI-7 · Run full validation once on each merge to `main`, on Linux, without publishing
|
|
86
|
+
|
|
87
|
+
- **Why:** a merge that breaks the full suite shows up the same hour, not at the next release.
|
|
88
|
+
- **Enforced by:** `main.yml:validation` · `node-ci.yml:check` · `node-ci.yml:bun` · `node-ci.yml:secrets`.
|
|
89
|
+
- **Example:** `main.yml` calls the repository's own `ci.yml`, which calls cli-base `node-ci.yml`. That runs
|
|
90
|
+
`pnpm lint`, `pnpm typecheck`, `pnpm test:coverage`, `pnpm build`, then the project's `checks` lines (for max:
|
|
91
|
+
contracts, the no-leak scan, the version check, `pnpm generate` and `git diff --exit-code`, the test matrix,
|
|
92
|
+
`docs:check` and parity), a Bun job, and a gitleaks scan of the whole history.
|
|
93
|
+
- **Exceptions:** platform suites (Windows, macOS, install checks) are skipped here and run at release, monthly or on
|
|
94
|
+
request. Nothing in this run publishes or tags. cli-docs runs its full validation before each deployment and monthly
|
|
95
|
+
instead; community has no build to validate.
|
|
96
|
+
- **Applies to:** the CLIs, cli-base, cli-core and cli-testing. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
97
|
+
|
|
98
|
+
### WC-CI-8 · Call cli-base `node-ci.yml` from `ci.yml`, pinned to a tag or commit
|
|
99
|
+
|
|
100
|
+
- **Why:** one shared workflow keeps the full suite the same everywhere, and a pin keeps a cli-base change from
|
|
101
|
+
breaking ten repositories at once.
|
|
102
|
+
- **Enforced by:** `ci.yml:ci`.
|
|
103
|
+
- **Example:** `uses: WireCatLabs/cli-base/.github/workflows/node-ci.yml@<commit> # v0.31.0`, with the project's own
|
|
104
|
+
checks in the `checks` input and `bun-run` for the Bun job. A job that only one repository needs stays beside it in
|
|
105
|
+
that repository's `ci.yml`.
|
|
106
|
+
- **Exceptions:** never pin to `main`. cli-base itself calls `./.github/workflows/node-ci.yml`. cli-docs has its own
|
|
107
|
+
full workflow for the site, and community has none.
|
|
108
|
+
- **Applies to:** the CLIs, cli-core and cli-testing. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
109
|
+
|
|
110
|
+
### WC-CI-9 · Run full validation and release security on the first day of each month, without publishing
|
|
111
|
+
|
|
112
|
+
- **Why:** a repository with no merges for weeks still meets new advisories, new runner images and expired pins.
|
|
113
|
+
- **Enforced by:** `monthly.yml:validation` · `monthly.yml:security`.
|
|
114
|
+
- **Example:** `monthly.yml`, from the community template, calls the repository's `ci.yml` and the pinned cli-base
|
|
115
|
+
`release-checks.yml`. Each repository has its own minute, between 02:17 and 03:21 UTC, so runs do not overlap.
|
|
116
|
+
- **Exceptions:** community has no build, so it runs only the release security checks, at 03:47 UTC. A monthly run
|
|
117
|
+
never publishes a package or deploys the site.
|
|
118
|
+
- **Applies to:** the ten repositories. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
119
|
+
|
|
120
|
+
### WC-CI-10 · Run heavy security checks only at release, monthly or on request
|
|
121
|
+
|
|
122
|
+
- **Why:** a pull request needs a fast secret check; source scanners take minutes and find nothing new on most
|
|
123
|
+
changes.
|
|
124
|
+
- **Enforced by:** `release.yml:checks` · `monthly.yml:security`.
|
|
125
|
+
- **Example:** cli-base `release-checks.yml` runs the production dependency audit, the workflow scan (`zizmor`),
|
|
126
|
+
CodeQL and a full-history gitleaks scan. A release depends on it. A pull request runs only the new-commit gitleaks
|
|
127
|
+
scan in the standards job.
|
|
128
|
+
- **Exceptions:** none. Do not add a heavy scanner to an editor hook, a stop hook or an ordinary pull request job.
|
|
129
|
+
- **Applies to:** the ten repositories. **Since:** cli-base 0.41.0. **Rulings:** sc:9.
|
|
130
|
+
|
|
131
|
+
### WC-CI-11 · Moved to [WC-SECR-20](security.md#wc-secr-20--pin-each-third-party-action-to-a-commit-and-give-each-workflow-read-only-defaults)
|
|
132
|
+
|
|
133
|
+
### WC-CI-12 · Merge to `main` only through a pull request whose checks all passed
|
|
134
|
+
|
|
135
|
+
- **Why:** a red merge breaks `main` for every parallel branch, and an admin merge skips the only gate there is.
|
|
136
|
+
- **Enforced by:** review only.
|
|
137
|
+
- **Example:** `gh pr checks <number>` shows every line as pass before `gh pr merge`. Never merge with `--admin`. A
|
|
138
|
+
release changes `main` the same way
|
|
139
|
+
([WC-REL-2](releases.md#wc-rel-2--main-changes-only-through-a-pull-request-also-during-a-release)).
|
|
140
|
+
- **Exceptions:** none.
|
|
141
|
+
- **Applies to:** the ten repositories. **Since:** cli-base 0.41.0. **Rulings:** NEED-381.
|
|
142
|
+
|
|
143
|
+
### WC-CI-13 · Start no live messenger or account test from a workflow
|
|
144
|
+
|
|
145
|
+
- **Why:** a live test acts on a real account; only a person who chose to run it may start it.
|
|
146
|
+
- **Enforced by:** review only.
|
|
147
|
+
- **Example:** the offline contracts run in full validation. The live scenarios run only by hand, through the CLI's
|
|
148
|
+
live-test procedure, on test accounts.
|
|
149
|
+
- **Exceptions:** none.
|
|
150
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
151
|
+
|
|
152
|
+
## Sources
|
|
153
|
+
|
|
154
|
+
These documents explain the rules on this page in more detail. Where one of them disagrees with this page, this page
|
|
155
|
+
holds. The procedures stay in their own homes.
|
|
156
|
+
|
|
157
|
+
- [community check schedule](https://github.com/WireCatLabs/community/blob/main/standards/CHECKS.md): the rough timings
|
|
158
|
+
- [cli-testing CI folder](https://github.com/WireCatLabs/cli-testing/tree/main/docs/ci): the cross-project timing
|
|
159
|
+
table, the monthly runbook and the contract runner
|