Files
2026-05-27 23:29:53 +01:00

9.5 KiB

Tasks — @suaveplan/password-strength (zxcvbn-style password strength estimator)

Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes for work that was already in main when this change opened. Do not tick a box for a test that doesn't exist or doesn't pin the requirement it claims to verify.

1. Spec authoring

  • 1.1 Read Wheeler 2016 USENIX paper end-to-end; record line-anchored citations into per-pattern .md files
  • 1.2 Read dropbox/zxcvbn reference impl (matching.coffee, scoring.coffee, time_estimates.coffee, feedback.coffee, frequency_lists.coffee, adjacency_graphs.coffee)
  • 1.3 Read ../suaveplan/enterprise/password-strength.md end-to-end
  • 1.4 Read openspec/AGENTS.md Sections 13 and 14
  • 1.5 proposal.md authored from openspec/templates/proposal.md
  • 1.6 ## Source mapping table populated; "no source line" justifications written
  • 1.7 specs/password-strength/spec.md delta drafted from openspec/templates/spec-delta.md
  • 1.8 Every requirement in the spec delta carries a **Verified by:** line naming concrete it(...) titles

2. Package scaffold

  • 2.1 packages/vault/password-strength/src/{matchers,scoring,assess,feedback,dicts,graphs,errors,schemas,observability,__benchmarks__}/ structure
  • 2.2 package.json — @suaveplan/password-strength@0.0.0, type: module, sideEffects: false, deps via workspace:^ / catalog:*
  • 2.3 tsconfig.lib.json extends @suaveplan/typescript-config/library
  • 2.4 vite.config.ts + vitest.config.ts (uses createTestConfig())
  • 2.5 README.md with Modules table linking every co-located .md
  • 2.6 Subpath exports map covers root + every feature subpath listed in the proposal

3. Implementation — pattern matchers

  • 3.1 src/matchers/dictionary/dictionary.ts — frequency-ranked lookup, l33t enumeration, reverse-string lookup
  • 3.2 src/matchers/sequence/sequence.ts — alphabetical + numeric, both directions, length ≥ 2
  • 3.3 src/matchers/repeat/repeat.ts — greedy chunk regex; recursive baseGuesses
  • 3.4 src/matchers/date/date.ts — separator + no-separator; 1900 ≤ year ≤ now + 50
  • 3.5 src/matchers/keyboard/keyboard.ts — adjacency-graph walk; turns + shiftedCount; pluggable graph
  • 3.6 src/matchers/regex/regex.ts — recent-year + registry-pluggable

4. Implementation — bundled dictionaries

  • 4.1 src/dicts/en/{passwords,english_wikipedia,male_names,female_names,surnames}.ts — frozen Map<string, number> rank tables
  • 4.2 src/dicts/de/, src/dicts/fr/, src/dicts/es/, src/dicts/zh-Hans/, src/dicts/ru/ — Wikipedia top-30k per locale
  • 4.3 src/dicts/load-dictionaries.ts — runtime swap returning fresh DictionarySet; DICTIONARY_INVALID on bad rank/token

5. Implementation — keyboard graphs

  • 5.1 src/graphs/qwerty/qwerty.ts — QWERTY adjacency table
  • 5.2 src/graphs/dvorak/dvorak.ts — DVORAK adjacency table
  • 5.3 src/graphs/azerty/azerty.ts — AZERTY adjacency table
  • 5.4 src/graphs/validate-graph.ts — GRAPH_INVALID on self-adjacency / non-symmetric edges

6. Implementation — scoring

  • 6.1 src/scoring/most-guessable-match-sequence.ts — n^2 DP; lowest-product cover; factorial penalty; fewest-matches tie-break
  • 6.2 src/scoring/estimate-guesses.ts — per-pattern closed forms (dictionary, sequence, repeat, date, keyboard, regex)
  • 6.3 src/scoring/crack-times.ts — four scenarios; adjustments multiplier map
  • 6.4 src/scoring/score.ts — five-bucket score function

7. Implementation — public API

  • 7.1 src/assess/assess.ts — full pipeline; userInputs; custom dictionaries; 200-char DoS guard with INPUT_TOO_LONG
  • 7.2 src/assess/assess-reactive.ts — debounce + cancel-on-newer; AbortSignal threading; ABORTED on cancellation
  • 7.3 src/feedback/feedback.ts — keyed strings via injected resolver; English fallback; RESOLVER_FAILED on resolver throw

8. Implementation — errors + schemas + observability

  • 8.1 src/errors/error-codes.ts — defineCodes("PWSTRENGTH", […]) with 6 codes; PasswordStrengthError
  • 8.2 src/schemas/*.ts — AssessOptionsSchema, FeedbackOptionsSchema, AssessReactiveOptionsSchema, CrackTimeOptionsSchema, MatchSchema, StrengthResultSchema
  • 8.3 src/observability/metrics.ts — pwstrength.assess_duration_ms, pwstrength.score_distribution_total, pwstrength.errors_total, pwstrength.matchers_run_total

9. Root barrel + subpath exports

  • 9.1 src/index.ts — explicit named re-exports (no export *)
  • 9.2 Every feature dir has its own barrel using explicit named exports
  • 9.3 Subpath export count in package.json matches feature module count

10. Documentation

  • 10.1 Sibling .md for every implementation .ts (≥200 words, basic + advanced examples, full API reference, Wheeler line citation in Implementation Notes)
  • 10.2 README links every co-located .md under ## Modules
  • 10.3 CLAUDE.md package listing updated under #### Vault orchestration (or appropriate section)

11. Tests

  • 11.1 src/matchers/dictionary/dictionary.test.ts — l33t / reverse / rank / every bundled dict
  • 11.2 src/matchers/sequence/sequence.test.ts — ascending + descending; lower/upper/digits
  • 11.3 src/matchers/repeat/repeat.test.ts — single + multi-char chunks; recursive baseGuesses
  • 11.4 src/matchers/date/date.test.ts — separator + no-separator; year bounds; ambiguous-parse preference
  • 11.5 src/matchers/keyboard/keyboard.test.ts — QWERTY walks; turns; shifted-count; DVORAK + AZERTY swap
  • 11.6 src/matchers/regex/regex.test.ts — recent-year; custom regex registration
  • 11.7 src/scoring/most-guessable-match-sequence.test.ts — empty / single / multi cover; factorial; fewest-matches tie-break; recursive repeat
  • 11.8 src/scoring/estimate-guesses.test.ts — closed-form per pattern; corpus agreement ≤1 bit log10
  • 11.9 src/scoring/crack-times.test.ts — four scenarios; adjustments; humanized strings
  • 11.10 src/scoring/score.test.ts — every bucket boundary (1e3 - 1, 1e3, 1e6 - 1, 1e6, …, 1e10)
  • 11.11 src/assess/assess.test.ts — full pipeline; userInputs; custom dictionaries; 199-char accept / 200-char reject
  • 11.12 src/assess/assess-reactive.test.ts — debounce; cancel-on-newer; AbortSignal
  • 11.13 src/feedback/feedback.test.ts — resolver dispatch; English fallback; RESOLVER_FAILED
  • 11.14 src/errors/error-codes.test.ts — every code thrown by ≥1 production path
  • 11.15 src/observability/metrics.test.ts — every instrument recorded against in-memory MeterProvider
  • 11.16 src/schemas/schemas.test.ts — every schema rejects malformed input with INVALID_OPTIONS
  • 11.17 src/conformance.test.ts — vendored Wheeler corpus (~120 vectors) agrees to ≤1 bit log10 and exact score
  • 11.18 src/properties.test.ts — fast-check: matcher commutativity, score-bucket monotonicity, crackTimes monotonicity, idempotence, INPUT_TOO_LONG boundary
  • 11.19 Every it(...) carries { timeout: 5000 } (or 30000 for genuinely async)
  • 11.20 Runner-neutral imports — grep for direct vitest / bun:test imports returns zero

12. Benchmarks

  • 12.1 src/__benchmarks__/assess_short_password.bench.ts
  • 12.2 src/__benchmarks__/assess_long_password.bench.ts
  • 12.3 src/__benchmarks__/assess_dictionary_heavy.bench.ts
  • 12.4 src/__benchmarks__/dp_n100.bench.ts
  • 12.5 src/__benchmarks__/dp_n200.bench.ts
  • 12.6 README publishes baseline numbers from a single run

13. Pre-archive verification (the 5-gate triple)

  • 13.1 bun run check — Biome 0 errors / 0 warnings / 0 info
  • 13.2 bunx turbo lint typecheck test --filter=@suaveplan/password-strength — green
  • 13.3 bunx turbo test:coverage --filter=@suaveplan/password-strength — 100/100/100/100
  • 13.4 bun scripts/openspec-conformance.ts @suaveplan/password-strength — exit 0, no error findings
  • 13.5 bunx turbo build --filter=@suaveplan/password-strength — green

14. Pre-archive 7-row checklist (AGENTS.md Section 13)

  • 14.1 Generics threaded; expectTypeOf assertions on every literal-type claim (Match discriminated union, score literal-0|1|2|3|4)
  • 14.2 Zod schemas wired at every declared boundary (assess, feedback, assessReactive, crackTimes, loadDictionaries)
  • 14.3 Every error code in error-codes.ts thrown by ≥1 production path (asserted in error-codes.test.ts)
  • 14.4 Strict-mode behaviors tested both on and off (escapeHtml, INPUT_TOO_LONG boundary)
  • 14.5 OTel instruments emitted from production paths and asserted with a real in-memory MeterProvider
  • 14.6 Lifecycle dedup tested where claimed — assessReactive cancel-on-newer dedups in-flight compute
  • 14.7 No retroactive tick — every box above reflects work done in this change

15. Archive

  • 15.1 Fold spec delta into openspec/specs/password-strength/spec.md
  • 15.2 Move openspec/changes/enterprise-password-strength/ → openspec/archive/<yyyy-mm-dd>-enterprise-password-strength/
  • 15.3 Refresh MANIFEST.md (bun scripts/folder-reorg/generate-manifest.ts)
  • 15.4 Refresh MISSING.yml (bun scripts/generate-missing.ts)
  • 15.5 Update STATUS.md row from Pending → Completed
  • 15.6 Update SUMMARY.md per-package paragraph
  • 15.7 bunx markdownlint-cli2 --fix clean across touched files