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(compat): add Runner.SkipDataPath for credentialless-CI exporters
The three §4 data-path subtests (JSONIsArray, CSVHasHeader,
DefaultIsMarkdown) invoke the binary with a real --format value and
expect a clean exit. For exporters whose data path requires
credentials that the CI environment does not have — crono's env-var
auth, liftoff's stored OAuth token at
~/.config/liftoff-export/auth.json, withings' OAuth refresh token —
a vanilla `--format json` invocation under compat's default empty
env exits non-zero with "not logged in" before the JSON-array check
fires. Today the only escape route is SupportedFormats: []string{},
which semantically claims "we implement no codecs" — misleading,
since liftoff/crono do implement markdown+json, just not from a
credentialless CI.
SkipDataPath is the explicit escape hatch the QUA-15 PR review
specifically proposed: when true, the three data-path subtests skip
with reason `data-path subtests disabled via Runner.SkipDataPath`,
while the parse-level subtests (HelpDocumentsFormatFlag,
UnknownFormatFails, FlagValidationIsHermetic) still run because
they only exercise flag parsing and expect non-zero exit on the bad
value. Composable with SupportedFormats — nil SupportedFormats +
SkipDataPath: true is the typical liftoff/crono shape today.
Flipping the bool back to false later — once auth is mockable in CI
or secrets are provisioned — promotes the codec rows from human-
attested to machine-attested per the CONTRACT.md Status table flip
plan, without losing the parse-level attestation we get for free
today.
Self-test (TestRunContract_SkipDataPath_GatesDataSubtests) is
adversarial: builds the stub with STUBCLI_FORMATS=__never__ so
every --format value is rejected, then runs the full bundle with
SkipDataPath: true. Parse-level subtests pass against the rejection
side; data-path subtests skip via the guard. Delete the guard in
jsonIsArray / csvHasHeader / defaultIsMarkdown and the test fails
because the data-path subtests run against a stub that rejects every
codec.
Unblocks QUA-16 (liftoff onboarding as second consumer of
compat/formats) without forcing the misleading
SupportedFormats: []string{} workaround.
Co-Authored-By: Paperclip <[email protected]>
Copy file name to clipboardExpand all lines: compat/README.md
+16-1Lines changed: 16 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -102,7 +102,7 @@ When `compat.Runner.Subcommands` is set, every row above runs once per declared
102
102
| `CSVHasHeader` | §4 | `--format csv` exits 0 and emits at least one non-empty line on stdout (the header row, present even on an empty result). |
103
103
| `DefaultIsMarkdown` | §4 | No `--format` flag produces byte-identical stdout to `--format markdown`. |
104
104
105
-
`JSONIsArray`, `CSVHasHeader`, and `DefaultIsMarkdown` invoke the data path with no extra args beyond `--format`. Integrators whose data path needs credentials or other env to succeed pass them via `compat.Runner.Env`. As with `dates`, `subcommand=NAME/...` subtest groups fire when `Subcommands` is set.
105
+
`JSONIsArray`, `CSVHasHeader`, and `DefaultIsMarkdown` invoke the data path with no extra args beyond `--format`. Integrators whose data path needs credentials or other env to succeed pass them via `compat.Runner.Env`; integrators whose data path is not runnable in CI at all today set `compat.Runner.SkipDataPath: true` (see below). As with `dates`, `subcommand=NAME/...` subtest groups fire when `Subcommands` is set.
**Per-CLI flip plan.** When a consumer wires the bundle into its CI with `SupportedFormats` matching its actual surface, the CONTRACT.md Status table's `--format` row for that CLI flips to **machine** for the codecs it declares. Codecs the CLI does not implement remain human-attested per-cell until the writer lands. Today the Status table has one cell per (CLI, codec) so the flip is per-cell, not per-row.
125
125
126
+
### Credentialless CI: `Runner.SkipDataPath`
127
+
128
+
The bundle's three data-path subtests (`JSONIsArray`, `CSVHasHeader`, `DefaultIsMarkdown`) invoke the CLI with a real `--format` value and expect a clean exit — which means the data path has to be runnable in the CI environment. For crono (env-var auth), liftoff (stored OAuth token), and withings (OAuth refresh token), that is not true today: a vanilla `--format json` invocation under `compat`'s default empty env exits non-zero with "not logged in" before the JSON-array check fires.
129
+
130
+
`compat.Runner.SkipDataPath` is the explicit escape hatch: when true, the data-path subtests skip with reason `data-path subtests disabled via Runner.SkipDataPath`. The parse-level subtests still run because they assert on flag parsing, not on a working data path. This lets an exporter wire the bundle in for parse-level attestation today, then flip the bool back to false later (once auth is mockable in CI or secrets are provisioned) to promote its codec rows from human to machine.
131
+
132
+
```go
133
+
formats.RunContract(t, compat.Runner{
134
+
Binary: os.Getenv("EXPORT_CLI_BIN"),
135
+
SkipDataPath: true, // CI does not have upstream credentials
136
+
})
137
+
```
138
+
139
+
`SkipDataPath` and `SupportedFormats` compose: `nil`SupportedFormats + `SkipDataPath: true` is the typical liftoff/crono shape today (declare the full surface, but skip the data-path subtests until creds are available). Use `SupportedFormats: []string{...}` instead of `SkipDataPath: true` when the CLI structurally lacks a codec — those are two different gaps and should not be confused.
140
+
126
141
## What it does NOT cover yet
127
142
128
143
The actual local-midnight semantics of `--since 2026-04-15` (the harmonization that just landed across crono/liftoff/withings) is still **human-attested** in the status table. Asserting it black-box requires either:
0 commit comments