harness init writes --lang and --title into harness.yaml unvalidated #271

Closed
opened 2026-10-13 20:32:14 +02:00 by dominik.polakovics · 1 comment

This was generated by AI during triage.

Summary

harness init substitutes --title and --lang into harness.yaml, hugo.toml and AGENTS.md as raw text: it neither validates the language tag nor renders the strings as the target file formats require. A bad --lang or a --title containing a single quote or a line break produces a site whose harness.yaml and hugo.toml do not load, and init exits 0 anyway. init must refuse invalid input before writing anything and render valid input safely.

Current behavior

Reproduced on main (3a6c020) with a binary built from ./cmd/harness:

Command init exit harness check on the new site
harness init --site s1 --lang "Bad Code" 0 exit 1: harness.yaml: languages entry "Bad Code" is not a valid language tag
harness init --site s4 --lang "en'" 0 exit 1: harness.yaml: parse …: [7:13] value is not allowed in this context
harness init --site s2 --title "Bob's Site" 0 exit 1: harness.yaml: parse …: [1:13] value is not allowed in this context (and hugo.toml line 5 is title = 'Bob's Site', equally broken)
harness init --site s3 --title $'Line1\nLine2' 0 exit 1: hugo.toml does not parse: … literal strings cannot have new lines

Cause: initcmd.substitute does strings.ReplaceAll of @@TITLE@@, @@LANG@@ and @@LANG_LABEL@@ into templates that already carry the quotes (title: '@@TITLE@@' in scaffold/site/harness.yaml, title = '@@TITLE@@' and defaultContentLanguage = '@@LANG@@' in scaffold/site/hugo.toml, # @@TITLE@@ — a deckle site in scaffold/site/AGENTS.md). initcmd.Run checks only that the target directory is empty; docs/harness.md currently documents the gap ("the code is written as given, init does not validate it").

harness lang add already gets this right for the same two files: langcmd.checkCode rejects a code that fails pagepath.ValidLangTag, langcmd.checkLabel rejects a label with a control character, and tomlQuote / yamlQuote in internal/langcmd render a string as a TOML value and a single-quoted YAML scalar respectively.

Desired behavior

  • harness init --lang <x> where x fails the language-tag rule exits 2 with a message on stderr naming the flag and the value, in the wording lang add uses ("Bad Code" is not a valid language code: use a lowercase BCP-47-ish tag like "de", "fr" or "pt-BR"). Nothing is written: the target directory is exactly as it was (absent stays absent, empty stays empty).
  • harness init --title <t> where t contains a line break or any other control character exits 2 the same way, with nothing written. Such a title can be held by none of the three files (hugo.toml TOML, harness.yaml YAML, the Markdown heading in AGENTS.md).
  • Any other title is written so that every file it lands in loads: ' in a title yields a harness.yaml and a hugo.toml that siteconfig.Load and Hugo accept, with the title read back verbatim. ", \ and non-ASCII text round-trip too. The default title and default language keep producing byte-identical output to today's scaffold (the manifest and hugo-build tests pin that).
  • The language label (English, Deutsch, else the code) is rendered through the same YAML rendering as the title, so the rule is applied in one place.

Decisions

  • Invalid --lang and an un-storable --title are usage errors (exit 2), not exit 1. docs/harness.md defines exit 2 as "a usage error", and serve already exits 2 for an invalid --addr or --preview-base-url value; both inputs here are flag values the caller typed. (lang add exits 1 on a bad code because the code is a positional reported through the --json status object; init has no --json and no status object.) Reason: a scripted init must be able to tell "you passed a bad flag" from "the directory was not empty" (exit 1).
  • One owner per rule; relocate, do not copy. Per CLAUDE.md "Every rule has exactly one owner": the language-tag rule is pagepath.ValidLangTag and init calls it. The "no control characters in a one-line config value" rule, the TOML string rendering and the YAML scalar rendering exist once already in internal/langcmd (checkLabel, tomlQuote, yamlQuote); move them to a package both initcmd and langcmd import (internal/siteconfig owns harness.yaml's shape and is imported by both, or a small new package) and have langcmd call the moved functions. Do not add a second tomlQuote/yamlQuote/checkLabel in initcmd, and do not import langcmd from initcmd.
  • Validate, then plan, then write — keep init's never-writes-on-refusal guarantee. initcmd.Run resolves every precondition before the first byte; flag validation belongs with those preconditions, before the conflict scan writes its verdict.
  • The templates stop carrying the quotes. A marker inside a quoted literal cannot be made safe by escaping the value alone across two formats; the substituted value must be rendered per file format (TOML for hugo.toml, YAML for harness.yaml, raw for the Markdown heading in AGENTS.md). How the marker expresses that (a per-format marker, or substitution that knows the destination file) is the implementer's call. The existing "unresolved @@ marker" guard in substitute must keep passing for the default scaffold.
  • Scope stays one issue. The three symptoms share one cause (raw substitution into typed config files) and one fix site.

Key interfaces

  • initcmd.Options{SiteDir, Title, Lang} and initcmd.Run — gain validation of Lang (language-tag rule) and Title (no control characters) that returns an error before scan/buildPlan; the CLI maps that error to exit 2, while *ConflictError keeps mapping to exit 1. A typed error (or a sentinel) lets cmd/harness tell the two apart.
  • initcmd.substitute(data, opts) — the markers in harness.yaml and hugo.toml must receive a rendered scalar, not raw text; AGENTS.md keeps raw text.
  • initSite in cmd/harness/main.go — the exit-code mapping; its stderr message starts with harness init: like every other refusal.
  • langcmd.checkLabel, langcmd.tomlQuote (in tomledit.go), langcmd.yamlQuote (in yamledit.go) — the owners to relocate; langcmd.checkCode is the model for the message text.
  • pagepath.ValidLangTag — the language-tag rule; the only predicate init may use for --lang.
  • Scaffold templates: scaffold/site/harness.yaml, scaffold/site/hugo.toml, scaffold/site/AGENTS.md, listed in initcmd.templatedFiles.
  • Starting hint: internal/initcmd/init.go, internal/langcmd/{langcmd,tomledit,yamledit}.go, cmd/harness/main.go.

Documentation and skills (owners per CLAUDE.md)

  • docs/harness.md, the init bullet under "Commands": replace "the code is written as given, init does not validate it" with the validated behaviour, the exit-2 refusals and that the title is quoted for each file. The harness.yaml section's "harness init writes title, languages, …" line stays true. Nothing new goes in README.md.
  • scaffold/site/AGENTS.md — documents neither init's flags nor the scaffold's quoting; no change. init writes its own title into the heading, which is not a documented behaviour.
  • Skills under scaffold/site/.claude/skills/ — setup-site, migrate-site and redo-theme mention harness init only as a prerequisite that has run; none walks through its flags. No skill change, but re-read those three mentions and confirm.
  • docs/agents/docs-map.md already routes cmd/harness and internal/initcmd to docs/harness.md; no change.

Acceptance criteria

  • harness init --site <empty-or-absent dir> --lang "Bad Code" exits 2, prints a message on stderr that names --lang and the value, and leaves the directory absent/empty.
  • The same for --lang "en'", --lang "" is unaffected (it still defaults to en), and --lang pt-BR still succeeds with {code: pt-BR, label: pt-BR}.
  • harness init --title $'a\nb' exits 2 with nothing written; a title with a tab or another control character behaves the same.
  • harness init --title "Bob's Site" exits 0; harness check --site <dir> reports 0 errors; siteconfig.Load returns Title == "Bob's Site"; hugo.toml parses and its title reads back as Bob's Site; AGENTS.md's first line is # Bob's Site — a deckle site.
  • Titles containing ", \, '' and non-ASCII text round-trip through harness.yaml and hugo.toml verbatim.
  • A title or language label that needs no escaping is written exactly as today (title: 'My Site', code: 'en', label: 'English', title = 'My Site'): the default-scaffold manifest, substitution and hugo-build tests pass unchanged apart from any new assertions.
  • checkLabel, tomlQuote and yamlQuote (or their renamed equivalents) exist once, in a package both initcmd and langcmd import; langcmd calls the moved functions; grep -rn "func tomlQuote\|func yamlQuote" finds one definition each.
  • The language-tag check in init is a call to pagepath.ValidLangTag (directly or through the shared checkCode), not a new regexp.
  • docs/harness.md no longer says init does not validate --lang, and documents the exit-2 refusals and the quoting; go test ./docs/ ./cmd/harness/ (the docs drift tests) pass.
  • harness lang add behaviour and output are unchanged (its tests pass as they are).

How to verify

  • Regression tests in internal/initcmd/init_test.go for each refused input (error returned, target directory untouched) and each round-tripped title (load harness.yaml via siteconfig.Load, parse hugo.toml with the TOML library the repo already uses, read the AGENTS.md heading). The quoted-title and bad-lang cases fail on main today.
  • Exit-code tests in cmd/harness/main_test.go next to TestInitSite…: exit 2 and stderr for the refusals, exit 1 unchanged for a non-empty target.
  • go test ./internal/initcmd/ ./internal/langcmd/ ./internal/siteconfig/ ./cmd/harness/ ./docs/ ./scaffold/ and then go test ./... (needs web/dist; a stub web/dist/index.html suffices for the Go build).
  • Manual: rebuild and rerun the four commands in the table above; the two bad inputs exit 2 with nothing written, the two titles produce sites on which harness check reports 0 errors and harness serve starts.

Assumptions to check

  • Checked during triage: internal/siteconfig imports only pagepath among internal packages, langcmd already imports it and initcmd imports no internal package at all, so siteconfig is a cycle-free home for the shared helpers. A small new package is equally fine if the helpers feel out of place there.
  • That the TOML parser Hugo uses accepts the basic-string escapes tomlQuote emits for every title the tests try (it is the same function lang add already relies on, but init's titles may hold characters lang add labels never did).
  • That goccy/go-yaml, which siteconfig.Load uses, reads '' inside a single-quoted scalar as one ' (it should; verify with the round-trip test rather than assume).
  • Checked during triage: no _test.go file asserts on the literal @@TITLE@@ / @@LANG@@ template lines, so the marker spelling can change freely. TestEmbeddedThemeMatchesScaffold compares the theme tree only.

Out of scope

  • A title containing @@ is refused today by the unresolved-marker guard with a misleading message; leave that behaviour and its message alone unless the marker redesign removes it for free.
  • A whitespace-only --title is written as is; --title "" keeps defaulting to My Site. Not changed.
  • Harmonising init's default label (English/Deutsch for en/de, else the code) with lang add's (always the code): two documented rules, not touched here.
  • Changing lang add's exit code for a bad code (1) or its messages.
  • Validating --site beyond what init does now.

Blocked by

None - can start immediately

Original report

Found while verifying docs/harness.md for #264.

  • harness init --lang "Bad Code" writes the value into harness.yaml as given; the new site's harness.yaml then fails to load (reproduced).
  • The scaffold template is title: '@@TITLE@@' with no escaping, so a --title containing a single quote very likely produces invalid YAML (read from the template, not run).

Expected: init validates --lang as a language tag (the rule pagepath owns) and quotes the title safely, refusing with exit 2 / 1 before writing anything.

> *This was generated by AI during triage.* ## Summary `harness init` substitutes `--title` and `--lang` into `harness.yaml`, `hugo.toml` and `AGENTS.md` as raw text: it neither validates the language tag nor renders the strings as the target file formats require. A bad `--lang` or a `--title` containing a single quote or a line break produces a site whose `harness.yaml` and `hugo.toml` do not load, and `init` exits 0 anyway. `init` must refuse invalid input before writing anything and render valid input safely. ## Current behavior Reproduced on `main` (3a6c020) with a binary built from `./cmd/harness`: | Command | `init` exit | `harness check` on the new site | | --- | --- | --- | | `harness init --site s1 --lang "Bad Code"` | 0 | exit 1: `harness.yaml: languages entry "Bad Code" is not a valid language tag` | | `harness init --site s4 --lang "en'"` | 0 | exit 1: `harness.yaml: parse …: [7:13] value is not allowed in this context` | | `harness init --site s2 --title "Bob's Site"` | 0 | exit 1: `harness.yaml: parse …: [1:13] value is not allowed in this context` (and `hugo.toml` line 5 is `title = 'Bob's Site'`, equally broken) | | `harness init --site s3 --title $'Line1\nLine2'` | 0 | exit 1: `hugo.toml does not parse: … literal strings cannot have new lines` | Cause: `initcmd.substitute` does `strings.ReplaceAll` of `@@TITLE@@`, `@@LANG@@` and `@@LANG_LABEL@@` into templates that already carry the quotes (`title: '@@TITLE@@'` in `scaffold/site/harness.yaml`, `title = '@@TITLE@@'` and `defaultContentLanguage = '@@LANG@@'` in `scaffold/site/hugo.toml`, `# @@TITLE@@ — a deckle site` in `scaffold/site/AGENTS.md`). `initcmd.Run` checks only that the target directory is empty; `docs/harness.md` currently documents the gap ("the code is written as given, `init` does not validate it"). `harness lang add` already gets this right for the same two files: `langcmd.checkCode` rejects a code that fails `pagepath.ValidLangTag`, `langcmd.checkLabel` rejects a label with a control character, and `tomlQuote` / `yamlQuote` in `internal/langcmd` render a string as a TOML value and a single-quoted YAML scalar respectively. ## Desired behavior - `harness init --lang <x>` where `x` fails the language-tag rule exits 2 with a message on stderr naming the flag and the value, in the wording `lang add` uses (`"Bad Code" is not a valid language code: use a lowercase BCP-47-ish tag like "de", "fr" or "pt-BR"`). Nothing is written: the target directory is exactly as it was (absent stays absent, empty stays empty). - `harness init --title <t>` where `t` contains a line break or any other control character exits 2 the same way, with nothing written. Such a title can be held by none of the three files (`hugo.toml` TOML, `harness.yaml` YAML, the Markdown heading in `AGENTS.md`). - Any other title is written so that every file it lands in loads: `'` in a title yields a `harness.yaml` and a `hugo.toml` that `siteconfig.Load` and Hugo accept, with the title read back verbatim. `"`, `\` and non-ASCII text round-trip too. The default title and default language keep producing byte-identical output to today's scaffold (the manifest and hugo-build tests pin that). - The language label (`English`, `Deutsch`, else the code) is rendered through the same YAML rendering as the title, so the rule is applied in one place. ## Decisions - **Invalid `--lang` and an un-storable `--title` are usage errors (exit 2), not exit 1.** `docs/harness.md` defines exit 2 as "a usage error", and `serve` already exits 2 for an invalid `--addr` or `--preview-base-url` value; both inputs here are flag values the caller typed. (`lang add` exits 1 on a bad code because the code is a positional reported through the `--json` status object; `init` has no `--json` and no status object.) Reason: a scripted `init` must be able to tell "you passed a bad flag" from "the directory was not empty" (exit 1). - **One owner per rule; relocate, do not copy.** Per `CLAUDE.md` "Every rule has exactly one owner": the language-tag rule is `pagepath.ValidLangTag` and `init` calls it. The "no control characters in a one-line config value" rule, the TOML string rendering and the YAML scalar rendering exist once already in `internal/langcmd` (`checkLabel`, `tomlQuote`, `yamlQuote`); move them to a package both `initcmd` and `langcmd` import (`internal/siteconfig` owns `harness.yaml`'s shape and is imported by both, or a small new package) and have `langcmd` call the moved functions. Do not add a second `tomlQuote`/`yamlQuote`/`checkLabel` in `initcmd`, and do not import `langcmd` from `initcmd`. - **Validate, then plan, then write — keep `init`'s never-writes-on-refusal guarantee.** `initcmd.Run` resolves every precondition before the first byte; flag validation belongs with those preconditions, before the conflict scan writes its verdict. - **The templates stop carrying the quotes.** A marker inside a quoted literal cannot be made safe by escaping the value alone across two formats; the substituted value must be rendered per file format (TOML for `hugo.toml`, YAML for `harness.yaml`, raw for the Markdown heading in `AGENTS.md`). How the marker expresses that (a per-format marker, or substitution that knows the destination file) is the implementer's call. The existing "unresolved `@@` marker" guard in `substitute` must keep passing for the default scaffold. - **Scope stays one issue.** The three symptoms share one cause (raw substitution into typed config files) and one fix site. ## Key interfaces - `initcmd.Options{SiteDir, Title, Lang}` and `initcmd.Run` — gain validation of `Lang` (language-tag rule) and `Title` (no control characters) that returns an error before `scan`/`buildPlan`; the CLI maps that error to exit 2, while `*ConflictError` keeps mapping to exit 1. A typed error (or a sentinel) lets `cmd/harness` tell the two apart. - `initcmd.substitute(data, opts)` — the markers in `harness.yaml` and `hugo.toml` must receive a rendered scalar, not raw text; `AGENTS.md` keeps raw text. - `initSite` in `cmd/harness/main.go` — the exit-code mapping; its stderr message starts with `harness init:` like every other refusal. - `langcmd.checkLabel`, `langcmd.tomlQuote` (in `tomledit.go`), `langcmd.yamlQuote` (in `yamledit.go`) — the owners to relocate; `langcmd.checkCode` is the model for the message text. - `pagepath.ValidLangTag` — the language-tag rule; the only predicate `init` may use for `--lang`. - Scaffold templates: `scaffold/site/harness.yaml`, `scaffold/site/hugo.toml`, `scaffold/site/AGENTS.md`, listed in `initcmd.templatedFiles`. - Starting hint: `internal/initcmd/init.go`, `internal/langcmd/{langcmd,tomledit,yamledit}.go`, `cmd/harness/main.go`. ## Documentation and skills (owners per CLAUDE.md) - `docs/harness.md`, the **`init`** bullet under "Commands": replace "the code is written as given, `init` does not validate it" with the validated behaviour, the exit-2 refusals and that the title is quoted for each file. The `harness.yaml` section's "`harness init` writes `title`, `languages`, …" line stays true. Nothing new goes in `README.md`. - `scaffold/site/AGENTS.md` — documents neither `init`'s flags nor the scaffold's quoting; no change. `init` writes its own title into the heading, which is not a documented behaviour. - Skills under `scaffold/site/.claude/skills/` — `setup-site`, `migrate-site` and `redo-theme` mention `harness init` only as a prerequisite that has run; none walks through its flags. No skill change, but re-read those three mentions and confirm. - `docs/agents/docs-map.md` already routes `cmd/harness` and `internal/initcmd` to `docs/harness.md`; no change. ## Acceptance criteria - [ ] `harness init --site <empty-or-absent dir> --lang "Bad Code"` exits 2, prints a message on stderr that names `--lang` and the value, and leaves the directory absent/empty. - [ ] The same for `--lang "en'"`, `--lang ""` is unaffected (it still defaults to `en`), and `--lang pt-BR` still succeeds with `{code: pt-BR, label: pt-BR}`. - [ ] `harness init --title $'a\nb'` exits 2 with nothing written; a title with a tab or another control character behaves the same. - [ ] `harness init --title "Bob's Site"` exits 0; `harness check --site <dir>` reports 0 errors; `siteconfig.Load` returns `Title == "Bob's Site"`; `hugo.toml` parses and its `title` reads back as `Bob's Site`; `AGENTS.md`'s first line is `# Bob's Site — a deckle site`. - [ ] Titles containing `"`, `\`, `''` and non-ASCII text round-trip through `harness.yaml` and `hugo.toml` verbatim. - [ ] A title or language label that needs no escaping is written exactly as today (`title: 'My Site'`, `code: 'en'`, `label: 'English'`, `title = 'My Site'`): the default-scaffold manifest, substitution and hugo-build tests pass unchanged apart from any new assertions. - [ ] `checkLabel`, `tomlQuote` and `yamlQuote` (or their renamed equivalents) exist once, in a package both `initcmd` and `langcmd` import; `langcmd` calls the moved functions; `grep -rn "func tomlQuote\|func yamlQuote"` finds one definition each. - [ ] The language-tag check in `init` is a call to `pagepath.ValidLangTag` (directly or through the shared `checkCode`), not a new regexp. - [ ] `docs/harness.md` no longer says `init` does not validate `--lang`, and documents the exit-2 refusals and the quoting; `go test ./docs/ ./cmd/harness/` (the docs drift tests) pass. - [ ] `harness lang add` behaviour and output are unchanged (its tests pass as they are). ## How to verify - Regression tests in `internal/initcmd/init_test.go` for each refused input (error returned, target directory untouched) and each round-tripped title (load `harness.yaml` via `siteconfig.Load`, parse `hugo.toml` with the TOML library the repo already uses, read the `AGENTS.md` heading). The quoted-title and bad-lang cases fail on `main` today. - Exit-code tests in `cmd/harness/main_test.go` next to `TestInitSite…`: exit 2 and stderr for the refusals, exit 1 unchanged for a non-empty target. - `go test ./internal/initcmd/ ./internal/langcmd/ ./internal/siteconfig/ ./cmd/harness/ ./docs/ ./scaffold/` and then `go test ./...` (needs `web/dist`; a stub `web/dist/index.html` suffices for the Go build). - Manual: rebuild and rerun the four commands in the table above; the two bad inputs exit 2 with nothing written, the two titles produce sites on which `harness check` reports 0 errors and `harness serve` starts. ## Assumptions to check - Checked during triage: `internal/siteconfig` imports only `pagepath` among internal packages, `langcmd` already imports it and `initcmd` imports no internal package at all, so `siteconfig` is a cycle-free home for the shared helpers. A small new package is equally fine if the helpers feel out of place there. - That the TOML parser Hugo uses accepts the basic-string escapes `tomlQuote` emits for every title the tests try (it is the same function `lang add` already relies on, but `init`'s titles may hold characters `lang add` labels never did). - That `goccy/go-yaml`, which `siteconfig.Load` uses, reads `''` inside a single-quoted scalar as one `'` (it should; verify with the round-trip test rather than assume). - Checked during triage: no `_test.go` file asserts on the literal `@@TITLE@@` / `@@LANG@@` template lines, so the marker spelling can change freely. `TestEmbeddedThemeMatchesScaffold` compares the theme tree only. ## Out of scope - A title containing `@@` is refused today by the unresolved-marker guard with a misleading message; leave that behaviour and its message alone unless the marker redesign removes it for free. - A whitespace-only `--title` is written as is; `--title ""` keeps defaulting to `My Site`. Not changed. - Harmonising `init`'s default label (`English`/`Deutsch` for `en`/`de`, else the code) with `lang add`'s (always the code): two documented rules, not touched here. - Changing `lang add`'s exit code for a bad code (1) or its messages. - Validating `--site` beyond what `init` does now. ## Blocked by None - can start immediately ## Original report Found while verifying docs/harness.md for #264. - `harness init --lang "Bad Code"` writes the value into `harness.yaml` as given; the new site's `harness.yaml` then fails to load (reproduced). - The scaffold template is `title: '@@TITLE@@'` with no escaping, so a `--title` containing a single quote very likely produces invalid YAML (read from the template, not run). Expected: `init` validates `--lang` as a language tag (the rule `pagepath` owns) and quotes the title safely, refusing with exit 2 / 1 before writing anything.
Author
Owner

This was generated by AI during triage.

Triage Notes

Reproduced, both halves. On main (3a6c020) harness init --lang "Bad Code", --lang "en'", --title "Bob's Site" and a title with a line break all exit 0 and leave a site on which harness check exits 1 (harness.yaml or hugo.toml does not parse). The single-quote case the report only read from the template is confirmed by running it; hugo.toml breaks the same way as harness.yaml.

Cause. initcmd.substitute string-replaces the markers into templates that already carry the quotes. harness lang add already owns the pieces init needs: pagepath.ValidLangTag via checkCode, checkLabel for control characters, and tomlQuote / yamlQuote in internal/langcmd.

Recommendation: bug, ready-for-agent. The body is now the agent brief; nothing is left open for the reporter.

One judgment call to confirm or flip: the brief settles both refusals as usage errors (exit 2), following serve's invalid-flag-value precedent and the docs' exit-code definition, even though lang add exits 1 on a bad code (it reports through a --json status object; init has none). Reply "exit 1" if you prefer lang add's convention and I will fold it in on the next run.

Ready to promote.

> *This was generated by AI during triage.* ## Triage Notes **Reproduced, both halves.** On `main` (3a6c020) `harness init --lang "Bad Code"`, `--lang "en'"`, `--title "Bob's Site"` and a title with a line break all exit 0 and leave a site on which `harness check` exits 1 (`harness.yaml` or `hugo.toml` does not parse). The single-quote case the report only read from the template is confirmed by running it; `hugo.toml` breaks the same way as `harness.yaml`. **Cause.** `initcmd.substitute` string-replaces the markers into templates that already carry the quotes. `harness lang add` already owns the pieces `init` needs: `pagepath.ValidLangTag` via `checkCode`, `checkLabel` for control characters, and `tomlQuote` / `yamlQuote` in `internal/langcmd`. **Recommendation:** `bug`, `ready-for-agent`. The body is now the agent brief; nothing is left open for the reporter. **One judgment call to confirm or flip:** the brief settles both refusals as usage errors (exit 2), following `serve`'s invalid-flag-value precedent and the docs' exit-code definition, even though `lang add` exits 1 on a bad code (it reports through a `--json` status object; `init` has none). Reply "exit 1" if you prefer `lang add`'s convention and I will fold it in on the next run. Ready to promote.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle#271
No description provided.