Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ grammar (§1.3), the `Store`/`Open` lifecycle, single-key and bundle ops,
OS-keyring backends (Keychain / Credential Manager / Secret Service / encrypted
file) with Linux fail-closed classification (§1.4), `--backend` flag plumbing,
redaction helpers, and legacy-migration helpers (§1.8). The `cache` package
implements the tier-1 universal core from `working-with-state.md` §5b
implements the tier-1 universal core from `working-with-state.md` §6b
(envelope + atomic write + freshness `Classify`). The `statedir` package
provides the shared path/dir resolver from `working-with-state.md` §5a.
provides the shared path/dir resolver from `working-with-state.md` §6a.

For component-by-component conformance status and the rollout matrix across
consumer CLIs, see `docs/working-with-state.md` §6 and §7 and
Expand Down
12 changes: 10 additions & 2 deletions docs/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ CI runs **build / test / lint** as distinct jobs, plus a PR-only title check:
| `test` | `make test` (or `make test-cover`) | push + PR |
| `lint` | `golangci-lint` per `repo-layout.md` §5 | push + PR |
| `identity-check` | assert `packaging/identity.yml` matches `.goreleaser`/winget/choco/nfpm/Homebrew (`distribution.md` §8.2) | push + PR |
| `pr-title` | assert the PR title is a conventional commit (`release.md` §1) | **pull_request only** |
| `pr-title` | assert the PR title is a conventional commit (`release.md` §1.1 grammar) **and** that the PR title and body are free of AI-tooling mentions (`repo-layout.md` §7 blocklist) | **pull_request only** |

`build`/`test`/`lint` are separate because **branch protection requires them as
independent status checks** (`repo-layout.md` §6). A single combined job exposes
Expand All @@ -66,6 +66,14 @@ no PR title to check on a `push`). Because it gates release eligibility,
optional, the gate is advisory and a non-conventional title can merge and either
mis-trigger or silently skip a release.

For the same squash-merge reason, `pr-title` also greps the **PR title and
body** against the `repo-layout.md` §7 AI-tooling blocklist: the landing
commit is built from the title plus — under the "title and description"
squash-message setting — the PR body, neither of which the local `commit-msg`
hook sees. The commit-details squash mode folds in individual commit messages
instead, and those the local hook already gates; between the two enforcement
points, every squash-message mode is covered.

**`build` is an aggregate, not the matrix.** A matrix job surfaces one check
*per leg* (`build (ubuntu-latest)`, `build (windows-latest)`, …), and that
shifting set is not a stable name branch protection can require. So the OS matrix
Expand Down Expand Up @@ -212,7 +220,7 @@ jobs:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Low (documentation:docs-reviewer): Cross-reference inconsistency: the job table (~line 56) cites release.md §1 for the conventional-commit check, but the YAML comment at this line cites release.md §1.1. One of the two should be made consistent — either both say §1 (the top-level section) or both say §1.1 (the grammar subsection).

Reply to this thread when addressed.

steps:
- uses: open-cli-collective/.github/actions/pr-title@v1 # §1 grammar
- uses: open-cli-collective/.github/actions/pr-title@v1 # release.md §1.1 grammar + repo-layout.md §7 blocklist
```

The composites pin `go-version` (from `go.mod`) and the golangci version, so a
Expand Down
75 changes: 0 additions & 75 deletions docs/data-pillar-primer.md

This file was deleted.

11 changes: 9 additions & 2 deletions docs/output-and-rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Reference implementation of the projection registry pattern (the `--fields` mach
- `--extended` adds columns; it does not replace default columns.
- Sorted most-recent-first where time-ordered (sprints, releases, deploys, etc.).
- **Separator collision:** the ` | ` triplet (space-pipe-space) is reserved. When a cell value contains it, the presentation layer MUST replace the embedded ` | ` with a single space (or a similar non-collision substitution) before emission. A naked `|` inside a value, surrounded by other characters, is fine — the triplet is the discriminator. Document the substitution choice per-CLI; do not silently mangle without telling the user.
- **Newlines in cells:** a cell value containing a newline breaks the one-row-per-line grammar entirely. The presentation layer MUST replace embedded newlines and carriage returns in cell values with a single space before emission — same posture as the separator substitution above.

```
KEY | STATUS | TYPE | PTS | ASSIGNEE | SUMMARY
Expand Down Expand Up @@ -184,7 +185,13 @@ Reference: jtk's `resolve.New(client).Board/Sprint/User` at `atlassian-cli/tools

Production **resource output has no color.** New CLIs MUST NOT add color to list/get/search output.

Color in **setup and diagnostic surfaces** — `init` wizards, `me`, `config test`, `config clear`, success/error glyphs on mutation confirmations — is acceptable and consistent with existing CLIs (jtk/cfl/sfdc/nrq use `fatih/color` decorators; gro uses `lipgloss`). Where any color is rendered, a `--no-color` flag MUST be supported and respected; `isatty` is NOT checked.
Color in **setup and diagnostic surfaces** — `init` wizards, `me`, `config test`, `config clear`, success/error glyphs on mutation confirmations — is acceptable and consistent with existing CLIs (jtk/cfl/sfdc/nrq use `fatih/color` decorators; gro uses `lipgloss`). Where any color is rendered:

- Color MUST auto-disable when the output stream is not a TTY. This is the default behavior of both recommended libraries — do NOT override it to force color onto pipes. ANSI codes must never land in a script or agent capture.
- A `--no-color` flag MUST be supported and respected.
- The `NO_COLOR` env var SHOULD be honored (both recommended libraries do).

*(Amended 2026-06-11: an earlier revision said "`isatty` is NOT checked" — that contradicted the recommended libraries' default behavior and the agent-capture use case. isatty gating is now the standard.)*

Color choice: prefer `fatih/color` (the family default; honors a `--no-color` toggle via a package-level var) or `lipgloss` (honors `NO_COLOR` env natively). Either is fine; pick one per CLI and stick with it.

Expand Down Expand Up @@ -232,7 +239,7 @@ The new docs are forward-looking. The following current divergences from this st

- **Resource-read JSON is widespread.** slck, sfdc, nrq, cfl all expose JSON on resource reads via global `-o json`; gro exposes it via per-command `--json`. Only jtk holds the §2 line (reserves JSON for `automation export`). New CLIs MUST NOT add resource-read JSON.
- **gro has no root `--no-color` flag** — `google-readonly/internal/cmd/root/root.go:107-109` registers a global `--verbose` and the credstore backend flag but no color flag. Its lipgloss styling at `google-readonly/internal/view/view.go:34` honors the `NO_COLOR` env natively, which papers over the gap for users who set the env, but the missing flag is a divergence from §8.
- **No CLI gates color on `isatty`.** This is intentional and consistent with §8 — `--no-color` is the documented opt-out.
- **isatty gating is expected via library defaults but unverified per CLI.** §8 (as amended 2026-06-11) requires color to auto-disable on non-TTY output; fatih/color and lipgloss do this by default, so conformance is expected unless a CLI overrides it — a per-CLI verification pass is tracked in cli-common#55.
- **No CLI has output goldens.** Per §9.5 this is recommended-not-normative pending the cli-common helper.

Command-surface divergences (init flag-skip failures, missing `set-credential`) are catalogued in `command-surface.md` §9. Scriptability divergences (missing `--non-interactive`, `me` not exiting non-zero) are catalogued in `scriptability.md` §9.
14 changes: 13 additions & 1 deletion docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ tradeoff for never hand-bumping the patch. The build stamps the version via
`-ldflags` from the tag; `version.txt` holds only `MAJOR.MINOR`, so there is no
full-version constant in the repo to drift.

**Run-number scope (sharp edge).** `GITHUB_RUN_NUMBER` is scoped to the
workflow **file path**. Renaming, moving, or deleting-and-recreating
`auto-release.yml` resets the count to 1, so the next tag can sort *below*
already-published versions (`v3.1.150` → `v3.1.2`) — a downgrade package
channels will refuse or mis-resolve. Never rename the auto-release workflow
file; the reusable-workflow migration (§6) keeps the local caller at the same
path for exactly this reason. If numbering ever does reset, bump `MAJOR.MINOR`
in `version.txt` before the next release so every new tag sorts above the old
line.

To release a new `MAJOR.MINOR`, bump `version.txt`. **The path gate (§3) MUST
include `version.txt`** so a deliberate `MAJOR.MINOR` bump ships on its own
merge. Current repos exclude it (§7) — meaning today a `version.txt`-only PR
Expand Down Expand Up @@ -225,7 +235,9 @@ jobs:
```

Inputs: `tag-prefix`, `version-file`, `release-paths`, `tool-paths`. Secret:
`tag-token` (the §3.1 dedicated token). Pin the `@v1` ref.
`tag-token` (the §3.1 dedicated token). Pin the `@v1` ref. Keep the caller at
`.github/workflows/auto-release.yml` — run numbers are path-scoped (§2), and a
renamed caller resets the patch sequence.

**GitHub App alternative (§3.1 preferred).** A caller job that `uses:` a
reusable workflow cannot also run `steps:`, so the App-token mint can't live in
Expand Down
42 changes: 37 additions & 5 deletions docs/repo-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ Monorepo (`atlassian-cli`): per-tool files live under `tools/<tool>/`
`go.work`, `LICENSE`, and CI. Tool-local agent entrypoints and
`docs/development.md` equivalents are allowed where they reduce navigation cost.

### §2.1 Library repos (no shipped binary)

A repo that ships no binary and publishes through no package channel —
`cli-common` itself — follows this doc under a reduced profile:

- **Required files:** `README.md`, `LICENSE`, `AGENTS.md`, `CLAUDE.md`,
`docs/development.md`, `.golangci.yml`, `Makefile`,
`.gitignore`/`.gitattributes`. Agent entrypoints are required in every repo
agents work in, library or not.
- **Not required:** `version.txt`, `.goreleaser.*`, `packaging/identity.yml`,
`CHANGELOG.md` — distribution files exist only where something ships.
- **Makefile:** the §4 library exemption applies, with `check` =
`tidy` + `lint` + `test` + `build` so a green local `check` still predicts a
green CI run.
- **Release/tagging:** semver tags are cut **manually**. `release.md`'s
auto-release and run-number patch scheme do NOT apply — a library with
co-evolving consumers must gate its tags on the consumer matrix being green
against the candidate SHA, and automating the tag would defeat that gate.
(`working-with-state.md` §6 is the concrete instance of this rule for
cli-common's state components.)
- **CI:** `ci.md` applies — build/test/lint across the three OSes,
`go-version-file: go.mod`, and `pr-title` on the same terms as every repo
(i.e., when the shared composite ships in the family rollout, `ci.md` §8).
`identity-check` is N/A (nothing ships). This profile creates no new
CI-restructuring obligation beyond `ci.md`'s existing rules.

---

## §3 Go version policy
Expand Down Expand Up @@ -122,10 +148,10 @@ green CI run.

**Library/shared-module exemption.** The full target set above applies to
**shipped-binary CLI repos**. A library or shared module that produces no binary
and ships through no package channel — e.g. `cli-common` itself, whose `make
check` is just `tidy` + `lint` + `test` — needs only `tidy`/`lint`/`test`/`build`
and is exempt from `install`/`release`/`snapshot`/`test-cover`. Do not force
distribution targets onto a repo that distributes nothing.
and ships through no package channel — e.g. `cli-common` itself (§2.1) — needs
only `tidy`/`lint`/`test`/`build`, with `check` running all four, and is exempt
from `install`/`release`/`snapshot`/`test-cover`. Do not force distribution
targets onto a repo that distributes nothing.

---

Expand Down Expand Up @@ -184,7 +210,13 @@ defaults (no config file) is non-conformant.
- **Conventional commits** drive releases (`release.md` §1).
- Commit messages MUST NOT mention AI tooling (Claude, Anthropic, ChatGPT,
Copilot, etc.). Enforce with a `commit-msg` hook that greps a blocklist and
rejects on match. Reference implementation (track it as
rejects on match. The hook alone is insufficient under squash merge: the
landing commit is built from the PR title plus, depending on the
squash-message setting, either the PR description or the branch's commit
messages — and the local hook sees only the last of those. The CI
`pr-title` check therefore greps the PR title and body against the same
blocklist (`ci.md` §2); between the two enforcement points every
squash-message mode is covered. Reference implementation (track it as
`scripts/hooks/commit-msg` and wire via `git config core.hooksPath scripts/hooks`):

```sh
Expand Down
4 changes: 2 additions & 2 deletions docs/scriptability.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Refer to `working-with-secrets.md` §1.5 — that doc is the source of truth. Su

New CLIs MUST implement both `init` (for first-time setup of multiple values at once) and `set-credential` (for credential rotation, `op run`–driven setup, MDM installers, and the multi-secret-stdin avoidance case). `sfdc` is missing `set-credential` today — that is the canonical divergence.

`set-credential` MUST also ship `--json` from the start per `output-and-rendering.md` §2 (the secrets standard target). Cross-ref: `working-with-secrets.md` §1.5.2 specifies the JSON envelope shape (`{"ref":..., "key":..., "backend":..., "written":true}`) and exit-code-per-failure-class contract.
`set-credential` MUST also ship `--json` from the start per `output-and-rendering.md` §2 (the secrets standard target). Cross-ref: `working-with-secrets.md` §1.5.2 specifies the JSON envelope shape (`{"ref":..., "key":..., "backend":..., "written":true}`) and the exit-code contract (binary success/failure is the MUST; per-class codes are advisory per §3.1).

---

Expand All @@ -76,7 +76,7 @@ New CLIs MUST implement both `init` (for first-time setup of multiple values at
### §3.1 Normative now

- **`<tool> me` MUST exit non-zero on auth failure or unreachable upstream.** This is the scripted health-check contract. nrq is the only CLI that currently enforces this (`newrelic-cli/internal/cmd/me/me.go:80-89`); slck `me` returns nil even with no tokens configured (`slack-chat-api/internal/cmd/me/me.go:101-105`) — divergence.
- **`<tool> set-credential` exits 0 on success and non-zero per failure class** — the specific failure classes are enumerated in `working-with-secrets.md` §1.5.2 (existing key + no `--overwrite`, disallowed key, keyring write error, locked keyring per §1.4). New CLIs SHOULD map these to the §3.2 taxonomy where applicable (existing-without-overwrite ≈ 1/generic, disallowed key ≈ 2/usage error, keyring write/locked ≈ 3/auth-config), but the precise code-per-class mapping is advisory until §3.2 becomes normative; the binary success/failure contract is what scripts can rely on today.
- **`<tool> set-credential` exits 0 on success and non-zero on failure** — the specific failure classes are enumerated in `working-with-secrets.md` §1.5.2 (existing key + no `--overwrite`, disallowed key, keyring write error, locked keyring per §1.4). New CLIs SHOULD map these to the §3.2 taxonomy where applicable (existing-without-overwrite ≈ 1/generic, disallowed key ≈ 2/usage error, keyring write/locked ≈ 3/auth-config), but the precise code-per-class mapping is advisory until §3.2 becomes normative; the binary success/failure contract is what scripts can rely on today.

### §3.2 Recommended target (advisory)

Expand Down
Loading
Loading