Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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) **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