You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: unify JSON envelope with the fleet core schema
Breaking JSON contract change, schema_version 2026-07-25 (ADR-0003):
the top-level errors array becomes a single error object (extras in
error.details), warnings move under meta, and meta gains profile,
duration_ms, and request_id while dropping generated_at. Adds a global
--pretty flag. Secret references migrate from the {env: NAME} map form
to the fleet-wide env:NAME string (setup wizard and docs updated; no
back-compat shim). All gocritic exclusions removed with the ~190
findings fixed in code, so .golangci.yml is canonical.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-`errors` — array of error objects (supports multi-error envelopes for partial failures).
67
-
- Each error: `code`, `message`, `category`, `retryable`.
69
+
-`error` — a single structured error object. When several errors are aggregated (e.g. multi-item partial failures), the primary error is the object and the remaining errors are carried in `error.details`.
70
+
- Each error: `code`, `message`, `category`, `retryable`, and optional `details` (an array of the same shape).
68
71
69
72
## Error Taxonomy
70
73
@@ -113,6 +116,9 @@ Provider errors are classified as:
113
116
114
117
## Schema Versioning
115
118
116
-
-**Major** — breaking contract changes.
117
-
-**Minor** — additive compatible fields.
118
-
-**Patch** — implementation-only changes (no schema change).
119
+
`schema_version` is a date string. It bumps to the date of the change on any
120
+
breaking change to the envelope shape; additive, backward-compatible fields do
121
+
not bump it. The `2026-07-25` version unified the envelope with the CLI fleet:
122
+
a single `error` object replaced the previous `errors[]` array, warnings moved
123
+
under `meta.warnings`, `meta` gained `profile`, `duration_ms`, and `request_id`,
124
+
and the redundant `meta.generated_at` was dropped.
Copy file name to clipboardExpand all lines: docs/CONFIG.md
+8-14Lines changed: 8 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,33 +4,27 @@
4
4
5
5
## Config Shape
6
6
7
-
Secret references use YAML objects with exactly one `env` key:
7
+
Secret references use the string form `env:NAME`, which resolves to the value of environment variable `NAME` at load time (an error is raised if it is unset):
8
8
9
9
```yaml
10
10
database:
11
11
path: ~/.money/data/money.db
12
-
encryption_key:
13
-
env: MONEY_DB_ENCRYPTION_KEY
12
+
encryption_key: "env:MONEY_DB_ENCRYPTION_KEY"
14
13
15
14
providers:
16
15
plaid:
17
-
client_id:
18
-
env: PLAID_CLIENT_ID
19
-
secret:
20
-
env: PLAID_SECRET
16
+
client_id: "env:PLAID_CLIENT_ID"
17
+
secret: "env:PLAID_SECRET"
21
18
environment: sandbox
22
19
products: [transactions]
23
20
country_codes: [US]
24
21
additional_consented_products: [investments]
25
22
required_if_supported_products: [liabilities]
26
23
optional_products: [auth]
27
-
redirect_uri:
28
-
env: PLAID_REDIRECT_URI
24
+
redirect_uri: "env:PLAID_REDIRECT_URI"
29
25
bridge:
30
-
client_id:
31
-
env: BRIDGE_CLIENT_ID
32
-
client_secret:
33
-
env: BRIDGE_CLIENT_SECRET
26
+
client_id: "env:BRIDGE_CLIENT_ID"
27
+
client_secret: "env:BRIDGE_CLIENT_SECRET"
34
28
```
35
29
36
30
Direct scalar values are allowed only for non-secrets such as `database.path`, `providers.plaid.environment`, `products`, `country_codes`, and Plaid Link consent product lists. Direct scalar secrets are accepted for manually edited files, but config loading emits a structured warning recommending `.env` references.
@@ -61,7 +55,7 @@ Profile names must be alphanumeric, hyphen, or underscore only; path traversal c
61
55
7. Validate required fields for the command being executed.
62
56
8. Return config values plus structured warnings, such as direct secrets in YAML or broad env-file permissions.
63
57
64
-
Environment variables complete explicit references; they do not form a magic override chain. For example, `PLAID_SECRET` is used only when config says `secret: { env:PLAID_SECRET }` or a setup/configure command writes that reference.
58
+
Environment variables complete explicit references; they do not form a magic override chain. For example, `PLAID_SECRET` is used only when config says `secret: "env:PLAID_SECRET"` or a setup/configure command writes that reference.
65
59
66
60
Plaid Dashboard OAuth bootstrap state is stored outside YAML at `plaid-dashboard-auth.json` beside the resolved config file. It is local bootstrap state for `money plaid login`, written `0600`, and may include Dashboard access/refresh tokens plus selected `team_id` and `client_id`. Provider API credentials still use the normal `.env` plus YAML `env:` references model.
Copy file name to clipboardExpand all lines: docs/CONTRACTS.md
+9-9Lines changed: 9 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# Command Contracts
2
2
3
-
`money` commands write one JSON envelope to stdout when `--json` is set. Human mode may print compact text, but automation should use JSON.
3
+
`money` commands write one JSON envelope to stdout when `--json` is set. Human mode may print compact text, but automation should use JSON. JSON output is compact by default; pass `--pretty` for indented output.
Errors use `ok: false` and `errors[]` entries with `code`, `message`, `category`, and `retryable`. JSON mode does not require stderr parsing.
28
+
Errors use `ok: false` and a single `error` object with `code`, `message`, `category`, and `retryable` (plus optional `details[]` when errors are aggregated). Warnings, when present, are carried under `meta.warnings`. JSON mode does not require stderr parsing.
The `--profile` flag selects a named configuration profile. The default profile uses `~/.money/config.yaml`; custom profiles use `~/.money/profiles/<name>/config.yaml`.
138
138
@@ -308,4 +308,4 @@ These commands never include Plaid API secrets, Dashboard OAuth tokens, masked s
308
308
309
309
## Monarch Compatibility Notes
310
310
311
-
The command names and stdout/stderr discipline follow Monarch CLI habits where useful. `money` differs by using object-wrapped collection fields, multi-error envelopes, explicit source provenance, encrypted local storage, and BYOK Provider adapters.
311
+
The command names and stdout/stderr discipline follow Monarch CLI habits where useful. `money` differs by using object-wrapped collection fields, a single structured `error` object (with aggregated errors in `error.details`), explicit source provenance, encrypted local storage, and BYOK Provider adapters.
`money` is one of five sibling CLIs (canvas, zenodo, flickr, monarch, money) that
8
+
all emit a machine-readable JSON envelope. Round 1 aligned the tools on a shared
9
+
core, but `money` still carried three shapes the rest of the fleet did not: a
10
+
top-level `errors[]` array, a top-level `warnings[]` array, and a `meta.generated_at`
11
+
timestamp. Divergent envelopes force agents and scripts to special-case each tool,
12
+
which is the opposite of what a fleet-wide contract is for.
13
+
14
+
ADR-0002 froze the previous shape (`{ ok, data, meta, warnings, errors }` with an
15
+
errors array "never a single object" and `meta` carrying `command`, `schema_version`,
16
+
`generated_at`). That decision predates the fleet-unification effort and its
17
+
envelope clauses are superseded here.
18
+
19
+
## Decision
20
+
21
+
Adopt the fleet core envelope. This is a breaking change; `schema_version` becomes
22
+
the date string `2026-07-25`.
23
+
24
+
-`errors[]` collapses to a single `error` object `{ code, message, category, retryable, details? }`. When multiple errors are aggregated (e.g. multi-item partial failures) the primary error is the object and the remainder go into `error.details`. `error` is omitted on success.
25
+
- Top-level `warnings[]` moves to `meta.warnings` (omitted when empty).
26
+
-`meta` gains `profile` (the active configuration profile), `duration_ms` (wall-clock handling time), and `request_id` (a UUID v4 generated once per invocation).
27
+
-`meta.generated_at` is dropped; it was redundant with request-scoped timing and non-deterministic in golden tests.
28
+
29
+
Tool-specific additive `meta` fields (`demo`, `pagination`) are retained. A single
30
+
`runtimeState.writeEnvelope` chokepoint stamps the request-scoped fields and honors
31
+
the global `--pretty` flag, so envelope bytes cannot drift between commands.
32
+
33
+
## Consequences
34
+
35
+
- Consumers that read `errors[0]` must read `error`; consumers that read top-level `warnings` must read `meta.warnings`. There is no compatibility shim.
36
+
-`request_id` gives each invocation a stable correlation id for logs and audits; `duration_ms` exposes handling latency without a wall-clock timestamp.
37
+
-`JSON_SCHEMA.md`, `docs/CONTRACTS.md`, and every inline/e2e envelope assertion were updated to the new shape.
38
+
-`github.com/google/uuid` is a direct dependency, matching the sibling tools.
0 commit comments