@turri-dev/cli 0.1.27 → 0.2.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/README.md +6 -6
- package/dist/advisor-chat.d.ts +63 -0
- package/dist/advisor-chat.js +290 -0
- package/dist/background-skills.d.ts +53 -0
- package/dist/background-skills.js +121 -0
- package/dist/bedrock-region.d.ts +66 -0
- package/dist/bedrock-region.js +144 -0
- package/dist/btw.d.ts +1 -1
- package/dist/btw.js +1 -1
- package/dist/change-plain-cli.js +20 -13
- package/dist/chat-idle.d.ts +7 -0
- package/dist/chat-idle.js +20 -0
- package/dist/chats.d.ts +10 -0
- package/dist/chats.js +23 -0
- package/dist/cli-options.d.ts +1 -2
- package/dist/cli-options.js +2 -7
- package/dist/cli.js +11 -3
- package/dist/config-command.d.ts +14 -0
- package/dist/config-command.js +35 -15
- package/dist/config-edit.d.ts +3 -3
- package/dist/config-edit.js +16 -12
- package/dist/config.d.ts +54 -34
- package/dist/config.js +40 -44
- package/dist/conform-cli.js +12 -10
- package/dist/conformance-switch.d.ts +34 -0
- package/dist/conformance-switch.js +108 -0
- package/dist/cultivate-cli.js +15 -5
- package/dist/drift-cli.js +2 -0
- package/dist/extension.d.ts +15 -0
- package/dist/extension.js +260 -83
- package/dist/host-session.d.ts +26 -0
- package/dist/host-session.js +30 -2
- package/dist/host.d.ts +5 -2
- package/dist/host.js +7 -1
- package/dist/init-config.d.ts +1 -1
- package/dist/init-config.js +20 -11
- package/dist/loop-cli.d.ts +3 -3
- package/dist/loop-cli.js +13 -16
- package/dist/loop-tools.js +18 -10
- package/dist/model-check.d.ts +8 -3
- package/dist/model-check.js +13 -8
- package/dist/new-project.js +12 -8
- package/dist/{validate-cli.js → parse-cli.js} +11 -5
- package/dist/parse-tool.d.ts +9 -0
- package/dist/parse-tool.js +33 -0
- package/dist/pi-launch.js +1 -1
- package/dist/plain/check.d.ts +53 -6
- package/dist/plain/check.js +217 -24
- package/dist/plain/concepts.d.ts +8 -0
- package/dist/plain/concepts.js +66 -23
- package/dist/plain/cycles.d.ts +21 -0
- package/dist/plain/cycles.js +190 -0
- package/dist/plain/errors.d.ts +4 -0
- package/dist/plain/errors.js +7 -0
- package/dist/plain/files.d.ts +18 -1
- package/dist/plain/files.js +39 -2
- package/dist/plain/graph.d.ts +33 -4
- package/dist/plain/graph.js +60 -12
- package/dist/plain/highlight.d.ts +3 -2
- package/dist/plain/highlight.js +4 -3
- package/dist/plain/index.d.ts +8 -5
- package/dist/plain/index.js +8 -5
- package/dist/plain/liquid.js +1 -6
- package/dist/plain/markdown.d.ts +32 -3
- package/dist/plain/markdown.js +146 -24
- package/dist/plain/module-concepts.js +2 -2
- package/dist/plain/parser.d.ts +83 -28
- package/dist/plain/parser.js +381 -260
- package/dist/plain/plain.tmLanguage.json +29 -3
- package/dist/plain/project.d.ts +48 -0
- package/dist/plain/project.js +155 -0
- package/dist/plain/spec.d.ts +90 -28
- package/dist/plain/spec.js +107 -133
- package/dist/plain/test-plain-file.d.ts +59 -0
- package/dist/plain/test-plain-file.js +232 -0
- package/dist/regenerate/activity.d.ts +20 -9
- package/dist/regenerate/activity.js +11 -4
- package/dist/regenerate/advisor-records.d.ts +93 -0
- package/dist/regenerate/advisor-records.js +110 -0
- package/dist/regenerate/advisor-runs.d.ts +87 -0
- package/dist/regenerate/advisor-runs.js +327 -0
- package/dist/regenerate/base-folder.d.ts +68 -0
- package/dist/regenerate/base-folder.js +168 -0
- package/dist/regenerate/cases.d.ts +9 -6
- package/dist/regenerate/cases.js +70 -14
- package/dist/regenerate/change-view.d.ts +8 -3
- package/dist/regenerate/change-view.js +71 -22
- package/dist/regenerate/chunk-plan.d.ts +36 -15
- package/dist/regenerate/chunk-plan.js +165 -59
- package/dist/regenerate/conform.d.ts +3 -0
- package/dist/regenerate/conform.js +6 -5
- package/dist/regenerate/conformance-agents.d.ts +18 -39
- package/dist/regenerate/conformance-agents.js +60 -88
- package/dist/regenerate/conformance.d.ts +23 -13
- package/dist/regenerate/conformance.js +119 -82
- package/dist/regenerate/diff-view.d.ts +1 -1
- package/dist/regenerate/diff-view.js +20 -4
- package/dist/regenerate/drift.d.ts +47 -15
- package/dist/regenerate/drift.js +101 -30
- package/dist/regenerate/jobs.d.ts +2 -2
- package/dist/regenerate/jobs.js +1 -1
- package/dist/regenerate/ledger.d.ts +19 -0
- package/dist/regenerate/loop.d.ts +62 -43
- package/dist/regenerate/loop.js +472 -341
- package/dist/regenerate/paths.d.ts +4 -0
- package/dist/regenerate/paths.js +7 -1
- package/dist/regenerate/plain-palette.js +2 -0
- package/dist/regenerate/prompt.d.ts +57 -19
- package/dist/regenerate/prompt.js +157 -122
- package/dist/regenerate/records.d.ts +43 -12
- package/dist/regenerate/records.js +102 -88
- package/dist/regenerate/regenerate.js +29 -11
- package/dist/regenerate/regression.d.ts +4 -4
- package/dist/regenerate/regression.js +14 -10
- package/dist/regenerate/renames.d.ts +42 -0
- package/dist/regenerate/renames.js +185 -0
- package/dist/regenerate/requires.d.ts +1 -1
- package/dist/regenerate/requires.js +10 -3
- package/dist/regenerate/resources.d.ts +5 -3
- package/dist/regenerate/resources.js +4 -4
- package/dist/regenerate/scripts-being-written.d.ts +19 -0
- package/dist/regenerate/scripts-being-written.js +30 -0
- package/dist/regenerate/spec-map-view.d.ts +2 -2
- package/dist/regenerate/spec-map-view.js +46 -15
- package/dist/regenerate/spec-map.d.ts +27 -5
- package/dist/regenerate/spec-map.js +92 -60
- package/dist/regenerate/spec-steps.d.ts +48 -5
- package/dist/regenerate/spec-steps.js +213 -28
- package/dist/regenerate/state.d.ts +4 -3
- package/dist/regenerate/stop-report.d.ts +1 -1
- package/dist/regenerate/stop-report.js +10 -10
- package/dist/regenerate/test-advisor.d.ts +101 -0
- package/dist/regenerate/test-advisor.js +379 -0
- package/dist/regenerate/test-kinds.d.ts +71 -28
- package/dist/regenerate/test-kinds.js +110 -63
- package/dist/regenerate/test-scripts.d.ts +2 -0
- package/dist/regenerate/test-scripts.js +4 -0
- package/dist/regenerate/testing-steps.d.ts +41 -0
- package/dist/regenerate/testing-steps.js +110 -0
- package/dist/regenerate/testing.d.ts +1 -1
- package/dist/regenerate/testing.js +5 -4
- package/dist/regenerate/unit-review.d.ts +1 -1
- package/dist/regenerate/unit-review.js +3 -3
- package/dist/regenerate-cli.js +11 -12
- package/dist/release-agent.js +4 -0
- package/dist/release-cli.js +2 -0
- package/dist/release-tools.js +2 -0
- package/dist/run-report.d.ts +2 -2
- package/dist/run-report.js +4 -3
- package/dist/self-invocation.d.ts +1 -1
- package/dist/self-invocation.js +1 -1
- package/dist/sentinel.js +1 -1
- package/dist/setup.d.ts +10 -0
- package/dist/setup.js +52 -4
- package/dist/skill.d.ts +10 -0
- package/dist/skill.js +6 -0
- package/dist/skills.js +3 -1
- package/dist/spec-tools.js +21 -31
- package/dist/test-plain-files.d.ts +41 -0
- package/dist/test-plain-files.js +177 -0
- package/docs/README.md +3 -1
- package/docs/authoring-plain.md +46 -13
- package/docs/chats.md +3 -1
- package/docs/config.md +20 -15
- package/docs/deploy.md +1 -1
- package/docs/development.md +2 -1
- package/docs/headless.md +15 -18
- package/docs/hosting.md +28 -12
- package/docs/installation.md +1 -0
- package/docs/parser.md +88 -27
- package/docs/regenerating.md +87 -52
- package/docs/sentinel.md +1 -1
- package/docs/skills-and-gates.md +3 -1
- package/docs/test-advisor.md +110 -0
- package/docs/theming.md +1 -1
- package/docs/using.md +17 -15
- package/genome/rules/bullet-continuation.md +8 -8
- package/genome/rules/concise-specs-examples.md +111 -80
- package/genome/rules/concise-specs-integration-examples.md +56 -41
- package/genome/rules/concise-specs.md +17 -16
- package/genome/rules/definitions.md +5 -5
- package/genome/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +1 -1
- package/genome/rules/func-specs.md +69 -74
- package/genome/rules/impl-reqs.md +4 -4
- package/genome/rules/integration-embedded-testing.md +20 -20
- package/genome/rules/integration-embedded.md +31 -31
- package/genome/rules/integration-standalone.md +9 -9
- package/genome/rules/integrations.md +18 -18
- package/genome/rules/linked-resources.md +19 -16
- package/genome/rules/module-structure.md +21 -14
- package/genome/rules/project-model.md +18 -17
- package/genome/rules/required-concepts.md +1 -1
- package/genome/rules/requires-modules.md +6 -5
- package/genome/rules/semantic-ids.md +49 -0
- package/genome/rules/stdlib.md +4 -5
- package/genome/rules/test-plain-files.md +130 -0
- package/genome/rules/test-reqs.md +8 -8
- package/genome/rules/turri-regeneration.md +23 -8
- package/genome/skills/analyze-2-func-specs/SKILL.md +36 -21
- package/genome/skills/analyze-func-specs/SKILL.md +46 -32
- package/genome/skills/break-down-func-spec/SKILL.md +91 -67
- package/genome/skills/consolidate-concepts/SKILL.md +10 -10
- package/genome/skills/convert-old-syntax/SKILL.md +68 -0
- package/genome/skills/convert-old-syntax/references/rubric.md +11 -0
- package/genome/skills/debug-specs/SKILL.md +62 -56
- package/genome/skills/implement-conformance-testing-script/SKILL.md +19 -15
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +2 -2
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +2 -2
- package/genome/skills/implement-unit-testing-script/SKILL.md +14 -10
- package/genome/skills/refactor-module/SKILL.md +35 -35
- package/genome/skills/resolve-section-ownership/SKILL.md +9 -9
- package/genome/skills/resolve-spec-conflict/SKILL.md +35 -29
- package/package.json +7 -6
- package/dist/regenerate/choose-conformance.d.ts +0 -32
- package/dist/regenerate/choose-conformance.js +0 -117
- /package/dist/{validate-cli.d.ts → parse-cli.d.ts} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Worked BAD to GOOD examples of concise
|
|
2
|
+
description: Worked BAD to GOOD examples of concise ***plain, one pair per conciseness failure mode
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
# Worked examples of concise
|
|
5
|
+
# Worked examples of concise ***plain
|
|
6
6
|
|
|
7
7
|
`concise-specs.md` is the rule; this file is the example bank behind it. Every entry names one failure mode, shows the padded version, and shows the rewrite that carries the same information.
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ Read the pairs, not the prose: in each one, the GOOD block says everything the B
|
|
|
10
10
|
|
|
11
11
|
The BAD blocks intentionally demonstrate the mistakes they name, including invalid syntax. Copy only from GOOD blocks.
|
|
12
12
|
|
|
13
|
-
Integration
|
|
13
|
+
Integration ***plain fails in its own ways — over-precision, inlined contract data, and edge cases the provider's documentation invites. Those examples are in `concise-specs-integration-examples.md`.
|
|
14
14
|
|
|
15
15
|
## Padding around one fact
|
|
16
16
|
|
|
@@ -19,11 +19,14 @@ BAD:
|
|
|
19
19
|
```plain
|
|
20
20
|
***functional specs***
|
|
21
21
|
|
|
22
|
-
-
|
|
22
|
+
# create-task
|
|
23
|
+
In order to provide users with a robust and seamless experience, :TaskService: exposes a comprehensive endpoint for the creation of new :Task: items.
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
# persist-task
|
|
26
|
+
The endpoint accepts a request and, provided that the incoming payload is valid and well-formed, proceeds to persist the :Task: appropriately.
|
|
25
27
|
|
|
26
|
-
-
|
|
28
|
+
# reject-invalid-payload
|
|
29
|
+
If the payload is not valid, an appropriate error is returned to the caller so that the user is informed of the problem.
|
|
27
30
|
```
|
|
28
31
|
|
|
29
32
|
GOOD:
|
|
@@ -31,13 +34,14 @@ GOOD:
|
|
|
31
34
|
```plain
|
|
32
35
|
***functional specs***
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
# create-task
|
|
38
|
+
A :User: can create a :Task: via `POST /tasks`.
|
|
39
|
+
- The request body is a :Task: without `id`.
|
|
40
|
+
- The response is `201` with the created :Task:.
|
|
41
|
+
- A request without a name returns `400`.
|
|
38
42
|
```
|
|
39
43
|
|
|
40
|
-
Three
|
|
44
|
+
Three functional specs became one, and the rewrite names the method, the path, the request shape, and both status codes — none of which the padded version pinned down.
|
|
41
45
|
|
|
42
46
|
## Rationale, motivation, and benefits
|
|
43
47
|
|
|
@@ -46,7 +50,8 @@ BAD:
|
|
|
46
50
|
```plain
|
|
47
51
|
***functional specs***
|
|
48
52
|
|
|
49
|
-
-
|
|
53
|
+
# search-customers
|
|
54
|
+
Because support staff frequently need to find customers quickly, and because scanning the full list is slow and error-prone, :CustomerDirectory: offers a search capability that greatly improves their daily workflow.
|
|
50
55
|
```
|
|
51
56
|
|
|
52
57
|
GOOD:
|
|
@@ -54,7 +59,8 @@ GOOD:
|
|
|
54
59
|
```plain
|
|
55
60
|
***functional specs***
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
# search-customers
|
|
63
|
+
:CustomerDirectory: returns :Customer: items whose name or email contains a search term, case-insensitively.
|
|
58
64
|
```
|
|
59
65
|
|
|
60
66
|
Why the feature exists changes nothing about what is built. What "search" matches does.
|
|
@@ -86,11 +92,12 @@ BAD:
|
|
|
86
92
|
```plain
|
|
87
93
|
***functional specs***
|
|
88
94
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
# list-orders
|
|
96
|
+
:OrderApi: returns a list of :Order: items over HTTP.
|
|
97
|
+
- The list contains :Order: items.
|
|
98
|
+
- If there are no :Order: items, the list is empty.
|
|
99
|
+
- Each :Order: has an `id`, which is unique.
|
|
100
|
+
- The caller receives the response after the request is sent.
|
|
94
101
|
```
|
|
95
102
|
|
|
96
103
|
GOOD:
|
|
@@ -98,8 +105,9 @@ GOOD:
|
|
|
98
105
|
```plain
|
|
99
106
|
***functional specs***
|
|
100
107
|
|
|
101
|
-
-
|
|
102
|
-
|
|
108
|
+
# list-orders
|
|
109
|
+
:OrderApi: returns the :Order: items of the authenticated :User: via `GET /orders`, newest first.
|
|
110
|
+
- The response is `200` with a JSON array, empty when the :User: has no :Order: items.
|
|
103
111
|
```
|
|
104
112
|
|
|
105
113
|
Every cut line was already implied: a list of :Order: items contains :Order: items, an identifier identifies, and a response follows a request. What survives is what a reader could *not* have guessed — the ownership filter, the sort order, and the response shape.
|
|
@@ -113,11 +121,14 @@ BAD:
|
|
|
113
121
|
```plain
|
|
114
122
|
***functional specs***
|
|
115
123
|
|
|
116
|
-
-
|
|
124
|
+
# validate-input
|
|
125
|
+
:OrderService: validates all incoming data before processing it.
|
|
117
126
|
|
|
118
|
-
-
|
|
127
|
+
# handle-errors
|
|
128
|
+
:OrderService: handles errors and returns meaningful error messages.
|
|
119
129
|
|
|
120
|
-
-
|
|
130
|
+
# save-order
|
|
131
|
+
:OrderService: saves the :Order: to storage so that it is not lost.
|
|
121
132
|
```
|
|
122
133
|
|
|
123
134
|
GOOD:
|
|
@@ -125,7 +136,8 @@ GOOD:
|
|
|
125
136
|
```plain
|
|
126
137
|
***functional specs***
|
|
127
138
|
|
|
128
|
-
-
|
|
139
|
+
# reject-wrong-total
|
|
140
|
+
:OrderService: rejects an :Order: whose `total` does not equal the sum of its :OrderLine: amounts, with `422`.
|
|
129
141
|
```
|
|
130
142
|
|
|
131
143
|
Validating input, reporting errors, and persisting on a save operation belong in the `***implementation reqs***` and a baseline is assumed. If there is a specific use case for a particular operation, it should be documented there, not in the `***functional specs***`. The one fact worth stating is *which* rule is enforced and what the caller sees when it fails.
|
|
@@ -208,7 +220,7 @@ GOOD:
|
|
|
208
220
|
- :ConformanceTests: mock every external HTTP call.
|
|
209
221
|
```
|
|
210
222
|
|
|
211
|
-
Every hedge hands the
|
|
223
|
+
Every hedge hands the coding agent a decision the ***plain was supposed to make. A functional spec states one behavior.
|
|
212
224
|
|
|
213
225
|
## Ornate vocabulary
|
|
214
226
|
|
|
@@ -217,7 +229,8 @@ BAD:
|
|
|
217
229
|
```plain
|
|
218
230
|
***functional specs***
|
|
219
231
|
|
|
220
|
-
-
|
|
232
|
+
# build-report
|
|
233
|
+
:ReportGenerator: is responsible for facilitating the utilization of a plurality of :DataSource: instances in order to initiate the generation of a :Report: prior to the performance of the validation of the resulting output.
|
|
221
234
|
```
|
|
222
235
|
|
|
223
236
|
GOOD:
|
|
@@ -225,22 +238,24 @@ GOOD:
|
|
|
225
238
|
```plain
|
|
226
239
|
***functional specs***
|
|
227
240
|
|
|
228
|
-
-
|
|
241
|
+
# build-report
|
|
242
|
+
:ReportGenerator: builds a :Report: from one or more :DataSource: items, then validates it.
|
|
229
243
|
```
|
|
230
244
|
|
|
231
245
|
Same sequence, plain words. See the substitution table in `concise-specs.md`.
|
|
232
246
|
|
|
233
|
-
##
|
|
247
|
+
## Detail that elaborates instead of disambiguating
|
|
234
248
|
|
|
235
249
|
BAD:
|
|
236
250
|
|
|
237
251
|
```plain
|
|
238
252
|
***functional specs***
|
|
239
253
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
254
|
+
# archive-project
|
|
255
|
+
A :User: can archive a :Project:.
|
|
256
|
+
- Archiving is an action the user performs on a project.
|
|
257
|
+
- The archive action is available to users who have access to the project.
|
|
258
|
+
- Once archived, the project is in the archived state.
|
|
244
259
|
```
|
|
245
260
|
|
|
246
261
|
GOOD:
|
|
@@ -248,23 +263,25 @@ GOOD:
|
|
|
248
263
|
```plain
|
|
249
264
|
***functional specs***
|
|
250
265
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
266
|
+
# archive-project
|
|
267
|
+
A :User: can archive a :Project: they own.
|
|
268
|
+
- An archived :Project: is excluded from :ProjectList:.
|
|
269
|
+
- Archiving a :Project: that is already archived returns `409`.
|
|
254
270
|
```
|
|
255
271
|
|
|
256
|
-
A
|
|
272
|
+
A detail bullet earns its line by removing an ambiguity. If it only says the statement line again in more words, it is filler.
|
|
257
273
|
|
|
258
|
-
## Restating the
|
|
274
|
+
## Restating the statement line
|
|
259
275
|
|
|
260
276
|
BAD:
|
|
261
277
|
|
|
262
278
|
```plain
|
|
263
279
|
***functional specs***
|
|
264
280
|
|
|
265
|
-
-
|
|
266
|
-
|
|
267
|
-
|
|
281
|
+
# expire-session
|
|
282
|
+
:SessionStore: expires a :Session: 30 minutes after its last use.
|
|
283
|
+
- Sessions do not live forever; they time out.
|
|
284
|
+
- The timeout is 30 minutes.
|
|
268
285
|
```
|
|
269
286
|
|
|
270
287
|
GOOD:
|
|
@@ -272,8 +289,9 @@ GOOD:
|
|
|
272
289
|
```plain
|
|
273
290
|
***functional specs***
|
|
274
291
|
|
|
275
|
-
-
|
|
276
|
-
|
|
292
|
+
# expire-session
|
|
293
|
+
:SessionStore: expires a :Session: 30 minutes after its last use.
|
|
294
|
+
- A request carrying an expired :Session: returns `401`.
|
|
277
295
|
```
|
|
278
296
|
|
|
279
297
|
## Synonym drift
|
|
@@ -283,23 +301,27 @@ BAD:
|
|
|
283
301
|
```plain
|
|
284
302
|
***functional specs***
|
|
285
303
|
|
|
286
|
-
|
|
304
|
+
# upload-document
|
|
305
|
+
A :User: can upload a :Document:.
|
|
287
306
|
|
|
288
|
-
-
|
|
307
|
+
# store-document
|
|
308
|
+
The system stores the submitted file in :DocumentStore:.
|
|
289
309
|
|
|
290
|
-
-
|
|
310
|
+
# retrieve-document
|
|
311
|
+
The uploaded asset is retrievable by its identifier.
|
|
291
312
|
```
|
|
292
313
|
|
|
293
|
-
Three names — `:Document:`, "file", "asset" — read as three things. A reader cannot tell whether "asset" is a fourth concept, and the
|
|
314
|
+
Three names — `:Document:`, "file", "asset" — read as three things. A reader cannot tell whether "asset" is a fourth concept, and the coding agent cannot either.
|
|
294
315
|
|
|
295
316
|
GOOD:
|
|
296
317
|
|
|
297
318
|
```plain
|
|
298
319
|
***functional specs***
|
|
299
320
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
321
|
+
# upload-document
|
|
322
|
+
A :User: can upload a :Document:.
|
|
323
|
+
- :DocumentStore: stores the :Document: and assigns it a :DocumentId:.
|
|
324
|
+
- A :User: can retrieve a :Document: by its :DocumentId:.
|
|
303
325
|
```
|
|
304
326
|
|
|
305
327
|
## Meta-commentary and scope disclaimers
|
|
@@ -309,13 +331,17 @@ BAD:
|
|
|
309
331
|
```plain
|
|
310
332
|
***functional specs***
|
|
311
333
|
|
|
312
|
-
-
|
|
334
|
+
# export-csv-size-note
|
|
335
|
+
This spec is intentionally kept small so that it stays within the complexity limit.
|
|
313
336
|
|
|
314
|
-
-
|
|
337
|
+
# export-csv-reference
|
|
338
|
+
As described above, :ExportService: writes a :CsvFile:.
|
|
315
339
|
|
|
316
|
-
-
|
|
340
|
+
# export-csv-no-changes
|
|
341
|
+
No changes are made to the existing :ImportService: or to the user interface.
|
|
317
342
|
|
|
318
|
-
-
|
|
343
|
+
# export-csv-future
|
|
344
|
+
Future specs will extend this behavior with additional formats.
|
|
319
345
|
```
|
|
320
346
|
|
|
321
347
|
GOOD:
|
|
@@ -323,12 +349,13 @@ GOOD:
|
|
|
323
349
|
```plain
|
|
324
350
|
***functional specs***
|
|
325
351
|
|
|
326
|
-
-
|
|
327
|
-
|
|
328
|
-
|
|
352
|
+
# export-csv
|
|
353
|
+
:ExportService: writes the :Order: items of a :BillingPeriod: to a :CsvFile:.
|
|
354
|
+
- The header row is `id,customer,total,currency`.
|
|
355
|
+
- An empty :BillingPeriod: produces a :CsvFile: with only the header row.
|
|
329
356
|
```
|
|
330
357
|
|
|
331
|
-
|
|
358
|
+
***plain describes the software, not itself. What a functional spec does *not* change is everything it does not mention, and what later functional specs will add belongs to them.
|
|
332
359
|
|
|
333
360
|
## Prose and tables inside a section
|
|
334
361
|
|
|
@@ -337,13 +364,14 @@ BAD:
|
|
|
337
364
|
```plain
|
|
338
365
|
***functional specs***
|
|
339
366
|
|
|
340
|
-
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
367
|
+
# apply-discounts
|
|
368
|
+
:PricingEngine: applies discounts.
|
|
369
|
+
The discount matrix is as follows:
|
|
370
|
+
| Tier | Volume | Discount |
|
|
371
|
+
|---|---|---|
|
|
372
|
+
| Bronze | 0-99 | 0% |
|
|
373
|
+
| Silver | 100-499 | 5% |
|
|
374
|
+
| Gold | 500+ | 12% |
|
|
347
375
|
```
|
|
348
376
|
|
|
349
377
|
GOOD:
|
|
@@ -351,15 +379,16 @@ GOOD:
|
|
|
351
379
|
```plain
|
|
352
380
|
***functional specs***
|
|
353
381
|
|
|
354
|
-
|
|
355
|
-
|
|
382
|
+
# apply-discounts
|
|
383
|
+
:PricingEngine: applies the :DiscountTier: matrix in [resources/discount-tiers.json](resources/discount-tiers.json) to an :Order: total.
|
|
384
|
+
- An :Order: below the lowest tier threshold gets no discount.
|
|
356
385
|
```
|
|
357
386
|
|
|
358
387
|
A section holds `- ` list items. Matrices, schemas, and payloads are external artifacts and are linked from `resources/` — see `linked-resources.md`.
|
|
359
388
|
|
|
360
389
|
## Split one crowded bullet across its owning sections
|
|
361
390
|
|
|
362
|
-
One bullet that mixes behavior, technology, and test policy is hard to review and partly ignored:
|
|
391
|
+
One bullet that mixes behavior, technology, and test policy is hard to review and partly ignored: turri reads each concept only from its owning section, so the unit-test and conformance-test facts below never reach the agents that need them.
|
|
363
392
|
|
|
364
393
|
BAD — everything in one place:
|
|
365
394
|
|
|
@@ -372,7 +401,8 @@ BAD — everything in one place:
|
|
|
372
401
|
|
|
373
402
|
***functional specs***
|
|
374
403
|
|
|
375
|
-
-
|
|
404
|
+
# resize-image
|
|
405
|
+
:ImageResizer:, implemented as a Python class using Pillow with a thread pool of 4 workers, resizes an uploaded :Image: to a thumbnail, where a thumbnail is defined as a JPEG at most 256 pixels on its longest edge.
|
|
376
406
|
```
|
|
377
407
|
|
|
378
408
|
GOOD — each fact in the section that owns it:
|
|
@@ -394,9 +424,10 @@ GOOD — each fact in the section that owns it:
|
|
|
394
424
|
|
|
395
425
|
***functional specs***
|
|
396
426
|
|
|
397
|
-
-
|
|
398
|
-
|
|
399
|
-
|
|
427
|
+
# resize-image
|
|
428
|
+
:ImageResizer: produces a :Thumbnail: from an :Image:.
|
|
429
|
+
- An :Image: already within the :Thumbnail: bounds is returned unchanged.
|
|
430
|
+
- An :Image: that is not a supported format raises :UnsupportedImageError:.
|
|
400
431
|
```
|
|
401
432
|
|
|
402
433
|
Each section now answers one question, and the definition, the mechanism, and the behavior are each one line to check instead of clauses buried in a paragraph about resizing. Use the split whenever a bullet answers more than one of *what is it*, *what does it do*, *how is it built*, and *how is it tested* — see `module-structure.md` for section ownership and canonical section order.
|
|
@@ -405,25 +436,25 @@ Use `resolve-section-ownership` to perform the split. It is not the same as spli
|
|
|
405
436
|
|
|
406
437
|
The most common single case of this is a functional spec that states a mechanism rather than a result — a retry schedule, a cache, a token refresh. `impl-reqs.md` owns HOW; a functional spec keeps what a caller observes. `concise-specs-integration-examples.md` works through several.
|
|
407
438
|
|
|
408
|
-
## Narrated
|
|
439
|
+
## Narrated scenarios
|
|
409
440
|
|
|
410
441
|
BAD:
|
|
411
442
|
|
|
412
443
|
```plain
|
|
413
|
-
***
|
|
444
|
+
***conformance tests***
|
|
414
445
|
|
|
415
|
-
|
|
446
|
+
# create-project
|
|
447
|
+
The tester should first open the application and log in with a valid account, then navigate to the projects area where they will be able to see the list of projects, and then they should create a new project by filling in the form, after which they should verify that everything worked as expected and the project shows up correctly.
|
|
416
448
|
```
|
|
417
449
|
|
|
418
450
|
GOOD:
|
|
419
451
|
|
|
420
452
|
```plain
|
|
421
|
-
***
|
|
453
|
+
***conformance tests***
|
|
422
454
|
|
|
423
|
-
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
- :ProjectList: contains exactly one :Project:, named `Apollo`.
|
|
455
|
+
# create-project
|
|
456
|
+
A signed-in :User: with no :Project: items creates a :Project: named `Apollo` and finds it in :ProjectList:.
|
|
457
|
+
- :ProjectList: contains exactly one :Project:, named `Apollo`.
|
|
427
458
|
```
|
|
428
459
|
|
|
429
|
-
One narrated paragraph becomes
|
|
460
|
+
One narrated paragraph becomes one checkable sentence, and "everything worked as expected" becomes an assertion.
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Worked BAD to GOOD examples of concise integration
|
|
2
|
+
description: Worked BAD to GOOD examples of concise integration ***plain, where over-precision and obvious edge cases dominate
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
# Worked examples of concise integration
|
|
5
|
+
# Worked examples of concise integration ***plain
|
|
6
6
|
|
|
7
|
-
`concise-specs.md` is the rule and `concise-specs-examples.md` is the general example bank. This file covers integration
|
|
7
|
+
`concise-specs.md` is the rule and `concise-specs-examples.md` is the general example bank. This file covers integration ***plain only, because it fails differently.
|
|
8
8
|
|
|
9
|
-
An integration is technically demanding, the provider's contract is large, and the author has the provider's documentation open while writing. The result is
|
|
9
|
+
An integration is technically demanding, the provider's contract is large, and the author has the provider's documentation open while writing. The result is ***plain that is *too precise*: status codes, field lists, retry arithmetic, and every edge case the API can produce, written out as prose bullets. It reads as rigorous and is unreviewable.
|
|
10
10
|
|
|
11
11
|
Conciseness here is almost never about covering less. `integrations.md` makes edge-case coverage a hard floor — auth, environments, versioning, pagination, rate limits, the error model, retries, idempotency, webhooks, data mapping, compliance, observability. Every one of those must be covered. The lever is **where** the coverage lives:
|
|
12
12
|
|
|
13
|
-
- **Shapes and enumerations go to `resources/`** — schemas, error maps, retry policy, rate-limit headers. A
|
|
14
|
-
- **The
|
|
13
|
+
- **Shapes and enumerations go to `resources/`** — schemas, error maps, retry policy, rate-limit headers. A ***plain bullet links to the file; it never restates its contents.
|
|
14
|
+
- **The ***plain keeps behavior** — what the integration does with the shape, and what a caller observes.
|
|
15
15
|
- **Mechanism goes to `***implementation reqs***`** — retry logic, exception hierarchies, token caching, connection handling. Integration precision is mostly HOW, and `impl-reqs.md` owns HOW. `***functional specs***` keeps only what a caller can observe.
|
|
16
16
|
|
|
17
|
-
That last lever is easy to miss, so several examples below move a bullet between sections rather than shortening it. A fact in the wrong section is worse than a verbose one:
|
|
17
|
+
That last lever is easy to miss, so several examples below move a bullet between sections rather than shortening it. A fact in the wrong section is worse than a verbose one: turri reads each concept only from its owning section, so a misplaced fact is silently ignored.
|
|
18
18
|
|
|
19
|
-
Applied consistently,
|
|
19
|
+
Applied consistently, ***plain gets shorter as coverage goes *up*, because each new corner case adds a line to a resource file rather than a paragraph to the ***plain.
|
|
20
20
|
|
|
21
21
|
The BAD blocks intentionally demonstrate the mistakes they name, including invalid syntax. Copy only from GOOD blocks.
|
|
22
22
|
|
|
@@ -31,11 +31,14 @@ BAD:
|
|
|
31
31
|
|
|
32
32
|
***functional specs***
|
|
33
33
|
|
|
34
|
-
-
|
|
34
|
+
# retry-transient-errors
|
|
35
|
+
:ProviderClient: retries the request when :ProviderApi: returns 408, 425, 429, 500, 502, 503, or 504, and also on connection errors, DNS failures, socket timeouts, TLS handshake failures, and connection resets.
|
|
35
36
|
|
|
36
|
-
-
|
|
37
|
+
# no-retry-permanent-errors
|
|
38
|
+
:ProviderClient: does not retry on 400, 401, 403, 404, 405, 409, 410, 415, or 422, and raises immediately.
|
|
37
39
|
|
|
38
|
-
-
|
|
40
|
+
# retry-on-429
|
|
41
|
+
On 429, :ProviderClient: waits and tries again, unless the retry budget is exhausted.
|
|
39
42
|
```
|
|
40
43
|
|
|
41
44
|
GOOD:
|
|
@@ -50,7 +53,7 @@ GOOD:
|
|
|
50
53
|
- :ProviderClient: classifies every failure of :ProviderApi: through [resources/error-map.yaml](resources/error-map.yaml) and retries the transient ones under [resources/retry-policy.yaml](resources/retry-policy.yaml).
|
|
51
54
|
```
|
|
52
55
|
|
|
53
|
-
The status-code lists move into `error-map.yaml`, which the
|
|
56
|
+
The status-code lists move into `error-map.yaml`, which the coding agent already reads to generate the exception hierarchy and the transient predicate. Coverage is unchanged and the classification now has one source of truth instead of two that can disagree — see *Transient vs. permanent exception classification* in `integrations.md`.
|
|
54
57
|
|
|
55
58
|
Note the section too. Classifying and retrying is mechanism, so it is an implementation req; what belongs in `***functional specs***` is only the part a caller observes — which exception each error category surfaces as.
|
|
56
59
|
|
|
@@ -76,7 +79,7 @@ GOOD:
|
|
|
76
79
|
- :Customer: is a :ProviderApi: record, defined by [resources/provider.openapi.yaml](resources/provider.openapi.yaml) under `components.schemas.Customer`.
|
|
77
80
|
```
|
|
78
81
|
|
|
79
|
-
Field lists rot the moment the provider adds a field, and the
|
|
82
|
+
Field lists rot the moment the provider adds a field, and the coding agent generates types from the schema, not from the sentence. Keep in the definition only what the schema cannot say — a stability or uniqueness guarantee, a unit, a lifecycle rule.
|
|
80
83
|
|
|
81
84
|
## Specifying the provider instead of the integration
|
|
82
85
|
|
|
@@ -91,13 +94,17 @@ BAD:
|
|
|
91
94
|
|
|
92
95
|
***functional specs***
|
|
93
96
|
|
|
94
|
-
-
|
|
97
|
+
# provider-success-response
|
|
98
|
+
:ProviderApi: returns `200` with a JSON body when a request succeeds.
|
|
95
99
|
|
|
96
|
-
-
|
|
100
|
+
# provider-not-found
|
|
101
|
+
:ProviderApi: returns `404` when the requested :Customer: does not exist.
|
|
97
102
|
|
|
98
|
-
-
|
|
103
|
+
# provider-rate-limit
|
|
104
|
+
:ProviderApi: rate-limits clients and includes the remaining quota in the response headers.
|
|
99
105
|
|
|
100
|
-
-
|
|
106
|
+
# provider-auth-header
|
|
107
|
+
:ProviderApi: requires the `Authorization` header on every request.
|
|
101
108
|
```
|
|
102
109
|
|
|
103
110
|
GOOD:
|
|
@@ -123,20 +130,21 @@ GOOD:
|
|
|
123
130
|
|
|
124
131
|
***functional specs***
|
|
125
132
|
|
|
126
|
-
-
|
|
133
|
+
# fetch-customer
|
|
134
|
+
:ProviderClient: fetches a :Customer: from :ProviderApi: by :CustomerId:.
|
|
127
135
|
```
|
|
128
136
|
|
|
129
137
|
Every BAD bullet is true, and none of them is deleted — they are moved. The success shape, the `404`, and the `Authorization` requirement are what an OpenAPI file exists to state; the rate-limit model has its own conventional files. Provider behavior with no machine-readable home goes in `resources/<provider>-notes.md`. `integrations.md` carries the full artifact table.
|
|
130
138
|
|
|
131
|
-
What stays in the sections is only what gets built. A bullet stating provider behavior
|
|
139
|
+
What stays in the sections is only what gets built. A bullet stating provider behavior regenerates into nothing — the coding agent cannot build the provider, and no conformance test can assert a sentence that makes no claim about the integration. State instead what the integration does when the provider behaves that way.
|
|
132
140
|
|
|
133
|
-
Note where the links hang: both artifacts attach to the :ProviderApi: concept, and everything else references the token. `linked-resources.md` requires each resource to be linked from exactly one place, so a second spec needing the contract cannot introduce a second, divergent reference to it.
|
|
141
|
+
Note where the links hang: both artifacts attach to the :ProviderApi: concept, and everything else references the token. `linked-resources.md` requires each resource to be linked from exactly one place, so a second functional spec needing the contract cannot introduce a second, divergent reference to it.
|
|
134
142
|
|
|
135
|
-
## One operation fragmented into several specs
|
|
143
|
+
## One operation fragmented into several functional specs
|
|
136
144
|
|
|
137
145
|
Integration work invites this: the author walks through the request lifecycle — credential check, discovery call, request shape, paging — and each step becomes its own spec.
|
|
138
146
|
|
|
139
|
-
BAD — four specs, one operation:
|
|
147
|
+
BAD — four functional specs, one operation:
|
|
140
148
|
|
|
141
149
|
```plain
|
|
142
150
|
***definitions***
|
|
@@ -147,22 +155,26 @@ BAD — four specs, one operation:
|
|
|
147
155
|
|
|
148
156
|
***functional specs***
|
|
149
157
|
|
|
150
|
-
|
|
151
|
-
|
|
158
|
+
# fetch-requires-api-key
|
|
159
|
+
A call to `fetch` fails before any request to :ProviderApi: is sent when `PROVIDER_API_KEY` is unset or empty.
|
|
160
|
+
- The failure names `PROVIDER_API_KEY`.
|
|
152
161
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
162
|
+
# fetch-reads-catalogue
|
|
163
|
+
A call to `fetch` reads the field catalogue from :ProviderApi: once, before requesting any :Record:.
|
|
164
|
+
- The catalogue yields the name of every field the account defines.
|
|
165
|
+
- A call to `fetch` whose catalogue read fails requests no :Record: items.
|
|
156
166
|
|
|
157
|
-
-
|
|
158
|
-
|
|
167
|
+
# fetch-requests-records
|
|
168
|
+
A request for :Record: items from :ProviderApi: names every field in the catalogue and asks for at most 100 records.
|
|
169
|
+
- Archived :Record: items are not requested.
|
|
159
170
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
171
|
+
# fetch-pages-records
|
|
172
|
+
A call to `fetch` requests pages until :ProviderApi: returns a response carrying no next-page cursor.
|
|
173
|
+
- Each page after the first is requested with the cursor from the previous response.
|
|
174
|
+
- The :Record: items of every page are carried forward together.
|
|
163
175
|
```
|
|
164
176
|
|
|
165
|
-
GOOD — one spec, with the result stated first:
|
|
177
|
+
GOOD — one functional spec, with the result stated first:
|
|
166
178
|
|
|
167
179
|
```plain
|
|
168
180
|
***definitions***
|
|
@@ -175,13 +187,14 @@ GOOD — one spec, with the result stated first:
|
|
|
175
187
|
|
|
176
188
|
***functional specs***
|
|
177
189
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
190
|
+
# fetch-records
|
|
191
|
+
:RecordSource: `fetch()` returns every non-archived :Record: with every field :ProviderApi: defines, paging under [resources/pagination.yaml](resources/pagination.yaml).
|
|
192
|
+
- :RecordSource: reads the field catalogue once per `fetch()`.
|
|
193
|
+
- `fetch()` fails before any request when `PROVIDER_API_KEY` is unset or empty, naming it.
|
|
194
|
+
- A failed catalogue read fails `fetch()` before any :Record: is requested.
|
|
182
195
|
```
|
|
183
196
|
|
|
184
|
-
Merging is not in tension with the complexity limit. Split a spec when it implies more than 200 changed lines of code (`break-down-func-spec`); merge when several
|
|
197
|
+
Merging is not in tension with the complexity limit. Split a functional spec when it implies more than 200 changed lines of code (`break-down-func-spec`); merge when several implement one operation that fits inside that budget. Fragment count is not a measure of size.
|
|
185
198
|
|
|
186
199
|
## The auth flow narrated step by step
|
|
187
200
|
|
|
@@ -190,9 +203,11 @@ BAD:
|
|
|
190
203
|
```plain
|
|
191
204
|
***functional specs***
|
|
192
205
|
|
|
193
|
-
|
|
206
|
+
# obtain-token
|
|
207
|
+
:ProviderAuth: first reads the client id and the client secret, then builds a form-encoded body containing `grant_type=client_credentials`, the client id, and the client secret, then posts that body to the token endpoint, then parses the JSON response, then extracts the `access_token` and the `expires_in` fields, then stores the token in memory together with the moment it expires, and then attaches it to subsequent requests as a bearer token.
|
|
194
208
|
|
|
195
|
-
-
|
|
209
|
+
# refresh-token
|
|
210
|
+
Before each request, :ProviderAuth: compares the current time to the stored expiry and, if the token has expired or is about to expire, repeats the whole procedure described above to obtain a new token.
|
|
196
211
|
```
|
|
197
212
|
|
|
198
213
|
GOOD:
|
|
@@ -223,7 +238,7 @@ GOOD:
|
|
|
223
238
|
|
|
224
239
|
The grant type, endpoint, body encoding, and response fields are in the security scheme. What survives is the policy the scheme does not describe: the env-var names, the refresh margin, the retry-once-on-401 rule, and the concurrency behavior.
|
|
225
240
|
|
|
226
|
-
Almost all of it is implementation reqs — acquisition, caching, and refresh are machinery a caller never sees. Not all of it, though, and the rewrite has to name a component to say the rest. :ProviderAuth: performs the grant and has no observable surface; :AuthTokenManager: is what consumes it and hands tokens to :ProviderClient:, so it is the concept a functional spec can take as its subject. Without that consumer the section is left with a subject-less bullet, which `func-specs.md` rules out — and that one spec is the only line here a live conformance test can assert.
|
|
241
|
+
Almost all of it is implementation reqs — acquisition, caching, and refresh are machinery a caller never sees. Not all of it, though, and the rewrite has to name a component to say the rest. :ProviderAuth: performs the grant and has no observable surface; :AuthTokenManager: is what consumes it and hands tokens to :ProviderClient:, so it is the concept a functional spec can take as its subject. Without that consumer the section is left with a subject-less bullet, which `func-specs.md` rules out — and that one functional spec is the only line here a live conformance test can assert.
|
|
227
242
|
|
|
228
243
|
## Per-endpoint test reqs
|
|
229
244
|
|