# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [1.1.0] — 2026-07-01 ### Summary v1.1.0 adds two new CLI commands, two global flags, import-verified React SDK detection, object-form custom wrapper support, provider validation, shell completion, and GitHub Action version pinning. ### Added - **`flaglint init`** — scaffolds `flaglint.config.json` with all fields at their defaults. Warns if a higher-precedence config file exists and would shadow the new one. - **`flaglint completion `** — generates shell completion scripts for bash, zsh, and fish. Source once for tab-completion of all subcommands, flags, and flag values. - **`--quiet` / `-q` global flag** — suppresses all progress output (spinner, summaries). Only errors appear on stderr. Useful in scripts and CI pipelines where only the exit code matters. - **`--verbose` global flag** — lowers the spinner update threshold to every file for detailed progress on large codebases. - **React SDK import-verified detection** — hooks (`useFlags`, `useLDClient`, `useVariation`), HOC (`withLDConsumer`), and provider (`LDProvider`, `asyncWithLDProvider`) are now detected via verified import chains, not name heuristics. Eliminates false positives from unrelated libraries. - **Object-form custom wrappers** — `wrappers` config now accepts `{ import, function, flagKeyArgument }` objects for import-verified detection of custom SDK wrappers in addition to the existing string form. - **GitHub Action `version` input** — pin the action to a specific FlagLint release instead of always using `@latest`. Example: `version: '1.1.0'`. - **`outputDir` config field** — specifies the directory for generated reports (default: `.`). Now scaffolded by `flaglint init`. - **ADR 010** — exit code contract: 0/1/2/3/130, stable across all v1.x. ### Fixed - **Provider validation** — unknown `provider` values in config now exit 2 with a clear error instead of silently falling back. - **Bootstrap pattern matching** — replaced hand-rolled glob converter in `validate --bootstrap-exclude` with `micromatch.isMatch()` for correct glob semantics. - **Baseline version field** — corrected ADR 008 documentation to match the shipped implementation: the `version` field is the string `"1"`, not the number `1`. ## [1.0.0] — 2026-06-22 ### Summary v1.0.0 is the CI-readiness release. The core scanner and migrator are stable. This release adds stable finding fingerprints, baseline-aware CI enforcement, and the documented safety model that makes FlagLint adoptable by platform teams without needing to fix all existing debt first. ### Added - **Stable finding fingerprints** — each finding now carries a `fingerprint: string` field (`launchdarkly:callType:flagKey:normalizedFilePath`) that remains stable across line-number changes. Fingerprints are the foundation for baseline mode and SARIF result identity. - **Baseline mode** — `flaglint audit --write-baseline ` captures current debt fingerprints. `flaglint validate --baseline --fail-on-new` fails CI only for findings not in the baseline, enabling day-one CI adoption without requiring teams to fix all historical debt first. - **Safety model docs** — `/docs/concepts/safety-model/` documents what FlagLint will and will not auto-rewrite, and why safety takes precedence over coverage. - **JSON output contract** — documented v1.x compatibility promise: existing fields will not be renamed, removed, or retyped within v1.x; new fields may be added additively only. - **Stable exit codes** — documented 0/1/2/3 contract: 0=success, 1=policy failure, 2=invalid input, 3=internal error. - **ADR 007** — stable finding fingerprint schema. - **ADR 008** — baseline mode design, file format, and edge case behavior. - **Expanded fixture coverage** — golden fixtures for bulk methods, detail methods, and false-positive guards. ### Scope (v1.x) - **Supported:** LaunchDarkly Node.js server SDK (`@launchdarkly/node-server-sdk`, `launchdarkly-node-server-sdk`) - **Out of scope:** Browser SDK, React SDK, Go, Python, Java — see safety model docs --- ## [0.8.0] - 2026-06-13 ### Changed - **`--cost-estimate` renamed to `--effort-estimate`** (breaking) — the flag outputs hours, not dollars; "effort" is more accurate. Update any scripts or CI pipelines that use `--cost-estimate`. ### Fixed - Homepage email-capture / Loops signup section removed. - Version displayed on homepage now read from `package.json` at build time — can no longer drift from the published npm version. - `robots.txt` sitemap URL updated to `/sitemap-index.xml` (was `/sitemap.xml`, which 404'd). - Audit sample numbers corrected to real fixture output: 13 unique flags across 19 call sites, readiness 53/100, estimate 20.8h–40h. ### Infrastructure - Vitest `testTimeout`/`hookTimeout` raised to 30 s to fix flaky Windows/Node 22 CI timeouts. - `npm publish --provenance` re-enabled in release workflow. - `scripts/metrics/collect.mjs` added: fetches npm downloads + GitHub stars/forks/issues and appends a JSON line to `.agent-output/metrics/history.jsonl`. --- ## [0.7.0] - 2026-06-07 ### Added - `flaglint audit --effort-estimate` — adds a directional migration-effort estimate to audit output. Computes low/high hour ranges from automatable and manual-review call counts using configurable planning heuristics. - `flaglint audit --hourly-rate ` — adds engineering cost projection (`costLow`/`costHigh`) to the estimate. Requires `--effort-estimate`. - Migration readiness score displayed in `flaglint audit` terminal output: 0–100 ratio of safely automatable calls to total detected LaunchDarkly calls, with grade (`ready` | `moderate` | `complex` | `not-applicable`) and progress bar. - New docs pages: Migration Readiness concept page and Cost Estimation CLI reference. --- ## [0.6.0] - 2026-06-02 ### Added - add flaglint audit command with risk-scored flag debt report - migrate homepage into Astro, unify site into single build - add dark/light/auto theme toggle + search to homepage - add Mermaid diagrams to Safety Model and How FlagLint Works - add Mermaid diagrams to Safety Model and How FlagLint Works - complete favicon stack for Google Search and mobile - replace plain-text architecture diagram with styled HTML flow - add JSON-LD SoftwareApplication structured data to homepage - align trust and privacy page nav with homepage nav - add Further Reading section to quickstart linking to blog posts - add cross-links between blog posts - rename blog to FlagLint Blog and set focused description - add Docs and Blog links to homepage nav and footer - add Blog link to marketing homepage nav - add blog to flaglint.dev using starlight-blog - add terminal demo GIF (scan, migrate --dry-run, validate CI gate) ### Fixed - docs UX series P1-P9 (GIF, TOC, title, sidebar, cards, blog frames) - UX and codebase series (A1–A5, B1–B5) - sync package-lock.json after rebase (add png-to-ico) - UX and codebase series (A1–A5, B1–B5) - align dynamic key label with scan reporter terminology - restore branded README header and correct docs links ### Changed - trigger cloudflare rebuild - untrack www/ build output + fix GIF paths + add Why FlagLint CTA - add excerpt markers to both blog posts for blog index truncation - rebuild after rebase onto main (PR #83 docs UX changes) - rebuild docs after rebase onto main (A1-A5/B1-B5 + favicon state) - rebuild docs after rebase onto main (favicon + blog state) - rebuild docs site with blog and demo GIF - add LinkedIn URL to blog post authors - remove debug artifacts from demo fixture folder - update version references to 0.5.4 ### Other - Merge feat/audit-command: add flaglint audit command - Merge pull request #86 from flaglint/feat/docs-ux-fixes - Merge pull request #85 from flaglint/fix/blog-excerpt-markers - Merge pull request #84 from flaglint/feat/homepage-theme-search - Merge pull request #83 from flaglint/fix/complete-series-p1-p12 - Merge pull request #82 from flaglint/feat/favicon-stack - Merge pull request #80 from flaglint/fix/validate-dynamic-key-label - Merge pull request #78 from flaglint/feat/architecture-diagram - Merge pull request #77 from flaglint/feat/structured-data-jsonld - Merge pull request #76 from flaglint/feat/consistent-nav - Merge pull request #75 from flaglint/feat/quickstart-further-reading - Merge branch 'main' into feat/quickstart-further-reading - Merge pull request #74 from flaglint/feat/blog-cross-links - Merge pull request #73 from flaglint/feat/blog-title-description - Merge pull request #72 from flaglint/feat/nav-footer-blog-docs - Merge pull request #71 from flaglint/chore/bump-version-0.5.4 - Update README.md - Merge pull request #70 from flaglint/chore/bump-version-0.5.4 - Merge pull request #69 from flaglint/chore/bump-version-0.5.4 - chore/bump-version-0.5.4 ## Unreleased ### Fixed - Added truth-gate coverage and scanner support for destructured CommonJS LaunchDarkly initializer aliases such as `const { init: ldInit } = require("@launchdarkly/node-server-sdk")`. This keeps the public alias/CJS provenance claim backed across scan, validate, validation SARIF, dry-run, and guarded apply flows. ## [0.5.2] - 2026-05-27 ### Fixed - Fixed LaunchDarkly client provenance for aliased named `init` imports from both supported Node.js server SDK package names. Calls initialized via `import { init as ldInit } from "@launchdarkly/node-server-sdk"` or legacy `launchdarkly-node-server-sdk` are now consistently detected by `scan`, enforced by `validate --no-direct-launchdarkly`, emitted in validation SARIF, and handled by existing guarded migration dry-run/apply flows. - No migration safety boundary changed: `migrate --apply` still requires a proven OpenFeature client binding and continues to skip dynamic keys, detail evaluations, bulk calls, unknown fallbacks, and ambiguous cases. ## [0.5.1] - 2026-05-27 ### Fixed - Corrected `migrate --dry-run` messaging when all previewed diffs use proven OpenFeature client bindings. Dry-run output no longer claims placeholder provider/client setup is required when configured imported bindings, aliases, or local `OpenFeature.getClient()` bindings are already present. - Clarified README and docs scope wording for both supported LaunchDarkly Node.js server SDK package names: current `@launchdarkly/node-server-sdk` and legacy `launchdarkly-node-server-sdk`. - Corrected OpenTelemetry feature-flag semantic-convention guidance to use the current `feature_flag.evaluation` event model and current attribute names. - Corrected homepage release-state and lower CTA messaging now that `flaglint@0.5.0` is published. - Narrowed broad flag-debt wording where it could imply comprehensive unused-flag lifecycle analysis rather than direct SDK coupling and migration review work. ## [0.5.0] - 2026-05-26 ### Added - **Configured imported OpenFeature client bindings** (`openFeatureClientBindings` in `.flaglintrc`): Declare shared OpenFeature client exports by import name and glob module pattern. `migrate --apply` recognises these imports as proven bindings without requiring every service file to call `OpenFeature.getClient()` locally. ```json { "openFeatureClientBindings": [ { "importName": "openFeatureClient", "modulePatterns": ["**/platform/feature-flags"] } ] } ``` - **TypeScript ESM `.js` import compatibility**: `modulePatterns` globs now match TypeScript source imports that carry a `.js` extension at the specifier level (`import { openFeatureClient } from "../platform/feature-flags.js"`) even when the configured pattern omits the extension (`**/platform/feature-flags`). - **`validate --format sarif`**: `flaglint validate --no-direct-launchdarkly --format sarif --output flaglint.sarif` emits SARIF 2.1.0 with rule id `flaglint.direct-launchdarkly` and level `error`. Designed for GitHub Code Scanning upload — each direct LaunchDarkly evaluation call produces a PR annotation. Zero violations produces a valid SARIF document that GitHub Code Scanning interprets as "all clear". - **Enterprise HTML audit report**: `flaglint scan --format html` now includes an Executive Summary (total call-sites, unique flags, auto-migratable vs. manual-review breakdown), Findings by Directory table, Recommended Next Steps workflow, and a Copy Markdown Summary clipboard button. - **Enterprise OpenFeature migration demo** (`examples/enterprise-checkout-service/`): end-to-end walkthrough across five Node.js services (checkout, pricing, analytics, product, flags-wrapper). Includes `before/`/`after/` snapshots, `after-complete/` (fully migrated, passes hard gate), generated reports, `.flaglintrc` config, and a sample GitHub Actions CI workflow. - **Docs site** (`www/docs/`): nine documentation pages covering getting started, all three commands, supported scope, OpenFeature provider setup, CI/GitHub Actions integration, OpenTelemetry observability guidance, safety model, and the enterprise demo. - **Enterprise trust documentation**: `SECURITY.md`, `CONTRIBUTING.md`, and `CODE_OF_CONDUCT.md` with SARIF rule-ID reference for the `flaglint.direct-launchdarkly` policy rule. - **OpenFeature + OpenTelemetry observability guidance** (`www/docs/opentelemetry.html`): documents how to instrument OpenFeature flag evaluations with OpenTelemetry using the OpenFeature hooks API. FlagLint does not emit runtime telemetry; this page explains the complementary integration pattern. ### Fixed - **Deterministic test execution from clean checkout**: `vitest` configuration no longer relies on `process.env.INIT_CWD` for test file discovery, ensuring the test suite is reproducible on first-time `npm ci && npm test` runs. ### Changed - Reposition README and homepage messaging around standardizing LaunchDarkly usage on OpenFeature while keeping LaunchDarkly as the provider. - Document the focused automation scope: LaunchDarkly Node.js server-side evaluation calls in TypeScript and JavaScript, with dynamic keys, detail evaluations, bulk calls, browser SDKs, React usage, and ambiguous patterns reported for manual review. - Align supported runtime documentation and package metadata to Node.js `>=20`. Resolves the Node.js engine metadata mismatch in `flaglint@0.4.1` (published as `>=22`). ### Security - Add Node.js 20/22 CI coverage, CodeQL analysis, Dependabot configuration, and vulnerability reporting instructions. - Update npm release workflow for Trusted Publishing/OIDC without long-lived npm publish tokens. ### Scope boundaries (non-claims) The following are explicitly out of scope for this release: - LaunchDarkly replacement — LaunchDarkly remains the feature flag provider throughout. - Automatic provider/bootstrap setup — `migrate --apply` never generates bootstrap files. - Flag deletion or billing reduction — FlagLint does not evaluate live flag values. - Built-in runtime OpenTelemetry instrumentation — see `www/docs/opentelemetry.html` for the complementary integration pattern using OpenFeature hooks. ## [0.4.1] - 2026-05-25 ### Fixed - Detail evaluation methods are classified as manual review and excluded from safe auto-transform counts. - Generated LaunchDarkly OpenFeature provider setup now uses the SDK key constructor correctly. - Evaluation-context guidance now states that either OpenFeature `targetingKey` or existing LaunchDarkly `key` is accepted. - Default one-file flags no longer trigger staleness solely because they occur in one file; explicit `minFileCount: 1` remains available. - Reporter output now says `Flags with review signals` instead of implying flags are safe to remove. - Public early-preview messaging now states the Node.js server-side migration scope and review/testing requirement. ## [0.4.0] - 2026-05-24 ### Added - Release preparation: bump package version to 0.4.0, normalize repository URL, and adjust release workflow to publish only from manual GitHub Releases. No publish or tag created by this change. ### Added - **`flaglint migrate --dry-run`**: Generates reviewable before/after diffs for every automatable call-site, including inline provider setup guidance (packages, bootstrap file, `targetingKey` context requirement). Does not write any files; output is to stdout. - **Docs**: Repositioned public copy and website messaging to explicitly state scope (LaunchDarkly Node.js server SDK only), clarify that `--apply` is guarded, confirm provider/bootstrap setup is manual, and limit precision/recall claims to the 120 deterministic benchmark cases within that supported scope. - **`flaglint migrate --apply`**: Applies only guarded, provably automatable transformations in-place. Safety contracts: refuses on a dirty git working tree (override with `--allow-dirty`); skips any file without a proven `openFeatureClient = OpenFeature.getClient()` binding from `@openfeature/server-sdk` (AST-grounded, not regex); never rewrites detail methods, dynamic keys, unknown fallbacks, or bulk calls; preserves `await` and all call arguments exactly; idempotent (re-running a stale analysis is a no-op via range-content guard). - **`flaglint validate [dir]`**: New command for CI enforcement. - Without `--no-direct-launchdarkly`: reports usages, always exits 0. - `--no-direct-launchdarkly`: exits 1 if any direct LaunchDarkly Node server evaluation call is found (static, dynamic, detail, or bulk — all count as violations). - `--bootstrap-exclude ` (repeatable): exclude provider bootstrap files from violations. Supports exact paths, `*` (within one directory), `**` (across directories), and `?` wildcards. - Never claims flags are stale or safe to delete. ### Scope clarification Current scope: **LaunchDarkly Node.js server-side SDK** (`launchdarkly-node-server-sdk`). React hooks, HOC, and client-side SDK patterns are detected by `scan` but are not automatically migrated by `--apply`. ## [0.3.0] - 2026-05-23 ### Added - **SARIF output**: `flaglint scan --format sarif --output flaglint.sarif` now emits SARIF 2.1.0 for GitHub Code Scanning / PR annotations. - **Persistent scan metadata**: `ScanResult` now includes `scannedAt` and `scanRoot`, giving JSON/SARIF/HTML reports a stable scan timestamp and source-root context. ### Fixed - **Config mutation**: `--exclude-tests` no longer mutates the loaded config object in both `scan` and `migrate` commands — uses spread instead of `push()` so the original config is never modified. - **Typed scan warnings**: `ScanResult.warnings` is now a typed `ScanWarning` union (`read-failure` | `parse-failure`) instead of opaque strings, preserving structured data at the domain boundary. - **StalenessEvaluator wired**: The `StalenessEvaluator` interface now has a call site in `scan()` — pass an `evaluator` to inject API-based staleness signals without touching core scanner logic. - **ScanConfig boundary**: `scan()` now accepts `ScanConfig` (scan-relevant fields only) rather than the full `FlagLintConfig`, decoupling the scanner from CLI output concerns (`reportTitle`, `outputDir`). - **Report count consistency**: Markdown and HTML stale candidate counts now exclude wildcard (`*`) usages, matching the CLI summary. ## [0.2.1] - 2026-05-23 ### Fixed - **Parse failure on generic TypeScript arrow functions** — `flagPredicate = (...)` and similar generic arrows in `.ts` files now parse correctly. Root cause: `@typescript-eslint/typescript-estree` wasn't receiving a `filePath`, so it couldn't apply TypeScript's extension-based JSX rules. Adding `filePath` tells the compiler to treat `.ts` files as non-JSX (generics parse cleanly) and `.tsx` as JSX (LDProvider detection still works). Validated against LaunchDarkly's own docs codebase. ### Added - **`wrappers` config option** — detect custom wrapper functions as flag usages. Add your wrapper names to `.flaglintrc`: ```json { "wrappers": ["flagPredicate", "useFlag", "getFlag", "isEnabled"] } ``` FlagLint will treat calls to these functions as `variation`-equivalent. Supports static and dynamic flag keys. Default is `[]` — no behaviour change for existing users. ## [0.2.0] - 2026-05-22 ### Breaking Changes - **`FlagUsage.isStale: boolean` replaced with `stalenessSignals: StalenessSignal[]`** The boolean had no provenance — you could not tell which signal (keyword, path, file-count, future LD API age) caused a flag to be marked stale. Replaced with a typed union array that records every signal that fired and why. **Migration:** Replace `usage.isStale` checks with the exported helper: ```typescript import { isStale } from "flaglint"; if (isStale(usage)) { ... } ``` JSON report consumers: the `usages[].isStale` field is gone. Use `usages[].stalenessSignals.length > 0` or the `isStale()` helper. Reports now include staleness provenance (source + keyword/pattern/count). - **Renamed config field: `staleThreshold` → `minFileCount`** The field was previously documented as "days before a flag is considered stale" but was actually implemented as a file-count threshold (a flag is stale if it appears in ≤ N files). The rename makes the actual behavior honest. **Migration:** In your `flaglint.config.json` or `.flaglintrc`, rename the field: ```json // Before { "staleThreshold": 5 } // After { "minFileCount": 5 } ``` ### Changed - Extracted shared stale detection logic (`STALE_KEYWORDS`, `checkStale`, `staleReason`) into `src/stale.ts` — single source of truth, eliminates duplicate keyword lists between scanner and reporter ### Roadmap - `v0.3`: Replace `minFileCount` with real date-based staleness detection via `git log` integration ## [0.1.5] - 2026-05-21 ### Fixed - Corrected the README CI badge URL. ## [0.1.0] - 2026-05-18 ### Added - `flaglint scan` command — scans codebase for LaunchDarkly SDK usage and reports flag inventory - `flaglint migrate` command — analyzes migration readiness and generates an OpenFeature migration plan - Detects `variation()`, `variationDetail()`, `allFlags()`, React hooks (`useFlags`, `useLDClient`), HOC (`withLDConsumer`), and Provider (`LDProvider`) patterns - Markdown, JSON, and HTML report formats - HTML reports include filterable flag table and light/dark mode support - Stale flag detection heuristics based on key names (`old`, `deprecated`, `legacy`, `temp`, `tmp`, `test`, `demo`) and file paths - Dynamic flag key identification — flags whose keys are determined at runtime - Migration readiness score (0–100) with per-pattern deductions - Automatic OpenFeature equivalent mapping for all detected call types - `.flaglintrc` config file support with Zod validation and clear error messages - `--dry-run` flag for `migrate` command - Exit code `1` when stale flags found (enables CI blocking) - GitHub Actions CI integration across Node.js 18, 20, and 22 - Test suite with 57 tests and ≥75% coverage across scanner, reporter, and config modules