diff --git a/bench/browser/package.json b/bench/browser/package.json index c6aa9bc2479..2bee2277cd9 100644 --- a/bench/browser/package.json +++ b/bench/browser/package.json @@ -11,7 +11,6 @@ "@moq/hang": "workspace:*", "@moq/net": "workspace:*", "@moq/publish": "workspace:*", - "@moq/signals": "workspace:*", "@moq/watch": "workspace:*" }, "devDependencies": { diff --git a/bun.lock b/bun.lock index d41d55eed1d..52adc78ccea 100644 --- a/bun.lock +++ b/bun.lock @@ -28,7 +28,6 @@ "@moq/hang": "workspace:*", "@moq/net": "workspace:*", "@moq/publish": "workspace:*", - "@moq/signals": "workspace:*", "@moq/watch": "workspace:*", }, "devDependencies": { @@ -84,7 +83,6 @@ "highlight.js": "^11.12.0", "open": "^11.0.4", "rolldown": "^1.2.13", - "solid-element": "^1.9.2", "solid-js": "^1.9.17", "tailwindcss": "^4.1.13", "typescript": "7.0.2", @@ -193,7 +191,6 @@ "dependencies": { "@moq/flate": "workspace:^", "@moq/net": "workspace:^", - "@moq/signals": "workspace:^", }, "devDependencies": { "@moq/clock": "workspace:^", @@ -1223,8 +1220,6 @@ "commander": ["commander@15.0.0", "", {}, "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg=="], - "component-register": ["component-register@0.8.8", "", {}, "sha512-djhwcxjY+X9dacaYUEOkOm7tda8uOEDiMDigWysu3xv54M8o6XDlsjR1qt5Y8QLGiKg51fqXFIR2HUTmt9ys0Q=="], - "concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="], "concurrently": ["concurrently@10.0.6", "", { "dependencies": { "chalk": "5.6.2", "rxjs": "7.8.2", "shell-quote": "1.12.0", "supports-color": "10.2.2", "tree-kill": "1.2.2", "yargs": "18.0.0" }, "bin": { "conc": "dist/bin/index.js", "concurrently": "dist/bin/index.js" } }, "sha512-qgICXXFp2/3nAebRLmxcINKEXM1mLK3ij/y31P02VTs/KIkddIeC05n73DxOXsXMB9LFSqNU1/na3lDlOhcbIw=="], @@ -1745,8 +1740,6 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "solid-element": ["solid-element@1.9.2", "", { "dependencies": { "component-register": "^0.8.7" }, "peerDependencies": { "solid-js": "^1.9.14" } }, "sha512-+6bK9YGAf47lrwWyTFXUVKCkKTkof3bn6yxknHJPAE5MGSCvx4DJH1KE3ltUjIXeDWhS2gU5yjpynkH709+Xag=="], - "solid-js": ["solid-js@1.9.17", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.6.8", "seroval-plugins": "~1.6.8" } }, "sha512-4GAmLOWPfNV8H9Jfn2LSFTpNEG6jiOKPiwd5lnLGQtehHaX6l3TOLzJqQ7mqwB/3k1Yy3zyumFVopmc/tm34ow=="], "solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="], diff --git a/demo/carla/web/src/input.ts b/demo/carla/web/src/input.ts index c93f293f7b6..e208ca74d79 100644 --- a/demo/carla/web/src/input.ts +++ b/demo/carla/web/src/input.ts @@ -20,7 +20,7 @@ export function takeoverState(measured: DriveState | undefined): DriveState { } /** Slew rates in normalized units per second. Heavy responses, with fast safety actions. */ -export const RATES = { +const RATES = { /** Steering: full lock takes ~2.2 s of held key. */ steer: 0.45, /** Recentering (C) is a touch quicker than turning further. */ @@ -36,7 +36,7 @@ export const RATES = { }; /** A gear change requires some brake and a released throttle. */ -export const SHIFT_MAX_THROTTLE = 0.01; +const SHIFT_MAX_THROTTLE = 0.01; /** Mirror the truck-side gear interlock so rejected shifts get immediate UI feedback. */ export function canShift(state: Pick): boolean { diff --git a/demo/carla/web/src/schemas.ts b/demo/carla/web/src/schemas.ts index 474aa3e527e..408f481f8c6 100644 --- a/demo/carla/web/src/schemas.ts +++ b/demo/carla/web/src/schemas.ts @@ -10,11 +10,11 @@ function openEnum(values: readonly Value[]) { } /** Transmission gear, mirrored from the truck's `drive::Gear`. */ -export const GearSchema = z.enum(["neutral", "forward", "reverse"]); +const GearSchema = z.enum(["neutral", "forward", "reverse"]); export type Gear = z.infer; /** The drive state the truck is currently honoring. */ -export const DriveStateSchema = z.object({ +const DriveStateSchema = z.object({ steering: z.number(), throttle: z.number(), brake: z.number(), @@ -27,7 +27,7 @@ export type DriveState = z.infer; * +x, speed in m/s. Deliberately not latitude and longitude - see * `demo/carla/sim/moq_carla_sim/roadmap.py`. */ -export const PoseSchema = z.object({ +const PoseSchema = z.object({ x: z.number(), y: z.number(), yaw: z.number(), @@ -40,12 +40,11 @@ export type Pose = z.infer; * (longitudinal positive forward, lateral positive right) and yaw rate in * deg/s. */ -export const MotionSchema = z.object({ +const MotionSchema = z.object({ yaw_rate: z.number(), accel_lon: z.number(), accel_lat: z.number(), }); -export type Motion = z.infer; /** * The truck's most recent contact. @@ -55,7 +54,7 @@ export type Motion = z.infer; * viewer who opens the page long after the impact ages it correctly instead of * treating the retained snapshot as a fresh crash. */ -export const CollisionSchema = z.object({ +const CollisionSchema = z.object({ at: z.number(), other: z.string(), impulse: z.number(), @@ -63,7 +62,7 @@ export const CollisionSchema = z.object({ export type Collision = z.infer; /** A camera receive/decode/render latency component echoed by the truck. */ -export const LatencyEntrySchema = z.object({ +const LatencyEntrySchema = z.object({ label: z.string(), ms: z.number(), }); diff --git a/demo/carla/web/src/wheel.ts b/demo/carla/web/src/wheel.ts index 53d5ee64074..118dd48955c 100644 --- a/demo/carla/web/src/wheel.ts +++ b/demo/carla/web/src/wheel.ts @@ -25,7 +25,7 @@ import { moveToward } from "./input.ts"; import type { DriveState } from "./schemas.ts"; /** How one physical axis maps onto a normalized control. */ -export const AxisMapSchema = z.object({ +const AxisMapSchema = z.object({ /** Index into `Gamepad.axes`. */ axis: z.number(), /** Raw value at rest: wheel centered, pedal up. */ @@ -36,7 +36,7 @@ export const AxisMapSchema = z.object({ export type AxisMap = z.infer; /** A complete device layout: which axis/button drives which control. */ -export const WheelProfileSchema = z.object({ +const WheelProfileSchema = z.object({ steer: AxisMapSchema, throttle: AxisMapSchema, brake: AxisMapSchema, @@ -77,14 +77,14 @@ export const WHEEL_RATES = { }; /** Ignore rim wobble this close to center, and rescale so full lock still reaches 1. */ -export const STEER_DEADZONE = 0.03; +const STEER_DEADZONE = 0.03; /** Pedal travel below this reads as fully released... */ -export const PEDAL_DEADZONE = 0.05; +const PEDAL_DEADZONE = 0.05; /** ...and above this as fully applied, so worn pedals can still reach the stops. */ -export const PEDAL_SATURATION = 0.95; +const PEDAL_SATURATION = 0.95; /** Movement (in normalized units) that counts as the driver touching the wheel. */ -export const MOVE_EPSILON = 0.02; +const MOVE_EPSILON = 0.02; /** * The wheel refuses to take over from the keyboard while the throttle is @@ -92,7 +92,7 @@ export const MOVE_EPSILON = 0.02; * control to the wheel. Applies to the handoff only; once armed, the pedal is * honored normally. */ -export const ARM_MAX_THROTTLE = 0.05; +const ARM_MAX_THROTTLE = 0.05; /** A control that can be bound to a wheel button. */ export type WheelButton = "gearUp" | "gearDown" | "estop"; @@ -126,7 +126,7 @@ function clamp(v: number, lo: number, hi: number): number { } /** Normalize a raw axis into 0..1 of its calibrated travel (may over/undershoot). */ -export function readAxis(axes: readonly number[], map: AxisMap): number { +function readAxis(axes: readonly number[], map: AxisMap): number { const raw = axes[map.axis]; const span = map.full - map.rest; if (raw === undefined || Math.abs(span) < 1e-6) return 0; @@ -414,7 +414,7 @@ function loadProfile(id: string): WheelProfile | undefined { } /** Minimum axis travel that counts as "the driver moved this control". */ -export const CAPTURE_THRESHOLD = 0.25; +const CAPTURE_THRESHOLD = 0.25; /** * Pick the axis that moved furthest from its resting value. @@ -463,7 +463,7 @@ export interface CalibrationStep { optional: boolean; } -export const CALIBRATION_STEPS: readonly CalibrationStep[] = [ +const CALIBRATION_STEPS: readonly CalibrationStep[] = [ { key: "rest", kind: "rest", diff --git a/demo/web/package.json b/demo/web/package.json index c0699581eb4..d31151a4134 100644 --- a/demo/web/package.json +++ b/demo/web/package.json @@ -26,7 +26,6 @@ "highlight.js": "^11.12.0", "open": "^11.0.4", "rolldown": "^1.2.13", - "solid-element": "^1.9.2", "solid-js": "^1.9.17", "tailwindcss": "^4.1.13", "typescript": "7.0.2", diff --git a/js/json/package.json b/js/json/package.json index d22f95fd044..8fa7fca7724 100644 --- a/js/json/package.json +++ b/js/json/package.json @@ -21,8 +21,7 @@ }, "dependencies": { "@moq/flate": "workspace:^", - "@moq/net": "workspace:^", - "@moq/signals": "workspace:^" + "@moq/net": "workspace:^" }, "peerDependencies": { "@zod/mini": "^4.5.0", diff --git a/js/net/src/ietf/max_age.ts b/js/net/src/ietf/max_age.ts index abd9730b437..c649f02fd41 100644 --- a/js/net/src/ietf/max_age.ts +++ b/js/net/src/ietf/max_age.ts @@ -2,7 +2,7 @@ import { SetupOption, type SetupOptions } from "./setup_options.ts"; /** The negotiated max-age Setup Option. @internal */ -export const MAX_AGE_OPTION = SetupOption.MaxAge; +const MAX_AGE_OPTION = SetupOption.MaxAge; /** The subscriber max-age message parameter. @internal */ export const MAX_AGE_PARAMETER = 0x915c6n; diff --git a/js/net/src/ietf/request_stream.ts b/js/net/src/ietf/request_stream.ts index 0bec16b6c47..52b151faf90 100644 --- a/js/net/src/ietf/request_stream.ts +++ b/js/net/src/ietf/request_stream.ts @@ -17,7 +17,7 @@ import { TRACK_STATUS_ERROR_14 } from "./track.ts"; import { type IetfVersion, Version } from "./version.ts"; /** Whether the draft treats the requester finishing its messages as cancellation. */ -export function finCancels(version: IetfVersion): boolean { +function finCancels(version: IetfVersion): boolean { return ( version === Version.DRAFT_14 || version === Version.DRAFT_15 || diff --git a/quest/m0/audit-lints.md b/quest/m0/audit-lints.md index a7a3468cd73..4907512110b 100644 --- a/quest/m0/audit-lints.md +++ b/quest/m0/audit-lints.md @@ -25,7 +25,7 @@ Scope is every language (decided 2026-10-20): clippy and rustc overrides, `clippy.toml`, `biome-ignore`, `@ts-expect-error`, `noqa` and `type: ignore`, Kotlin `@Suppress`, and the equivalents in Go, Swift, Dart, and C/C++. `dead_code` and `unused` suppressions belong to the [dead code -audit](/quest/m1/audit-dead-code.md); skip them here. Generated or vendored +audit](/quest/m2/audit-dead-code.md); skip them here. Generated or vendored bindings (`moq-nvenc`, `moq-v4l/src/sys`, `moq-rtmp/src/rml`, and the untracked UniFFI output) keep their blanket allows unless the generator can emit clean code. diff --git a/quest/m0/audit-simplify.md b/quest/m0/audit-simplify.md index 8dda4d297fc..8fcc04ab321 100644 --- a/quest/m0/audit-simplify.md +++ b/quest/m0/audit-simplify.md @@ -23,7 +23,7 @@ follow-up through `/quest-plan`. Narrowing a feature or reshaping an API is the maintainer's call, so those are proposals with a recommendation, filed as follow-ups. Unlike the [dead -code audit](/quest/m1/audit-dead-code.md), this targets code that is used but +code audit](/quest/m2/audit-dead-code.md), this targets code that is used but overbuilt. Starting points (2026-10-19): look for one concept implemented twice (across diff --git a/quest/m1/README.md b/quest/m1/README.md index 5e5e87c05b5..bf4fefdf582 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -135,7 +135,6 @@ lite-07 stream type here. - [Allocation-free single-range subscriber window](/quest/m1/window-alloc.md) - a one-range subscribe resolves its window without a heap allocation, recovering the join cost per-subscriber ranges added - [moq-net internals](/quest/m1/net-internals.md) - no per-group session clones, no transport type parameter in lite and IETF internals, and the `Local` check measured - [Subscribe update churn](/quest/m1/subscribe-update-churn.md) - a viewer sends SUBSCRIBE_UPDATE only on a meaningful change, measured after the publisher stall fix -- [Dead code and API surface audit](/quest/m1/audit-dead-code.md) - repeat the removal audit before the next major release - [Interop coverage](/quest/m1/interop-coverage/README.md) - the riskiest moq-transport features moq-net exposes get cross-implementation tests over the even drafts 14 to 22 - [Runner nightly](/quest/m1/interop-runner-nightly.md) - a nightly job runs the upstream interop runner against our relay and clients, so newly registered implementations with a published image are tested without a change here - [CI workflow audit](/quest/m1/audit-ci.md) - every workflow and job gives a signal someone sees, at a cost that signal justifies, or is fixed, moved, or deleted diff --git a/quest/m1/audit-dead-code.md b/quest/m1/audit-dead-code.md deleted file mode 100644 index af9aafb949a..00000000000 --- a/quest/m1/audit-dead-code.md +++ /dev/null @@ -1,67 +0,0 @@ -# [M] Dead code and API surface audit - -## Goal - -Code, exports, Cargo features, and CLI flags that nothing uses are deleted, or -are planned for deletion when removal breaks a public API. - -## Plan - -Recurs once per major release (decided 2026-10-19). The PR that finishes -this run also adds the next one with the same file name at the end of the next -milestone's `Required` (m0 to m1, m1 to m2). The successor keeps the Goal -and this paragraph; the rest of its Plan is rewritten with what this run -learned: baselines, results, and what to check first. A successor placed in m2 -also lists the [release cut](/quest/m2/release/cut.md) under `Required`, so it -waits for the release in flight to ship. Only `/quest-delete` ends the -recurrence. Fixes land in the run's PR, one commit each, rather than as follow-ups. -Only a change that needs a maintainer decision (public API, scope) or is -independently landable and too large to review alongside the rest becomes a -follow-up through `/quest-plan`. - - -Removing a public item is a breaking change and the maintainer's call, so it is -always a follow-up with its `MIGRATE.md` entry, never an inline fix. - -The m0 run (2026-10-19) removed seven unused JS wire codec classes, internal -aliases and token constants, module-local exports, two unused SVGs, one unused -formatter, and two unused build dependencies. Rust's draft-17 PUBLISH_BLOCKED -codec now compiles only for tests/fuzzing; six stale lite lint suppressions -were removed. An all-features-only unused moq-stats benchmark import was -also removed. Public entrypoints, Cargo features, flags, and wire behavior -were preserved. - -Start by comparing the live tree against these baselines: - -- Rust had 410 dead-code/unused suppression attributes, 331 conditional on - features or platforms; 403 remain after this run. Keep the conditional - model/session separation, generated settings and NVENC ABI tables, retained - drop guards, compile-time Send assertions, deserialization rejection - fixtures, and shared test/benchmark harnesses. Recheck their actual callers - rather than deleting attributes by count. `cargo shear` found no unused - dependencies. The wasm-only getrandom dependency selects a backend. -- Knip 6.42.0 found the JS removals above; the scoped final run had no unused - JS exports, types, or files. Its defaults miss Bun tests, TSX, - worklets, shell-launched test clients, and documentation imports, and treat - MPEG-TS fixtures as TypeScript. Supply those entrypoints explicitly, inspect - package `exports`, and search the whole tree before removing a finding. - Preserve the TypeScript DOM override dependency; it is compiler-selected. - Preserve `rolldown` in every package whose Vite config loads - `js/common/vite-plugin-worklet.ts`; `js/common` has no manifest of its own. - Keep the one-off analyzer outside the package dependency graph unless its - false positives can be reduced enough for CI. -- There were 144 Cargo feature declarations across 25 manifests. The - moq-native tombstone's empty compatibility features, the internal transport - and test marker features, platform gates, and all-features nightly coverage - are deliberate. GCS/Azure select object_store backends and the QUIC - crypto/logging features serve external consumers; lack of an explicit - downstream feature spelling does not make them dead. -- The usage registries declared 118 distinct long flags. Registry drift tests - cover the generated settings surface. Trace a setting through the config - merge and runtime consumer; a flag missing from prose/examples alone is - not evidence that it is unused. Preserve hidden deprecated spellings that - fail with migration guidance. - -No public removal was justified by this run. Propose any future public -removals separately with their migration and consumer evidence. The public -model and performance audits own their API and benchmark decisions. diff --git a/quest/m2/README.md b/quest/m2/README.md index 946d057fd7e..0b8eea91473 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -196,6 +196,7 @@ ranked just below deterministic TS legs, which it requires. - [FromStr parsers](/quest/m2/from-str-trait.md) - hang, moq-auth, and moq-msf implement `FromStr` instead of inherent `from_str` - [Drop dead items](/quest/m2/drop-dead-items.md) - the audit's uncalled public items and test helpers are removed - [Dependency and alternatives audit](/quest/m2/audit-deps.md) - dependencies are current, deduplicated, needed, and the best maintained option +- [Dead code and API surface audit](/quest/m2/audit-dead-code.md) - repeat the removal audit before the next major release - [Test coverage audit](/quest/m2/audit-test-coverage.md) - repeat the coverage audit against the m1 baselines after the next release ships - [Documentation audit](/quest/m2/audit-docs.md) - repeat the docs audit after the release ships, against the m1 baselines - [Test cost audit](/quest/m2/audit-test-cost.md) - the slowest, flakiest, and least valuable tests are fixed, deleted, or planned once the release ships diff --git a/quest/m2/audit-dead-code.md b/quest/m2/audit-dead-code.md new file mode 100644 index 00000000000..ce466744dbb --- /dev/null +++ b/quest/m2/audit-dead-code.md @@ -0,0 +1,80 @@ +# [M] Dead code and API surface audit + +## Goal + +Code, exports, Cargo features, and CLI flags that nothing uses are deleted, or +are planned for deletion when removal breaks a public API. + +## Plan + +Recurs once per major release (decided 2026-10-19). The PR that finishes +this run also adds the next one with the same file name at the end of the next +milestone's `Required` (m0 to m1, m1 to m2). The successor keeps the Goal +and this paragraph; the rest of its Plan is rewritten with what this run +learned: baselines, results, and what to check first. A successor placed in m2 +also lists the [release cut](/quest/m2/release/cut.md) under `Required`, so it +waits for the release in flight to ship. Only `/quest-delete` ends the +recurrence. Fixes land in the run's PR, one commit each, rather than as follow-ups. +Only a change that needs a maintainer decision (public API, scope) or is +independently landable and too large to review alongside the rest becomes a +follow-up through `/quest-plan`. + +Removing a public item is a breaking change and the maintainer's call, so it is +always a follow-up with its `MIGRATE.md` entry, never an inline fix. + +The m1 run (2026-10-20) removed unused `@moq/signals` dependencies from +`@moq/json` and the browser bench, the web demo's unused `solid-element`, and +module-local exports in the carla web demo and `js/net`'s IETF +`MAX_AGE_OPTION` and `finCancels`. No Rust code, Cargo feature, or flag was +removed. It ran while the lint audit's PR rewrote every Rust suppression, +so the dead-code attributes were counted but left alone. + +Start by comparing the live tree against these baselines: + +- Rust had 423 dead-code/unused suppression lines: 77 unconditional, 346 + through `cfg_attr`. The unconditional ones are drop guards, compile-time + Send assertions, generated settings registries, shared test and benchmark + harnesses, deserialization fixtures, FFI tables, and two `GroupConsumer` + helpers waiting on the [answering API](/quest/m1/public-model/answer.md). + Once the lint audit lands, every unconditional one is an `expect`, so a + stale one fails the build; check what the conditional ones still guard. + `cargo shear` found no unused dependencies. +- Rustc misses unused `pub` items. Tokenizing the tree and keeping `pub` + declarations whose name appears exactly once (`.scratch` script, not + checked in) found, outside generated bindings: + - cfg(test) helpers `track::Subscriber::assert_error` and + `AnnounceConsumer::assert_next_restarted`/`assert_next_ended` in + moq-net's model, left alone because open PRs churned those files. + - Public items no consumer calls: `moq_net::Version::uses_setup_negotiation`, + `moq_tokio::Server::serve_both`, `quic::web` `ClientBuilder::with_server_certificates` + and `ServerBuilder::with_certificate`, `hang` `Catalog::to_json_pretty`, + `moq_audio::Format::is_planar`/`is_float`, and the vendored forks + (`moq-quic`, `moq-sock`, `moq-v4l`) that keep upstream's surface. + Rerun it first; a name used only by its definition is the shortlist. +- Knip 6.42.0 with explicit entries for Bun tests, `*.browser.ts`, benches, + examples, worklets, TSX, every `test/*` client, the repo-root scripts, and + `ignore` for `rs/**/test_data` reports only real findings plus root + devDependencies used as CLIs, `tsx`/`wrangler` run by scripts, and the + `@ubjs` runtimes the wasm baseline bundles at run time. Preserve the + TypeScript DOM override and `rolldown` wherever a Vite config loads + `js/common/vite-plugin-worklet.ts`. `doc/concept/audio-jitter/reference.ts` + exports constants only it uses; it was skipped because an open PR rewrote + it. +- There were 150 Cargo feature declarations across 27 manifests. Only three + have no in-tree user: the moq-native tombstone's `android-logcat`, the QUIC + FIPS crypto feature, and moq-cli's `nvenc`/`nvdec` aliases. moq-video and + moq-transcode already dropped their own `nvenc`/`nvdec` aliases, so moq-cli's + are the next public removal to propose. +- The settings registries declared 120 distinct long flags, 186 counting + every `usage` declaration. Each one reaches a runtime consumer; the + `--client-*`/`--server-*` QUIC and TLS spellings and the relay's legacy auth + fields are hidden deprecated spellings that fail with migration guidance. + Trace a flag through the config merge, not through prose or examples. + +No public removal landed in this run. Propose any public removals +separately with their migration and consumer evidence. The public model and +performance audits own their API and benchmark decisions. + +## Required + +- [Release cut](/quest/m2/release/cut.md) - the release in flight ships before the next audit starts diff --git a/quest/m2/drop-dead-items.md b/quest/m2/drop-dead-items.md index e3f15952f12..6870a4980b9 100644 --- a/quest/m2/drop-dead-items.md +++ b/quest/m2/drop-dead-items.md @@ -21,4 +21,4 @@ Public API: breaking for moq-cli (features) and moq-net (`Version`), with a ## Related -- [Dead code and API surface audit](/quest/m1/audit-dead-code.md) - the audit that found them +- [Dead code and API surface audit](/quest/m2/audit-dead-code.md) - the next audit run, whose baselines record these findings