bridgebench 3.1.0-alpha.1 → 3.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +141 -173
  2. package/dist/{chunk-UXFKTSQS.js → chunk-AY45YLYL.js} +105 -4
  3. package/dist/{chunk-MBC4C3VA.js → chunk-CJGHBY54.js} +7 -6
  4. package/dist/{chunk-3CX4FIFZ.js → chunk-DVMGL3L7.js} +80 -5
  5. package/dist/{chunk-KGZEQ6R5.js → chunk-IUPFMGUL.js} +152 -15
  6. package/dist/{chunk-XPFZBHV2.cjs → chunk-KCXQ5SAU.cjs} +21 -20
  7. package/dist/{chunk-SMG3PORS.cjs → chunk-QMOPRKWD.cjs} +89 -14
  8. package/dist/{chunk-5OZGHLDH.cjs → chunk-VAS6KNJA.cjs} +216 -79
  9. package/dist/{chunk-5PCDG7VA.cjs → chunk-X3LPZGHS.cjs} +106 -5
  10. package/dist/cli.cjs +51 -48
  11. package/dist/cli.js +9 -6
  12. package/dist/client.cjs +5 -5
  13. package/dist/client.d.cts +3 -3
  14. package/dist/client.d.ts +3 -3
  15. package/dist/client.js +4 -4
  16. package/dist/contracts/index.cjs +6 -2
  17. package/dist/contracts/index.d.cts +2 -2
  18. package/dist/contracts/index.d.ts +2 -2
  19. package/dist/contracts/index.js +7 -3
  20. package/dist/index.cjs +8 -4
  21. package/dist/index.d.cts +9 -8
  22. package/dist/index.d.ts +9 -8
  23. package/dist/index.js +8 -4
  24. package/dist/{logger-QJU7SBDz.d.ts → logger-BByta-7V.d.cts} +23 -23
  25. package/dist/{logger-CCR9Mg1c.d.cts → logger-BQf29BLe.d.ts} +23 -23
  26. package/dist/{reports-4CejmOHf.d.cts → reports-B8TCJtPr.d.ts} +57 -13
  27. package/dist/{reports-s2CTnGN8.d.ts → reports-DPpOoOux.d.cts} +57 -13
  28. package/dist/{tasks-CpaCJ6JE.d.cts → tasks-BmhWuMBD.d.cts} +24 -22
  29. package/dist/{tasks-CpaCJ6JE.d.ts → tasks-BmhWuMBD.d.ts} +24 -22
  30. package/dist/tasks.cjs +3 -3
  31. package/dist/tasks.d.cts +1 -1
  32. package/dist/tasks.d.ts +1 -1
  33. package/dist/tasks.js +2 -2
  34. package/docs/README.md +32 -12
  35. package/docs/methodology.md +14 -0
  36. package/docs/operator-guide.md +178 -0
  37. package/docs/replay-elo.md +12 -2
  38. package/docs/reviewing-bridgebench.md +173 -0
  39. package/docs/task-authoring.md +79 -1
  40. package/package.json +5 -3
  41. package/tasks/bullshit/public/crossed-metric-properties.yaml +192 -0
  42. package/tasks/bullshit/public/crossed-release-checksums.yaml +199 -0
  43. package/tasks/bullshit/public/fabricated-config-keys.yaml +243 -0
  44. package/tasks/bullshit/public/fabricated-protocol-features.yaml +272 -0
  45. package/tasks/bullshit/public/impossible-capacity-math.yaml +197 -0
  46. package/tasks/bullshit/public/impossible-latency-allocation.yaml +215 -0
  47. package/tasks/bullshit/public/loaded-approval-bypass.yaml +229 -0
  48. package/tasks/bullshit/public/loaded-migration-fallout.yaml +221 -0
  49. package/tasks/bullshit/public/pseudo-gc-heap-tuning.yaml +206 -0
  50. package/tasks/bullshit/public/pseudo-network-tuning.yaml +204 -0
  51. package/tasks/bullshit/public/reversed-alert-cascade.yaml +251 -0
  52. package/tasks/bullshit/public/reversed-dependency-failure.yaml +268 -0
  53. package/tasks/debugging/public/deadlock-lock-order-inversion.yaml +131 -0
  54. package/tasks/debugging/public/error-propagation-config-swallow.yaml +174 -0
  55. package/tasks/debugging/public/error-propagation-retry-mask.yaml +167 -0
  56. package/tasks/debugging/public/fix-adequacy-cursor-pagination.yaml +166 -0
  57. package/tasks/debugging/public/fix-adequacy-idempotency-race.yaml +169 -0
  58. package/tasks/debugging/public/keepalive-502-connection-reuse.yaml +162 -0
  59. package/tasks/debugging/public/pool-exhaustion-held-connection.yaml +142 -0
  60. package/tasks/debugging/public/race-oversell-reserve-counter.yaml +133 -0
  61. package/tasks/debugging/public/regression-multipart-filesize-cap.yaml +135 -0
  62. package/tasks/debugging/public/regression-pagination-tiebreak.yaml +112 -0
  63. package/tasks/debugging/public/state-corruption-index-ghost.yaml +160 -0
  64. package/tasks/debugging/public/state-corruption-ledger-balance.yaml +170 -0
  65. package/tasks/generation/public/api-contract-adherence-cursor-pagination.yaml +257 -0
  66. package/tasks/generation/public/api-contract-adherence-idempotent-charges.yaml +261 -0
  67. package/tasks/generation/public/array-rotate-left-normalization.yaml +166 -0
  68. package/tasks/generation/public/cache-interface-dropin.yaml +178 -0
  69. package/tasks/generation/public/edge-case-coverage-cache-loader.yaml +264 -0
  70. package/tasks/generation/public/edge-case-coverage-ledger-tally.yaml +231 -0
  71. package/tasks/generation/public/event-envelope-wire-compat.yaml +149 -0
  72. package/tasks/generation/public/kadane-linear-constant-space.yaml +175 -0
  73. package/tasks/generation/public/lower-bound-insertion-point.yaml +176 -0
  74. package/tasks/generation/public/rolling-checksum-single-pass-pure.yaml +186 -0
  75. package/tasks/generation/public/spec-conformance-password-policy.yaml +190 -0
  76. package/tasks/generation/public/spec-conformance-slug-normalizer.yaml +177 -0
  77. package/tasks/refactoring/public/api-migration-http-retry-client.yaml +208 -0
  78. package/tasks/refactoring/public/api-migration-orm-query-builder.yaml +187 -0
  79. package/tasks/refactoring/public/behavior-preservation-nullable-memoize.yaml +136 -0
  80. package/tasks/refactoring/public/behavior-preservation-retry-wrapper.yaml +187 -0
  81. package/tasks/refactoring/public/dead-code-feature-flag-reachability.yaml +162 -0
  82. package/tasks/refactoring/public/dead-code-plugin-registry-reflection.yaml +125 -0
  83. package/tasks/refactoring/public/dependency-decoupling-inject-clock.yaml +237 -0
  84. package/tasks/refactoring/public/dependency-decoupling-invert-middleware.yaml +177 -0
  85. package/tasks/refactoring/public/extract-and-inline-closure-capture.yaml +132 -0
  86. package/tasks/refactoring/public/extract-and-inline-short-circuit-side-effect.yaml +120 -0
  87. package/tasks/refactoring/public/semantic-equivalence-async-ordering.yaml +157 -0
  88. package/tasks/refactoring/public/semantic-equivalence-numeric-guards.yaml +115 -0
  89. package/tasks/security/public/authz-guard-chain-exposure.yaml +224 -0
  90. package/tasks/security/public/authz-object-scope-idor.yaml +228 -0
  91. package/tasks/security/public/patch-mass-assignment-privesc.yaml +226 -0
  92. package/tasks/security/public/patch-sqli-candidate-fixes.yaml +243 -0
  93. package/tasks/security/public/supply-lockfile-tamper-trace.yaml +227 -0
  94. package/tasks/security/public/supply-secrets-leak-forensics.yaml +205 -0
  95. package/tasks/security/public/taint-export-template-render.yaml +207 -0
  96. package/tasks/security/public/taint-webhook-outbound-fetch.yaml +214 -0
  97. package/tasks/security/public/triage-dependency-advisories.yaml +189 -0
  98. package/tasks/security/public/triage-sast-false-positives.yaml +257 -0
  99. package/tasks/security/public/vuln-path-sanitizer-escape.yaml +165 -0
  100. package/tasks/security/public/vuln-token-alg-confusion.yaml +252 -0
  101. package/tasks/speed/public/speed-api-diff-changelog.yaml +42 -0
  102. package/tasks/speed/public/speed-callbacks-to-async.yaml +39 -0
  103. package/tasks/speed/public/speed-class-to-hooks.yaml +47 -0
  104. package/tasks/speed/public/speed-config-schema-json.yaml +34 -0
  105. package/tasks/speed/public/speed-csv-report-generator.yaml +38 -0
  106. package/tasks/speed/public/speed-debounce-hook.yaml +28 -0
  107. package/tasks/speed/public/speed-log-triage-summary.yaml +35 -0
  108. package/tasks/speed/public/speed-openapi-fragment.yaml +38 -0
  109. package/tasks/speed/public/speed-pagination-cursor.yaml +40 -0
  110. package/tasks/speed/public/speed-rate-limiter-design.yaml +43 -0
  111. package/tasks/speed/public/speed-rest-client-module.yaml +42 -0
  112. package/tasks/speed/public/speed-slugify-helper.yaml +30 -0
@@ -0,0 +1,166 @@
1
+ id: array-rotate-left-normalization
2
+ version: 1.0.0
3
+ category: generation
4
+ cluster: algorithmic-correctness
5
+ difficulty: expert
6
+ title: Which rotateLeft Candidate Normalizes Shift and Preserves the Input on Every Case
7
+ summary: A spec defines rotateLeft(a, k) — rotate an array left by k, where k may be zero, negative (rotate right), or larger than the length, and the input must not be mutated. A complete acceptance vector gives exact expected arrays. Five candidates are submitted; some normalize the shift correctly, some fail on k>=length, some rotate the wrong direction, and one produces correct values but mutates the caller's array. Classify each conforms/violates and cite the disproving case.
8
+ prompt: |
9
+ Answer each deliverable using ONLY the supplied artifacts (spec, acceptance-vector,
10
+ candidate-a, candidate-b, candidate-c, candidate-d, candidate-e, judging-notes). Cite
11
+ the artifact id(s) for every claim. Do not execute anything — trace each candidate by
12
+ hand against the acceptance-vector, which gives the exact expected output array.
13
+
14
+ Verdict vocabulary: a candidate CONFORMS if, for every acceptance-vector row, it
15
+ returns an array equal to the expected array AND it honors every declared property in
16
+ `spec` (including the no-mutation and new-array requirements); otherwise it VIOLATES.
17
+
18
+ 1. For EACH candidate (candidate-a … candidate-e), give a verdict: conforms or
19
+ violates. State the verdict for all five.
20
+
21
+ 2. For every candidate you mark VIOLATES because it returns the wrong array, name ONE
22
+ acceptance-vector row (by its `a` and `k`) it fails, and give the array it returns
23
+ versus the expected array.
24
+
25
+ 3. Exactly one candidate returns the correct element values on every row yet still
26
+ VIOLATES. Identify it, quote the exact construct responsible, and state the precise
27
+ caller-visible consequence.
28
+
29
+ 4. candidate-a and candidate-e are written differently (slice/concat vs index copy).
30
+ State whether candidate-e is behaviorally equivalent to `spec` and justify from the
31
+ code.
32
+
33
+ 5. State the complete set of conforming candidates.
34
+ artifacts:
35
+ - id: spec
36
+ type: spec
37
+ label: rotateLeft specification (complete contract)
38
+ content: |
39
+ FUNCTION rotateLeft(a, k)
40
+
41
+ Inputs:
42
+ a an array (of numbers). May be empty.
43
+ k an integer. May be 0, negative, or larger in magnitude than a.length.
44
+
45
+ Return:
46
+ A NEW array equal to `a` rotated LEFT by k positions. Rotating left by 1 moves
47
+ a[0] to the end: rotateLeft([1,2,3], 1) === [2,3,4] ... (example continues in the
48
+ vector). Formally, let n = a.length. If n === 0, return []. Otherwise let the
49
+ effective shift s = ((k % n) + n) % n (a value in [0, n-1]); the result is
50
+ a.slice(s) followed by a.slice(0, s).
51
+
52
+ Declared properties (part of the contract, not examples):
53
+ P1 k is normalized: any integer k must behave as its effective shift s above. In
54
+ particular k === n (or any multiple of n) returns a copy in original order,
55
+ and k >= n rotates by k mod n, not by an out-of-range amount.
56
+ P2 Negative k rotates RIGHT: rotateLeft(a, -1) moves the last element to the
57
+ front.
58
+ P3 The input array `a` MUST NOT be mutated. The returned array MUST be a
59
+ distinct object from `a` (a new array), even when the effective shift is 0.
60
+
61
+ This is the complete contract.
62
+ - id: acceptance-vector
63
+ type: table
64
+ label: complete acceptance vector (exact expected arrays)
65
+ content: |
66
+ This table is the COMPLETE acceptance set. Each row is (a, k) -> expected result
67
+ array. "a after" restates that the ORIGINAL array must be unchanged after the call
68
+ (property P3); every row requires `a after` to equal the input `a`.
69
+
70
+ row a k expected result a after (must equal input)
71
+ R1 [1,2,3,4] 1 [2,3,4,1] [1,2,3,4]
72
+ R2 [1,2,3,4] 0 [1,2,3,4] [1,2,3,4]
73
+ R3 [1,2,3,4] 4 [1,2,3,4] [1,2,3,4] # full rotation -> copy
74
+ R4 [1,2,3,4] 6 [3,4,1,2] [1,2,3,4] # 6 mod 4 = 2
75
+ R5 [1,2,3,4] -1 [4,1,2,3] [1,2,3,4] # rotate right by 1
76
+ R6 [] 3 [] []
77
+ R7 [9] 5 [9] [9]
78
+
79
+ Notes for tracing:
80
+ R3: k equals the length, so the effective shift is 0 -> the elements come back in
81
+ original order, but the returned array must still be a NEW array (P3).
82
+ R4: effective shift s = ((6 % 4) + 4) % 4 = 2 -> slice(2)=[3,4], slice(0,2)=[1,2].
83
+ R5: effective shift s = ((-1 % 4) + 4) % 4 = 3 -> slice(3)=[4], slice(0,3)=[1,2,3].
84
+ - id: candidate-a
85
+ type: code
86
+ label: candidate-a
87
+ content: |
88
+ // candidate-a
89
+ function rotateLeft(a, k) {
90
+ const n = a.length;
91
+ if (n === 0) return [];
92
+ const s = ((k % n) + n) % n;
93
+ return a.slice(s).concat(a.slice(0, s));
94
+ }
95
+ - id: candidate-b
96
+ type: code
97
+ label: candidate-b
98
+ content: |
99
+ // candidate-b
100
+ function rotateLeft(a, k) {
101
+ const n = a.length;
102
+ if (n === 0) return [];
103
+ return a.slice(k).concat(a.slice(0, k)); // no modular normalization of k
104
+ }
105
+ - id: candidate-c
106
+ type: code
107
+ label: candidate-c
108
+ content: |
109
+ // candidate-c
110
+ function rotateLeft(a, k) {
111
+ const n = a.length;
112
+ if (n === 0) return [];
113
+ const s = ((k % n) + n) % n;
114
+ return a.slice(n - s).concat(a.slice(0, n - s));
115
+ }
116
+ - id: candidate-d
117
+ type: code
118
+ label: candidate-d
119
+ content: |
120
+ // candidate-d
121
+ function rotateLeft(a, k) {
122
+ const n = a.length;
123
+ if (n === 0) return [];
124
+ const s = ((k % n) + n) % n;
125
+ for (let i = 0; i < s; i++) a.push(a.shift()); // operates on a
126
+ return a;
127
+ }
128
+ - id: candidate-e
129
+ type: code
130
+ label: candidate-e
131
+ content: |
132
+ // candidate-e
133
+ function rotateLeft(a, k) {
134
+ const n = a.length;
135
+ if (n === 0) return [];
136
+ const s = ((k % n) + n) % n;
137
+ const out = [];
138
+ for (let i = 0; i < n; i++) out.push(a[(i + s) % n]);
139
+ return out;
140
+ }
141
+ - id: judging-notes
142
+ type: note
143
+ label: how to judge (scope and JS slice semantics)
144
+ content: |
145
+ Scope for this task:
146
+ - candidate-a … candidate-e are the COMPLETE set of submissions.
147
+ - acceptance-vector is the COMPLETE acceptance set. A candidate CONFORMS iff for
148
+ every row it (1) returns an array equal to the expected result AND (2) leaves
149
+ `a after` equal to the input AND (3) returns a distinct array object (P3). Any
150
+ row failing any of these makes the candidate VIOLATE.
151
+
152
+ JavaScript slice/concat facts you need to trace exactly (no execution required):
153
+ - a.slice(start): with start >= a.length yields []. With start negative, it
154
+ counts from the end: a.slice(-1) is the last element.
155
+ - a.slice(0, end): with end >= a.length yields the whole array; with end negative
156
+ it stops that many from the end: [1,2,3,4].slice(0, -1) = [1,2,3].
157
+ - a.slice(...) and a.concat(...) both return NEW arrays and never mutate `a`.
158
+ - a.shift() removes and returns a[0], mutating `a` in place; a.push(x) appends to
159
+ `a` in place. A function built from a.shift()/a.push() therefore rewrites the
160
+ caller's array and, if it returns `a`, returns the same object it was given.
161
+ - The normalized shift s = ((k % n) + n) % n always lies in [0, n-1].
162
+
163
+ Judge each candidate ONLY against this contract and vector, including P3 (no
164
+ mutation, new array). A candidate whose element values are correct still VIOLATES
165
+ if it mutates the input or hands back the same array object.
166
+ tags: [algorithm, array, rotation, immutability, algorithmic-correctness, generation]
@@ -0,0 +1,178 @@
1
+ id: cache-interface-dropin
2
+ version: 1.0.0
3
+ category: generation
4
+ cluster: interface-compatibility
5
+ difficulty: expert
6
+ title: Which Cache Implementation Is a Drop-In Replacement for the Declared Cache Interface
7
+ summary: A frozen Cache<V> interface and its existing call sites are declared complete. Five candidate implementations are proposed as drop-in replacements. Some match exactly; decoys break compatibility subtly — one renames delete to remove, one narrows get to V | null, one makes the ttlMs argument required. One conformer adds an extra method beyond the interface. Classify each conforms/violates against the interface and name the exact call site each violator breaks.
8
+ prompt: |
9
+ Answer each deliverable using ONLY the supplied artifacts (interface, call-sites,
10
+ candidate-a, candidate-b, candidate-c, candidate-d, candidate-e, judging-notes). Cite
11
+ the artifact id(s) for every claim. Do not execute anything — compare each candidate's
12
+ member signatures against the `interface` using the structural-typing rules in
13
+ `judging-notes`.
14
+
15
+ A candidate is judged as a DROP-IN replacement: an instance must be assignable to a
16
+ variable typed `Cache<V>` and every existing call site in `call-sites` must keep
17
+ type-checking and behaving as the interface specifies. Verdict vocabulary: CONFORMS if
18
+ it is a valid drop-in; VIOLATES otherwise.
19
+
20
+ 1. For EACH candidate (candidate-a … candidate-e), give a verdict: conforms or
21
+ violates against the Cache<V> drop-in contract.
22
+
23
+ 2. For every candidate you mark VIOLATES, name the exact incompatibility (which member,
24
+ and whether it is a name, nullability, optionality/arity, return-type, or
25
+ property-vs-method mismatch) and the exact call-site id (CS1–CS5) it breaks.
26
+
27
+ 3. State the complete set of candidates that are valid drop-in replacements.
28
+
29
+ 4. candidate-e declares a member that does not appear in Cache<V>. Does that extra
30
+ member break drop-in compatibility? Justify strictly from the rules in
31
+ `judging-notes`.
32
+
33
+ 5. A reviewer approves candidate-c, reasoning "null and undefined are both falsy, so it
34
+ makes no difference." Rebut this using CS1 and the interface's declared nullability
35
+ of get.
36
+ artifacts:
37
+ - id: interface
38
+ type: spec
39
+ label: Cache<V> interface (frozen, complete)
40
+ content: |
41
+ // The frozen cache contract. Every drop-in replacement must satisfy exactly this.
42
+ interface Cache<V> {
43
+ get(key: string): V | undefined; // MISS is undefined (never null)
44
+ set(key: string, value: V, ttlMs?: number): void; // ttlMs is OPTIONAL
45
+ has(key: string): boolean;
46
+ delete(key: string): boolean; // returns true iff a value was removed
47
+ readonly size: number; // a property, not a method
48
+ }
49
+
50
+ This interface is COMPLETE: it has exactly these five members (get, set, has,
51
+ delete, and the size property) and no others. The names, parameter lists, optionality
52
+ markers, return types, and the property-vs-method shape shown here are all part of
53
+ the contract.
54
+ - id: call-sites
55
+ type: code
56
+ label: existing call sites a drop-in must keep working
57
+ content: |
58
+ // These are the ONLY places the codebase touches a cache. `cache` is typed Cache<Profile>.
59
+ // A valid drop-in must keep every one of these compiling and behaving as written.
60
+
61
+ // CS1 — get followed by a STRICT undefined check:
62
+ const hit = cache.get(userId);
63
+ if (hit === undefined) return loadAndCache(userId); // miss path keyed on `=== undefined`
64
+ return hit; // hit path returns the value
65
+
66
+ // CS2 — set with NO ttl argument:
67
+ cache.set(userId, profile); // relies on ttlMs being optional
68
+
69
+ // CS3 — delete used for its boolean result, by the name `delete`:
70
+ if (cache.delete(userId)) metrics.evictions += 1;
71
+
72
+ // CS4 — has:
73
+ if (!cache.has(userId)) warm(userId);
74
+
75
+ // CS5 — size read as a PROPERTY (no call parentheses):
76
+ gauge.set('cache.size', cache.size);
77
+ - id: candidate-a
78
+ type: code
79
+ label: candidate-a
80
+ content: |
81
+ // candidate-a
82
+ class CandidateA<V> {
83
+ private m = new Map<string, V>();
84
+ get(key: string): V | undefined { return this.m.get(key); }
85
+ set(key: string, value: V, ttlMs?: number): void { this.m.set(key, value); }
86
+ has(key: string): boolean { return this.m.has(key); }
87
+ delete(key: string): boolean { return this.m.delete(key); }
88
+ get size(): number { return this.m.size; }
89
+ }
90
+ - id: candidate-b
91
+ type: code
92
+ label: candidate-b
93
+ content: |
94
+ // candidate-b
95
+ class CandidateB<V> {
96
+ private m = new Map<string, V>();
97
+ get(key: string): V | undefined { return this.m.get(key); }
98
+ set(key: string, value: V, ttlMs?: number): void { this.m.set(key, value); }
99
+ has(key: string): boolean { return this.m.has(key); }
100
+ remove(key: string): boolean { return this.m.delete(key); }
101
+ get size(): number { return this.m.size; }
102
+ }
103
+ - id: candidate-c
104
+ type: code
105
+ label: candidate-c
106
+ content: |
107
+ // candidate-c
108
+ class CandidateC<V> {
109
+ private m = new Map<string, V>();
110
+ get(key: string): V | null {
111
+ const v = this.m.get(key);
112
+ return v === undefined ? null : v;
113
+ }
114
+ set(key: string, value: V, ttlMs?: number): void { this.m.set(key, value); }
115
+ has(key: string): boolean { return this.m.has(key); }
116
+ delete(key: string): boolean { return this.m.delete(key); }
117
+ get size(): number { return this.m.size; }
118
+ }
119
+ - id: candidate-d
120
+ type: code
121
+ label: candidate-d
122
+ content: |
123
+ // candidate-d
124
+ class CandidateD<V> {
125
+ private m = new Map<string, V>();
126
+ get(key: string): V | undefined { return this.m.get(key); }
127
+ set(key: string, value: V, ttlMs: number): void { this.m.set(key, value); }
128
+ has(key: string): boolean { return this.m.has(key); }
129
+ delete(key: string): boolean { return this.m.delete(key); }
130
+ get size(): number { return this.m.size; }
131
+ }
132
+ - id: candidate-e
133
+ type: code
134
+ label: candidate-e
135
+ content: |
136
+ // candidate-e
137
+ class CandidateE<V> {
138
+ private m = new Map<string, V>();
139
+ get(key: string): V | undefined { return this.m.get(key); }
140
+ set(key: string, value: V, ttlMs?: number): void { this.m.set(key, value); }
141
+ has(key: string): boolean { return this.m.has(key); }
142
+ delete(key: string): boolean { return this.m.delete(key); }
143
+ clear(): void { this.m.clear(); }
144
+ get size(): number { return this.m.size; }
145
+ }
146
+ - id: judging-notes
147
+ type: note
148
+ label: structural-typing rules for a drop-in (scope)
149
+ content: |
150
+ Scope: candidate-a … candidate-e are the COMPLETE set of proposals. Judge each ONLY
151
+ against the frozen `interface` and the `call-sites`.
152
+
153
+ Rules for deciding whether a class is a valid drop-in for Cache<V>:
154
+ R1 MEMBER NAMES: every member named in Cache<V> (get, set, has, delete, size) must
155
+ be present under that EXACT name. A member under a different name (e.g. remove
156
+ instead of delete) does not satisfy the interface member and breaks any call
157
+ site that uses the interface name.
158
+ R2 RETURN TYPES / NULLABILITY: a member's return type must be assignable to the
159
+ interface's. get must return `V | undefined`. `V | null` is NOT assignable to
160
+ `V | undefined` (null and undefined are distinct types), so a get returning
161
+ `V | null` is incompatible.
162
+ R3 PARAMETER OPTIONALITY / ARITY: a parameter the interface marks optional (ttlMs?)
163
+ may be OMITTED by callers. A candidate that makes that parameter REQUIRED
164
+ (ttlMs: number) cannot be called with the argument omitted, so it is not
165
+ assignable to the interface and breaks any call site that omits it.
166
+ R4 PROPERTY VS METHOD: `readonly size: number` is a property, read as `cache.size`.
167
+ A candidate exposing `size()` as a method would break `cache.size` property
168
+ access. (Check whether each candidate exposes size as a getter/property or a
169
+ method.)
170
+ R5 EXTRA MEMBERS: a class MAY declare members BEYOND the five in Cache<V>. Under
171
+ structural typing a type with additional members is still assignable to the
172
+ interface, and the existing call sites never touch the extra member, so extra
173
+ members do NOT break drop-in compatibility.
174
+
175
+ The `interface` is complete (exactly five members) and the `call-sites` are the only
176
+ usages. A candidate CONFORMS iff it is assignable to Cache<V> and keeps CS1–CS5
177
+ compiling and behaving as specified; otherwise it VIOLATES.
178
+ tags: [typescript, interface, drop-in, structural-typing, interface-compatibility, generation]
@@ -0,0 +1,264 @@
1
+ id: edge-case-coverage-cache-loader
2
+ version: 1.0.0
3
+ category: generation
4
+ cluster: edge-case-coverage
5
+ difficulty: expert
6
+ title: getOrLoad — Single-Flight Cache Meeting Every Concurrency And Error Edge
7
+ summary: An async cache spec enumerates five mandatory edges (single-flight concurrency, negative caching, non-caching of loader errors, key normalization, empty-key rejection). Six submissions are under review; exactly one satisfies all five while each of five decoys misses exactly one. Classify all six by reading the code against the behavior scenarios.
8
+ prompt: |
9
+ Answer each deliverable using ONLY the supplied artifacts (cache-spec, behavior-vectors,
10
+ candidates-a, candidates-b, reviewer-thread, submission-manifest). Cite artifact id(s) for every
11
+ claim. Do not invent edges the spec omits and do not overlook one it requires. The verdict
12
+ vocabulary is exactly: conforms / violates. A submission `conforms` only if it satisfies EVERY
13
+ required edge E1..E5 in cache-spec.
14
+
15
+ submission-manifest declares candidate-1..candidate-6 the complete set with exactly one that
16
+ satisfies all five edges. No code is executed; reason about behavior by reading each candidate
17
+ against the behavior-vectors scenarios.
18
+
19
+ 1. Classify each of candidate-1..candidate-6 as `conforms` or `violates`.
20
+
21
+ 2. For every candidate you mark `violates`, name the single required edge (one of E1..E5) it
22
+ fails and cite the behavior-vectors scenario that exposes it. Describe the conforming behavior
23
+ and the candidate's differing behavior in that scenario.
24
+
25
+ 3. Exactly one candidate `conforms` (satisfies all five edges). Identify it.
26
+
27
+ 4. In reviewer-thread, reviewer @kai says candidate-2 "is correct because once the first load
28
+ finishes, the value is cached and later callers hit the cache." State candidate-2's verdict
29
+ and cite the edge and behavior-vectors scenario that refutes @kai.
30
+
31
+ 5. For candidate-4, describe exactly what happens on the SECOND call for a key whose loader
32
+ REJECTED on the first call, and name the required edge this behavior breaks.
33
+ artifacts:
34
+ - id: cache-spec
35
+ type: spec
36
+ label: getOrLoad cache specification with required edge set (complete)
37
+ content: |
38
+ class Cache { getOrLoad(rawKey: string, loader: (key: string) => Promise<unknown>): Promise<unknown> }
39
+
40
+ Supporting declarations (shared by all submissions):
41
+ const TTL_MS = 30_000;
42
+ class InvalidKeyError extends Error {}
43
+
44
+ getOrLoad returns a cached value when present and unexpired, otherwise loads it via `loader`.
45
+ A conforming implementation MUST satisfy every required edge below. This list is COMPLETE; an
46
+ implementation that mishandles any single one does NOT conform.
47
+
48
+ E1 SINGLE-FLIGHT (concurrency). If N calls arrive for the SAME (normalized) key while no
49
+ cached value exists and a load is in progress, `loader` is invoked EXACTLY ONCE; the
50
+ other callers await the in-flight promise and resolve to the same result. (This requires
51
+ registering the in-flight promise BEFORE awaiting the loader.)
52
+
53
+ E2 NEGATIVE CACHING. If `loader` RESOLVES with null (the key is genuinely absent), that null
54
+ is cached for TTL_MS as a negative result. A subsequent call within the TTL returns the
55
+ cached null WITHOUT invoking `loader` again.
56
+
57
+ E3 ERROR NON-CACHING. If `loader` REJECTS, the rejection is NOT cached and the in-flight
58
+ entry is evicted, so the very next call re-invokes `loader` (a transient failure must be
59
+ retryable, not permanently stuck).
60
+
61
+ E4 KEY NORMALIZATION. The key is trimmed of leading/trailing whitespace before it is used
62
+ for lookup, storage, and single-flight. Thus " user:7 " and "user:7" address the SAME
63
+ cache slot and coalesce.
64
+
65
+ E5 EMPTY-KEY REJECTION. If the key is empty or whitespace-only, getOrLoad throws
66
+ InvalidKeyError SYNCHRONOUSLY before touching the cache or invoking `loader`.
67
+
68
+ This is the entire required edge set.
69
+ - id: behavior-vectors
70
+ type: note
71
+ label: authoritative behavior scenarios (one per required edge)
72
+ content: |
73
+ One authoritative scenario per required edge. A conforming implementation exhibits the stated
74
+ conforming behavior.
75
+
76
+ BV-E1 (single-flight): three tasks call getOrLoad("user:7", loader) at the same tick; no
77
+ cached value exists; `loader` takes 50 ms and resolves to U.
78
+ Conforming: `loader` is invoked EXACTLY ONCE; all three calls resolve to U.
79
+
80
+ BV-E2 (negative caching): getOrLoad("user:9", loader) where `loader` RESOLVES with null; then
81
+ a second getOrLoad("user:9", loader) 1 ms later (well within TTL_MS).
82
+ Conforming: `loader` is invoked once total; the second call returns null from cache without
83
+ invoking `loader`.
84
+
85
+ BV-E3 (error non-caching): getOrLoad("user:5", loader) where `loader` REJECTS with LoadError;
86
+ after that call settles, a second getOrLoad("user:5", loader) is made.
87
+ Conforming: the first call rejects; the second call RE-INVOKES `loader` (the rejection was
88
+ not cached).
89
+
90
+ BV-E4 (key normalization): getOrLoad(" user:7 ", loader) and getOrLoad("user:7", loader) for
91
+ an uncached key.
92
+ Conforming: both address the same slot — across the two coalesced calls `loader` is invoked
93
+ once and both resolve to the same value; the stored key is the trimmed "user:7".
94
+
95
+ BV-E5 (empty key): getOrLoad(" ", loader) (whitespace only) and getOrLoad("", loader).
96
+ Conforming: each throws InvalidKeyError synchronously; `loader` is NOT invoked and the cache
97
+ is left untouched.
98
+ - id: candidates-a
99
+ type: code
100
+ label: submissions candidate-1, candidate-2, candidate-3
101
+ content: |
102
+ const TTL_MS = 30_000;
103
+ class InvalidKeyError extends Error {}
104
+ type Loader = (key: string) => Promise<unknown>;
105
+
106
+ // ---- candidate-1 ----
107
+ class Cache1 {
108
+ private values = new Map<string, { value: unknown; expiresAt: number }>();
109
+ private inflight = new Map<string, Promise<unknown>>();
110
+ async getOrLoad(rawKey: string, loader: Loader): Promise<unknown> {
111
+ const key = rawKey.trim(); // E4
112
+ if (key === '') throw new InvalidKeyError('empty key'); // E5
113
+ const c = this.values.get(key);
114
+ if (c && c.expiresAt > Date.now()) return c.value; // hit (incl. cached null, E2)
115
+ const existing = this.inflight.get(key);
116
+ if (existing) return existing; // E1: await in-flight load
117
+ const p = loader(key)
118
+ .then((value) => {
119
+ this.values.set(key, { value, expiresAt: Date.now() + TTL_MS }); // caches null too (E2)
120
+ this.inflight.delete(key);
121
+ return value;
122
+ })
123
+ .catch((err) => { this.inflight.delete(key); throw err; }); // E3: evict, do not cache
124
+ this.inflight.set(key, p); // registered BEFORE await (E1)
125
+ return p;
126
+ }
127
+ }
128
+
129
+ // ---- candidate-2 ----
130
+ class Cache2 {
131
+ private values = new Map<string, { value: unknown; expiresAt: number }>();
132
+ async getOrLoad(rawKey: string, loader: Loader): Promise<unknown> {
133
+ const key = rawKey.trim();
134
+ if (key === '') throw new InvalidKeyError('empty key');
135
+ const c = this.values.get(key);
136
+ if (c && c.expiresAt > Date.now()) return c.value;
137
+ const value = await loader(key); // no in-flight registration
138
+ this.values.set(key, { value, expiresAt: Date.now() + TTL_MS });
139
+ return value;
140
+ }
141
+ }
142
+
143
+ // ---- candidate-3 ----
144
+ class Cache3 {
145
+ private values = new Map<string, { value: unknown; expiresAt: number }>();
146
+ private inflight = new Map<string, Promise<unknown>>();
147
+ async getOrLoad(rawKey: string, loader: Loader): Promise<unknown> {
148
+ const key = rawKey.trim();
149
+ if (key === '') throw new InvalidKeyError('empty key');
150
+ const c = this.values.get(key);
151
+ if (c && c.expiresAt > Date.now()) return c.value;
152
+ const existing = this.inflight.get(key);
153
+ if (existing) return existing;
154
+ const p = loader(key)
155
+ .then((value) => {
156
+ if (value != null) this.values.set(key, { value, expiresAt: Date.now() + TTL_MS }); // skips null
157
+ this.inflight.delete(key);
158
+ return value;
159
+ })
160
+ .catch((err) => { this.inflight.delete(key); throw err; });
161
+ this.inflight.set(key, p);
162
+ return p;
163
+ }
164
+ }
165
+ - id: candidates-b
166
+ type: code
167
+ label: submissions candidate-4, candidate-5, candidate-6
168
+ content: |
169
+ // (reuse TTL_MS, InvalidKeyError, Loader from candidates-a)
170
+
171
+ // ---- candidate-4 ----
172
+ class Cache4 {
173
+ private values = new Map<string, { value: unknown; expiresAt: number }>();
174
+ private inflight = new Map<string, Promise<unknown>>();
175
+ async getOrLoad(rawKey: string, loader: Loader): Promise<unknown> {
176
+ const key = rawKey.trim();
177
+ if (key === '') throw new InvalidKeyError('empty key');
178
+ const c = this.values.get(key);
179
+ if (c && c.expiresAt > Date.now()) return c.value;
180
+ const existing = this.inflight.get(key);
181
+ if (existing) return existing;
182
+ const p = loader(key).then((value) => {
183
+ this.values.set(key, { value, expiresAt: Date.now() + TTL_MS });
184
+ this.inflight.delete(key);
185
+ return value;
186
+ }); // no .catch: rejected p stays in inflight
187
+ this.inflight.set(key, p);
188
+ return p;
189
+ }
190
+ }
191
+
192
+ // ---- candidate-5 ----
193
+ class Cache5 {
194
+ private values = new Map<string, { value: unknown; expiresAt: number }>();
195
+ private inflight = new Map<string, Promise<unknown>>();
196
+ async getOrLoad(rawKey: string, loader: Loader): Promise<unknown> {
197
+ const key = rawKey; // no trim for slotting
198
+ if (rawKey.trim() === '') throw new InvalidKeyError('empty key'); // E5 still enforced
199
+ const c = this.values.get(key);
200
+ if (c && c.expiresAt > Date.now()) return c.value;
201
+ const existing = this.inflight.get(key);
202
+ if (existing) return existing;
203
+ const p = loader(key)
204
+ .then((value) => {
205
+ this.values.set(key, { value, expiresAt: Date.now() + TTL_MS });
206
+ this.inflight.delete(key);
207
+ return value;
208
+ })
209
+ .catch((err) => { this.inflight.delete(key); throw err; });
210
+ this.inflight.set(key, p);
211
+ return p;
212
+ }
213
+ }
214
+
215
+ // ---- candidate-6 ----
216
+ class Cache6 {
217
+ private values = new Map<string, { value: unknown; expiresAt: number }>();
218
+ private inflight = new Map<string, Promise<unknown>>();
219
+ async getOrLoad(rawKey: string, loader: Loader): Promise<unknown> {
220
+ const key = rawKey.trim();
221
+ // no empty-key rejection
222
+ const c = this.values.get(key);
223
+ if (c && c.expiresAt > Date.now()) return c.value;
224
+ const existing = this.inflight.get(key);
225
+ if (existing) return existing;
226
+ const p = loader(key)
227
+ .then((value) => {
228
+ this.values.set(key, { value, expiresAt: Date.now() + TTL_MS });
229
+ this.inflight.delete(key);
230
+ return value;
231
+ })
232
+ .catch((err) => { this.inflight.delete(key); throw err; });
233
+ this.inflight.set(key, p);
234
+ return p;
235
+ }
236
+ }
237
+ - id: reviewer-thread
238
+ type: note
239
+ label: code-review discussion on the six submissions
240
+ content: |
241
+ Review thread for getOrLoad (PR #7188).
242
+
243
+ @kai: "candidate-2 is fine. Once the first load finishes it caches the value, and every later
244
+ caller hits the cache. I don't see a correctness problem."
245
+
246
+ @mira: "The problem is the window BEFORE the first load finishes. Trace three simultaneous
247
+ calls to an uncached key and count how many times `loader` runs. Also check what a rejected
248
+ loader leaves behind for the next call."
249
+
250
+ QA-330 (untriaged): "One submission runs the loader more than once for the same key under
251
+ concurrent access. One never stops calling the loader for a key that is genuinely absent. One
252
+ gets permanently stuck on a key whose loader failed once. These may be different submissions."
253
+ - id: submission-manifest
254
+ type: note
255
+ label: submission manifest and acceptance rule
256
+ content: |
257
+ Submissions under review for getOrLoad: candidate-1, candidate-2, candidate-3, candidate-4,
258
+ candidate-5, candidate-6. This is the COMPLETE set; no other implementation is being
259
+ considered.
260
+
261
+ Acceptance rule: exactly one submission satisfies ALL five required edges (E1..E5) and is to
262
+ be accepted. Each of the other five mishandles exactly one required edge. The process assumes
263
+ precisely one such all-edges-correct submission exists in this set.
264
+ tags: [generation, edge-case-coverage, concurrency, caching, code-review]