Skip to content

Cast inventory — TamperWard production source (#383)

Every type assertion (x as T, <T>x) and non-null assertion (x!) in src/ on main before this change, counted on the TypeScript AST by harness/fp-study/cast-inventory.mjs, with its classification and what replaced it. as const is a literal-type request and is not counted. There were no as any casts and no as unknown as T double casts.

formmainnow
as T / <T>x (ordinary type assertion)600
x! (non-null assertion)140
as any00
as unknown as T00

Classes

  • untrusted JSON — a document that same-UID, candidate or third-party code can write (snapshots, telemetry, the ledger, hook payloads, package.json, Docker inspect output, the policy file). Replaced by readers that check every field before building the typed value (src/narrow.ts, ptreeFrom, fsEventFrom, ledgerEntryFrom, readWatcherHealth, hooksOf, validate()).
  • thrown error / process resulte as NodeJS.ErrnoException, e as Error & { stderr }. Replaced by errnoCode, errorMessage, execFailure.
  • guard narrows — the code had already checked the value; the cast re-asserted what a type predicate, an in check or a restructured condition proves.
  • literal / const list(LIST as readonly string[]).includes(x). Replaced by .some.
  • non-null invariantx! standing for a fact the control flow can state.
  • TypeScript internal APIparseDiagnostics and text are not on the public node types; 'prop' in node narrows without asserting the whole node.

Security-sensitive paths (trust boundaries: policy, ledger, hook payload, envelope, verifier, telemetry, git, dependency attestation) are marked .

site (main)formwhat was assertedclassreplaced by
adapters/claude/changes.ts:102as-Te as { stdout?: string | Buffer; status?: number | null; code?: s…thrown error / process resultexecFailure()
adapters/claude/changes.ts:145as-Traw as { old_string?: string; new_string?: string }untrusted JSONisRecord + asStr per field
cli/doctor.ts:119as-Tvalue as Record<string, unknown>type guard bodyisRecord()
cli/doctor.ts:513as-Te as Error & { stderr?: string | Buffer }thrown error / process resultexecFailure()
cli/hook.ts:81as-Tparsed as ClaudeHookInputuntrusted JSONhook payload built field by field
cli/hook.ts:500as-T[] as Finding[]literal typingannotated variable
cli/init.ts:342as-TJSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8')) as { sc…untrusted JSONisRecord chain
cli/init.ts:466as-Tparsed as ClaudeSettingsuntrusted JSONhooksOf() rebuilds the checked shape
cli/init.ts:780non-nullexistingRel!non-null invariantexistingRel checked in the condition
cli/report.ts:23as-TFORMATS as string[]literal / const listFORMATS.some
cli/run.ts:552as-Tsupervisor.error as NodeJS.ErrnoExceptionthrown error / process resulterrnoCode()
cli/run.ts:633as-Te as NodeJS.ErrnoExceptionthrown error / process resulterrnoCode()
cli/run.ts:688non-nullpid!non-null invariantpid !== null before pidAlive
cli/run.ts:698non-nullpid!non-null invariantsame narrowed pid
cli/trace-verify.ts:196non-nullrel!non-null invariantrel !== null branch restructured
cli/trace-verify.ts:302non-nullprefix.split(sep).at(-1)!non-null invariant?? prefix
cli/verify.ts:344non-nullpending.shift()!non-null invariantfor-loop over shift() until undefined
cli/verify.ts:362non-nulldependencyRoot!non-null invariantdeps domain resolves only with a dependency root
cli/watch.ts:93as-TJSON.parse(readFileSync(watcherHealthPath(log), 'utf8')) as Partial…untrusted JSONreadWatcherHealth builds from checked fields
cli/watch.ts:100as-Tvalue as WatcherHealthuntrusted JSONsame
cli/watch.ts:112as-Te as NodeJS.ErrnoExceptionthrown error / process resulterrnoCode()
detectors/ci-tampering.ts:419non-nulll.match(/^\s*/)!non-null invariantlength - trimStart().length
detectors/ci-tampering.ts:795non-nullc.after!non-null invariantc.after != null in the condition
detectors/coverage-lowering.ts:57as-TMETRICS as readonly string[]literal / const listMETRICS.some
detectors/coverage-lowering.ts:508as-T'lower' as Directionliteral / const listcontextual SimpleKey return type
detectors/coverage-lowering.ts:744non-nullo.after!non-null invariantfilter predicate types after: string
detectors/coverage-lowering.ts:760non-nullo.after!non-null invariantsame predicate
detectors/fs-events.ts:167as-TJSON.parse(line) as FsEventuntrusted JSONfsEventFrom() validates each record
detectors/hook-tampering.ts:62as-Tv as Settingsuntrusted JSONisObj predicate already narrows
detectors/hook-tampering.ts:313as-Tc.tools as Set<string>guard narrowstools null handled inline
detectors/hook-tampering.ts:447as-Te.h as Objguard narrowsisObj guard (schemaProblem already rejected non-objects)
detectors/hook-tampering.ts:505as-Tc.before as stringguard narrowsc.before == null ternary
detectors/hook-tampering.ts:563as-T(base[section] ??= {}) as Record<string, unknown>guard narrowsbase typed as nested Record
detectors/hook-tampering.ts:564as-T(sec[group] ??= {}) as Record<string, unknown>guard narrowssame
detectors/hook-tampering.ts:870as-Tc.after as stringguard narrowsc.after != null in the condition
detectors/hook-wiring.ts:866as-Ti.pin as stringguard narrowsflatMap with the pin check
detectors/hook-wiring.ts:1348non-nulltarget!non-null invarianttarget narrowed before the closure
detectors/hook-wiring.ts:1408as-Tcwd as stringguard narrowsdiskCwd resolved once
detectors/policy-diff.ts:43as-Tv as RawPolicyShapeuntrusted JSONraw shape is Record<string, unknown>; fields read via isRecord/verifyBlock/ruleOverrides
detectors/snapshot-only.ts:51as-Tsnaps[0] as Extract<Change, { kind: 'file' }>guard narrowsfilter predicate c is FileChange
detectors/snapshot-rewrite.ts:105as-Tc.oldPath as stringguard narrowsoldPath ?? path
detectors/suite-config.ts:217as-TPYTEST_INI_ORDER as readonly string[]literal / const listfindIndex
detectors/suite-config.ts:441as-Tx as ts.ParenthesizedExpression | ts.AsExpression | ts.SatisfiesE…guard narrowsts.isSatisfiesExpression guard chain
detectors/suite-config.ts:518as-Te as ts.ParenthesizedExpression | ts.AsExpression | ts.SatisfiesE…guard narrowssame
detectors/test-deletion.ts:102as-Tfn as ts.ArrowFunction | ts.FunctionExpressionguard narrowsfind with a type predicate
detectors/test-deletion.ts:275as-TJSON.parse(src ?? '{}') as { scripts?: Record<string, unknown> }untrusted JSONisRecord chain
detectors/test-deletion.ts:608as-TPYTEST_INI_ORDER as readonly string[]literal / const listPYTEST_INI_ORDER.some
detectors/test-skip.ts:221as-Tsf as ts.SourceFile & { parseDiagnostics?: readonly ts.Diagnostic[] }TypeScript internal API'parseDiagnostics' in sf narrowing
detectors/test-skip.ts:461as-Tnode as ts.Node & { text?: unknown }TypeScript internal API'text' in node narrowing
disk.ts:50as-Te as { code?: unknown }thrown error / process resulterrnoCode()
effect.ts:200as-TJSON.parse(readFileSync(p, 'utf8')) as PTreeuntrusted JSONptreeFrom() validates each entry
engine.ts:87as-T{ rule: 'detector-error', severity: 'block', message: Detector "${…`literal typingannotated Finding
git/build.ts:52as-Te as { stderr?: string | Buffer; message?: string }thrown error / process resultexecFailure()
git/build.ts:307as-Tc as { path: string }guard narrowsflatMap on kind
policy-load.ts:37as-TSEVERITIES as readonly unknown[]literal / const listSEVERITIES.some
policy-load.ts:83as-Tr as Record<string, unknown>untrusted JSONvalidate() returns the proved shape; parsePolicy takes unknown
policy-load.ts:90as-Tr.rules as Record<string, unknown>untrusted JSONvalidate() returns the proved shape; parsePolicy takes unknown
policy-load.ts:106as-Tr.protected as Record<string, unknown>untrusted JSONvalidate() returns the proved shape; parsePolicy takes unknown
policy-load.ts:113as-Tr.signoff as Record<string, unknown>untrusted JSONvalidate() returns the proved shape; parsePolicy takes unknown
policy-load.ts:118as-Tr.signoff as Record<string, unknown>untrusted JSONvalidate() returns the proved shape; parsePolicy takes unknown
policy-load.ts:126as-Tr.verify as Record<string, unknown>untrusted JSONvalidate() returns the proved shape; parsePolicy takes unknown
policy-load.ts:141as-Tv.image as stringuntrusted JSONvalidate() returns the proved shape; parsePolicy takes unknown
policy-load.ts:202as-Te as Errorthrown error / process resulterrorMessage()
policy-load.ts:207as-Traw as RawPolicyuntrusted JSONvalidate() returns the proved shape; parsePolicy takes unknown
policy.ts:65as-T{ ...(out[name] ?? {}), ...(cfg ?? {}) } as Policy['rules'][string]merge typingRuleConfig.severity optional (what mergeRules always produced)
signoff.ts:65as-Te as LedgerEntryuntrusted JSONledgerEntryFrom() validates each field
signoff.ts:131non-nullnormalizedHead!non-null invariantearly return on !head
suite-diagnostics.ts:111non-nullch.codePointAt(0)!non-null invariant?? 0
suite-diagnostics.ts:327as-TJSON.parse(stdoutText) as RawResultuntrusted JSONisRecord + streamFromRaw(unknown)
suite-diagnostics.ts:392as-Tsupervisor.error as NodeJS.ErrnoExceptionthrown error / process resulterrnoCode()
verifier-backend.ts:151as-Tmetadata as { Config?: unknown }untrusted JSONisRecord chain
verifier-backend.ts:155as-Tconfig as { Volumes?: unknown }untrusted JSONsame
verifier-backend.ts:160as-Tvolumes as Record<string, unknown>untrusted JSONsame
wiring.ts:34as-TJSON.parse(readFileSync(join(here, rel), 'utf8')) as { name?: strin…untrusted JSONisRecord

Remaining assertions

None in src/. The one place a cast would have been the only way to reach a value — TypeScript's non-public parseDiagnostics — is read through an in check instead. Test files keep their assertions: they are fixtures, not the surface that decides whether production typechecks, and ts-cast-growth excludes them for the same reason.

Keeping it that way

ts-cast-growth (warn) reports net growth of this surface on every change; the repository's own policy enables it. The inventory script can be re-run at any time:

sh
node harness/fp-study/cast-inventory.mjs src

Apache-2.0. Every headline number is measured; the pre-registered predictions — including the refuted ones — are committed to the repo.