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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +283 -0
- data/docs/agent-skills.md +60 -0
- data/docs/benchmark.md +24 -0
- data/docs/constrained-partition-pairs-results.md +44 -0
- data/docs/finite-strings-results.md +75 -0
- data/docs/implementation-status.md +65 -0
- data/docs/input-selection-results.md +123 -0
- data/docs/lint-cleanup-plan.md +12 -0
- data/docs/method-targets-results.md +68 -0
- data/docs/minitest-compatibility.md +33 -0
- data/docs/optional-arguments-plan.md +25 -0
- data/docs/optional-arguments-results.md +45 -0
- data/docs/pairwise-partitions-results.md +53 -0
- data/docs/pairwise-results.md +77 -0
- data/docs/pairwise-suggestions-results.md +104 -0
- data/docs/rbs-implementation-status.md +115 -0
- data/docs/rbs-string-literals-results.md +58 -0
- data/docs/real-project-pilot.md +84 -0
- data/docs/report-format.md +244 -0
- data/examples/age_policy/README.md +39 -0
- data/examples/age_policy/faulty/lib/age_policy.rb +9 -0
- data/examples/age_policy/faulty/test/age_policy_examples_test.rb +17 -0
- data/examples/age_policy/faulty/test/age_policy_test.rb +21 -0
- data/examples/age_policy/lib/age_policy.rb +8 -0
- data/examples/age_policy/run.rb +23 -0
- data/examples/age_policy/specification.rb +8 -0
- data/examples/age_policy/test/age_policy_test.rb +18 -0
- data/examples/constrained_partition_pairs/README.md +40 -0
- data/examples/constrained_partition_pairs/lib/account_eligibility.rb +8 -0
- data/examples/constrained_partition_pairs/requirements.md +11 -0
- data/examples/constrained_partition_pairs/run.rb +22 -0
- data/examples/constrained_partition_pairs/specification.rb +17 -0
- data/examples/constrained_partition_pairs/test_account_eligibility.rb +34 -0
- data/examples/input_selection/README.md +22 -0
- data/examples/input_selection/delivery_policy.rb +11 -0
- data/examples/input_selection/requirements.md +9 -0
- data/examples/input_selection/specification.rb +8 -0
- data/examples/input_selection/test_delivery_policy.rb +38 -0
- data/examples/method_targets/README.md +30 -0
- data/examples/method_targets/lib/delivery_defaults.rb +8 -0
- data/examples/method_targets/lib/delivery_options.rb +12 -0
- data/examples/method_targets/requirements.md +20 -0
- data/examples/method_targets/specification_objects.rb +17 -0
- data/examples/method_targets/specification_strings.rb +15 -0
- data/examples/pairwise/README.md +46 -0
- data/examples/pairwise/constrained_requirements.md +6 -0
- data/examples/pairwise/constrained_specification.rb +11 -0
- data/examples/pairwise/corrected/lib/delivery_options.rb +11 -0
- data/examples/pairwise/corrected/test/delivery_options_test.rb +28 -0
- data/examples/pairwise/faulty/lib/delivery_options.rb +11 -0
- data/examples/pairwise/faulty/test/gap_delivery_options_test.rb +10 -0
- data/examples/pairwise/faulty/test/initial_delivery_options_test.rb +23 -0
- data/examples/pairwise/requirements.md +11 -0
- data/examples/pairwise/run.rb +36 -0
- data/examples/pairwise/specification.rb +9 -0
- data/examples/pairwise/three_boolean/lib/boolean_batch.rb +8 -0
- data/examples/pairwise/three_boolean/test/boolean_batch_test.rb +28 -0
- data/examples/pairwise/three_boolean_specification.rb +10 -0
- data/examples/pairwise_partitions/README.md +34 -0
- data/examples/pairwise_partitions/lib/age_policy.rb +8 -0
- data/examples/pairwise_partitions/requirements.md +8 -0
- data/examples/pairwise_partitions/run.rb +22 -0
- data/examples/pairwise_partitions/specification.rb +13 -0
- data/examples/pairwise_partitions/test_age_policy.rb +29 -0
- data/examples/pairwise_suggestions/README.md +48 -0
- data/examples/pairwise_suggestions/run.rb +55 -0
- data/examples/pairwise_suggestions/three_boolean/lib/suggested_boolean_batch.rb +8 -0
- data/examples/pairwise_suggestions/three_boolean/requirements.md +7 -0
- data/examples/pairwise_suggestions/three_boolean/specification.rb +11 -0
- data/examples/pairwise_suggestions/three_boolean/test/baseline_test.rb +9 -0
- data/examples/pairwise_suggestions/three_boolean/test/complete_test.rb +22 -0
- data/examples/partition_gap.rb +8 -0
- data/examples/rbs_registration/README.md +58 -0
- data/examples/rbs_registration/implementations/age_gt/registration.rb +8 -0
- data/examples/rbs_registration/implementations/age_gte/registration.rb +8 -0
- data/examples/rbs_registration/requirements.md +9 -0
- data/examples/rbs_registration/sig/registration.rbs +3 -0
- data/examples/rbs_registration/specification.rb +10 -0
- data/examples/rbs_registration/test_registration.rb +43 -0
- data/examples/rbs_string_literals/README.md +34 -0
- data/examples/rbs_string_literals/lib/delivery_options.rb +9 -0
- data/examples/rbs_string_literals/requirements.md +11 -0
- data/examples/rbs_string_literals/sig/delivery_options.rbs +3 -0
- data/examples/rbs_string_literals/specification.rb +7 -0
- data/examples/rbs_string_literals/test_delivery_options.rb +46 -0
- data/examples/shipping.rb +12 -0
- data/exe/specproof +7 -0
- data/lib/specproof/checker.rb +144 -0
- data/lib/specproof/cli.rb +292 -0
- data/lib/specproof/collector.rb +304 -0
- data/lib/specproof/combinations.rb +226 -0
- data/lib/specproof/composer.rb +97 -0
- data/lib/specproof/coverage.rb +229 -0
- data/lib/specproof/coverage_report.rb +334 -0
- data/lib/specproof/domains.rb +124 -0
- data/lib/specproof/loader.rb +285 -0
- data/lib/specproof/minitest.rb +443 -0
- data/lib/specproof/model.rb +198 -0
- data/lib/specproof/partition_space.rb +268 -0
- data/lib/specproof/planner.rb +125 -0
- data/lib/specproof/report.rb +248 -0
- data/lib/specproof/skills.rb +188 -0
- data/lib/specproof/sources/rbs.rb +137 -0
- data/lib/specproof/sources/rbs_projector.rb +268 -0
- data/lib/specproof/suggestion_report.rb +159 -0
- data/lib/specproof/suggestions.rb +352 -0
- data/lib/specproof/target_name.rb +100 -0
- data/lib/specproof/type_constraints.rb +76 -0
- data/lib/specproof/version.rb +7 -0
- data/lib/specproof.rb +100 -0
- data/sig/specproof.rbs +222 -0
- data/skills/specproof-specify/SKILL.md +282 -0
- data/skills/specproof-specify/references/usage.md +265 -0
- data/skills/specproof-test-gaps/SKILL.md +214 -0
- data/skills/specproof-test-gaps/references/usage.md +231 -0
- 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`.
|