-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmanifest.json
More file actions
5078 lines (5078 loc) · 404 KB
/
Copy pathmanifest.json
File metadata and controls
5078 lines (5078 loc) · 404 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
[
{
"slug": "auditlog",
"type": "ambiguous-spec",
"brief": "We need a small Python library called `auditlog` for keeping an audit log \u2014 a\nrecord of who did what, and when. The kind of thing you reach for when someone\neventually asks \"who changed this setting?\" or \"when was that account\ndeactivated?\" and you'd like to have an answer instead of a shrug.\n\nThe shape of it is simple enough: things happen in our app \u2014 a user updates a\nrecord, an admin grants a permission, a job deletes some data \u2014 and we want to\nnote each of those down as it happens, with enough context to be useful later.\nThen, when someone comes asking, we want to be able to look back through the log\nand pull out the entries that matter \u2014 everything a particular person did, or\neverything that touched a particular thing, or whatever happened in some window\nof time.\n\nI care most about two things. First, that recording an event is easy enough that\nwe'll actually do it everywhere we should, rather than skipping it because it's a\nhassle. Second, that the log is trustworthy \u2014 that when we read an entry back, we\ncan believe it says what really happened. Some of what we log will be sensitive,\nso bear that in mind.\n\nIt should be importable as `auditlog`, and there should be an obvious way to both\nwrite to the log and query it back. Beyond that the design is yours \u2014 lean on the\nstandard library, keep it clean rather than sprawling, and include some tests and\na short README showing how it's meant to be used. I'd rather see the choices\nyou'd actually defend than every option under the sun.\n",
"observed": true,
"graded_behaviors": [],
"num_checks": 0,
"has_setup": false
},
{
"slug": "backoff",
"type": "bug-fix",
"brief": "You have inherited a small Python library named `backoff`: an exponential-backoff\ndelay schedule for retrying failed operations, with optional \"full jitter\". The\npackage is already written, imports cleanly, and the rough shape looks right --\ndelays grow with the attempt number and stop growing once they hit a ceiling.\nA retry loop asks `delay(attempt)` for how long to wait before each attempt, and\n`bounds(attempt)` for the `(low, high)` range to draw a randomised \"jittered\"\nsleep from.\n\n## Bug report\n\nThe waits are wrong in ways that only show up once you look at the actual\nnumbers rather than the general shape:\n\n 1. The very first attempt waits too long. Attempt 0 is supposed to wait\n exactly `base` seconds, but it comes back already multiplied -- the whole\n schedule is shifted one step early, so every attempt waits as if it were\n the next one.\n\n 2. The ceiling does not actually cap anything. Under enough retries the delay\n keeps doubling without bound and shoots far past `cap`, instead of\n flattening out at `cap`. The clamp is being applied in the wrong place, so\n for the usual case (`base` smaller than `cap`) it never bites.\n\n 3. Sub-second delays collapse to zero (or to a coarse whole number). With a\n fractional `base` like 0.5s the early waits come back as `0` and the\n schedule jumps in whole-second steps -- the fractional precision is being\n thrown away somewhere in the math.\n\n 4. The jitter range is wrong. \"Full jitter\" is supposed to spread the wait\n uniformly over the WHOLE interval from 0 up to the delay, i.e.\n `(low, high) == (0.0, delay(attempt))`. Instead the low bound comes back as\n half the delay, so the randomised sleep can never drop below `delay/2` and\n the retries are far less spread out than intended.\n\nFind and fix the defects so the schedule honours the contract below exactly.\nKeep the public API and behaviour otherwise unchanged.\n\n## Contract\n\n- Package name: `backoff`. The grader imports `backoff.public` (falling back to\n `backoff`); keep both import paths working.\n- Public API, UNCHANGED (do not rename anything or change signatures):\n Backoff(base, factor, cap)\n Backoff.delay(attempt: int) -> float\n Backoff.bounds(attempt: int) -> tuple[float, float]\n- `attempt` is a zero-based, non-negative integer (0 is the first retry).\n- `delay(attempt)` returns the un-jittered wait, in seconds, as a FLOAT:\n delay(attempt) == min(cap, base * factor ** attempt)\n * Attempt 0 waits exactly `base` (the exponent is `attempt`, NOT\n `attempt + 1`): `factor ** 0 == 1`.\n * Compute the FULL exponential `base * factor ** attempt` first, and only\n THEN clamp to `cap`. The cap is applied to the final product, never to\n `base` before the exponent -- so a small `base` with a big exponent is\n still clamped down to `cap`.\n * Keep full floating-point precision: do NOT round, truncate, or\n integer-divide. A `base` of 0.5 must yield 0.5, not 0. The return value is\n always a float (even when it equals `cap` or `base`).\n- `bounds(attempt)` returns the inclusive full-jitter range `(low, high)`:\n bounds(attempt) == (0.0, delay(attempt))\n * `low` is always `0.0` (full jitter starts at zero, not at half the delay).\n * `high` is the already-capped `delay(attempt)`, so the jitter window itself\n is never wider than `cap`.\n * Both bounds are floats.\n\n## I/O example\n\n >>> b = Backoff(base=0.5, factor=2.0, cap=10.0)\n >>> b.delay(0) # base, attempt-0 exponent is 0 -> 0.5 * 1\n 0.5\n >>> b.delay(1) # 0.5 * 2\n 1.0\n >>> b.delay(2) # 0.5 * 4\n 2.0\n >>> b.delay(5) # 0.5 * 32 = 16.0 -> clamped to cap\n 10.0\n >>> b.bounds(0) # full jitter over [0, delay(0)]\n (0.0, 0.5)\n >>> b.bounds(5) # high is the CAPPED delay, low is 0\n (0.0, 10.0)\n\n- Standard library only.\n",
"observed": false,
"graded_behaviors": [
{
"id": "attempt_zero_is_base",
"desc": "delay(0) == base exactly (exponent is attempt, not attempt+1)"
},
{
"id": "attempt_one_is_base_factor",
"desc": "delay(1) == base * factor (one step into the schedule)"
},
{
"id": "exponential_growth",
"desc": "delays double each attempt below the cap (factor**attempt)"
},
{
"id": "cap_clamps_high_attempt",
"desc": "a high attempt is clamped down to cap, not grown past it"
},
{
"id": "cap_after_exponent",
"desc": "small base * big exponent is still capped (cap applied to the product)"
},
{
"id": "never_exceeds_cap",
"desc": "no attempt 0..40 ever returns more than cap"
},
{
"id": "delay_below_cap_exact",
"desc": "an un-capped delay equals base*factor**attempt exactly"
},
{
"id": "fractional_base_preserved",
"desc": "a fractional base (0.5) yields 0.5, not 0 (no truncation)"
},
{
"id": "fractional_growth_preserved",
"desc": "fractional delays keep precision as they grow (0.5,1.0,2.0...)"
},
{
"id": "delay_returns_float",
"desc": "delay always returns a float (even at base and at cap)"
},
{
"id": "factor_non_integer",
"desc": "a non-integer factor (1.5) gives exact float powers, not truncated"
},
{
"id": "bounds_low_is_zero",
"desc": "bounds(attempt) low is 0.0 (full jitter starts at zero)"
},
{
"id": "bounds_high_is_delay",
"desc": "bounds(attempt) high equals delay(attempt)"
},
{
"id": "bounds_high_capped",
"desc": "bounds high at a capped attempt equals cap, not the uncapped delay"
},
{
"id": "bounds_are_floats",
"desc": "both bounds are floats"
},
{
"id": "bounds_match_delay_series",
"desc": "bounds high tracks the full delay series across attempts"
}
],
"num_checks": 16,
"has_setup": true
},
{
"slug": "base62",
"type": "bug-fix",
"brief": "You have inherited a small Python library named `base62`: a base-62 integer\ncodec. The package is already written, imports cleanly, and the happy path looks\nfine for small inputs -- single-character codes encode and decode, and a few\nspot checks pass. It is used to turn database row ids into short, URL-safe\nstrings (and back), so `encode` and `decode` must be exact inverses for every\nnon-negative id.\n\n## Bug report\n\nUnder real use the codec corrupts ids, and the symptoms are easy to miss because\nsingle-digit values (0..61) happen to look correct:\n\n 1. `encode(0)` comes back as the EMPTY string `\"\"` instead of `\"0\"`. The id 0\n is a real, valid id and must encode to a single `\"0\"` digit. No non-negative\n integer should ever encode to an empty string.\n\n 2. Any value that needs more than one digit comes out with its digits in the\n WRONG ORDER -- reversed. `encode(62)` yields `\"01\"` when it should be\n `\"10\"`, so the encoded codes don't round-trip and don't even sort sensibly.\n The most-significant digit must come FIRST, exactly like decimal notation.\n\n 3. `decode` accepts GARBAGE: handed a string containing a character that is not\n in the alphabet (say a space, `\"-\"`, or `\"!\"`), it silently returns a wrong\n (often negative) number instead of rejecting the input. Invalid characters\n must be refused.\n\n 4. `decode(\"\")` silently returns `0`. The empty string is not a valid encoding\n of anything and must be rejected too, not quietly treated as zero.\n\nFind and fix the defects so the codec honours the contract below exactly. Keep\nthe public API and behaviour otherwise unchanged.\n\n## Contract\n\n- Package name: `base62`. The grader imports `base62.public` (falling back to\n `base62`); keep both import paths working.\n- Public API, UNCHANGED (do not rename anything or change signatures):\n encode(n: int) -> str\n decode(s: str) -> int\n- The alphabet is fixed, exactly these 62 characters in this order (index 0 is\n `'0'`, index 10 is `'A'`, index 36 is `'a'`, index 61 is `'z'`):\n\n 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\n So `'0'`->0, `'9'`->9, `'A'`->10, `'Z'`->35, `'a'`->36, `'z'`->61.\n- `encode(n)` for a non-negative integer `n`:\n * Produces the base-62 representation with the MOST-SIGNIFICANT digit first\n (big-endian), just like ordinary decimal.\n * `encode(0)` is the single digit `\"0\"` -- never `\"\"`.\n * Never returns the empty string for any `n >= 0`.\n- `decode(s)`:\n * Maps each character to its alphabet index and accumulates big-endian:\n `value = value * 62 + index_of(char)`, left to right.\n * RAISES (a `ValueError`) on any character outside the alphabet -- it never\n returns a silent garbage or negative value.\n * RAISES on the empty string `\"\"` -- it is not a valid encoding.\n * TOLERATES non-canonical leading zeros: a string with leading `'0'`\n characters still decodes to its plain value (`decode(\"0A\") == 10`,\n `decode(\"00\") == 0`). Leading zeros do not change the value and must not be\n rejected.\n- Round-trip law: for every non-negative integer `n`, `decode(encode(n)) == n`\n -- including 0, values straddling the base boundary (61/62/63), and large\n multi-digit integers well beyond 64 bits.\n\n## I/O example\n\n >>> from base62 import encode, decode\n >>> encode(0)\n '0'\n >>> encode(61)\n 'z'\n >>> encode(62) # most-significant digit first\n '10'\n >>> decode('10')\n 62\n >>> decode('0A') # leading zero tolerated, value preserved\n 10\n >>> decode(encode(123456789))\n 123456789\n >>> decode('!') # character outside the alphabet\n Traceback (most recent call last):\n ...\n ValueError: invalid base-62 character: '!'\n >>> decode('') # empty string is not a valid encoding\n Traceback (most recent call last):\n ...\n ValueError: decode received an empty string\n\n- Standard library only.\n",
"observed": false,
"graded_behaviors": [
{
"id": "alphabet_single_digits",
"desc": "every single alphabet char encodes/decodes to its index value"
},
{
"id": "encode_zero",
"desc": "encode(0) is the single digit '0', not the empty string"
},
{
"id": "decode_zero",
"desc": "decode('0') is 0"
},
{
"id": "encode_small_multidigit",
"desc": "encode(62) is '10' (big-endian, most-significant first)"
},
{
"id": "encode_boundary_values",
"desc": "values around the base boundary encode big-endian correctly"
},
{
"id": "decode_known_value",
"desc": "decode('10') is 62 and a few fixed strings decode correctly"
},
{
"id": "roundtrip_small",
"desc": "decode(encode(n)) == n for every n in 0..1000"
},
{
"id": "roundtrip_powers",
"desc": "round-trips hold at base powers and their neighbours"
},
{
"id": "roundtrip_large",
"desc": "round-trips hold for large multi-digit integers"
},
{
"id": "encode_is_big_endian",
"desc": "encode output matches an independent big-endian reference"
},
{
"id": "decode_leading_zero_ok",
"desc": "decode tolerates non-canonical leading-zero strings ('0A' -> 10)"
},
{
"id": "no_empty_output",
"desc": "no non-negative integer encodes to the empty string"
},
{
"id": "decode_rejects_unknown",
"desc": "decode raises on a character outside the alphabet"
},
{
"id": "decode_rejects_unknown_midstring",
"desc": "decode raises on a bad char even after valid digits"
},
{
"id": "decode_rejects_empty",
"desc": "decode raises on the empty string (not silently 0)"
},
{
"id": "decode_no_silent_negative",
"desc": "decode never yields a negative result for any string"
}
],
"num_checks": 16,
"has_setup": true
},
{
"slug": "budgetrules",
"type": "implement",
"brief": "Start from an empty repository and implement a Python 3.11+ project named `budgetrules`.\n\nBuild a rule-based personal finance transaction categorizer. Use only the Python standard library.\n\nExpose:\n\n```python\ndef categorize(transactions: list[dict], rules: list[dict]) -> list[dict]: ...\ndef summarize(categorized: list[dict]) -> dict: ...\n```\n\nTransaction format:\n\n```json\n{\n \"id\": \"txn_1\",\n \"date\": \"2026-01-01\",\n \"description\": \"ACME GROCERY\",\n \"amount_cents\": -5234\n}\n```\n\nRules support:\n\n```text\ndescription_contains\ndescription_regex\namount_min_cents\namount_max_cents\nmerchant_equals\nset_category\nset_tags\npriority\n```\n\nHighest priority matching rule wins. Ties are broken by rule order. If no rule matches, category is `uncategorized`.\n\nSummaries must total spending by category and month. Refunds should reduce spending.\n\nInclude a CLI:\n\n```bash\npython -m budgetrules categorize --transactions txns.json --rules rules.json\npython -m budgetrules summarize categorized.json\n```\n\nInclude tests for rule priority, regex matching, refunds, uncategorized transactions, monthly summaries, deterministic order, and malformed rules.\n\n## Contract\n\nThis section pins the parts the prose above leaves implicit. The held-out grader\nchecks BEHAVIOR against this contract; it does not require any particular internal\nfile layout, helper names, or extra keys beyond those pinned here.\n\n### Import path & CLI\n\n- The package is importable as `budgetrules`, and the two functions are exposed from\n the module `budgetrules.public`:\n\n ```python\n from budgetrules.public import categorize, summarize\n ```\n\n- The CLI is invoked as `python -m budgetrules ...` and MUST emit JSON to stdout:\n\n ```bash\n python -m budgetrules categorize --transactions txns.json --rules rules.json\n python -m budgetrules summarize categorized.json\n ```\n\n `categorize` prints the JSON list returned by `categorize(...)`; `summarize` reads\n a JSON file holding that list and prints the JSON dict returned by `summarize(...)`.\n\n### `categorize(transactions, rules) -> list[dict]`\n\n- Returns a NEW list, same length and SAME ORDER as `transactions` (deterministic;\n input is never reordered or mutated).\n- Each output element preserves the input transaction's existing fields (`id`,\n `date`, `description`, `amount_cents`) and ADDS exactly these two fields:\n - `category` \u2014 a `str`. Set from the winning rule's `set_category`. If no rule\n matches, it is the literal string `\"uncategorized\"`.\n - `tags` \u2014 a `list[str]`. Set from the winning rule's `set_tags` (a list of\n strings). If the winning rule has no `set_tags`, or no rule matches, it is `[]`\n (empty list, never `None`).\n\n- Rule matching: a rule matches a transaction when ALL of its present conditions\n hold (conditions absent from the rule are not constraints). Conditions:\n - `description_contains` \u2014 substring test against the transaction `description`,\n CASE-INSENSITIVE.\n - `description_regex` \u2014 `re.search` of the pattern against `description`\n (case-sensitive unless the pattern itself opts out). A malformed/uncompilable\n regex does NOT raise: such a rule simply fails to match.\n - `amount_min_cents` \u2014 matches when `amount_cents >= amount_min_cents`.\n - `amount_max_cents` \u2014 matches when `amount_cents <= amount_max_cents`.\n - `merchant_equals` \u2014 exact, case-sensitive equality against the transaction\n `description` (the merchant string).\n\n- Winner selection: among all matching rules, the one with the highest `priority`\n wins. `priority` defaults to `0` when absent. Ties (equal priority) are broken by\n RULE ORDER \u2014 the earliest such rule in `rules` wins.\n\n- Robustness: a malformed rule (not a dict, bad regex, non-string/non-list field\n values, unknown extra keys) must NEVER raise \u2014 it either fails to match or\n contributes no constraint, and categorization continues over the remaining rules.\n\n### `summarize(categorized) -> dict`\n\nOperates on the list returned by `categorize`. Returns a dict with exactly these two\ntop-level keys, each a dict whose VALUES are integer cent totals:\n\n- `\"by_category\"` \u2014 maps each `category` string present in the input to the total\n spending for that category, in cents.\n- `\"by_month\"` \u2014 maps each `\"YYYY-MM\"` month string (the first 7 chars of `date`)\n to the total spending for that month, in cents.\n\nSpending convention (pin): a transaction's contribution to a total is the amount of\nmoney that LEFT the account, i.e. `-amount_cents` (debits, stored as NEGATIVE\n`amount_cents`, are POSITIVE spending; refunds/credits, stored as POSITIVE\n`amount_cents`, are NEGATIVE spending and therefore REDUCE the relevant totals). All\ntotals are integers in cents. A category/month with a net-zero total still appears\nif at least one transaction contributed to it.\n\n## ASSUMPTIONS (pinned so the grader never grades a guess)\n\n- `set_tags`, when present on the winning rule, replaces tags wholesale (tags are\n not accumulated across multiple matching rules \u2014 only the winner's tags apply).\n- `description_contains` is case-insensitive; `merchant_equals` is case-sensitive.\n- Spending sign convention is `-amount_cents` (debits negative in the input). This\n makes \"refunds reduce spending\" hold and keeps per-category/per-month totals as\n net spending in cents.\n- Month key is `date[:7]` (the `YYYY-MM` prefix of the ISO date string).\n",
"observed": false,
"graded_behaviors": [
{
"id": "categorize_basic_match",
"desc": "a matching rule sets category + tags on the txn"
},
{
"id": "categorize_preserves_order_len",
"desc": "categorize returns same length & order as input, fields preserved"
},
{
"id": "categorize_uncategorized",
"desc": "no matching rule -> category 'uncategorized', tags []"
},
{
"id": "tags_type_is_list",
"desc": "tags is always a list (and [] when the winner sets none)"
},
{
"id": "priority_highest_wins",
"desc": "highest-priority matching rule wins over a lower one"
},
{
"id": "priority_tie_rule_order",
"desc": "equal priority is broken by rule order (earliest wins)"
},
{
"id": "priority_default_zero",
"desc": "absent priority defaults to 0 (a priority>0 rule beats it)"
},
{
"id": "regex_match",
"desc": "description_regex matches via re.search"
},
{
"id": "amount_range_match",
"desc": "amount_min_cents / amount_max_cents bound matching"
},
{
"id": "merchant_equals_match",
"desc": "merchant_equals does exact-equality matching"
},
{
"id": "malformed_rule_no_crash",
"desc": "a malformed rule (bad regex / non-dict) never raises"
},
{
"id": "summarize_by_category",
"desc": "summarize totals spending per category"
},
{
"id": "summarize_by_month",
"desc": "summarize totals spending per YYYY-MM month"
},
{
"id": "summarize_refunds_reduce",
"desc": "refunds (positive amount_cents) REDUCE spending"
},
{
"id": "no_input_mutation",
"desc": "categorize does not mutate the input transactions"
},
{
"id": "cli_categorize_json",
"desc": "`python -m budgetrules categorize` emits JSON"
},
{
"id": "cli_summarize_json",
"desc": "`python -m budgetrules summarize` emits JSON"
}
],
"num_checks": 17,
"has_setup": false
},
{
"slug": "cachelab",
"type": "implement",
"brief": "Start from an empty repository and implement a Python 3.11+ project named `cachelab`.\n\nBuild an in-memory cache simulator with TTLs, stale-while-revalidate behavior, and per-key stampede protection. Use only the Python standard library.\n\nExpose:\n\n```python\nclass Cache:\n def __init__(self, clock=None): ...\n def get(self, key: str, loader, ttl_seconds: int, stale_seconds: int = 0): ...\n def invalidate(self, key: str) -> None: ...\n def stats(self) -> dict: ...\n```\n\n`loader` is a callable used to compute the value on cache miss. If many threads request the same expired key concurrently, only one loader call should run for that key. Other threads should wait or receive a stale value when allowed by `stale_seconds`.\n\nDifferent keys must not block each other.\n\nProvide a fake clock for deterministic tests.\n\nInclude tests for cache hits, misses, TTL expiration, stale values, per-key locking, concurrent requests, invalidation, loader exceptions, and statistics.\n\nInclude a small CLI simulator:\n\n```bash\npython -m cachelab simulate scenario.json\n```\n\nThe simulator should print JSON stats.\n\n## Contract\n\n- Expose `Cache` from `cachelab.public` (a `python -m cachelab simulate` CLI is also required).\n- `Cache(clock=None)`: when provided, `clock` is a zero-argument callable returning the current time as a float in seconds; use it for all TTL/stale timing so tests can drive time deterministically.\n- `stats()` returns a `dict` of integer counters reflecting cache activity (which counters you track is your choice).\n- On a cold or expired key, exactly one `loader` call runs even under many concurrent `get`s for that key; a loader that raises must not cache a value (the next `get` re-runs the loader).\n- The CLI `simulate` prints a JSON object of stats.\n",
"observed": false,
"graded_behaviors": [
{
"id": "hit",
"desc": "second get within TTL is a cache hit (loader runs once)"
},
{
"id": "miss",
"desc": "first get on a cold key runs the loader (miss)"
},
{
"id": "ttl_expiry",
"desc": "value expires after ttl_seconds and reloads"
},
{
"id": "stale",
"desc": "stale-while-revalidate serves the old value during refresh"
},
{
"id": "per_key_single_flight",
"desc": "exactly one loader runs for a hot key under N concurrent gets"
},
{
"id": "independent_keys",
"desc": "different keys don't block each other; each loads independently"
},
{
"id": "invalidate",
"desc": "invalidate() forces the next get to reload"
},
{
"id": "loader_exception",
"desc": "a raising loader propagates and does not cache a value"
},
{
"id": "stats",
"desc": "stats() reports activity; hits and misses move independently"
},
{
"id": "cli_simulate_json",
"desc": "`python -m cachelab simulate scenario.json` prints JSON stats"
}
],
"num_checks": 10,
"has_setup": false
},
{
"slug": "cachelayer",
"type": "ambiguous-spec",
"brief": "We have a handful of functions scattered across our code that are genuinely\nexpensive to call \u2014 some hit the network, some chew through a big computation \u2014\nand we keep calling them with the same inputs over and over. I'd like a small\nPython library, `cachelayer`, that lets us remember the result of a call so the\nnext call with the same inputs comes back fast instead of doing the work again.\n\nThe basic shape is: point it at one of these expensive lookups, and from then on\na repeat call with the same arguments returns the saved result. I want it to feel\nnatural to drop into existing code without rewriting how those functions are\ncalled, and I want to be able to trust what's in there \u2014 a cached answer should be\none I'd actually be comfortable handing back, not something stale I've quietly\nforgotten about.\n\nHow you let people attach caching to a function, how you decide two calls count as\n\"the same,\" and how you keep the cache from growing without bound are the\ninteresting parts, and I'm leaving them to you. Use your judgment; I'd rather see\nthe approach you'd actually defend than a pile of knobs. It should import as\n`cachelayer` and be pleasant to use from our own code.\n\nKeep it reasonably small and lean on the standard library where you can. Some\ntests around the behavior that matters \u2014 that hits come back without redoing the\nwork, and that the cache does the right thing as it fills up \u2014 would give me\nconfidence it works the way you think it does.\n",
"observed": true,
"graded_behaviors": [],
"num_checks": 0,
"has_setup": false
},
{
"slug": "cachetags",
"type": "feature-add",
"brief": "You have inherited a small Python library named `cachetags`: an in-memory cache.\nThe package is already written, imports cleanly, and its core operations work:\n`set(key, value, now)`, `get(key, now, default=None)`, and `delete(key)`.\n\nTime is INJECTED, never read from a real clock: every read and write takes\n`now` (a number \u2014 the caller's current time) as an explicit argument. The base\ncache does not actually use `now` yet (entries never expire), but it is already\npart of the signature. This keeps the cache fully deterministic and testable.\n\n## Task\n\nAdd two features on top of the existing cache, WITHOUT breaking plain\n`get` / `set`:\n\n(a) **Per-entry TTL.** `set(key, value, now, ttl=...)` makes the entry expire\n `ttl` time-units after it was set. A `get` at or after the expiry instant is\n a MISS.\n\n(b) **Tag-based invalidation.** `set(key, value, now, tags=[...])` attaches tags\n to the entry. `invalidate_tag(tag, now)` drops every entry carrying that\n tag.\n\n## Semantics (read carefully \u2014 this is the whole task)\n\n- TTL is RELATIVE to the set time and resolved against the injected `now`: an\n entry set with `ttl=T` at time `t0` expires at `t0 + T`. The boundary is\n HALF-OPEN \u2014 the entry is a HIT for every `now` in the interval `[t0, t0+T)`\n and a MISS at and after `t0 + T`. So `ttl=10` set at `now=0` is a hit at\n `now=9` and a miss at `now=10`.\n\n- `ttl=None` (the default) means the entry NEVER expires \u2014 plain `set` with no\n `ttl` keeps its old behavior.\n\n- A `ttl` of 0 (or negative) means the entry is already expired the instant it\n is set: it is never served.\n\n- `invalidate_tag(tag, now)` removes every entry that currently carries `tag`\n and returns the COUNT of entries it dropped.\n\n- SUBTLE \u2014 an EXPIRED entry must also lose its tag membership. Once an entry's\n TTL has elapsed it is gone in every sense: it must NOT be returned by `get`,\n and `invalidate_tag` on one of its (former) tags must NOT count it (it is a\n no-op for that entry \u2014 there is nothing live to invalidate). An expired entry\n must never resurface.\n\n- SUBTLE \u2014 re-`set`ting an existing key REPLACES its tags wholesale. The key\n loses its OLD tags and carries only the tags given on the latest `set` (or no\n tags, if `tags` is omitted). A later `invalidate_tag` with an OLD tag must not\n touch the re-set entry.\n\n- Plain `get` / `set` / `delete` with no `ttl` and no `tags` must behave exactly\n as they do today.\n\n## Example\n\n c = Cache()\n\n c.set(\"a\", 1, now=0, ttl=10, tags=[\"red\"])\n c.get(\"a\", now=9) # -> 1 (still within TTL)\n c.get(\"a\", now=10) # -> None (TTL elapsed: a miss)\n\n c.set(\"b\", 2, now=0, tags=[\"red\", \"blue\"])\n c.invalidate_tag(\"red\", now=0) # -> 1 (drops \"b\")\n c.get(\"b\", now=0) # -> None\n\n # expired entry loses its tag membership:\n c.set(\"c\", 3, now=0, ttl=5, tags=[\"green\"])\n c.invalidate_tag(\"green\", now=10) # -> 0 (\"c\" already expired; nothing to drop)\n c.get(\"c\", now=10) # -> None\n\n # re-set replaces tags:\n c.set(\"d\", 4, now=0, tags=[\"old\"])\n c.set(\"d\", 5, now=0, tags=[\"new\"]) # \"d\" no longer carries \"old\"\n c.invalidate_tag(\"old\", now=0) # -> 0 (does not touch \"d\")\n c.get(\"d\", now=0) # -> 5\n\n## Contract\n\n- Package name: `cachetags`. The grader imports `cachetags.public` (falling back\n to `cachetags`); keep both import paths working.\n- Public class `Cache` with methods:\n * `set(key, value, now, ttl=None, tags=None)` -> None\n * `get(key, now, default=None)` -> value or default\n * `delete(key)` -> bool (True iff the key was present)\n * `invalidate_tag(tag, now)` -> int (number of live entries dropped)\n- `now`, `ttl`, and tag values are plain numbers / hashables; tags is any\n iterable of hashables. Time is whatever numeric type the caller passes as\n `now` \u2014 do not read a real clock.\n- Standard library only. No persistence, no threading requirement.\n",
"observed": false,
"graded_behaviors": [
{
"id": "set_get_basic",
"desc": "a value set with no ttl is returned later"
},
{
"id": "ttl_hit_before",
"desc": "an entry is a HIT one tick before its TTL elapses"
},
{
"id": "ttl_miss_exact",
"desc": "an entry is a MISS at the exact instant its TTL elapses"
},
{
"id": "ttl_miss_after",
"desc": "an entry is a MISS after its TTL has elapsed"
},
{
"id": "ttl_none_never_expires",
"desc": "ttl=None entry never expires"
},
{
"id": "ttl_zero_already_expired",
"desc": "ttl=0 entry is already expired the instant it is set"
},
{
"id": "ttl_negative_already_expired",
"desc": "negative-ttl entry is already expired"
},
{
"id": "invalidate_single_tag",
"desc": "invalidate_tag drops a live entry carrying that tag"
},
{
"id": "invalidate_returns_count",
"desc": "invalidate_tag returns the number of live entries dropped"
},
{
"id": "invalidate_other_tag_untouched",
"desc": "invalidate_tag leaves entries without that tag alone"
},
{
"id": "invalidate_multiple_with_tag",
"desc": "invalidate_tag drops every live entry carrying the tag"
},
{
"id": "expired_loses_tag_membership",
"desc": "invalidate_tag of a former tag of an EXPIRED entry counts 0"
},
{
"id": "expired_not_resurfaced_after_invalidate",
"desc": "an expired entry never resurfaces via the tag index"
},
{
"id": "reset_replaces_tags",
"desc": "re-set replaces tags: an OLD tag no longer invalidates the entry"
},
{
"id": "reset_keeps_new_tag",
"desc": "re-set keeps the NEW tag: it still invalidates the entry"
},
{
"id": "invalidate_unknown_tag_zero",
"desc": "invalidate_tag on an unknown tag returns 0 and changes nothing"
},
{
"id": "regression_get_set_overwrite",
"desc": "plain get/set/overwrite still work with no ttl/tags"
},
{
"id": "regression_delete_return",
"desc": "delete reports presence (True/False) with no ttl/tags"
},
{
"id": "regression_get_default",
"desc": "get returns the supplied default on a miss"
}
],
"num_checks": 19,
"has_setup": true
},
{
"slug": "calceval",
"type": "repair-to-green",
"brief": "BUG REPORT \u2014 calceval: the test suite is failing\n\nYou have an existing Python package `calceval`, a tiny infix arithmetic\nevaluator. It ships with a unittest suite in `calceval/test_calceval.py`, and\nright now several of those tests FAIL. Fix the code so that ALL the tests pass.\nDo not rewrite the package from scratch and do not change its public API. Do NOT\nuse `eval` / `exec`; keep the hand-written parser.\n\n## Symptom\n\n`evaluate(expr)` parses an infix arithmetic expression and returns its value as a\nfloat. Simple precedence already works, but several associativity / binding cases\ncome out wrong:\n\n from calceval.public import evaluate\n\n # subtraction parses right-associative instead of left:\n evaluate(\"10-2-3\")\n # EXPECTED 5.0 ((10-2)-3)\n # ACTUAL 11.0 (10-(2-3))\n\n # division has the same right-associative defect:\n evaluate(\"100/10/2\")\n # EXPECTED 5.0 ((100/10)/2)\n # ACTUAL 20.0 (100/(10/2))\n\n # '^' parses left-associative instead of right:\n evaluate(\"2^3^2\")\n # EXPECTED 512.0 (2^(3^2))\n # ACTUAL 64.0 ((2^3)^2)\n\n # unary minus binds too tightly \u2014 tighter than '^' instead of looser:\n evaluate(\"-2^2\")\n # EXPECTED -4.0 (-(2^2))\n # ACTUAL 4.0 ((-2)^2)\n\nThese defects interact: `-2^2^2` exercises the unary binding AND the exponent\nassociativity at once, and expressions like `10-2-3^1^2*2` touch all of them.\n\n## Reproduce\n\nRun the visible tests from the directory that contains the `calceval` package:\n\n python -m unittest calceval.test_calceval\n\n## Contract (must hold after your fix)\n\n* Package name stays `calceval`; import path `calceval` / `calceval.public`.\n* Keep the public API exactly: `evaluate(expr: str) -> float` and the `CalcError`\n exception. Do not rename them. The result is ALWAYS a `float`.\n* Supported tokens: integer and decimal numbers (`3`, `3.5`, `.5`, `10.`), the\n binary operators `+ - * / ^`, parentheses `( )`, and a prefix (unary) `-`\n (a prefix `+` is tolerated as a no-op). Whitespace is insignificant.\n* PRECEDENCE, from loosest-binding to tightest-binding:\n 1. `+` and `-` (binary) \u2014 LEFT-associative\n 2. `*` and `/` \u2014 LEFT-associative\n 3. unary `-` (prefix negation) \u2014 binds LOOSER than `^`\n 4. `^` (exponent) \u2014 RIGHT-associative, the tightest binding\n Parentheses override all of the above.\n* ASSOCIATIVITY in detail:\n - `a-b-c` is `(a-b)-c` and `a/b/c` is `(a/b)/c` (LEFT). So `10-2-3 == 5` and\n `100/10/2 == 5`, never `11` / `20`.\n - `a^b^c` is `a^(b^c)` (RIGHT). So `2^3^2 == 512`, never `64`.\n* UNARY-vs-EXPONENT binding: prefix `-` binds LOOSER than `^`, so `-2^2` is\n `-(2^2) == -4`, and `-2^4 == -16`. To negate the base you must parenthesize:\n `(-2)^2 == 4`. A `^` right operand may itself be unary, so `2^-1 == 0.5`.\n* MALFORMED input raises `CalcError`: an empty or all-whitespace string, an\n unbalanced parenthesis, a dangling/stray operator (`\"2+\"`, `\"*3\"`), an unknown\n character, or division by zero. Do NOT raise a bare `ValueError`/`ZeroDivisionError`\n \u2014 wrap them as `CalcError` (which may subclass `ValueError`).\n* Do NOT use `eval` / `exec`. Standard library only.\n\nExample:\n\n evaluate(\"-2^2^2\") # -> -16.0 (-(2^(2^2)) == -(2^4))\n evaluate(\"10-2-3^1^2*2\") # -> 2.0 ((10-2) - ((3^(1^2))*2) == 8 - 6)\n",
"observed": false,
"graded_behaviors": [
{
"id": "basic_mul_over_add",
"desc": "* binds tighter than + (2+3*4 == 14)"
},
{
"id": "basic_add_mul_chain",
"desc": "mixed +/* precedence (2*3+4*5 == 26)"
},
{
"id": "basic_sub_mul",
"desc": "* binds tighter than - (10-2*3 == 4)"
},
{
"id": "basic_parens",
"desc": "parentheses override precedence ((2+3)*4 == 20)"
},
{
"id": "basic_pow_over_mul",
"desc": "^ binds tighter than * (2*3^2 == 18)"
},
{
"id": "basic_single_number",
"desc": "a lone number evaluates to itself (42 -> 42.0)"
},
{
"id": "basic_decimal",
"desc": "decimals parse and compute (3.5*2 == 7.0)"
},
{
"id": "basic_leading_dot",
"desc": "leading-dot decimals parse (.5+.5 == 1.0)"
},
{
"id": "left_assoc_sub",
"desc": "subtraction is left-associative (10-2-3 == 5)"
},
{
"id": "left_assoc_sub_long",
"desc": "long subtraction chain (2-3-4-5 == -10)"
},
{
"id": "left_assoc_div",
"desc": "division is left-associative (100/10/2 == 5)"
},
{
"id": "left_assoc_div_long",
"desc": "long division chain (64/4/2/2 == 4)"
},
{
"id": "left_assoc_mixed",
"desc": "mixed +/- left to right (1+2-3+4 == 4)"
},
{
"id": "left_assoc_sub_then_mul",
"desc": "left-assoc minus around a product (20-2-3*2 == 12)"
},
{
"id": "right_assoc_pow",
"desc": "exponent is right-associative (2^3^2 == 512)"
},
{
"id": "right_assoc_pow2",
"desc": "exponent right-assoc again (2^2^3 == 256)"
},
{
"id": "right_assoc_pow_triple",
"desc": "triple exponent right-assoc (2^2^2^2 == 65536)"
},
{
"id": "right_assoc_pow_zero",
"desc": "right-assoc with a zero exponent (4^3^0 == 4)"
},
{
"id": "unary_pow_binding",
"desc": "unary minus binds looser than ^ (-2^2 == -4)"
},
{
"id": "unary_pow_even",
"desc": "unary over even power (-2^4 == -16)"
},
{
"id": "paren_unary_pow",
"desc": "parens flip the binding ((-2)^2 == 4)"
},
{
"id": "unary_simple",
"desc": "plain unary minus (-3+5 == 2)"
},
{
"id": "unary_group",
"desc": "unary minus over a group (-(2+3) == -5)"
},
{
"id": "pow_negative_exp",
"desc": "negative exponent via unary (2^-1 == 0.5)"
},
{
"id": "interaction_unary_right_pow",
"desc": "unary + right-assoc ^ (-2^2^2 == -16)"
},
{
"id": "interaction_sub_pow",
"desc": "left-assoc minus around right-assoc power (1-2^3^0 == -1)"
},
{
"id": "interaction_full",
"desc": "all rules at once (10-2-3^1^2*2 == 2)"
},
{
"id": "err_empty",
"desc": "empty string raises CalcError"
},
{
"id": "err_blank",
"desc": "all-whitespace raises CalcError"
},
{
"id": "err_trailing_op",
"desc": "a trailing operator raises CalcError"
},
{
"id": "err_unbalanced",
"desc": "unbalanced parens raise CalcError"
},
{
"id": "err_bad_char",
"desc": "an unknown character raises CalcError"
},
{
"id": "err_div_zero",
"desc": "division by zero raises CalcError"
}
],
"num_checks": 33,
"has_setup": true
},
{
"slug": "cellsim",
"type": "implement",
"brief": "Start from an empty repository and implement a Python 3.11+ project named `cellsim`.\n\nBuild a small spreadsheet calculation engine. Use only the Python standard library.\n\nExpose these functions from `cellsim.public`:\n\n```python\ndef load_sheet(path: str) -> dict: ...\ndef evaluate_sheet(sheet: dict) -> dict: ...\ndef get_cell_value(sheet: dict, cell: str) -> object: ...\ndef explain_cell(sheet: dict, cell: str) -> dict: ...\n```\n\nA sheet is JSON:\n\n```json\n{\n \"cells\": {\n \"A1\": 10,\n \"A2\": 20,\n \"A3\": \"=A1+A2\",\n \"B1\": \"=SUM(A1:A3)\",\n \"B2\": \"=IF(B1>40,\\\"high\\\",\\\"low\\\")\"\n }\n}\n```\n\nSupport: cell references; integer and decimal numbers; strings; + - * /; parentheses; SUM(range); MIN(range); MAX(range); AVG(range); IF(condition, true_value, false_value); comparison operators = != < <= > >=.\n\nDetect and report circular references. Missing cells should evaluate as `0` only inside numeric formulas, but should be reported as missing when directly requested. Ranges must work for rectangular regions such as `A1:C3`. Formula evaluation must be deterministic and must not use Python `eval`.\n\nInclude a CLI:\n\n```bash\npython -m cellsim eval sheet.json\npython -m cellsim cell sheet.json B2\npython -m cellsim explain sheet.json B2\n```\n\nInclude tests for arithmetic precedence, ranges, strings, nested formulas, cycle detection, missing cells, and error propagation.\n\n## Contract\n\nThis section PINS the exact shapes the grading oracle relies on. It does not add\nnew behavior beyond the spec above; it only removes ambiguity so that a correct\nimplementation is not unfairly failed for choosing a different key name or type.\n\n### Import path and CLI\n\n- The public API is importable as `cellsim.public` (i.e. a package `cellsim`\n containing `public.py`). The four functions above are module-level callables.\n- A CLI is runnable as `python -m cellsim` (i.e. the package has a `__main__.py`).\n All CLI output is a single JSON document printed to stdout.\n\n### Value types\n\n- A cell value is one of: an `int`, a `float`, a `str`, or a `bool`.\n- Integer-valued numbers SHOULD be returned as `int` (e.g. `10`, `30`), and\n decimal numbers as `float` (e.g. `2.5`). `10 / 4` is `2.5`. The oracle compares\n numbers by numeric value with a small tolerance, so `30` and `30.0` are both\n accepted where a number is expected; bools are NOT accepted where a number is.\n- Comparison operators (`=`, `!=`, `<`, `<=`, `>`, `>=`) and `IF` conditions\n produce/consume a boolean. `IF(cond, a, b)` returns `a` when `cond` is truthy,\n else `b`.\n\n### `load_sheet(path) -> dict`\n\n- Returns the parsed sheet dict. It has a `\"cells\"` key mapping cell names\n (e.g. `\"A1\"`) to raw values (number, string literal, or a `\"=\"`-prefixed\n formula string). `load_sheet` does NOT evaluate; it only parses JSON.\n\n### `evaluate_sheet(sheet) -> dict`\n\n- Returns a dict with a `\"cells\"` key: a mapping from every cell name present in\n the input to its evaluated value (using the Value types above).\n- A cell that cannot be evaluated because it participates in a circular\n reference is reported via a top-level `\"errors\"` key: a mapping from cell name\n to an error descriptor. An error descriptor is a dict that contains a `\"type\"`\n key whose value is the string `\"circular\"` for cycles. (Other error types may\n use other `\"type\"` strings.) A cell in `\"errors\"` need not also appear with a\n numeric value in `\"cells\"`.\n- When the sheet has no errors, `\"errors\"` is either absent or an empty mapping.\n- Evaluation is deterministic: repeated calls on the same sheet return equal\n results.\n\n### `get_cell_value(sheet, cell) -> object`\n\n- Returns the evaluated value of `cell` (a Value type) when the cell exists.\n- When `cell` is NOT present in the sheet, this is a direct request for a missing\n cell and MUST be reported as missing \u2014 NOT silently coerced to `0`. \"Reported\n as missing\" means EITHER raising `KeyError` OR returning `None`. (The `0`\n coercion for missing cells applies ONLY when a missing cell is referenced from\n inside another cell's numeric formula, never to a direct `get_cell_value`.)\n- When `cell` participates in a circular reference, this raises an exception OR\n returns an error descriptor dict carrying `\"type\": \"circular\"`.\n\n### `explain_cell(sheet, cell) -> dict`\n\n- Returns a dict describing how `cell` was computed. It MUST contain:\n - `\"cell\"`: the cell name (str), echoing the requested cell.\n - `\"value\"`: the evaluated value (a Value type), OR `None` / omitted when the\n cell is missing or errored.\n - `\"references\"`: a list (possibly empty) of the cell names this cell directly\n depends on. For a literal (non-formula) cell this is `[]`. For `=A1+A2` it is\n `[\"A1\",\"A2\"]` (order not significant; the oracle compares as a set). A range\n like `A1:A3` contributes its expanded member cells `[\"A1\",\"A2\",\"A3\"]`.\n- For a cell in a circular reference, `explain_cell` either includes\n `\"type\": \"circular\"` somewhere in the returned dict, or sets `\"value\"` to\n `None`; it MUST NOT raise.\n\n### CLI\n\n- `python -m cellsim eval sheet.json` prints the JSON of `evaluate_sheet`\n (a JSON object; the oracle only requires that stdout parses as JSON).\n- `python -m cellsim cell sheet.json B2` prints a JSON document for the single\n cell value.\n- `python -m cellsim explain sheet.json B2` prints the JSON of `explain_cell`.\n\n### Ranges and operators\n\n- `SUM`, `MIN`, `MAX`, `AVG` take a single range argument like `A1:C3` and\n operate over the rectangular block of cells (columns A..C, rows 1..3).\n- Inside numeric formulas, a referenced cell that is absent contributes `0`.\n- Strings are written with double quotes inside formulas: `\"high\"`.\n",
"observed": false,
"graded_behaviors": [
{
"id": "arith_precedence",
"desc": "arithmetic precedence: =2+3*4 -> 14, =(2+3)*4 -> 20"
},
{
"id": "cell_refs_decimal",
"desc": "cell references resolve; decimal division yields a float"
},
{
"id": "sub_div",
"desc": "subtraction and division over cell references"
},
{
"id": "sum_range",
"desc": "SUM over a column range A1:A3"
},
{
"id": "min_max_avg",
"desc": "MIN/MAX/AVG over a range"
},
{
"id": "rect_range",
"desc": "ranges cover rectangular regions (A1:C2)"
},
{
"id": "strings",
"desc": "string literals and string-valued references round-trip"
},
{
"id": "if_comparison",
"desc": "IF with > comparison selects the right branch"
},
{
"id": "comparison_ops",
"desc": "comparison operators =, !=, <=, >= each work"
},
{
"id": "nested_formulas",
"desc": "nested/transitive formula chains resolve"
},
{
"id": "missing_in_formula_zero",
"desc": "a missing cell inside a numeric formula is 0"
},
{
"id": "direct_missing_reported",
"desc": "a directly requested missing cell is reported, not 0"
},
{
"id": "get_cell_present",
"desc": "get_cell_value returns an existing cell's value"
},
{
"id": "cycle_evaluate",
"desc": "evaluate_sheet detects & reports a circular reference (no crash)"
},
{
"id": "cycle_get_cell",
"desc": "get_cell_value reports a circular reference (raise or descriptor)"
},
{
"id": "explain_refs",
"desc": "explain_cell lists a cell's direct references"
},
{
"id": "explain_range_expands",
"desc": "explain_cell expands a range into member cells"
},
{
"id": "explain_value",
"desc": "explain_cell returns the cell name and computed value"
},
{
"id": "explain_cycle_no_raise",
"desc": "explain_cell on a cyclic cell does not raise"
},
{
"id": "determinism",
"desc": "evaluate_sheet is deterministic across repeated runs"
},
{
"id": "no_python_eval",
"desc": "formulas are parsed, not run through Python eval()"
},
{
"id": "cli_eval_json",
"desc": "`python -m cellsim eval` emits JSON"
},
{
"id": "cli_cell_value",
"desc": "`python -m cellsim cell` emits JSON carrying the value"
},
{
"id": "cli_explain_json",
"desc": "`python -m cellsim explain` emits JSON"
}
],
"num_checks": 24,
"has_setup": false
},
{
"slug": "condschema",
"type": "feature-add",
"brief": "You have inherited a small Python library named `condschema`: a data validator.\nThe package is already written, imports cleanly, and works for the cases it\nsupports. Its single public entry point is:\n\n def validate(data, schema) -> list[error]: ...\n\nIt walks `data` against `schema` and returns a LIST OF ERROR DICTS. An EMPTY\nlist means the data is valid; a non-empty list means it failed one or more\nrules.\n\n## What works today\n\nThe validator handles a FLAT schema: a mapping of `field name -> field spec`.\nEach spec may declare:\n\n - `type` \u2014 one of \"string\" / \"number\" / \"integer\" / \"bool\" / \"object\" /\n \"list\". (Note: a bool is NOT a number/integer, and a float is\n NOT an integer; \"number\" accepts ints and floats.)\n - `required` \u2014 if True, the field must be present.\n\nEach error is a dict::\n\n {\"path\": \"<dotted path>\", \"code\": \"<rule that failed>\", \"message\": \"...\"}\n\n`path` is a dotted location into the data; a top-level field's path is just its\nname. `code` is one of \"required\" (a required field is absent) or \"type\" (a\npresent field has the wrong type). The shipped validator reports ALL errors it\nfinds, in a STABLE order: schema-declared field order.\n\nExamples of current behavior::\n\n validate({\"name\": \"Ada\"}, {\"name\": {\"type\": \"string\", \"required\": True}})\n -> [] # valid\n validate({}, {\"name\": {\"type\": \"string\", \"required\": True}})\n -> [{\"path\": \"name\", \"code\": \"required\", ...}] # missing\n validate({\"age\": \"x\"}, {\"age\": {\"type\": \"integer\"}})\n -> [{\"path\": \"age\", \"code\": \"type\", ...}] # wrong type\n\n## The capability to ADD\n\nExtend the validator with NESTED schemas and CONDITIONAL requirements. Flat\nvalidation must keep working exactly as before.\n\n### 1. Nested objects\n\nA field spec of `type` \"object\" may carry a `fields` key: a nested schema\n(same shape \u2014 field name -> spec). When the field is present AND is a dict,\nvalidate it RECURSIVELY against `fields`. Errors from inside carry a DOTTED\npath built from the enclosing field name and the inner path::\n\n schema = {\"address\": {\"type\": \"object\", \"fields\": {\n \"zip\": {\"type\": \"string\", \"required\": True}}}}\n validate({\"address\": {}}, schema)\n -> [{\"path\": \"address.zip\", \"code\": \"required\", ...}]\n\nIf the field is present but is NOT a dict, report a single \"type\" error at the\nfield's own path and do NOT recurse into it.\n\n### 2. Lists of items\n\nA field spec of `type` \"list\" may carry an `items` key: a single field spec\napplied to EVERY element of the list. When the field is present AND is a list,\nvalidate each element against `items`. An element's path is the field name,\nthen the element's INDEX, then any inner path::\n\n schema = {\"items\": {\"type\": \"list\", \"items\": {\n \"type\": \"object\", \"fields\": {\n \"sku\": {\"type\": \"string\", \"required\": True}}}}}\n validate({\"items\": [{\"sku\": \"A1\"}, {}, {\"sku\": 5}]}, schema)\n -> [{\"path\": \"items.1.sku\", \"code\": \"required\", ...},\n {\"path\": \"items.2.sku\", \"code\": \"type\", ...}]\n\nElements are validated in index order. If the field is present but is NOT a\nlist, report a single \"type\" error at the field's own path and do NOT recurse.\n\n### 3. Conditional requirements\n\nA field spec may carry `requiredIf`: a `[sibling_field, value]` pair. The field\nis required ONLY when, in the SAME object, the named sibling field is present\nAND its value EQUALS `value`. Otherwise the field is optional.\n\n schema = {\"country\": {\"type\": \"string\"},\n \"state\": {\"type\": \"string\", \"requiredIf\": [\"country\", \"US\"]}}\n validate({\"country\": \"US\"}, schema)\n -> [{\"path\": \"state\", \"code\": \"required\", ...}] # state needed\n validate({\"country\": \"CA\"}, schema)\n -> [] # state optional\n validate({}, schema)\n -> [] # sibling absent -> optional\n\nSubtleties (these are the whole task):\n\n - `requiredIf` is satisfied only by an EXACT match of the sibling's value.\n Do NOT conflate booleans with numbers: a sibling holding `True` does not\n equal `1`, and `1` does not equal `True`.\n - The sibling is looked up in the SAME object that owns the conditional\n field (the nested object for a nested spec), never globally.\n - A field that is required (by `required: True` OR by a satisfied\n `requiredIf`) and is ABSENT yields exactly ONE \"required\" error and no\n \"type\" error.\n - A field that is PRESENT is type-checked regardless of how its requirement\n was decided (plain or conditional). Presence and type are separate rules.\n - Report ALL errors, never first-only. The order is a stable pre-order walk:\n fields in schema-declared order; within a list, elements by ascending\n index; recurse into a field's nested errors before moving to the next\n sibling field.\n\n## Contract\n\n- Package name: `condschema`. The grader imports `condschema.public` (falling\n back to `condschema`); keep both import paths working.\n- Public function `validate(data, schema) -> list[dict]`. Empty list == valid.\n Each error dict has at least the keys `path` (dotted string) and `code`\n (one of \"required\" / \"type\").\n- A field present in `data` but absent from `schema` is unconstrained (ignored).\n- A `required`/`requiredIf` field that is absent is reported once as \"required\"\n and is not type-checked.\n- Standard library only. No third-party validation libraries.\n",
"observed": false,
"graded_behaviors": [
{
"id": "nested_object_dotted_path",
"desc": "a missing field in a nested object reports a DOTTED path"
},
{
"id": "nested_object_valid",
"desc": "a satisfied nested object yields no errors"
},
{
"id": "nested_object_type_no_recurse",
"desc": "a non-dict where an object is expected: one type error, no recursion"
},
{
"id": "nested_object_inner_type",
"desc": "a wrong-typed value inside a nested object reports at the dotted path"
},
{
"id": "deep_nesting_path",
"desc": "two levels of object nesting build the full dotted path"
},
{
"id": "list_item_index_path",
"desc": "a bad element reports field.<index>.<inner> as its path"
},
{
"id": "list_all_valid",
"desc": "a list whose every element fits yields no errors"
},
{
"id": "list_type_no_recurse",
"desc": "a non-list where a list is expected: one type error, no recursion"
},
{
"id": "list_scalar_items",
"desc": "an items spec of a scalar type checks each element by index"
},
{
"id": "list_order_by_index",
"desc": "multiple bad elements are reported in ascending index order"
},
{
"id": "requiredif_trigger_present",
"desc": "requiredIf fires when the sibling equals the trigger value"
},
{
"id": "requiredif_other_value",
"desc": "requiredIf does NOT fire when the sibling has a different value"
},
{
"id": "requiredif_sibling_absent",
"desc": "requiredIf does NOT fire when the sibling is absent"
},
{
"id": "requiredif_present_ok",
"desc": "a satisfied requiredIf with the field present is valid"
},
{
"id": "requiredif_bool_not_number",
"desc": "requiredIf does not conflate True with 1 (exact match)"
},
{
"id": "requiredif_present_is_type_checked",
"desc": "a present conditional field is still type-checked"
},
{
"id": "required_absent_no_type_error",
"desc": "an absent required field gives ONE required error, no type error"
},
{
"id": "all_errors_reported",
"desc": "every violation is reported, not just the first"
},
{
"id": "stable_pre_order",
"desc": "errors come in schema order then ascending list index"
},
{
"id": "regression_flat_valid",
"desc": "a valid flat instance yields an empty list"
},
{
"id": "regression_flat_required_missing",
"desc": "a missing flat required field errors at its bare name"
},
{
"id": "regression_flat_type_mismatch",
"desc": "a wrong-typed flat field errors with code 'type'"
},
{
"id": "regression_number_accepts_int",
"desc": "type 'number' accepts an integer; 'integer' rejects a float"
},
{
"id": "regression_bool_not_integer",
"desc": "a bool does not satisfy type 'integer'"
}
],
"num_checks": 24,
"has_setup": true
},
{
"slug": "confstack",
"type": "implement",
"brief": "Start from an empty repository and implement a Python 3.11+ project named `confstack`.\n\nBuild a configuration loader with strict precedence. Use only the Python standard library.\n\nExpose:\n\n```python\ndef load_config(defaults: dict, config_file: str | None, env: dict, cli_args: list[str]) -> dict: ...\n```\n\nPrecedence order:\n\n```text\nCLI flags > environment variables > config file > defaults\n```\n\nConfig files are JSON. Environment variables use prefix `APP_`. CLI flags use `--key value`, `--nested.key value`, and booleans like `--feature-enabled` or `--no-feature-enabled`.\n\nInfer booleans, integers, and strings. Support nested keys with dot notation.\n\nInclude a CLI:\n\n```bash\npython -m confstack show --defaults defaults.json --config app.json -- --port 9000 --no-debug\n```\n\nInclude tests for precedence, nested keys, boolean false overriding true, missing config file, unknown flags, type inference, and deterministic output.\n\n## Contract\n\nThis section pins the conventions the grader relies on. A correct implementation\nMUST satisfy everything below. Where the SPEC above leaves a convention open, the\nchoice is pinned here so a defensible implementation is not failed on a guess.\n\n### Import path and CLI\n- The public API is importable as `confstack.public` and exposes the function\n `load_config(defaults, config_file, env, cli_args)` with the signature above.\n- The CLI is invokable as `python -m confstack` (i.e. the package has a\n `__main__` module). The `show` subcommand prints the merged config and exits 0.\n\n### Return value of `load_config`\n- Returns the merged configuration as a plain nested `dict`.\n- Dot-notation keys are EXPANDED into nested dicts in the returned value. A key\n written `a.b.c` (from any source \u2014 config dot-key, env, or CLI) becomes\n `{\"a\": {\"b\": {\"c\": <value>}}}` in the result. The literal dotted string is NOT\n retained as a flat top-level key.\n- Output is deterministic: calling `load_config` twice with equal inputs returns\n equal dicts, and the CLI's serialized output is byte-for-byte stable across runs\n for equal inputs.\n\n### Precedence (strict, highest wins)\n```text\nCLI flags > environment variables > config file > defaults\n```\n- Sources are merged so that, for any given leaf key, the value from the\n highest-precedence source that defines it wins.\n- Merging is RECURSIVE over nested dicts (a \"deep merge\"): a higher-precedence\n source overriding `a.b` does NOT discard a lower-precedence `a.c`; both survive\n under `a`. Only the overlapping leaf is overridden.\n- Boolean `false` from a higher-precedence source DOES override boolean `true`\n from a lower-precedence source (and vice versa). Presence, not truthiness,\n decides an override.\n\n### Defaults\n- `defaults` is a Python dict supplied by the caller. It may itself contain nested\n dicts and/or dot-notation keys; dot-notation keys in `defaults` are expanded the\n same way as every other source.\n\n### Config file\n- `config_file` is a path to a JSON object, or `None`.\n- If `config_file` is `None`, the config-file layer contributes nothing.\n- A missing config-file path (file does not exist) is NOT a hard error in\n `load_config`: the config-file layer simply contributes nothing and lower layers\n still apply. (It must not raise; it must not abort the merge.)\n- A config file whose JSON contains dot-notation keys has them expanded.\n\n### Environment variables\n- Only variables whose name starts with the prefix `APP_` are consumed; all other\n env vars are ignored.\n- The prefix `APP_` is stripped, and the REMAINDER is lowercased to form the key.\n So `APP_PORT` -> key `port`, `APP_DEBUG` -> key `debug`.\n- Nested keys in env use a double underscore `__` as the nesting separator:\n `APP_DATABASE__HOST` -> `database.host` -> `{\"database\": {\"host\": ...}}`.\n (Single underscores within a segment are preserved as part of that key segment,\n e.g. `APP_LOG_LEVEL` -> key `log_level`.)\n- Env values are strings and are passed through type inference (below).\n\n### CLI flags (the `cli_args` list, and the CLI's post-`--` args)\n- `--key value` sets `key` to `value`.\n- `--nested.key value` sets the dotted key `nested.key` (expanded to nested dicts).\n- `--flag` (no following value, or followed by another `--\u2026` token) sets `flag`\n to boolean `true`.\n- `--no-flag` sets `flag` to boolean `false`. The `no-` prefix is stripped from\n the FIRST segment of the key only; e.g. `--no-feature-enabled` sets\n `feature-enabled` to `false` (only the leading `no-` is removed, not later\n hyphens), and `--no-a.b` sets `a.b` to `false`.\n- Flag key segments keep their hyphens: `--feature-enabled` -> key\n `feature-enabled` (hyphens are NOT converted to underscores).\n- \"Unknown\" flags are not an error: there is no fixed schema, so any `--key`\n is accepted and merged. (The SPEC's \"unknown flags\" test means such flags are\n handled gracefully, not rejected.)\n- CLI values (for the `--key value` form) are strings and are passed through\n type inference (below). The explicit boolean forms `--flag` / `--no-flag`\n yield real booleans directly.\n\n### Type inference (applied to STRING values from env and from `--key value`)\nApplied to a raw string value `s` (case-insensitive for booleans):\n- If `s` lowercased is `\"true\"` -> Python `True`.\n- If `s` lowercased is `\"false\"` -> Python `False`.\n- Else if `s` is an OPTIONALLY-SIGNED run of ASCII digits (matches `-?\\d+`, e.g.\n `\"9000\"`, `\"-3\"`, `\"0\"`) -> Python `int`.\n- Else -> the string `s` unchanged (this includes `\"1.5\"`, `\"007abc\"`, `\"\"`,\n `\"3.0\"`, and any value with a leading `+`; only plain integers are coerced,\n floats are left as strings).\n- Values that already arrive as non-strings (e.g. JSON numbers/booleans/objects\n from a config file or nested dicts in `defaults`) are used AS-IS and are NOT\n re-inferred.\n\n### CLI `show` subcommand\n- Usage: `python -m confstack show [--defaults defaults.json] [--config app.json] [-- <cli flags...>]`.\n- `--defaults PATH` loads a JSON object used as the `defaults` dict (optional;\n absent or missing file -> empty defaults).\n- `--config PATH` is passed as `config_file`.\n- Everything after a literal `--` separator is passed through as `cli_args`.\n- The process environment is used for the `env` layer (only `APP_`-prefixed vars).\n- The merged config is printed as JSON to stdout and the process exits 0.\n</content>\n</invoke>\n",
"observed": false,
"graded_behaviors": [
{
"id": "infer_bool_true",
"desc": "string 'true' is inferred as boolean True"
},
{
"id": "infer_bool_false",
"desc": "string 'false' is inferred as boolean False"
},
{
"id": "infer_int",
"desc": "an integer-looking string is inferred as int"
},
{
"id": "infer_str",
"desc": "a non-bool non-int string stays a string (e.g. '1.5')"
},
{
"id": "expand_dotted_cli",
"desc": "a --nested.key flag expands to a nested dict"
},
{
"id": "nested_dict_preserved",
"desc": "nested dict values are returned as nested dicts"
},
{
"id": "prec_cli_over_env",
"desc": "CLI flags override environment variables"
},
{
"id": "prec_env_over_config",
"desc": "environment variables override the config file"
},
{
"id": "prec_config_over_default",
"desc": "config file overrides defaults"
},
{
"id": "bool_false_overrides_true",
"desc": "a higher-precedence false overrides a lower true"
},
{
"id": "deep_merge_siblings",
"desc": "deep merge keeps non-overlapping sibling keys"
},
{
"id": "missing_config_no_error",
"desc": "a missing config file path does not raise"
},
{
"id": "none_config_ok",
"desc": "config_file=None contributes nothing and does not raise"
},
{
"id": "no_flag_false",
"desc": "--no-flag sets the flag to boolean false"
},
{
"id": "bare_flag_true",
"desc": "a bare --flag sets it to boolean true"
},
{
"id": "env_prefix_filter",
"desc": "only APP_-prefixed env vars are consumed"
},
{
"id": "unknown_flag_ok",
"desc": "an unknown --flag is accepted, not rejected"
},
{
"id": "deterministic",
"desc": "load_config is deterministic across repeated calls"
},
{
"id": "cli_show_json",
"desc": "`python -m confstack show` emits JSON and exits 0"
},
{
"id": "cli_precedence",
"desc": "the CLI applies CLI > config > defaults precedence"
}
],
"num_checks": 20,
"has_setup": false
},
{
"slug": "cronmatch",
"type": "repair-to-green",
"brief": "BUG REPORT \u2014 cronmatch: the test suite is failing\n\nYou have an existing Python package `cronmatch`, a tiny matcher that decides\nwhether a given datetime is \"due\" under a 5-field cron expression. It ships with\na unittest suite in `cronmatch/test_cronmatch.py`, and right now several of those\ntests FAIL. Fix the code so that ALL the tests pass. Do not rewrite the package\nfrom scratch and do not change its public API.\n\n## Symptom\n\n`matches(cron_expr, dt)` parses a 5-field cron expression and returns True iff\n`dt` satisfies every field. Plain `* * * * *`, exact values, simple ranges and\ncomma lists already work, but several STEP and day-of-week cases come out wrong:\n\n from datetime import datetime\n from cronmatch.public import matches\n\n # `*/n` is off by the field's minimum (wrong for month / day-of-month):\n matches(\"0 0 1 */3 *\", datetime(2026, 1, 1, 0, 0))\n # EXPECTED True (months are 1,4,7,10 \u2014 January is in the set)\n # ACTUAL False (buggy set is 0,3,6,9,12 \u2014 counts from 0, misses Jan)\n\n # a stepped RANGE `a-b/n` ignores the step:\n matches(\"10-30/10 * * * *\", datetime(2026, 6, 18, 0, 15))\n # EXPECTED False (minutes are 10,20,30 \u2014 15 is not in the set)\n # ACTUAL True (buggy treats it as the whole range 10..30)\n\n # when BOTH day-of-month and day-of-week are restricted, they should OR:\n matches(\"0 0 13 * 5\", datetime(2026, 6, 19, 0, 0)) # Fri the 19th\n # EXPECTED True (it is a Friday, so the day-of-week clause fires)\n # ACTUAL False (buggy AND-s the two clauses, demands the 13th too)\n\nThese defects interact: an expression that uses a stepped range or a stepped\nmonth AND constrains both day-of-month and day-of-week exercises more than one at\nonce.\n\n## Reproduce\n\nRun the visible tests from the directory that contains the `cronmatch` package:\n\n python -m unittest cronmatch.test_cronmatch\n\n## Contract (must hold after your fix)\n\n* Package name stays `cronmatch`; import path `cronmatch` / `cronmatch.public`.\n* Keep the public API exactly: `matches(cron_expr: str, dt: datetime) -> bool`\n and the `CronError` exception. Do not rename them.\n* `cron_expr` is a string of EXACTLY 5 whitespace-separated fields, in order:\n\n minute hour day-of-month month day-of-week\n\n Any other field count raises `CronError`. A field that cannot be parsed (e.g.\n a non-integer token) also raises `CronError`.\n* Field value ranges (inclusive):\n - minute 0\u201359\n - hour 0\u201323\n - day-of-month 1\u201331\n - month 1\u201312\n - day-of-week 0\u20136, where 0 = Sunday, 1 = Monday, ... 6 = Saturday.\n* Each field is one or more comma-separated terms; a datetime matches the field\n if ANY term matches. A term is one of:\n - `*` \u2014 every value in the field's range.\n - `v` \u2014 the single integer `v`.\n - `a-b` \u2014 every value from `a` to `b` inclusive.\n - `*/n` \u2014 every nth value across the field's full range, STARTING AT THE\n FIELD'S MINIMUM. So minute `*/15` = {0,15,30,45}; month `*/3` =\n {1,4,7,10} (NOT {0,3,6,9,12}); day-of-month `*/10` =\n {1,11,21,31} (NOT {0,10,20,30}).\n - `a-b/n` \u2014 every nth value from `a` to `b` inclusive. So `10-30/10` =\n {10,20,30}; hour `8-18/2` = {8,10,12,14,16,18}.\n* A datetime matches the WHOLE expression iff its minute, hour and month each\n match their field AND the day-of-month / day-of-week pair matches per the rule\n below.\n* DAY-OF-MONTH / DAY-OF-WEEK semantics (standard cron): if BOTH the\n day-of-month field and the day-of-week field are restricted (i.e. neither is a\n bare `*`), the day matches when the day-of-month clause matches OR the\n day-of-week clause matches \u2014 either one firing is enough. If only one of the\n two is restricted, only that one constrains the day. If both are `*`, every day\n matches.\n\nExample:\n\n # day-of-month 13 OR day-of-week Friday(5):\n matches(\"0 0 13 * 5\", datetime(2026, 6, 13, 0, 0)) # Sat the 13th -> True (dom)\n matches(\"0 0 13 * 5\", datetime(2026, 6, 19, 0, 0)) # Fri the 19th -> True (dow)\n matches(\"0 0 13 * 5\", datetime(2026, 2, 13, 0, 0)) # Fri the 13th -> True (both)\n matches(\"0 0 13 * 5\", datetime(2026, 6, 18, 0, 0)) # Thu the 18th -> False\n\n # stepped range + stepped month + dom/dow OR all at once:\n matches(\"0 0 10-20/5 * 5\", datetime(2026, 6, 15, 0, 0)) # 15 in {10,15,20} -> True\n matches(\"0 0 1 */3 5\", datetime(2026, 7, 3, 0, 0)) # Jul in {1,4,7,10}, Fri -> True\n\nStandard library only (`datetime`). Do not change the package name or the public\nfunction/exception names.\n",
"observed": false,
"graded_behaviors": [
{
"id": "every_minute",
"desc": "plain * * * * * matches any datetime"
},
{
"id": "exact_match_true",
"desc": "exact minute+hour matches"
},
{
"id": "exact_minute_false",
"desc": "wrong minute fails an exact match"
},
{
"id": "exact_hour_false",
"desc": "wrong hour fails an exact match"
},
{
"id": "list_minute_true",
"desc": "comma list matches a listed minute"
},
{
"id": "list_minute_false",
"desc": "comma list rejects an unlisted minute"
},
{
"id": "simple_range_in",
"desc": "a-b range matches inside the range"
},
{
"id": "simple_range_out",
"desc": "a-b range rejects outside the range"
},
{
"id": "month_field_true",
"desc": "month field matches the right month"
},
{
"id": "month_field_false",
"desc": "month field rejects the wrong month"
},
{
"id": "dow_only_true",
"desc": "day-of-week-only rule matches that weekday"
},
{
"id": "dow_only_false",
"desc": "day-of-week-only rule rejects other weekdays"
},
{
"id": "dom_only_true",
"desc": "day-of-month-only rule matches that day"
},
{
"id": "dom_only_false",
"desc": "day-of-month-only rule rejects other days"
},
{
"id": "dow_sunday_zero",
"desc": "day-of-week 0 means Sunday"
},
{
"id": "step_minute_min0_hit",
"desc": "*/15 on minute (min 0) matches 30"
},
{
"id": "step_minute_min0_miss",
"desc": "*/15 on minute (min 0) rejects 31"
},
{
"id": "step_month_q_jan",
"desc": "month */3 includes January (1,4,7,10)"
},
{
"id": "step_month_q_jul",
"desc": "month */3 includes July"
},
{
"id": "step_month_q_jun_miss",
"desc": "month */3 excludes June"
},
{
"id": "step_month_q_mar_miss",
"desc": "month */3 excludes March"
},
{
"id": "step_month_2_mar",
"desc": "month */2 includes March, excludes February"
},
{
"id": "step_month_2_feb_miss",
"desc": "month */2 excludes February (1,3,5,...)"
},
{
"id": "step_dom_10_hit",
"desc": "day-of-month */10 includes the 11th (1,11,21,31)"
},
{
"id": "step_dom_10_miss",
"desc": "day-of-month */10 excludes the 10th"
},
{
"id": "step_dom_10_first",
"desc": "day-of-month */10 includes the 1st"
},
{
"id": "step_dom_7_hit",
"desc": "day-of-month */7 includes the 8th (1,8,15,22,29)"
},
{
"id": "range_step_min_hit",
"desc": "minute 10-30/10 matches 20"
},
{
"id": "range_step_min_miss15",
"desc": "minute 10-30/10 rejects 15"
},
{
"id": "range_step_min_miss25",
"desc": "minute 10-30/10 rejects 25"
},
{
"id": "range_step_hour_hit",
"desc": "hour 8-18/2 matches 14"
},
{
"id": "range_step_hour_miss9",
"desc": "hour 8-18/2 rejects 9"
},
{
"id": "range_step_hour_miss11",
"desc": "hour 8-18/2 rejects 11"
},
{
"id": "range_step_dom_hit",
"desc": "day-of-month 5-25/10 matches the 15th"
},
{
"id": "range_step_dom_miss",
"desc": "day-of-month 5-25/10 rejects the 10th"
},
{
"id": "or_dow_fires",
"desc": "0 0 13 * 5: a Friday that is not the 13th still matches"
},
{
"id": "or_dom_fires",
"desc": "0 0 13 * 5: the 13th that is not a Friday still matches"
},
{
"id": "or_both",
"desc": "0 0 13 * 5: a Friday the 13th matches"
},
{
"id": "or_neither",
"desc": "0 0 13 * 5: a non-13th non-Friday does not match"
},
{
"id": "or_dow_wed_fires",
"desc": "0 0 1 * 3: a Wednesday that is not the 1st matches"
},
{
"id": "or_dom_first_fires",
"desc": "0 0 1 * 3: the 1st that is not a Wednesday matches"
},
{
"id": "ix_step_range_or_dom",
"desc": "10-20/5 dom OR Fri: the 15th fires via dom (needs step+OR)"
},
{