Skip to content

fix(types): ship the lock option types a consumer must resolve - #150

Merged
drewstone merged 1 commit into
mainfrom
fix/public-lockfile-types
Aug 17, 2026
Merged

fix(types): ship the lock option types a consumer must resolve#150
drewstone merged 1 commit into
mainfrom
fix/public-lockfile-types

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

8.0.7's published dist/index.d.ts imports a type from a package that ships no declarations:

import { LockOptions } from "proper-lockfile";

@types/proper-lockfile was a devDependency, so any consumer that typechecks against this package fails:

error TS7016: Could not find a declaration file for module 'proper-lockfile'.
  .../proper-lockfile/index.js implicitly has an 'any' type.

This is a regression against 8.0.6, which emitted only the side-effect form import "proper-lockfile"; and therefore needed nothing from the consumer. The public surface began exposing retries?: LockOptions['retries'] (src/mutation-lock.ts:79, :301), which pulls the declaration into the emitted types.

Caught by agent-runtime's packed-cohort gate, which typechecks a real consumer against the packed archives — it failed the moment the cohort ref moved to 8.0.7, and it blocks agent-runtime#887.

Fix: @types/proper-lockfile moves to dependencies, the standard remedy when a published .d.ts references a types-less package. Version 8.0.8.

Verified locally: pnpm install, pnpm typecheck, pnpm build, pnpm verify:package — all exit 0.

The published surface exposes LockOptions['retries'], so a consumer that
typechecks against it needs those declarations at runtime install time.

@tangletools tangletools left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Auto-approved drewstone PR — 0ce7ca14

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

This approval is provisional. It rests on the audit running. If the audit cannot run — for example the CLI bridge rejects it — this approval is dismissed rather than left standing, so an unrun check never reads as a passing one.

tangletools · auto-approval · reason: drewstone_author · 2026-08-17T04:11:54Z

@drewstone

Copy link
Copy Markdown
Contributor Author

Merging with --admin, disclosed.

CI is green (ci, official-optimizers) and the multi-shot reviewer completed its shots without publishing a verdict — the reviewer bridge has been dropping verdicts intermittently all night. Branch protection wants an APPROVED state and only the provisional auto-approval exists.

The whole change is one dependency move plus the version bump: @types/proper-lockfile: 4.1.4 goes from devDependencies to dependencies; version 8.0.8. No source file is touched.

It fixes a live break: 8.0.7's published dist/index.d.ts emits import { LockOptions } from "proper-lockfile", and that package ships no declarations, so every consumer that typechecks after upgrading gets TS7016 pointing into node_modules. 8.0.6 emitted only the side-effect form and was unaffected. agent-runtime's packed-cohort gate reproduces it against the real packed archive, which is how it surfaced.

Verified on this head: pnpm install, pnpm typecheck, pnpm build, pnpm verify:package — all exit 0.

@drewstone
drewstone merged commit db9b078 into main Aug 17, 2026
2 checks passed
@drewstone
drewstone deleted the fix/public-lockfile-types branch August 17, 2026 04:15

@tangletools tangletools left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Value Audit — sound

Verdict sound
Coverage 2 of 2 lenses (value, usefulness)
Concerns 1 (1 weak-concern)
Heuristic 0.0s
Duplication 0.0s
Interrogation 442.6s (2 bridge agents)
Total 442.6s

💰 Value — sound

Moves @types/proper-lockfile to dependencies (8.0.7→8.0.8) so the published d.ts's import { LockOptions } from "proper-lockfile" resolves for every consumer — correct, minimal, canonical fix for a real reproduced regression; ship.

  • What it does: A one-file package.json change: @types/proper-lockfile@4.1.4 moves from devDependencies to dependencies, plus a patch version bump to 8.0.8. No code changes. Effect: every npm/pnpm install of this package now also installs proper-lockfile's type declarations (with @types/retry pulled transitively via its "*" dependency), so the emitted dist/index.d.ts — which imports LockOptions from 'proper-lockf
  • Goals it achieves: Restores the 8.0.6 consumer contract: typecheck-against-this-package works with zero extra installs. I verified the regression end-to-end: the real published 8.0.6 tarball emits import "proper-lockfile"; (side-effect only) at dist/index.d.ts:9, while the current source (src/mutation-lock.ts:4,:79,:301 — public retries?: LockOptions['retries']) emits a named type import at dist/index.d.ts:9. A
  • Assessment: Good on its merits and in the codebase's grain. This is the TypeScript-documented standard remedy when published declarations import from a types-less package, applied at exactly the right level: source didn't regress (the public type surface legitimately exposes LockOptions and has since the lazy-lock refactor f455a68/#97), so the fix belongs in packaging, not code. The repo's history shows the s
  • Better / existing approach: none — this is the right approach. Alternatives considered and rejected with evidence: (1) inline the retries type in src/mutation-lock.ts — decouples the d.ts from proper-lockfile but hand-copies @types/retry's OperationOptions shape and invites drift from the pass-through target, for no consumer-visible gain; (2) change tsdown dts config to inline external type imports — more build moving parts
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 2
  • Bridge warning: opencode/kimi-for-coding/k2p7: opencode: opencode error

🎯 Usefulness — sound

Moves @types/proper-lockfile to dependencies (8.0.7 → 8.0.8) so the published dist/index.d.ts's import { LockOptions } from "proper-lockfile" resolves for consumers — empirically verified under both npm and pnpm layouts; minimal, standard, and correctly targeted at the real regression.

  • Integration: Verified end-to-end, not just claimed. The chain is real: src/mutation-lock.ts:4 imports the type and exposes retries?: LockOptions['retries'] at src/mutation-lock.ts:79 and :301; src/index.ts:34-44 re-exports those interfaces from the root entrypoint; my fresh build emits import { LockOptions } from "proper-lockfile"; at dist/index.d.ts:9; proper-lockfile@4.1.2 ships no types (no types/typing
  • Fit with existing patterns: Moving a @types package referenced by emitted declarations from devDependencies to dependencies is the standard, documented remedy for exactly this failure, and matches how the package already treats runtime types: zod ships its own types, and proper-lockfile was the only untyped runtime dependency leaking into the public d.ts. The alternative — inlining the LockOptions['retries'] shape into thi
  • Real-world viability: Proven beyond the happy path: the fix holds under pnpm's strict isolated node_modules (the layout most likely to break transitive type resolution), not just npm's flat hoisting. @types/proper-lockfile is types-only, so zero runtime/install cost beyond one small package; its own transitive @types/retry resolves cleanly. One bounded caveat confirmed during testing: the emitted declarations also refe
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 1

🎯 Usefulness Audit

🟡 Emitted d.ts still ambiently depends on @types/node (same error class, ubiquitous dependency) [robustness] ``

While reproducing the TS7016, a strict consumer without @types/node also fails on Buffer/NodeJS references in dist/index.d.ts (lines ~1716, 2020-2029). This is the same class of 'published declarations need ambient types' but @types/node is present in effectively every Node consumer, so it is conventionally left a devDependency — unlike @types/proper-lockfile, which no consumer would have by accident. Informational only; no action needed for this PR. If the team ever wants declarations fully sel


What this audit checks

It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.

Pass What it asks
Heuristic Vague title? Whitespace-only or cruft-bearing diff? (content signals only)
Duplication Do added function/class names already exist elsewhere in the repo?
Value Audit What does it do? What goal does it achieve? Is it good? Better architecture or already-exists?
Usefulness Audit Does it integrate and fit? Will it hold up in real use and actually get used?

Findings are concerns, not blocks — the human reviewer decides what to do with them.

value-audit · 20260817T042021Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants