Operant Studio
KNUCK-A040

KNUCK-A040: dead-fallback lint -- flag ?? on any field the Prisma schema declares non-null

Suggested
Justin Cooke

Body

## Context

`apps/web/src/app/api/studio/[team]/deps/route.ts` line 60 contains:

```ts
actor: edge.createdBy ?? "system",
```

After KNUCK-A007b1a promoted `TaskDependencyEdge.createdBy` from `String?` to `String` (NOT NULL), the `?? "system"` branch is unreachable dead code -- Prisma will never return a null `createdBy`. Dead fallbacks like this hide the real invariant, and if the fallback ever fires (e.g. due to a driver bug), it silently violates the actor-prefix contract by writing an unprefixed value into an audit log.

## Acceptance Criteria

- New ESLint rule `operant/no-dead-fallback-on-non-null-field`
- Rule parses `packages/database/prisma/schema/*.prisma` at lint time, builds a set of `Model.field -> non-null`
- Flags `??`, `||`, and `!== null ? ... : ...` on any expression of form `<var>.<field>` where the model+field pair is in the non-null set
- Suggested autofix: remove the fallback and the alternate branch
- Escape hatch: `// eslint-disable-next-line operant/no-dead-fallback-on-non-null-field` with mandatory reason
- Applies to `apps/**/*.ts`, `packages/**/*.ts` (excluding test files, per project convention)
- Ship with unit tests for: flagged (non-null field with fallback), not-flagged (nullable field with fallback), autofix output

## Non-goals

- Not enforcing at runtime (compile-time only)
- Not handling deeply-nested field access (e.g. `x.y.z ?? d` where `z` is on a joined model)

## Effort

Single Ledger ticket. Blockers: A007b1a merged (done). Related but not blocked by A039.

## Filed as follow-on to

PR #278 (KNUCK-A007b1a).


---
<!-- admission-rejected -->
**Admission rejected (missing_feature_id):** ticket body is missing `feature_id: FEAT-...` frontmatter — declare which feature this ticket implements

Attachments

Loading attachments…

Comments

Loading comments…