Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
# 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
- [x] 1.1 Read Wheeler 2016 USENIX paper end-to-end; record line-anchored citations into per-pattern `.md` files
- [x] 1.2 Read `dropbox/zxcvbn` reference impl (matching.coffee, scoring.coffee, time_estimates.coffee, feedback.coffee, frequency_lists.coffee, adjacency_graphs.coffee)
- [x] 1.3 Read `../suaveplan/enterprise/password-strength.md` end-to-end
- [x] 1.4 Read `openspec/AGENTS.md` Sections 13 and 14
- [x] 1.5 `proposal.md` authored from `openspec/templates/proposal.md`
- [x] 1.6 `## Source mapping` table populated; "no source line" justifications written
- [x] 1.7 `specs/password-strength/spec.md` delta drafted from `openspec/templates/spec-delta.md`
- [x] 1.8 Every requirement in the spec delta carries a `**Verified by:**` line naming concrete `it(...)` titles
## 2. Package scaffold
- [x] 2.1 `packages/vault/password-strength/src/{matchers,scoring,assess,feedback,dicts,graphs,errors,schemas,observability,__benchmarks__}/` structure
- [x] 2.2 `package.json` — `@suaveplan/password-strength@0.0.0`, `type: module`, `sideEffects: false`, deps via `workspace:^` / `catalog:*`
- [x] 2.3 `tsconfig.lib.json` extends `@suaveplan/typescript-config/library`
- [x] 2.4 `vite.config.ts` + `vitest.config.ts` (uses `createTestConfig()`)
- [x] 2.5 `README.md` with Modules table linking every co-located `.md`
- [x] 2.6 Subpath `exports` map covers root + every feature subpath listed in the proposal
## 3. Implementation — pattern matchers
- [x] 3.1 `src/matchers/dictionary/dictionary.ts` — frequency-ranked lookup, l33t enumeration, reverse-string lookup
- [x] 3.2 `src/matchers/sequence/sequence.ts` — alphabetical + numeric, both directions, length ≥ 2
- [x] 3.3 `src/matchers/repeat/repeat.ts` — greedy chunk regex; recursive `baseGuesses`
- [x] 3.4 `src/matchers/date/date.ts` — separator + no-separator; `1900 ≤ year ≤ now + 50`
- [x] 3.5 `src/matchers/keyboard/keyboard.ts` — adjacency-graph walk; `turns` + `shiftedCount`; pluggable graph
- [x] 3.6 `src/matchers/regex/regex.ts` — recent-year + registry-pluggable
## 4. Implementation — bundled dictionaries
- [x] 4.1 `src/dicts/en/{passwords,english_wikipedia,male_names,female_names,surnames}.ts` — frozen `Map<string, number>` rank tables
- [x] 4.2 `src/dicts/de/`, `src/dicts/fr/`, `src/dicts/es/`, `src/dicts/zh-Hans/`, `src/dicts/ru/` — Wikipedia top-30k per locale
- [x] 4.3 `src/dicts/load-dictionaries.ts` — runtime swap returning fresh `DictionarySet`; `DICTIONARY_INVALID` on bad rank/token
## 5. Implementation — keyboard graphs
- [x] 5.1 `src/graphs/qwerty/qwerty.ts` — QWERTY adjacency table
- [x] 5.2 `src/graphs/dvorak/dvorak.ts` — DVORAK adjacency table
- [x] 5.3 `src/graphs/azerty/azerty.ts` — AZERTY adjacency table
- [x] 5.4 `src/graphs/validate-graph.ts` — `GRAPH_INVALID` on self-adjacency / non-symmetric edges
## 6. Implementation — scoring
- [x] 6.1 `src/scoring/most-guessable-match-sequence.ts` — `n^2` DP; lowest-product cover; factorial penalty; fewest-matches tie-break
- [x] 6.2 `src/scoring/estimate-guesses.ts` — per-pattern closed forms (dictionary, sequence, repeat, date, keyboard, regex)
- [x] 6.3 `src/scoring/crack-times.ts` — four scenarios; `adjustments` multiplier map
- [x] 6.4 `src/scoring/score.ts` — five-bucket score function
## 7. Implementation — public API
- [x] 7.1 `src/assess/assess.ts` — full pipeline; `userInputs`; custom dictionaries; 200-char DoS guard with `INPUT_TOO_LONG`
- [x] 7.2 `src/assess/assess-reactive.ts` — debounce + cancel-on-newer; `AbortSignal` threading; `ABORTED` on cancellation
- [x] 7.3 `src/feedback/feedback.ts` — keyed strings via injected resolver; English fallback; `RESOLVER_FAILED` on resolver throw
## 8. Implementation — errors + schemas + observability
- [x] 8.1 `src/errors/error-codes.ts` — `defineCodes("PWSTRENGTH", […])` with 6 codes; `PasswordStrengthError`
- [x] 8.2 `src/schemas/*.ts` — `AssessOptionsSchema`, `FeedbackOptionsSchema`, `AssessReactiveOptionsSchema`, `CrackTimeOptionsSchema`, `MatchSchema`, `StrengthResultSchema`
- [x] 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
- [x] 9.1 `src/index.ts` — explicit named re-exports (no `export *`)
- [x] 9.2 Every feature dir has its own barrel using explicit named exports
- [x] 9.3 Subpath export count in `package.json` matches feature module count
## 10. Documentation
- [x] 10.1 Sibling `.md` for every implementation `.ts` (≥200 words, basic + advanced examples, full API reference, Wheeler line citation in Implementation Notes)
- [x] 10.2 README links every co-located `.md` under `## Modules`
- [x] 10.3 CLAUDE.md package listing updated under `#### Vault orchestration` (or appropriate section)
## 11. Tests
- [x] 11.1 `src/matchers/dictionary/dictionary.test.ts` — l33t / reverse / rank / every bundled dict
- [x] 11.2 `src/matchers/sequence/sequence.test.ts` — ascending + descending; lower/upper/digits
- [x] 11.3 `src/matchers/repeat/repeat.test.ts` — single + multi-char chunks; recursive `baseGuesses`
- [x] 11.4 `src/matchers/date/date.test.ts` — separator + no-separator; year bounds; ambiguous-parse preference
- [x] 11.5 `src/matchers/keyboard/keyboard.test.ts` — QWERTY walks; turns; shifted-count; DVORAK + AZERTY swap
- [x] 11.6 `src/matchers/regex/regex.test.ts` — recent-year; custom regex registration
- [x] 11.7 `src/scoring/most-guessable-match-sequence.test.ts` — empty / single / multi cover; factorial; fewest-matches tie-break; recursive repeat
- [x] 11.8 `src/scoring/estimate-guesses.test.ts` — closed-form per pattern; corpus agreement ≤1 bit log10
- [x] 11.9 `src/scoring/crack-times.test.ts` — four scenarios; `adjustments`; humanized strings
- [x] 11.10 `src/scoring/score.test.ts` — every bucket boundary (`1e3 - 1`, `1e3`, `1e6 - 1`, `1e6`, …, `1e10`)
- [x] 11.11 `src/assess/assess.test.ts` — full pipeline; `userInputs`; custom dictionaries; 199-char accept / 200-char reject
- [x] 11.12 `src/assess/assess-reactive.test.ts` — debounce; cancel-on-newer; `AbortSignal`
- [x] 11.13 `src/feedback/feedback.test.ts` — resolver dispatch; English fallback; `RESOLVER_FAILED`
- [x] 11.14 `src/errors/error-codes.test.ts` — every code thrown by ≥1 production path
- [x] 11.15 `src/observability/metrics.test.ts` — every instrument recorded against in-memory `MeterProvider`
- [x] 11.16 `src/schemas/schemas.test.ts` — every schema rejects malformed input with `INVALID_OPTIONS`
- [x] 11.17 `src/conformance.test.ts` — vendored Wheeler corpus (~120 vectors) agrees to ≤1 bit log10 and exact `score`
- [x] 11.18 `src/properties.test.ts` — fast-check: matcher commutativity, score-bucket monotonicity, `crackTimes` monotonicity, idempotence, `INPUT_TOO_LONG` boundary
- [x] 11.19 Every `it(...)` carries `{ timeout: 5000 }` (or 30000 for genuinely async)
- [x] 11.20 Runner-neutral imports — `grep` for direct `vitest` / `bun:test` imports returns zero
## 12. Benchmarks
- [x] 12.1 `src/__benchmarks__/assess_short_password.bench.ts`
- [x] 12.2 `src/__benchmarks__/assess_long_password.bench.ts`
- [x] 12.3 `src/__benchmarks__/assess_dictionary_heavy.bench.ts`
- [x] 12.4 `src/__benchmarks__/dp_n100.bench.ts`
- [x] 12.5 `src/__benchmarks__/dp_n200.bench.ts`
- [x] 12.6 README publishes baseline numbers from a single run
## 13. Pre-archive verification (the 5-gate triple)
- [x] 13.1 `bun run check` — Biome 0 errors / 0 warnings / 0 info
- [x] 13.2 `bunx turbo lint typecheck test --filter=@suaveplan/password-strength` — green
- [x] 13.3 `bunx turbo test:coverage --filter=@suaveplan/password-strength` — 100/100/100/100
- [x] 13.4 `bun scripts/openspec-conformance.ts @suaveplan/password-strength` — exit 0, no `error` findings
- [x] 13.5 `bunx turbo build --filter=@suaveplan/password-strength` — green
## 14. Pre-archive 7-row checklist (AGENTS.md Section 13)
- [x] 14.1 Generics threaded; `expectTypeOf` assertions on every literal-type claim (Match discriminated union, score literal-`0|1|2|3|4`)
- [x] 14.2 Zod schemas wired at every declared boundary (`assess`, `feedback`, `assessReactive`, `crackTimes`, `loadDictionaries`)
- [x] 14.3 Every error code in `error-codes.ts` thrown by ≥1 production path (asserted in `error-codes.test.ts`)
- [x] 14.4 Strict-mode behaviors tested both on and off (`escapeHtml`, `INPUT_TOO_LONG` boundary)
- [x] 14.5 OTel instruments emitted from production paths and asserted with a real in-memory `MeterProvider`
- [x] 14.6 Lifecycle dedup tested where claimed — `assessReactive` cancel-on-newer dedups in-flight compute
- [x] 14.7 No retroactive tick — every box above reflects work done in this change
## 15. Archive
- [x] 15.1 Fold spec delta into `openspec/specs/password-strength/spec.md`
- [x] 15.2 Move `openspec/changes/enterprise-password-strength/` → `openspec/archive/<yyyy-mm-dd>-enterprise-password-strength/`
- [x] 15.3 Refresh `MANIFEST.md` (`bun scripts/folder-reorg/generate-manifest.ts`)
- [x] 15.4 Refresh `MISSING.yml` (`bun scripts/generate-missing.ts`)
- [x] 15.5 Update `STATUS.md` row from `Pending` → `Completed`
- [x] 15.6 Update `SUMMARY.md` per-package paragraph
- [x] 15.7 `bunx markdownlint-cli2 --fix` clean across touched files