specproof 0.1.0

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 (119) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +10 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +283 -0
  5. data/docs/agent-skills.md +60 -0
  6. data/docs/benchmark.md +24 -0
  7. data/docs/constrained-partition-pairs-results.md +44 -0
  8. data/docs/finite-strings-results.md +75 -0
  9. data/docs/implementation-status.md +65 -0
  10. data/docs/input-selection-results.md +123 -0
  11. data/docs/lint-cleanup-plan.md +12 -0
  12. data/docs/method-targets-results.md +68 -0
  13. data/docs/minitest-compatibility.md +33 -0
  14. data/docs/optional-arguments-plan.md +25 -0
  15. data/docs/optional-arguments-results.md +45 -0
  16. data/docs/pairwise-partitions-results.md +53 -0
  17. data/docs/pairwise-results.md +77 -0
  18. data/docs/pairwise-suggestions-results.md +104 -0
  19. data/docs/rbs-implementation-status.md +115 -0
  20. data/docs/rbs-string-literals-results.md +58 -0
  21. data/docs/real-project-pilot.md +84 -0
  22. data/docs/report-format.md +244 -0
  23. data/examples/age_policy/README.md +39 -0
  24. data/examples/age_policy/faulty/lib/age_policy.rb +9 -0
  25. data/examples/age_policy/faulty/test/age_policy_examples_test.rb +17 -0
  26. data/examples/age_policy/faulty/test/age_policy_test.rb +21 -0
  27. data/examples/age_policy/lib/age_policy.rb +8 -0
  28. data/examples/age_policy/run.rb +23 -0
  29. data/examples/age_policy/specification.rb +8 -0
  30. data/examples/age_policy/test/age_policy_test.rb +18 -0
  31. data/examples/constrained_partition_pairs/README.md +40 -0
  32. data/examples/constrained_partition_pairs/lib/account_eligibility.rb +8 -0
  33. data/examples/constrained_partition_pairs/requirements.md +11 -0
  34. data/examples/constrained_partition_pairs/run.rb +22 -0
  35. data/examples/constrained_partition_pairs/specification.rb +17 -0
  36. data/examples/constrained_partition_pairs/test_account_eligibility.rb +34 -0
  37. data/examples/input_selection/README.md +22 -0
  38. data/examples/input_selection/delivery_policy.rb +11 -0
  39. data/examples/input_selection/requirements.md +9 -0
  40. data/examples/input_selection/specification.rb +8 -0
  41. data/examples/input_selection/test_delivery_policy.rb +38 -0
  42. data/examples/method_targets/README.md +30 -0
  43. data/examples/method_targets/lib/delivery_defaults.rb +8 -0
  44. data/examples/method_targets/lib/delivery_options.rb +12 -0
  45. data/examples/method_targets/requirements.md +20 -0
  46. data/examples/method_targets/specification_objects.rb +17 -0
  47. data/examples/method_targets/specification_strings.rb +15 -0
  48. data/examples/pairwise/README.md +46 -0
  49. data/examples/pairwise/constrained_requirements.md +6 -0
  50. data/examples/pairwise/constrained_specification.rb +11 -0
  51. data/examples/pairwise/corrected/lib/delivery_options.rb +11 -0
  52. data/examples/pairwise/corrected/test/delivery_options_test.rb +28 -0
  53. data/examples/pairwise/faulty/lib/delivery_options.rb +11 -0
  54. data/examples/pairwise/faulty/test/gap_delivery_options_test.rb +10 -0
  55. data/examples/pairwise/faulty/test/initial_delivery_options_test.rb +23 -0
  56. data/examples/pairwise/requirements.md +11 -0
  57. data/examples/pairwise/run.rb +36 -0
  58. data/examples/pairwise/specification.rb +9 -0
  59. data/examples/pairwise/three_boolean/lib/boolean_batch.rb +8 -0
  60. data/examples/pairwise/three_boolean/test/boolean_batch_test.rb +28 -0
  61. data/examples/pairwise/three_boolean_specification.rb +10 -0
  62. data/examples/pairwise_partitions/README.md +34 -0
  63. data/examples/pairwise_partitions/lib/age_policy.rb +8 -0
  64. data/examples/pairwise_partitions/requirements.md +8 -0
  65. data/examples/pairwise_partitions/run.rb +22 -0
  66. data/examples/pairwise_partitions/specification.rb +13 -0
  67. data/examples/pairwise_partitions/test_age_policy.rb +29 -0
  68. data/examples/pairwise_suggestions/README.md +48 -0
  69. data/examples/pairwise_suggestions/run.rb +55 -0
  70. data/examples/pairwise_suggestions/three_boolean/lib/suggested_boolean_batch.rb +8 -0
  71. data/examples/pairwise_suggestions/three_boolean/requirements.md +7 -0
  72. data/examples/pairwise_suggestions/three_boolean/specification.rb +11 -0
  73. data/examples/pairwise_suggestions/three_boolean/test/baseline_test.rb +9 -0
  74. data/examples/pairwise_suggestions/three_boolean/test/complete_test.rb +22 -0
  75. data/examples/partition_gap.rb +8 -0
  76. data/examples/rbs_registration/README.md +58 -0
  77. data/examples/rbs_registration/implementations/age_gt/registration.rb +8 -0
  78. data/examples/rbs_registration/implementations/age_gte/registration.rb +8 -0
  79. data/examples/rbs_registration/requirements.md +9 -0
  80. data/examples/rbs_registration/sig/registration.rbs +3 -0
  81. data/examples/rbs_registration/specification.rb +10 -0
  82. data/examples/rbs_registration/test_registration.rb +43 -0
  83. data/examples/rbs_string_literals/README.md +34 -0
  84. data/examples/rbs_string_literals/lib/delivery_options.rb +9 -0
  85. data/examples/rbs_string_literals/requirements.md +11 -0
  86. data/examples/rbs_string_literals/sig/delivery_options.rbs +3 -0
  87. data/examples/rbs_string_literals/specification.rb +7 -0
  88. data/examples/rbs_string_literals/test_delivery_options.rb +46 -0
  89. data/examples/shipping.rb +12 -0
  90. data/exe/specproof +7 -0
  91. data/lib/specproof/checker.rb +144 -0
  92. data/lib/specproof/cli.rb +292 -0
  93. data/lib/specproof/collector.rb +304 -0
  94. data/lib/specproof/combinations.rb +226 -0
  95. data/lib/specproof/composer.rb +97 -0
  96. data/lib/specproof/coverage.rb +229 -0
  97. data/lib/specproof/coverage_report.rb +334 -0
  98. data/lib/specproof/domains.rb +124 -0
  99. data/lib/specproof/loader.rb +285 -0
  100. data/lib/specproof/minitest.rb +443 -0
  101. data/lib/specproof/model.rb +198 -0
  102. data/lib/specproof/partition_space.rb +268 -0
  103. data/lib/specproof/planner.rb +125 -0
  104. data/lib/specproof/report.rb +248 -0
  105. data/lib/specproof/skills.rb +188 -0
  106. data/lib/specproof/sources/rbs.rb +137 -0
  107. data/lib/specproof/sources/rbs_projector.rb +268 -0
  108. data/lib/specproof/suggestion_report.rb +159 -0
  109. data/lib/specproof/suggestions.rb +352 -0
  110. data/lib/specproof/target_name.rb +100 -0
  111. data/lib/specproof/type_constraints.rb +76 -0
  112. data/lib/specproof/version.rb +7 -0
  113. data/lib/specproof.rb +100 -0
  114. data/sig/specproof.rbs +222 -0
  115. data/skills/specproof-specify/SKILL.md +282 -0
  116. data/skills/specproof-specify/references/usage.md +265 -0
  117. data/skills/specproof-test-gaps/SKILL.md +214 -0
  118. data/skills/specproof-test-gaps/references/usage.md +231 -0
  119. metadata +165 -0
@@ -0,0 +1,265 @@
1
+ # SpecProof command and report reference
2
+
3
+ This reference targets SpecProof `0.1.x`, JSON schema `1`, and
4
+ `input-basics-v1`. Confirm `specproof --version` and the report's
5
+ `specproof_version`, but do not infer finite-string support from a `0.1.x`
6
+ version alone. A fresh `plan` from the installed build must accept the
7
+ declaration and report a typed string value; otherwise preserve the requested
8
+ value and report that the build does not support it. Report unsupported
9
+ versions or value kinds instead of guessing field meanings.
10
+
11
+ Pairwise finite planning is opt-in and supported when the installed build
12
+ accepts `--criterion pairwise-finite-v1`. The default remains
13
+ `input-basics-v1`; verify the actual `criterion` in every report rather than
14
+ inferring it from a package version. A pairwise plan requires a `combinations`
15
+ group for every target. `pairwise-partitions-v1` is a separate opt-in criterion
16
+ for complete, non-overlapping integer partitions crossed with finite values.
17
+ It supports all-allowed groups and explicit whole-partition restrictions.
18
+
19
+ Run commands from the application project and use its normal Ruby/Bundler
20
+ environment:
21
+
22
+ ```sh
23
+ specproof check spec/registration.rb --format json
24
+ specproof plan spec/registration.rb --format json
25
+ ```
26
+
27
+ For a mixed DSL/RBS declaration, repeat `--rbs FILE` for supplied RBS files
28
+ and `--target 'Class#instance_method'` for each selected target. RBS requires
29
+ at least one explicit target, and a target without an RBS file is an error.
30
+ Keep both options the same across check and plan:
31
+
32
+ ```sh
33
+ bundle exec specproof check spec/registration.rb --rbs sig/registration.rbs --target 'Registration#eligible?' --format json
34
+ bundle exec specproof plan spec/registration.rb --rbs sig/registration.rbs --target 'Registration#eligible?' --format json
35
+ ```
36
+
37
+ For an RBS-only contract, omit the positional DSL path and retain both explicit
38
+ RBS options:
39
+
40
+ ```sh
41
+ bundle exec specproof plan --rbs sig/registration.rbs --target 'Registration#eligible?' --format json
42
+ ```
43
+
44
+ The DSL also accepts Ruby method objects for directly defined public methods
45
+ on named classes and modules:
46
+
47
+ ```ruby
48
+ SpecProof.define DeliveryOptions.instance_method(:build) do
49
+ input :quantity, range: 0..100
50
+ end
51
+
52
+ SpecProof.define DeliveryOptions.method(:build) do
53
+ input :quantity, range: 0..100
54
+ end
55
+ ```
56
+
57
+ Use a canonical string target such as `"DeliveryOptions#build"` or
58
+ `"DeliveryOptions.build"` when checking/planning offline or when loading the
59
+ owner would trigger autoload. Strings do not resolve the target. Use an
60
+ `UnboundMethod`/`Method` object when the owner is already loaded and reflection
61
+ is useful; target methods are not invoked. Equivalent forms normalize to the
62
+ same canonical target, digest, and obligation IDs. Instance objects must be
63
+ unbound; aliases, inherited singleton methods, anonymous owners, and methods
64
+ bound to arbitrary instances are unsupported. RBS selectors remain explicit
65
+ strings and can select `def self.method` on a named class or module. Reports
66
+ retain string target identities, while source navigation depends on editor LSP
67
+ support.
68
+
69
+ `check` returns declared gap, overlap, empty-domain, and out-of-domain findings.
70
+ It cannot decide which of two conflicting policies is intended. `plan` requires
71
+ a specification with no semantic findings and uses the `input-basics-v1`
72
+ criterion. It proposes domain endpoints, neighboring values, and declared
73
+ partition boundaries; its output is a set of candidates for review.
74
+
75
+ For finite joint scenarios, declare one `combinations` group per target and
76
+ select `pairwise-finite-v1` explicitly:
77
+
78
+ ```ruby
79
+ SpecProof.define "DeliveryOptions#build" do
80
+ input :expedited, values: [false, true]
81
+ input :notify, values: [false, true]
82
+ combinations :expedited, :notify, all_allowed: true
83
+ end
84
+ ```
85
+
86
+ `all_allowed: true` explicitly says every tuple is valid; it does not establish
87
+ behavioral independence. Alternatively, use a nonempty block containing only
88
+ requirement-backed `forbid` assignments. A partial assignment forbids every
89
+ matching completion. Rules are data, never executable predicates. Never add a
90
+ restriction just to remove a missing pair. A pair is required only if it has
91
+ an allowed full assignment as a completion, so feasibility must account for
92
+ all group inputs. A forbidden tuple with three inputs can still contain
93
+ individually feasible pairs.
94
+
95
+ The CLI form is
96
+ `specproof plan PATH --criterion pairwise-finite-v1 --format json`. `check`
97
+ does not accept a criterion. Pairwise plans require a group for every supplied
98
+ target and reject oversized domains rather than sampling them.
99
+
100
+ Use `pairwise-partitions-v1` only when every grouped integer range has
101
+ `allow_gaps: false`, `allow_overlaps: false`, and explicit partitions, and every other
102
+ grouped input uses finite values. The partitions form categories, and each
103
+ finite value is a singleton category. Use `all_allowed: true` only when every
104
+ full tuple is allowed; otherwise declare requirement-backed `forbid`
105
+ assignments. Integer restrictions select a whole partition with
106
+ `in_partition(:name)`, and finite inputs use exact values:
107
+
108
+ ```ruby
109
+ combinations :age, :account, :country do
110
+ forbid age: in_partition(:minor), account: "business"
111
+ end
112
+ ```
113
+
114
+ Restrictions are partial assignments over full tuples. A pair is required only
115
+ when an allowed full completion exists; a restriction involving a third input
116
+ can exclude a pair indirectly. A forbidden runtime tuple fulfills no pair
117
+ obligations. Integer restrictions cannot use raw values or partial ranges, and
118
+ finite inputs continue to require exact values. The criterion reports category
119
+ pairs, not boundary candidates or a claim of behavioral equivalence. Never add
120
+ a restriction merely to erase a missing pair.
121
+
122
+ ```ruby
123
+ SpecProof.define "AgePolicy#eligible?" do
124
+ input :age, range: 0..120, allow_gaps: false, allow_overlaps: false do
125
+ partition :minor, range: 0...18
126
+ partition :adult, range: 18..120
127
+ end
128
+ input :country, values: ["ro", "de"]
129
+ combinations :age, :country, all_allowed: true
130
+ end
131
+ ```
132
+
133
+ Run `specproof plan spec/age.rb --criterion pairwise-partitions-v1 --format json`.
134
+ `check` remains criterion-independent.
135
+
136
+ JSON reports use `schema_version: 1`. Check reports include `kind: "check"`,
137
+ `contract_digest`, and `findings`. Plan reports include `kind: "plan"`, the
138
+ same digest, `criterion`, and `obligations`. Every planned obligation has a
139
+ stable `id`, target, input name, typed value, reasons, and source locations.
140
+ Integer values are decimal strings inside a typed value object. Compare the
141
+ digest when comparing reports; compare obligation IDs when linking tests to
142
+ planned values. Re-run the current command before relying on old output.
143
+ Finite strings add `{ "kind": "string", "value": "..." }` under schema 1.
144
+ Consumers must handle this kind wherever typed values and witnesses occur.
145
+
146
+ For `pairwise-finite-v1`, each obligation instead has an `inputs` object with
147
+ two input names mapped to typed values; the row also has the shared stable ID,
148
+ target, reasons, and source locations. Pair names are sorted canonically.
149
+ `excluded_pairs` lists typed pairs with no allowed full completion, their
150
+ `no_allowed_completion` reason, and relevant constraint sources. These pairs
151
+ are excluded from the denominator. Runtime rows add status and hit counts to
152
+ the planned pair fields. A forbidden runtime tuple satisfies no pair
153
+ obligations. Keep pair coverage separate from test assertions and output
154
+ correctness.
155
+
156
+ For `pairwise-partitions-v1`, the `age` typed value is a category object, for
157
+ example `{ "kind": "partition", "name": "minor", "lower": "0", "upper": "17" }`.
158
+ Bounds are inclusive in the report. Other finite input values retain their
159
+ ordinary typed value. A pair row therefore describes categories, not a set of
160
+ boundary-value obligations. Runtime collection counts a call when its actual
161
+ integer belongs to the declared category and its full assignment is allowed.
162
+ The normalized constraint value used for the contract digest has a distinct
163
+ canonical encoding, `{ "kind": "partition_reference", "name": "minor" }`;
164
+ the existing partition obligation encoding is unchanged.
165
+
166
+ Use only the declaration forms supported by SpecProof:
167
+
168
+ ```ruby
169
+ SpecProof.define "Checkout#shipping" do
170
+ input :cents, range: 0..100_000, allow_gaps: false, allow_overlaps: false do
171
+ partition :standard, range: 0..4_999
172
+ partition :free, range: 5_000..100_000
173
+ end
174
+ end
175
+ ```
176
+
177
+ When finite-string support is present, enumerate exact text values with
178
+ `values`, for example `values: [nil, 1, "user_123"]`. This is a finite scenario
179
+ subset, not a model of all strings. Symbols and strings with the same spelling
180
+ are distinct values. Only exact `String` instances containing valid UTF-8 or
181
+ US-ASCII are accepted; US-ASCII is canonicalized to UTF-8. Empty strings,
182
+ whitespace, Unicode, and embedded NUL are valid. Invalid bytes, other encodings,
183
+ and String subclasses are rejected. Values are not trimmed, case-folded, or
184
+ Unicode-normalized. SpecProof owns frozen copies of declared strings and
185
+ observed entry values, so mutating an application argument after method entry
186
+ does not alter matching or freeze the application's string. A retained
187
+ outside-domain string witness is full-sized even though witness count is
188
+ bounded.
189
+
190
+ Do not add inferred partitions to close a gap or remove an overlap. Explain
191
+ the witnesses and ask for the intended policy.
192
+
193
+ Successful check and plan reports both include
194
+ `scope.completeness_scope: "analyzed_inputs"` and `scope.input_scope`. Each
195
+ scope entry contains a target, its `analyzed_inputs` names, and any
196
+ `excluded_inputs` with a name, reason, and declaration source. By default every
197
+ named runtime parameter is analyzed. An intentionally partial contract must
198
+ explicitly exclude every remaining named parameter with a reason and retain at
199
+ least one analyzed input; there are no wildcards or implicit exclusions. For
200
+ an RBS-selected target, DSL `input` and `exclude_input` names must match its
201
+ signature. An exclusions-only DSL declaration is valid when supported finite
202
+ inputs can be imported from RBS. A type-only unsupported input may be excluded
203
+ only by an explicit declaration; an unsupported selected union remains an
204
+ atomic error.
205
+
206
+ The report digest represents normalized behavioral domains, partition
207
+ policies, and sorted excluded names. Exclusion reason/source and declaration
208
+ order do not affect it. No-exclusion digests and obligation IDs remain stable.
209
+ It does not fingerprint all DSL source text, the full RBS signature, or
210
+ implementation correctness. Provenance source objects require `path` and
211
+ `line`; schema 1 permits additional fields, so consumers must tolerate unknown
212
+ fields. A source's `kind` identifies its family but does not change obligation
213
+ identity. Read the coverage reference for runtime `declaration_sources` and
214
+ collection scope; offline check/plan scope describes the declaration and does
215
+ not attest runtime binding or that tests loaded.
216
+ RBS string literals, finite unions of supported literals, and optional literal
217
+ forms provide exact finite scenarios without duplicate DSL inputs. Unrestricted
218
+ RBS `String` and selected unions containing unsupported members fail atomically.
219
+ Return types are ignored and do not supply expected behavior. Verify support
220
+ with a fresh plan; do not infer it from `0.1.x`.
221
+
222
+ ## Pairwise suggestions
223
+
224
+ After an intact runtime coverage report records missing pair obligations, ask
225
+ SpecProof for allowed group assignments with:
226
+
227
+ ```sh
228
+ specproof suggest spec/delivery.rb --report tmp/coverage.json --format json
229
+ specproof suggest --rbs sig/delivery.rbs --target 'Delivery#build' spec/delivery.rb --report tmp/coverage.json --format json
230
+ ```
231
+
232
+ There must be exactly one nonempty `--report`. The command infers its criterion
233
+ from the validated coverage report and rejects `--criterion`; it does not add
234
+ a new default to check or plan. It uses `greedy-pairwise-v1` for
235
+ `pairwise-finite-v1` and `greedy-partition-pairwise-v1` for
236
+ `pairwise-partitions-v1`. It replans the supplied current declarations, then
237
+ checks schema 1, `kind: "coverage"`, the pairwise criterion, digest, exact
238
+ target and analyzed/excluded/group scope, exact typed
239
+ obligation denominator, row statuses/hits, counters, and missing/exercised
240
+ projections. The report must be completed, intact, free of errors and
241
+ incomplete reasons, and have no unknown observations. `tests_passed` must be a
242
+ Boolean; `false` is retained as the actual test outcome rather than rejected.
243
+ Filtered reports are usable only for their recorded scope.
244
+
245
+ The output retains `source_run_id`, `source_tests_passed`, and `source_scope`.
246
+ It marks `assignment_scope: "combination_group"` and
247
+ `minimality: "not_guaranteed"`. Each suggestion has a typed full group
248
+ assignment and `covers` lists only newly covered missing IDs. The rows' union
249
+ must equal the source missing set. `unassigned_inputs` lists every analyzed
250
+ parameter outside the group and every explicitly excluded parameter; the
251
+ caller remains responsible for supplying those values. Runtime evidence still
252
+ requires all analyzed inputs to be valid.
253
+
254
+ For a partition pair, the typed plan contains category names and bounds, while
255
+ the suggestion contains an ordinary concrete integer using that category's
256
+ lower endpoint. It is a representative call value only; it does not establish
257
+ boundary coverage or expected behavior.
258
+
259
+ No candidate is executed and no expected output is inferred. Use a separate
260
+ requirement as the assertion oracle before writing an assertion. The digest
261
+ does not fingerprint implementation or test files; rerun the intended suite in
262
+ a fresh process after either changes and inspect its new run ID and result.
263
+ Generation rejects reports over 32 MiB and work over 10,000 cumulative full
264
+ assignments, 10,000 cumulative possible pair rows, or 1,000,000 candidate-to-pair
265
+ memberships. It never truncates or returns partial suggestions.
@@ -0,0 +1,214 @@
1
+ ---
2
+ name: specproof-test-gaps
3
+ description: Finds untested SpecProof input obligations and adds evidence-backed Minitest coverage for Ruby methods. Use when reviewing boundary or pairwise gaps, integrating SpecProof with Minitest, suggesting allowed assignments, or verifying a fresh coverage report.
4
+ ---
5
+
6
+ # Find and close SpecProof test gaps
7
+
8
+ Use SpecProof plans to find candidate inputs missing from a Minitest suite.
9
+ Use an independent requirement or trusted existing test as the expected-result
10
+ oracle. SpecProof observes method calls; it does not inspect assertions, replay
11
+ tests, infer expected results, or prove application correctness.
12
+
13
+ ## Workflow
14
+
15
+ 1. Read the request, independent behavior requirements, target method, current
16
+ tests, and the input specification. Confirm that the canonical target
17
+ (`Owner#method` for an instance method or `Owner.method` for a class/module
18
+ singleton method) and parameter names match the real method signature.
19
+ Default scope is strict: every named
20
+ parameter is analyzed. If the task deliberately covers only some inputs,
21
+ every other name must have an explicit `exclude_input :name, reason: "..."`
22
+ declaration, and at least one input must remain analyzed. Do not introduce
23
+ exclusions to make findings, missing obligations, or a strict run disappear.
24
+ 2. Create or validate the intended input declaration before measuring coverage.
25
+ Run `specproof check PATH --format json`, then
26
+ `specproof plan PATH --format json`; inspect findings, errors, digest, and
27
+ input scope. Resolve or report every finding. Do not change domains,
28
+ partitions, or exclusions merely to make a check or report green. Ask before
29
+ changing the declared contract when its intent is uncertain. Record each
30
+ candidate's stable obligation ID, value,
31
+ and source locations, plus every excluded input and its reason. A plan's
32
+ candidate set covers only its analyzed inputs.
33
+ For a finite-string scenario such as `values: [nil, 1, "user_123"]`, keep
34
+ each value and type exactly as required; `"user_123"` and `:user_123` are
35
+ distinct. Use finite strings only after a fresh plan reports a string value.
36
+ Do not infer support from `0.1.x`, and do not drop or relabel a required
37
+ string to obtain a valid report. If the installed build rejects a required
38
+ string, report that unsupported value rather than substituting a symbol or
39
+ omitting it.
40
+ Use `input-basics-v1` for marginal input candidates unless requirements
41
+ explicitly describe finite joint scenarios. For pairwise coverage, opt in
42
+ with `--criterion pairwise-finite-v1` and require a `combinations` group
43
+ for every target. `all_allowed: true` is an explicit claim that every tuple
44
+ is valid, not a claim of behavioral independence. A `forbid` clause must
45
+ come from a requirement; never add one to erase a missing pair. Pair
46
+ feasibility depends on an allowed full assignment, including all other
47
+ group inputs. For bounded integer inputs, use `pairwise-partitions-v1` only
48
+ when each grouped range has an explicit complete, non-overlapping partition
49
+ and all other grouped inputs are finite values. Use `all_allowed: true` only
50
+ when every full tuple is allowed; otherwise encode stated restrictions with
51
+ a nonempty block of `forbid` assignments. Integer selectors must use
52
+ `in_partition(:name)` for a whole declared partition; finite inputs require
53
+ exact values. Pair feasibility depends on whether any allowed full
54
+ completion exists, including the values of other group inputs. A call that
55
+ matches a forbidden assignment contributes no pair coverage. Never change
56
+ restrictions to remove a missing pair. Explicit partitions on finite-valued
57
+ inputs remain unsupported. This criterion counts category pairs, not
58
+ boundary candidates or inferred behavior equivalence. Use `allow_gaps: false` and
59
+ `allow_overlaps: false` to require that grouping. Omitted options allow
60
+ gaps/overlaps. When migrating legacy specifications, invert the booleans:
61
+ `exhaustive: true` becomes `allow_gaps: false`, and `disjoint: true` becomes
62
+ `allow_overlaps: false`. Do not weaken either policy to close a reported gap.
63
+ 3. Before editing tests or production code, run the intended current suite in a
64
+ fresh process and capture its coverage report as the baseline. Check its run
65
+ ID, digest, input scope, completion, integrity, and original test result.
66
+ Use an intact report to identify existing input evidence; an incomplete
67
+ report cannot establish a baseline gap. Preserve the baseline result as
68
+ recorded, including `tests_passed: false` when the current suite fails.
69
+ For pairwise runs, compare the exact planned pair IDs and typed assignments.
70
+ Partition-pair rows identify category names and bounds; a suggestion uses a
71
+ lower-endpoint input representative. That input is not a behavioral oracle
72
+ or a boundary-coverage claim.
73
+ A method call can satisfy only pairs present together in that valid tuple;
74
+ evidence from separate calls is never combined into a synthetic pair.
75
+ 4. For each uncovered value, derive its expected result from an independent
76
+ normative requirement or source document. A trusted existing test can
77
+ preserve the exact case it states, but cannot establish a general boundary
78
+ rule from one example. If there is no oracle, ask a precise question and do
79
+ not invent an assertion. A candidate value alone does not say what the
80
+ method should return.
81
+ 5. Add a meaningful assertion for each agreed gap. Keep the obligation ID and
82
+ the normative requirement/source location beside the assertion, such as in
83
+ its failure message. Identify the implementation method as the code under
84
+ test, not as the source of expected behavior. Preserve every existing domain
85
+ obligation unless an independently justified requirement change is explicit.
86
+ 6. Change production behavior only when the task explicitly authorizes a
87
+ behavior fix. For an authorized fix, add the failing regression assertion
88
+ and capture its failure against the current implementation before making
89
+ the smallest requirement-backed change. For a test-only or review task,
90
+ preserve production code and existing assertions; keep and report a failing
91
+ regression with `tests_passed: false` when the requirement exposes an
92
+ unresolved defect.
93
+ 7. Rerun the intended test command in a fresh process after the final source or
94
+ test edit. Collection observes calls made during that run only; a previous
95
+ report cannot establish fresh coverage. Verify the newly written report has
96
+ a new `run_id`, the current `contract_digest`, input scope, and obligation
97
+ IDs. Confirm its completion, integrity, actual test result, and
98
+ `scope.targets`, `scope.declaration_sources`, run arguments, and filters
99
+ match the actual test command and loaded test files. `scope.targets` and
100
+ `scope.declaration_sources` are runtime coverage fields; offline check and
101
+ plan results do not have them. If a source changes, run the suite again and
102
+ inspect a fresh report. Compare `scope.completeness_scope` and
103
+ `scope.input_scope` with both the current plan and the actual named runtime
104
+ signature; matching obligation IDs alone do not establish that the analyzed
105
+ scope is the same. Human summaries must disclose excluded names and reasons.
106
+ `100%` means all obligations for analyzed inputs were exercised; it says
107
+ nothing about excluded values or behavior, outputs, interactions, or complete
108
+ method correctness. A matching digest does not fingerprint all source text,
109
+ the full RBS signature, or implementation correctness. Report the actual
110
+ `tests_passed` value and any unresolved finding. Never narrow or exclude
111
+ inputs, weaken strict missing policy, or alter production behavior just to
112
+ turn a failing result green.
113
+
114
+ For a pairwise baseline, you may use `specproof suggest PATH --report FILE`
115
+ after checking the report against a fresh plan with the criterion named in the
116
+ coverage report. `suggest` infers that criterion and accepts no `--criterion`
117
+ option. Suggestions require a completed, intact report with exact typed obligations, current digest and
118
+ declared scope, consistent rows and projections, no errors or unknowns, and a
119
+ Boolean original `tests_passed` value. A failing test result is accepted and
120
+ preserved. A filtered report supports only its recorded scope. Do not infer
121
+ freshness from the digest: it does not include implementation or test bytes,
122
+ so run the intended suite again after changes.
123
+
124
+ Treat each row as an unexecuted, allowed full assignment for the combination
125
+ group. Its `covers` field lists missing pair IDs newly covered by that row;
126
+ the output has no expected result. Inspect `unassigned_inputs`, which lists
127
+ analyzed inputs outside the group and every explicitly excluded parameter,
128
+ then supply valid values for those names. Every analyzed input must be valid
129
+ before runtime pair evidence counts. Use the independent normative
130
+ requirement to write assertions; never generate expected values from the
131
+ implementation or the suggestions. Never add exclusions or `forbid` clauses
132
+ to erase a gap or make a report green.
133
+
134
+ When testing a pairwise interaction, derive the expected output from an
135
+ independent rule for each combination. A pair coverage hit says only that the
136
+ input combination reached the method. Keep the baseline, any newly failing
137
+ requirement-backed assertion, and any corrected implementation run in separate
138
+ fresh processes, with a fresh report and run ID for each. A passing pairwise
139
+ report and passing assertions establish evidence only for the declared inputs
140
+ and assertions; they do not prove exhaustive tuple coverage or behavioral
141
+ independence. `pairwise-partitions-v1` records whether an observed value belongs
142
+ to a declared category. It does not prove that all values in that category
143
+ behave alike or that a category pair was tested at its boundaries. Derive each
144
+ expected result from an independent requirement.
145
+
146
+ For adapter setup, report fields, and evidence checks, read
147
+ [the bundled coverage report reference](references/usage.md).
148
+
149
+ ## Minitest adapter setup
150
+
151
+ Use canonical string targets for offline planning and when the application
152
+ owner may not be loaded; strings do not resolve or autoload that owner. Use
153
+ `Owner.instance_method(:name)` or `Owner.method(:name)` when code is already
154
+ loaded and a method object is useful. These use reflection without invocation
155
+ and are accepted only for directly defined public methods on named classes or
156
+ modules. Unbound instance methods identify their defining class. Aliases,
157
+ inherited singleton methods, anonymous owners, and methods bound to arbitrary
158
+ instances are unsupported. Recheck object targets against the current direct
159
+ definition when declaring them. Equivalent strings and method objects share
160
+ canonical string report targets and obligation IDs. Navigation from a target
161
+ name depends on the editor's language server. For live collection, require the
162
+ application code before installing the adapter. Method objects and matching
163
+ digests say nothing about returned values; derive assertions from the
164
+ independent requirement and collect a fresh report after any implementation,
165
+ specification, or test change.
166
+
167
+ Keep Minitest in the application's test environment and use the supported
168
+ version range `>= 5.26.2` and `< 5.28.0`, with matching package/runtime major
169
+ and minor versions. Prefer `~> 5.26.2` for ordinary published packages.
170
+ Require application code and Minitest before installing the
171
+ adapter. Install it once in the suite entrypoint:
172
+
173
+ ```ruby
174
+ require "minitest/autorun"
175
+ require "registration"
176
+ require "specproof/minitest"
177
+
178
+ SpecProof::Minitest.install!(
179
+ specifications: ["spec/registration.rb"],
180
+ output: "tmp/specproof-coverage.json",
181
+ fail_on_missing: false
182
+ )
183
+ ```
184
+
185
+ Use `fail_on_missing: false` when first adding the adapter to a suite with no
186
+ established missing-obligation policy, so gaps can be reported without changing
187
+ the suite's existing pass/fail behavior. Preserve an existing
188
+ `fail_on_missing: true` policy; the adapter still writes its report when
189
+ unobserved obligations make the run fail. Never disable that strict policy just
190
+ to obtain a green result. Use the report to decide whether gaps remain; a
191
+ passing suite alone does not close them. Keep independent input scope intact;
192
+ do not exclude an input simply because it has missing obligations or a
193
+ non-primitive value.
194
+
195
+ RBS supplies input types only. It does not supply expected return values or
196
+ behavioral output assertions. Derive each assertion from an independent rule
197
+ or trusted stated requirement. A coverage candidate or a successful hit
198
+ establishes only that an input obligation was exercised; it does not establish
199
+ the expected result or prove correctness.
200
+
201
+ Finite strings are explicitly enumerated text scenarios, not open-ended String
202
+ coverage. A supported build accepts only exact String instances containing
203
+ valid UTF-8 or US-ASCII text (canonicalized to UTF-8); empty strings,
204
+ whitespace, Unicode, and embedded NUL are valid. Invalid bytes, other encodings,
205
+ and String subclasses are rejected. No trimming, case folding, or Unicode
206
+ normalization occurs. SpecProof copies and freezes owned declaration and
207
+ method-entry snapshots without freezing application values; post-entry
208
+ mutation cannot change matching. A direct Coverage observation likewise owns
209
+ retained strings. Outside-domain string witnesses retain the full string even
210
+ though only a bounded number of witnesses are retained. Explicitly selected
211
+ RBS string literals and finite unions of supported literals provide exact
212
+ scenarios; unrestricted RBS `String` and unions containing unsupported members
213
+ remain unsupported and fail atomically. Verify support from a fresh plan; do
214
+ not infer it from `0.1.x`.