settings-2: apply Paul's approved copy and pin it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 files changed,
+105
−8
docs/site/cli.md+1 −5docs/superpowers/plans/2026-10-07-instance-settings-2-copy.json+23 −0internal/updatewords/words_approved_test.go+79 −0internal/updatewords/words_authorize.go+2 −3
diff --git a/docs/site/cli.md b/docs/site/cli.md| index 145715248..6cf74eb7b 100644 |
| --- a/docs/site/cli.md |
| +++ b/docs/site/cli.md |
| @@ -575,11 +575,7 @@ rollback history, tagged releases, anything inside the bake window, and each |
| box's own adopted versions are never pruned, so nothing promoted or recent |
| disappears by policy. `--off` goes back to keeping everything. |
| -`carlos releases authorize <app> <version>` shows a build's manifest and file |
| -digests and, when you type yes, signs a promotion record for exactly those |
| -bytes. Every promotion writes one; this is for a build promoted before |
| -records existed, so an instance can be pinned to it or recovered onto it on |
| -a box that does not already have it. |
| +`carlos releases authorize <app> <version>` is for operators. It shows a build's manifest and file digests and, when you type yes, signs a promotion record for exactly those bytes. Every promotion writes one; this is for a build promoted before records existed, so an instance can be pinned to it or recovered onto it on a box that does not already have it. |
| ### carlos version |
diff --git a/docs/superpowers/plans/2026-10-07-instance-settings-2-copy.json b/docs/superpowers/plans/2026-10-07-instance-settings-2-copy.json| new file mode 100644 |
| index 000000000..0f81b0862 |
| --- /dev/null |
| +++ b/docs/superpowers/plans/2026-10-07-instance-settings-2-copy.json |
| @@ -0,0 +1,23 @@ |
| +{ |
| + "approved_by": "Paul", |
| + "approved_on": "2026-10-09", |
| + "entries": [ |
| + {"id": "settings.authorize.usage", "file": "internal/updatewords/words_authorize.go", "text": "usage: carlos releases authorize [--app <a>] [--account <sqid-or-name>] [--yes] <version>"}, |
| + {"id": "settings.authorize.flag-yes", "file": "internal/updatewords/words_authorize.go", "text": "sign without asking"}, |
| + {"id": "settings.authorize.flag-console", "file": "internal/updatewords/words_authorize.go", "text": "console origin to authorize through (API mode only; default: the only logged-in one)"}, |
| + {"id": "settings.authorize.intro", "file": "internal/updatewords/words_authorize.go", "text": "Authorize %s %s with this manifest:"}, |
| + {"id": "settings.authorize.manifest-row", "file": "internal/updatewords/words_authorize.go", "text": " manifest %s"}, |
| + {"id": "settings.authorize.artifact-row", "file": "internal/updatewords/words_authorize.go", "text": " %s %s"}, |
| + {"id": "settings.authorize.prompt", "file": "internal/updatewords/words_authorize.go", "text": "Sign a promotion record for exactly these bytes? Type yes to continue: "}, |
| + {"id": "settings.authorize.done", "file": "internal/updatewords/words_authorize.go", "text": "authorized %s %s: it can now be pinned and recovered on any box"}, |
| + {"id": "settings.authorize.already", "file": "internal/updatewords/words_authorize.go", "text": "%s %s already has a promotion record for these bytes; nothing to sign"}, |
| + {"id": "settings.authorize.aborted", "file": "internal/updatewords/words_authorize.go", "text": "nothing signed"}, |
| + {"id": "settings.authorize.changed", "file": "internal/updatewords/words_authorize.go", "text": "the manifest of %s changed since it was shown. Nothing was signed. Run the command again"}, |
| + {"id": "settings.authorize.root-only", "file": "internal/updatewords/words_authorize.go", "text": "authorizing a build needs the deployment's root key. A scoped key cannot do it"}, |
| + {"id": "settings.authorize.not-shipped", "file": "internal/updatewords/words_authorize.go", "text": "%s has not been shipped, or its manifest cannot be read"}, |
| + {"id": "settings.authorize.no-key", "file": "internal/updatewords/words_authorize.go", "text": "this console has no release key, so it cannot sign a promotion record"}, |
| + {"id": "settings.authorize.write-failed", "file": "internal/updatewords/words_authorize.go", "text": "could not confirm the promotion record was written. Run the command again to check"}, |
| + {"id": "settings.evidence.no-record", "file": "internal/updatewords/words_authorize.go", "text": "no promotion record: pick a newer build, or ask an operator to authorize this one"}, |
| + {"id": "settings.authorize.docs", "file": "internal/updatewords/words_authorize.go", "text": "`carlos releases authorize <app> <version>` is for operators. It shows a build's manifest and file digests and, when you type yes, signs a promotion record for exactly those bytes. Every promotion writes one; this is for a build promoted before records existed, so an instance can be pinned to it or recovered onto it on a box that does not already have it."} |
| + ] |
| +} |
diff --git a/internal/updatewords/words_approved_test.go b/internal/updatewords/words_approved_test.go| new file mode 100644 |
| index 000000000..b18168ccc |
| --- /dev/null |
| +++ b/internal/updatewords/words_approved_test.go |
| @@ -0,0 +1,79 @@ |
| +package updatewords |
| + |
| +import ( |
| + "encoding/json" |
| + "os" |
| + "sort" |
| + "strings" |
| + "testing" |
| +) |
| + |
| +// The settings-2 copy Paul approved (2026-10-09). The record must name exactly |
| +// the IDs of All(), each text must be what Text returns byte for byte, and the |
| +// CLI docs page must carry the authorize paragraph verbatim. |
| +func TestApprovedCopyIsApplied(t *testing.T) { |
| + raw, err := os.ReadFile("../../docs/superpowers/plans/2026-10-07-instance-settings-2-copy.json") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + var rec struct { |
| + ApprovedBy string `json:"approved_by"` |
| + ApprovedOn string `json:"approved_on"` |
| + Entries []struct{ ID, File, Text string } |
| + } |
| + if err := json.Unmarshal(raw, &rec); err != nil { |
| + t.Fatal(err) |
| + } |
| + if rec.ApprovedBy == "" || rec.ApprovedOn == "" { |
| + t.Fatal("the record has no approval") |
| + } |
| + var got, want []string |
| + docs := "" |
| + for _, e := range rec.Entries { |
| + got = append(got, e.ID) |
| + if e.Text == "" { |
| + t.Errorf("%s: the approved text is empty", e.ID) |
| + continue |
| + } |
| + if e.File != "internal/updatewords/words_authorize.go" { |
| + t.Errorf("%s: unexpected file %s", e.ID, e.File) |
| + } |
| + if e.ID == "settings.authorize.docs" { |
| + docs = e.Text |
| + } |
| + if !has(e.ID) { |
| + continue // reported by the ID comparison below |
| + } |
| + if txt := Text(e.ID); txt != e.Text { |
| + t.Errorf("%s: text is %q, approved %q", e.ID, txt, e.Text) |
| + } |
| + } |
| + for _, d := range All() { |
| + want = append(want, d.ID) |
| + } |
| + sort.Strings(got) |
| + sort.Strings(want) |
| + if strings.Join(got, "\n") != strings.Join(want, "\n") { |
| + t.Errorf("record IDs %v, want the IDs of All() %v", got, want) |
| + } |
| + |
| + if docs == "" { |
| + t.Fatal("the record has no settings.authorize.docs entry") |
| + } |
| + page, err := os.ReadFile("../../docs/site/cli.md") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if !strings.Contains(string(page), docs) { |
| + t.Errorf("docs/site/cli.md does not contain the approved authorize paragraph %q", docs) |
| + } |
| +} |
| + |
| +func has(id string) bool { |
| + for _, d := range All() { |
| + if d.ID == id { |
| + return true |
| + } |
| + } |
| + return false |
| +} |
diff --git a/internal/updatewords/words_authorize.go b/internal/updatewords/words_authorize.go| index fb2ad946a..6aefb970d 100644 |
| --- a/internal/updatewords/words_authorize.go |
| +++ b/internal/updatewords/words_authorize.go |
| @@ -1,10 +1,9 @@ |
| package updatewords |
| func init() { |
| - // TODO-COPY: drafts awaiting Paul's copy review (copy-review/strings.json). |
| add( |
| Draft{ID: "settings.evidence.no-record", |
| - Text: "no promotion record: authorize it or pick a newer build", |
| + Text: "no promotion record: pick a newer build, or ask an operator to authorize this one", |
| Context: "Why a pin, an install request or a recovery onto an older build was refused: the build was promoted before promotion records existed, or never promoted. Shown in carlos instances list, on the instance page, and returned by the console when a pin is set."}, |
| Draft{ID: "settings.authorize.intro", |
| Text: "Authorize %s %s with this manifest:", |
| @@ -52,7 +51,7 @@ func init() { |
| Text: "console origin to authorize through (API mode only; default: the only logged-in one)", |
| Context: "Help text for the --console flag of carlos releases authorize."}, |
| Draft{ID: "settings.authorize.docs", |
| - Text: "`carlos releases authorize <app> <version>` shows a build's manifest and file digests and, when you type yes, signs a promotion record for exactly those bytes. Every promotion writes one; this is for a build promoted before records existed, so an instance can be pinned to it or recovered onto it on a box that does not already have it.", |
| + Text: "`carlos releases authorize <app> <version>` is for operators. It shows a build's manifest and file digests and, when you type yes, signs a promotion record for exactly those bytes. Every promotion writes one; this is for a build promoted before records existed, so an instance can be pinned to it or recovered onto it on a box that does not already have it.", |
| Context: "The CLI docs page's paragraph about carlos releases authorize."}, |
| ) |
| } |