feat(core): .inspect() redact (ADR 0018) + .report() result object (ADR 0019)#342
Merged
Conversation
Add `InspectOptions.redact` (default off) so a consumer can scrub secret-named fields from `raw` before logging or forwarding it. Opt-in by design: `raw` exists to catch a stray token in an *undeclared* field, and a name-based redactor can't catch a secret in an innocuously-named field — so redaction is a safe-sharing convenience, never a leak guarantee, and must not default on (that would create false confidence). - rename the secret-key predicate isSecretQueryKey -> isSecretKey, keep isSecretQueryKey as an alias (URL scrubbers/auth unchanged) - add redactSecretsDeep(value, extra?) in util.ts: deep-clone, replace keys matching the shared denylist (or caller `extra` patterns via matchPath) with 'REDACTED'; never mutates input - wire it into the .inspect() consumer in stitch.ts, AFTER findings are computed (findings run on the unredacted body — safe, detailFor emits kinds only, never values); no engine change - export isSecretKey + redactSecretsDeep Flips ADR 0018 to Accepted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ADR 0019) (#343) Add the run-diagnostics surface ADR 0016 held the line against, plus the `source` discriminator (folds in two of 0016's four deferrals). - `Inspection<T>` gains `source: 'live' | 'cache' | 'stream'` — the interpretant of `raw` (why it is/isn't null). It lands on the minimal object (not the report) because it explains an Inspection field; revises 0016's guess that source belonged on the enhanced object. - new `.report()` -> `RunReport<T> extends Inspection<T>` adding `attempts`, `timing { ms, waited? }`, the redacted `config`, and the `cache` outcome. `.inspect()` stays the minimal "raw + drift" probe. Built entirely from the existing event spine — `attempts`/`done.ms`/ `Σ progress.waitedMs`/the `phase:'cache'` detail — so NO new engine events (per-attempt latency is deferred). A shared `drainRun` feeds both consumers; `source` precedence is stream > cache > live. `config` is the already-redacted `__config`, never `__rawConfig`. Never throws. Flips ADR 0019 to Accepted. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#343 merged into this branch added the .report() instance method but the generated playground-completions.generated.ts was not re-run, so the verify gate's `git diff --exit-code` on the generated file failed. Pure regeneration via `pnpm --filter @stitchapi/docs gen:completions`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…et for ADR 0018+0019 Resolve main's P5 (value→data) / P17 (waited/elapsed de-suffix) renames against the ADR 0018 (.inspect redact) + ADR 0019 (.report/source) work: - Inspection<T>: keep main's canonical `data` (+ @deprecated `value`) alongside the ADR 0018 raw-redaction JSDoc and ADR 0019's `source`. - drainRun/makeInspection: read canonical `ev.data`/`ev.waited`/`ev.elapsed` (the *Ms / value aliases are @deprecated post-merge); co-set data+value and carry `source`. New specs switch off deprecated `.value` to `.data`. Bundle: the two features add ~0.45 KB to the core path (both attach to the core Stitch surface — `.report()` can't tree-shake), reaching 23.77/19.25 KB gzip. Raise the gate 23.55→23.95 / 19.0→19.45 KB (tight ~0.2 KB headroom per convention) and move the advertised whole-entry figure ~23→~24 kB across the 5 drift-gated files. import{stitch} stays ~19 kB. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements ADR 0018 and ADR 0019 — two of the four deferrals from ADR 0016 (
.inspect()/raw/findings, merged in #334). ADR 0019's branch (#343) was stacked on this one and merged into it, so both land together here; #343 is not inmainindependently. Supersedes the docs-only #337.ADR 0018 — opt-in
redactfor.inspect()'srawAdds
InspectOptions.redact(default off) to scrub secret-named fields fromrawbefore logging/forwarding. Opt-in by design:rawexists to catch a stray token in an undeclared field, and a name-based redactor can't catch a secret in an innocuously-named field — so redaction is a safe-sharing convenience, never a leak guarantee. Defaulting it on would create false confidence.isSecretQueryKey→isSecretKey, keepisSecretQueryKeyas an alias (URL scrubbers/auth unchanged)redactSecretsDeep(value, extra?): deep-clone, replace keys matching the shared denylist (or callerextrapatterns viamatchPath) with'REDACTED'; never mutates input.inspect()after findings are computed (findings run on the unredacted body — safe,detailForemits kinds only, never values)isSecretKey+redactSecretsDeep. No engine change.ADR 0019 — enhanced result object (
.report()) + thesourcediscriminatorsource: 'live' | 'cache' | 'stream'onInspection<T>— the interpretant ofraw(explains whyrawisnull). Free: derived from the existing event spine..report()→RunReport<T> extends Inspection<T>— the inspection plus run diagnostics:attempts,timing({ ms, waited? }), the resolved+redactedconfig(the__configprojection, never__rawConfig), and a fine-grainedcacheoutcome. Never throws; a network probe like.inspect(). Built entirely off the spine — no new engine events.Merged with
main+ verificationRebased onto current
mainand resolved its freshly-landed contract renames against this work:value→dataon result envelopes):Inspectionkeeps canonicaldata+@deprecated value;drainRunreadsev.data.waited/elapsedduration de-suffix):drainRunreads canonicalev.waited/ev.elapsed(the merge would otherwise have silently read now-deprecatedev.waitedMs/ev.ms); new specs switched off deprecated.valueto.data.Green locally: typecheck (src+tests), 1153 core tests, contract ratchet, lint, exports, type-decls (tsd), format, bundle-size, size-docs.
Bundle budget (deliberate bump)
Both features attach to the core Stitch surface (
.report()can't tree-shake), adding ~0.45 KB to the core path → 23.77 / 19.25 KB gzip. Budget raised 23.55→23.95 / 19.0→19.45 KB (tight ~0.2 KB headroom, per the gate's convention), with the rationale recorded inbundle-size.mjs. The whole-entry advertised figure moves ~23 → ~24 kB across the 5 drift-gated files;import { stitch }stays ~19 kB.🤖 Generated with Claude Code