@jjchill/probity-rules 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.
- package/CHANGELOG.md +18 -0
- package/GLOSSARY.template.md +33 -0
- package/README.md +96 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/presets/js.d.ts +63 -0
- package/dist/presets/js.d.ts.map +1 -0
- package/dist/presets/js.js +112 -0
- package/dist/presets/js.js.map +1 -0
- package/dist/presets/kmp.d.ts +27 -0
- package/dist/presets/kmp.d.ts.map +1 -0
- package/dist/presets/kmp.js +253 -0
- package/dist/presets/kmp.js.map +1 -0
- package/dist/presets/kotlin.d.ts +40 -0
- package/dist/presets/kotlin.d.ts.map +1 -0
- package/dist/presets/kotlin.js +172 -0
- package/dist/presets/kotlin.js.map +1 -0
- package/dist/presets/swift.d.ts +10 -0
- package/dist/presets/swift.d.ts.map +1 -0
- package/dist/presets/swift.js +285 -0
- package/dist/presets/swift.js.map +1 -0
- package/dist/rules/acceptance-language.d.ts +95 -0
- package/dist/rules/acceptance-language.d.ts.map +1 -0
- package/dist/rules/acceptance-language.js +443 -0
- package/dist/rules/acceptance-language.js.map +1 -0
- package/dist/rules/gates.d.ts +125 -0
- package/dist/rules/gates.d.ts.map +1 -0
- package/dist/rules/gates.js +285 -0
- package/dist/rules/gates.js.map +1 -0
- package/dist/rules/kotlin.d.ts +323 -0
- package/dist/rules/kotlin.d.ts.map +1 -0
- package/dist/rules/kotlin.js +722 -0
- package/dist/rules/kotlin.js.map +1 -0
- package/dist/rules/ports-and-adapters.d.ts +86 -0
- package/dist/rules/ports-and-adapters.d.ts.map +1 -0
- package/dist/rules/ports-and-adapters.js +366 -0
- package/dist/rules/ports-and-adapters.js.map +1 -0
- package/dist/rules/scoping.d.ts +68 -0
- package/dist/rules/scoping.d.ts.map +1 -0
- package/dist/rules/scoping.js +93 -0
- package/dist/rules/scoping.js.map +1 -0
- package/dist/rules/spec-test-parity.d.ts +164 -0
- package/dist/rules/spec-test-parity.d.ts.map +1 -0
- package/dist/rules/spec-test-parity.js +456 -0
- package/dist/rules/spec-test-parity.js.map +1 -0
- package/dist/rules/swift.d.ts +50 -0
- package/dist/rules/swift.d.ts.map +1 -0
- package/dist/rules/swift.js +50 -0
- package/dist/rules/swift.js.map +1 -0
- package/dist/rules/ubiquitous-language.d.ts +36 -0
- package/dist/rules/ubiquitous-language.d.ts.map +1 -0
- package/dist/rules/ubiquitous-language.js +140 -0
- package/dist/rules/ubiquitous-language.js.map +1 -0
- package/dist/scripts/scope-report.d.ts +3 -0
- package/dist/scripts/scope-report.d.ts.map +1 -0
- package/dist/scripts/scope-report.js +184 -0
- package/dist/scripts/scope-report.js.map +1 -0
- package/kiro/README.md +20 -0
- package/kiro/kiro-agent.template.json +45 -0
- package/kiro/kiro-transcript-to-claude.py +183 -0
- package/kiro/probity-kiro-translate.py +132 -0
- package/kiro/probity-kiro.sh +87 -0
- package/kiro/skill-activation-forced-eval.sh +45 -0
- package/package.json +68 -0
- package/probity.config.kmp.ts +40 -0
- package/probity.config.kotlin.ts +37 -0
- package/probity.config.swift.ts +38 -0
- package/probity.config.ts +43 -0
- package/scripts/spec-parity.mjs +345 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Probity rule-entry factory — Swift/iOS preset.
|
|
3
|
+
*
|
|
4
|
+
* Same enforcement stack as the Kotlin presets (see hooks/PROBITY.md),
|
|
5
|
+
* recalibrated for a native iOS app: an Xcode workspace with the
|
|
6
|
+
* acceptance suite in AcceptanceTests/ following the four-layer model
|
|
7
|
+
* (Specs + Component test glue / Scenarios / DSL / Drivers), XCUITest
|
|
8
|
+
* plus an app-hosted component-test target, MVVM view models over
|
|
9
|
+
* use-case ports, and Markdown Given/When/Then specs in docs/specs/.
|
|
10
|
+
* Calibrated against a production CocoaPods + SwiftPM app — adjust
|
|
11
|
+
* globs and package names to your layout.
|
|
12
|
+
*
|
|
13
|
+
* Probity runs on Node: `npm install -D @nizos/probity` (a
|
|
14
|
+
* one-dependency package.json with `"type": "module"` next to the
|
|
15
|
+
* xcworkspace is fine).
|
|
16
|
+
*/
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { enforceTdd, forbidContentPattern } from '@nizos/probity';
|
|
19
|
+
import { enforceAcceptanceLanguage, enforceControlledPreconditions, } from '../rules/acceptance-language.js';
|
|
20
|
+
import { enforceCharacterizationResolution, enforceProbeReversion, requireGreenTestRun, withCharacterizationTest, withInverseScenarioGuidance, withMutationProbe, withTelemetryFastPath, } from '../rules/kotlin.js';
|
|
21
|
+
import { enforceAdapterObservability, enforcePortsBoundary, } from '../rules/ports-and-adapters.js';
|
|
22
|
+
import { enforceSpecTestParity, requireSpecBackedAcceptanceTest, surfaceScenarioLinkBreakage, } from '../rules/spec-test-parity.js';
|
|
23
|
+
import { SWIFT_FIXED_SLEEPS, SWIFT_PROBE_FILE_PATTERN, XCODEBUILD_TEST_COMMAND, XCODEBUILD_TEST_FAILED, XCODEBUILD_TEST_SUCCEEDED, XCUITEST_MECHANICS, } from '../rules/swift.js';
|
|
24
|
+
import { surfaceGlossaryTermBreakage } from '../rules/ubiquitous-language.js';
|
|
25
|
+
// Acceptance test files live under AcceptanceTests/ (capital A) — the
|
|
26
|
+
// parity scanners' default pattern expects a lowercase `acceptance/`
|
|
27
|
+
// segment, so every spec-traceability rule below overrides it.
|
|
28
|
+
const ACCEPTANCE_TEST_FILES = /AcceptanceTests[/\\]/;
|
|
29
|
+
// Swift telemetry lines the fast-path recognizes as complete
|
|
30
|
+
// single-line instrumentation (SudoLogging-style logger calls).
|
|
31
|
+
const SWIFT_TELEMETRY_LINES = [
|
|
32
|
+
/^[\w.]*logger\.(?:event|info|debug|error|warning)\(.*\)$/i,
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Rule entries as a factory over the project root, mirroring the KMP
|
|
36
|
+
* preset: Probity reads the default export; tooling (scope reports,
|
|
37
|
+
* workflow evals) can instantiate the same blocks against another
|
|
38
|
+
* root. Relative globs are anchored by Probity's loadConfig against
|
|
39
|
+
* this file's directory.
|
|
40
|
+
*/
|
|
41
|
+
export function swiftRuleEntries(root) {
|
|
42
|
+
const glossary = join(root, 'docs/GLOSSARY.md');
|
|
43
|
+
// Rule ordering principle: Probity stops at the first violation, so
|
|
44
|
+
// every deterministic screen (pattern match, free, instant) is
|
|
45
|
+
// listed before any AI-validated rule (a model call per matching
|
|
46
|
+
// write).
|
|
47
|
+
return [
|
|
48
|
+
// ── Deterministic wall ───────────────────────────────────────────
|
|
49
|
+
// No fixed waits anywhere in the acceptance suite: synchronize
|
|
50
|
+
// with XCTest expectations, predicates, or
|
|
51
|
+
// waitForExistence(timeout:) — never sleep.
|
|
52
|
+
{
|
|
53
|
+
files: ['AcceptanceTests/**'],
|
|
54
|
+
rules: [
|
|
55
|
+
forbidContentPattern({
|
|
56
|
+
match: SWIFT_FIXED_SLEEPS,
|
|
57
|
+
reason: 'Fixed waits are races: XCUITest flakiness starts here. ' +
|
|
58
|
+
'Wait for the concluding observable state with XCTest ' +
|
|
59
|
+
'expectations, predicate expectations, or ' +
|
|
60
|
+
'waitForExistence(timeout:) (see the acceptance-testing ' +
|
|
61
|
+
'and ios-development skills).',
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
// XCUITest mechanics belong only in the driver that owns the
|
|
66
|
+
// deployed-app scope. Specs, scenario bodies, the DSL, and the
|
|
67
|
+
// app-hosted component tests must stay implementation-neutral.
|
|
68
|
+
{
|
|
69
|
+
files: [
|
|
70
|
+
'AcceptanceTests/Specs/**',
|
|
71
|
+
'AcceptanceTests/Scenarios/**',
|
|
72
|
+
'AcceptanceTests/DSL/**',
|
|
73
|
+
'AcceptanceTests/Component/**',
|
|
74
|
+
],
|
|
75
|
+
rules: [
|
|
76
|
+
forbidContentPattern({
|
|
77
|
+
match: XCUITEST_MECHANICS,
|
|
78
|
+
reason: 'XCUIApplication/XCUIElement outside AcceptanceTests/Drivers: ' +
|
|
79
|
+
'only protocol drivers know how to reach the SUT. Move the ' +
|
|
80
|
+
'mechanics into the XCUITest driver and express this layer ' +
|
|
81
|
+
'in domain language (four-layer model, acceptance-testing ' +
|
|
82
|
+
'skill).',
|
|
83
|
+
}),
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
// Spec-first, at write time: adding a new acceptance test case
|
|
87
|
+
// (func test…) requires a Covers: tag resolving to a
|
|
88
|
+
// `## Scenario:` heading that already exists in docs/specs — the
|
|
89
|
+
// feature file is written before the test that claims it. A file
|
|
90
|
+
// whose existing tag already resolves may gain further driver
|
|
91
|
+
// tests for the same scenario (one scenario, many drivers).
|
|
92
|
+
// Scoped to the whole suite directory, not *Tests.swift: XCTest
|
|
93
|
+
// only discovers `func test…` methods, so the declaration pattern
|
|
94
|
+
// is the real detector and a creative filename can't dodge the
|
|
95
|
+
// rule. NOTE the directory itself is the boundary — acceptance
|
|
96
|
+
// tests added outside it (e.g. a new App/Tests/ target) are
|
|
97
|
+
// invisible to every rule here until the globs learn the path.
|
|
98
|
+
{
|
|
99
|
+
files: ['AcceptanceTests/**'],
|
|
100
|
+
rules: [
|
|
101
|
+
requireSpecBackedAcceptanceTest({ specsDir: join(root, 'docs/specs') }),
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
// Editing a spec must not silently break the tests that claim its
|
|
105
|
+
// scenarios: removing or renaming a covered `## Scenario:` heading
|
|
106
|
+
// blocks with the list of affected tests.
|
|
107
|
+
{
|
|
108
|
+
files: ['docs/specs/**/*.feature.md'],
|
|
109
|
+
rules: [
|
|
110
|
+
surfaceScenarioLinkBreakage({
|
|
111
|
+
testRoots: [root],
|
|
112
|
+
testFilePattern: ACCEPTANCE_TEST_FILES,
|
|
113
|
+
}),
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
// Ubiquitous-language drift: renaming or removing a glossary term
|
|
117
|
+
// that specs, tests, or code still use blocks the glossary edit
|
|
118
|
+
// with the list of users.
|
|
119
|
+
{
|
|
120
|
+
files: ['docs/GLOSSARY.md'],
|
|
121
|
+
rules: [surfaceGlossaryTermBreakage({ searchRoots: [root] })],
|
|
122
|
+
},
|
|
123
|
+
// ── AI-validated judgment layer ──────────────────────────────────
|
|
124
|
+
// Inner loop: test-driven-development over app sources and the
|
|
125
|
+
// acceptance suite. On a project whose only suite is the
|
|
126
|
+
// acceptance scheme, the observed red comes from
|
|
127
|
+
// `xcodebuild … test` — the outside-in loop, not an exemption.
|
|
128
|
+
// The mutation-probe wrapper lets a deliberate break marked
|
|
129
|
+
// `// probity: mutation-probe` through without a red-before-green
|
|
130
|
+
// demand; enforceProbeReversion below holds the commit hostage
|
|
131
|
+
// until it is reverted. Telemetry-only additions pass
|
|
132
|
+
// deterministically so the observability rule and the TDD gate
|
|
133
|
+
// never contradict each other.
|
|
134
|
+
// App/*.swift catches root-level sources (AppDelegate.swift lives
|
|
135
|
+
// beside App/Sources, not inside it) — audit these globs against
|
|
136
|
+
// your tree with scripts/scope-report.ts; a write no rule matches
|
|
137
|
+
// is a silent free pass.
|
|
138
|
+
{
|
|
139
|
+
files: [
|
|
140
|
+
'App/*.swift',
|
|
141
|
+
'App/Sources/**',
|
|
142
|
+
'AcceptanceTests/**',
|
|
143
|
+
'VPNNetworkExtension/**',
|
|
144
|
+
],
|
|
145
|
+
rules: [
|
|
146
|
+
// The inverse-scenario wrapper changes only the DENY TEXT, and
|
|
147
|
+
// only on the test-control layer (the acceptance composition
|
|
148
|
+
// root): when a fixture has no red demanding it because the
|
|
149
|
+
// environment already satisfies the scenario's Given, the
|
|
150
|
+
// correct move — write the inverse scenario and let its red
|
|
151
|
+
// drive the fixture — is stated at the decision point instead
|
|
152
|
+
// of leaving "observe a red first" to read as "delete the
|
|
153
|
+
// control".
|
|
154
|
+
// The characterization wrapper sanctions the FIRST test for
|
|
155
|
+
// behavior that predates it (born green, so no red can be
|
|
156
|
+
// observed before it exists): a test-layer write carrying
|
|
157
|
+
// `// probity: characterization` passes, and the marker only
|
|
158
|
+
// comes off through a proof-checked removal (a recorded run
|
|
159
|
+
// where that test failed under a mutation probe). The
|
|
160
|
+
// resolution gate below blocks commits while a marker is on
|
|
161
|
+
// disk, so the bypass is a round-trip, not an exemption.
|
|
162
|
+
withInverseScenarioGuidance(withCharacterizationTest(withMutationProbe(withTelemetryFastPath(enforceTdd(), {
|
|
163
|
+
patterns: SWIFT_TELEMETRY_LINES,
|
|
164
|
+
filePattern: /\.swift$/,
|
|
165
|
+
})), { filePattern: /AcceptanceTests[/\\]/ }), { filePattern: /App[/\\]Sources[/\\]Acceptance[/\\]/ }),
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
// Preconditions are controlled, not observed: a driver method
|
|
169
|
+
// named for a Given must establish it (fixture key, launch
|
|
170
|
+
// environment, programmed fake), and control wiring is never
|
|
171
|
+
// deleted just because the scenario passes without it — on a
|
|
172
|
+
// brownfield system the environment produces the sad path for
|
|
173
|
+
// free, which is exactly when the seam matters most (the success
|
|
174
|
+
// path is unreachable until the port is controlled).
|
|
175
|
+
{
|
|
176
|
+
files: ['AcceptanceTests/Drivers/**', 'App/Sources/Acceptance/**'],
|
|
177
|
+
rules: [enforceControlledPreconditions()],
|
|
178
|
+
},
|
|
179
|
+
// Boundaries: ports-and-adapters judgments the screens can't make
|
|
180
|
+
// — thin adapters, vendor types in port signatures, names
|
|
181
|
+
// conflicting with the glossary. Point at wherever your ports and
|
|
182
|
+
// core behavior live (here: module view-models/use-cases and the
|
|
183
|
+
// application-owned provider ports).
|
|
184
|
+
{
|
|
185
|
+
files: ['App/Sources/Modules/**', 'App/Sources/Utilities/Providers/**'],
|
|
186
|
+
rules: [enforcePortsBoundary({ glossaryPath: glossary })],
|
|
187
|
+
},
|
|
188
|
+
// Adapters must be thin, but not blind: a new adapter path doing
|
|
189
|
+
// external I/O carries boundary observability (structured event,
|
|
190
|
+
// port tap, or span). Delta-based — legacy paths migrate
|
|
191
|
+
// incrementally.
|
|
192
|
+
{
|
|
193
|
+
files: [
|
|
194
|
+
'App/Sources/**/Adapters/**',
|
|
195
|
+
'App/Sources/**/Services/**',
|
|
196
|
+
'App/Sources/**/Analytics/**',
|
|
197
|
+
],
|
|
198
|
+
rules: [
|
|
199
|
+
withTelemetryFastPath(enforceAdapterObservability({
|
|
200
|
+
conventionHint: 'This codebase uses SudoLogging (a Logger built by the ' +
|
|
201
|
+
'MySudoVpn logger factories — one structured, greppable ' +
|
|
202
|
+
'line per boundary call/outcome), and/or a recording ' +
|
|
203
|
+
'port-tap decorator wired where the app composes its ' +
|
|
204
|
+
'dependencies.',
|
|
205
|
+
}), { patterns: SWIFT_TELEMETRY_LINES, filePattern: /\.swift$/ }),
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
// Outer loop: acceptance-testing. The Language Test on the spec
|
|
209
|
+
// layer: Markdown specs, test-case glue, scenario bodies, and the
|
|
210
|
+
// DSL — everything above the drivers must read as pure domain
|
|
211
|
+
// language. Drivers are layer 3 (they know accessibility
|
|
212
|
+
// identifiers, hosted views, view-model ports) — excluded.
|
|
213
|
+
{
|
|
214
|
+
files: [
|
|
215
|
+
'docs/specs/**/*.feature.md',
|
|
216
|
+
'AcceptanceTests/Specs/**',
|
|
217
|
+
'AcceptanceTests/Component/**',
|
|
218
|
+
'AcceptanceTests/Scenarios/**',
|
|
219
|
+
'AcceptanceTests/DSL/**',
|
|
220
|
+
],
|
|
221
|
+
rules: [enforceAcceptanceLanguage({ glossaryPath: glossary })],
|
|
222
|
+
},
|
|
223
|
+
// ── Ship gates ───────────────────────────────────────────────────
|
|
224
|
+
// Definition of done, made mechanical: every non-wip scenario in
|
|
225
|
+
// docs/specs is claimed by an acceptance test (Covers: tag), and
|
|
226
|
+
// every tag resolves to a real scenario. Brownfield adoption:
|
|
227
|
+
// generate a baseline once with scripts/spec-parity.mjs
|
|
228
|
+
// --write-baseline and burn it down (see hooks/PROBITY.md).
|
|
229
|
+
// Per-scenario driver mapping (optional): declare named driver
|
|
230
|
+
// scopes and tag scenarios that need more than the default suite —
|
|
231
|
+
// `## Scenario [system]: …` then requires a covering test whose
|
|
232
|
+
// path matches that scope. Tags are floors, not ceilings; with a
|
|
233
|
+
// shared scenario layer (AcceptanceTests/Scenarios/) the extra
|
|
234
|
+
// covering test is a thin spec class calling the existing body.
|
|
235
|
+
// The example scopes match the calibration app's layout — the
|
|
236
|
+
// XCUITest target under Specs/, the app-hosted component target
|
|
237
|
+
// under Component/. CALIBRATE TO YOUR LAYOUT before uncommenting.
|
|
238
|
+
enforceSpecTestParity({
|
|
239
|
+
specsDir: join(root, 'docs/specs'),
|
|
240
|
+
testRoots: [root],
|
|
241
|
+
testFilePattern: ACCEPTANCE_TEST_FILES,
|
|
242
|
+
baselinePath: join(root, 'docs/specs/.parity-baseline'),
|
|
243
|
+
// driverScopes: [
|
|
244
|
+
// { name: 'system', filePattern: /AcceptanceTests[/\\]Specs[/\\]/ },
|
|
245
|
+
// { name: 'hosted-ui', filePattern: /AcceptanceTests[/\\]Component[/\\]/ },
|
|
246
|
+
// ],
|
|
247
|
+
// defaultScopes: ['hosted-ui'],
|
|
248
|
+
}),
|
|
249
|
+
// The commit half of the mutation-probe round-trip: no commit
|
|
250
|
+
// while a `probity: mutation-probe` marker is still on disk.
|
|
251
|
+
enforceProbeReversion({
|
|
252
|
+
roots: [root],
|
|
253
|
+
filePattern: SWIFT_PROBE_FILE_PATTERN,
|
|
254
|
+
}),
|
|
255
|
+
// The commit half of the characterization round-trip: no commit
|
|
256
|
+
// while a `probity: characterization` marker is still on disk —
|
|
257
|
+
// the marker only comes off once the transcript records the
|
|
258
|
+
// marked test failing under a mutation probe.
|
|
259
|
+
enforceCharacterizationResolution({
|
|
260
|
+
roots: [root],
|
|
261
|
+
filePattern: SWIFT_PROBE_FILE_PATTERN,
|
|
262
|
+
}),
|
|
263
|
+
// No commit on an unverified tree. Matches `xcodebuild … test`
|
|
264
|
+
// and xcresulttool summary readbacks; the recorded output must
|
|
265
|
+
// actually be green (** TEST SUCCEEDED ** or "result": "Passed"),
|
|
266
|
+
// not merely exist. CALIBRATE AGAINST YOUR REAL COMMAND: a
|
|
267
|
+
// `-quiet` run suppresses the verdict banner entirely (verified
|
|
268
|
+
// live), so a runbook that mandates -quiet must also mandate the
|
|
269
|
+
// `xcrun xcresulttool get test-results summary` readback — that
|
|
270
|
+
// readback is the output this gate accepts. Run your documented
|
|
271
|
+
// test command once and confirm successPattern matches what it
|
|
272
|
+
// actually prints before trusting the gate.
|
|
273
|
+
// Scope the commit-on-green gate to code the acceptance suite
|
|
274
|
+
// actually validates; infra/docs/tooling-only commits (CI config,
|
|
275
|
+
// lint config, Markdown, probity.config.ts, rules/**) aren't gated
|
|
276
|
+
// on a fresh xcodebuild run. Adjust the paths to your layout.
|
|
277
|
+
requireGreenTestRun({
|
|
278
|
+
command: XCODEBUILD_TEST_COMMAND,
|
|
279
|
+
successPattern: XCODEBUILD_TEST_SUCCEEDED,
|
|
280
|
+
failurePattern: XCODEBUILD_TEST_FAILED,
|
|
281
|
+
enforceForPaths: /^(?:App|AcceptanceTests|VPNNetworkExtension)\//,
|
|
282
|
+
}),
|
|
283
|
+
];
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=swift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swift.js","sourceRoot":"","sources":["../../presets/swift.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Rule } from '@nizos/probity';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps `enforceAcceptanceLanguage` so the most common spec-layer
|
|
4
|
+
* write — a Kotlin test file gaining exactly one new `@Test` whose
|
|
5
|
+
* added lines only call vocabulary that already exists in the
|
|
6
|
+
* suite's DSL/Robot/driver files — passes deterministically, with no
|
|
7
|
+
* model call. Rationale: when two rule scopes overlap (the TDD rule's
|
|
8
|
+
* Kotlin fast-path plus this rule on `acceptance/**`), a single-test
|
|
9
|
+
* write otherwise still pays one AI call despite being advertised as
|
|
10
|
+
* free.
|
|
11
|
+
*
|
|
12
|
+
* The fast-path is conservative on three axes; failing any one falls
|
|
13
|
+
* through to the wrapped AI rule (it can only skip work, never
|
|
14
|
+
* block):
|
|
15
|
+
* 1. the write must add exactly one `@Test`;
|
|
16
|
+
* 2. no added line may contain mechanism vocabulary
|
|
17
|
+
* (backend/server/database/url/click/... — the deterministic
|
|
18
|
+
* screen errs broad);
|
|
19
|
+
* 3. every identifier the added lines call must already be declared
|
|
20
|
+
* in this file or a sibling `.kt` file in the same directory —
|
|
21
|
+
* a brand-new DSL step name is exactly what the validator should
|
|
22
|
+
* judge.
|
|
23
|
+
*
|
|
24
|
+
* Markdown/Gherkin spec writes never fast-path — prose is where the
|
|
25
|
+
* Language Test earns its keep.
|
|
26
|
+
*/
|
|
27
|
+
export declare function withAcceptanceLanguageFastPath(rule: Rule): Rule;
|
|
28
|
+
/**
|
|
29
|
+
* AI-validated enforcement of the `acceptance-testing` skill's
|
|
30
|
+
* Language Test: executable specifications stay in domain language —
|
|
31
|
+
* no UI mechanics, protocols, or persistence details — assert a
|
|
32
|
+
* single outcome, and (when a glossary is supplied) use the
|
|
33
|
+
* ubiquitous language verbatim.
|
|
34
|
+
*
|
|
35
|
+
* Applies to: write actions. Scope it with a `{ files, rules }` block
|
|
36
|
+
* to the spec layer only (e.g. `specs/**`, `**\/*.feature`) — the DSL
|
|
37
|
+
* and protocol-driver layers are supposed to contain the mechanics
|
|
38
|
+
* this rule blocks, and every matching write costs an AI call.
|
|
39
|
+
*
|
|
40
|
+
* @param options.glossaryPath — absolute path to the project's
|
|
41
|
+
* ubiquitous-language glossary (see the `ubiquitous-language`
|
|
42
|
+
* skill). Resolve it in the config file, e.g.
|
|
43
|
+
* `fileURLToPath(new URL('./docs/GLOSSARY.md', import.meta.url))`.
|
|
44
|
+
* When set and readable, the glossary is included in the
|
|
45
|
+
* validator's prompt and vocabulary conflicts become violations.
|
|
46
|
+
* @param options.requireGlossaryEntry — strict vocabulary mode: when
|
|
47
|
+
* true (and a glossary is supplied and readable), a domain concept
|
|
48
|
+
* in spec content with no glossary entry becomes a violation —
|
|
49
|
+
* "the glossary conversation happens first" (ubiquitous-language
|
|
50
|
+
* skill). Default false: only conflicts with existing entries
|
|
51
|
+
* violate, so an empty or young glossary doesn't block everything.
|
|
52
|
+
* @param options.instructions — overrides or extends the default
|
|
53
|
+
* language rules text. Pass a string to replace it, or a function
|
|
54
|
+
* `(defaults) => ...` to extend it.
|
|
55
|
+
* @param options.maxGlossaryChars — truncate the glossary beyond this
|
|
56
|
+
* length when building the prompt (default 8000).
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* { files: ['specs/**', 'acceptance/**', '**\/*.feature'], rules: [enforceAcceptanceLanguage()] }
|
|
60
|
+
*/
|
|
61
|
+
export declare function enforceAcceptanceLanguage(options?: {
|
|
62
|
+
glossaryPath?: string;
|
|
63
|
+
requireGlossaryEntry?: boolean;
|
|
64
|
+
instructions?: string | ((defaults: string) => string);
|
|
65
|
+
maxGlossaryChars?: number;
|
|
66
|
+
}): Rule;
|
|
67
|
+
/**
|
|
68
|
+
* AI-validated enforcement of the `acceptance-testing` skill's
|
|
69
|
+
* controlled-precondition principle: a scenario's Given is established
|
|
70
|
+
* by the test (fixture, programmed stub, substituted port), never
|
|
71
|
+
* inherited from the ambient environment. Catches the two moves that
|
|
72
|
+
* silently hand a Given to the environment:
|
|
73
|
+
*
|
|
74
|
+
* 1. a driver method whose name states a precondition but whose body
|
|
75
|
+
* sets no fixture/stub/launch state (the no-op precondition
|
|
76
|
+
* driver), and
|
|
77
|
+
* 2. removing control fixtures because the scenario passes without
|
|
78
|
+
* them — the brownfield trap where the environment produces the
|
|
79
|
+
* sad path for free, and the deleted fixture leaves the success
|
|
80
|
+
* path unspecifiable.
|
|
81
|
+
*
|
|
82
|
+
* Applies to: write actions. Scope it with a `{ files, rules }` block
|
|
83
|
+
* to the driver and test-control layers (e.g.
|
|
84
|
+
* `AcceptanceTests/Drivers/**` plus the acceptance composition root) —
|
|
85
|
+
* the opposite scoping from `enforceAcceptanceLanguage`, which
|
|
86
|
+
* excludes those layers. Every matching write costs an AI call.
|
|
87
|
+
*
|
|
88
|
+
* @param options.instructions — overrides or extends the default
|
|
89
|
+
* precondition rules text. Pass a string to replace it, or a
|
|
90
|
+
* function `(defaults) => ...` to extend it.
|
|
91
|
+
*/
|
|
92
|
+
export declare function enforceControlledPreconditions(options?: {
|
|
93
|
+
instructions?: string | ((defaults: string) => string);
|
|
94
|
+
}): Rule;
|
|
95
|
+
//# sourceMappingURL=acceptance-language.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acceptance-language.d.ts","sourceRoot":"","sources":["../../rules/acceptance-language.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAU,IAAI,EAA2B,MAAM,gBAAgB,CAAA;AA8Q3E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAmD/D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACrB,GACL,IAAI,CAyCN;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;CAClD,GACL,IAAI,CAiCN"}
|