@jabworks/condux 0.16.0 → 0.17.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jabworks/condux",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "OpenCode plugin for the condux workflow toolkit — bundles the condux skills and injects the specialist agents (coder, explorer, planner, researcher) plus an opt-in plan-review listener.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -95,8 +95,24 @@ Parse `commit`, `date`, and the **absolute spec path** from the output — use t
95
95
 
96
96
  Read [references/templates.md](references/templates.md) for the structure of each file.
97
97
 
98
+ **The layering rule.** Every concern file opens with its summary table; the
99
+ reasoning sits underneath; prose appears only where it earns its keep — the
100
+ reasoning a future reader must be able to follow. Fill the table layer first:
101
+ it is what a scanning human, `preflight`'s drift check, and an agent loading
102
+ the spec all read, so write each row as a claim someone could check.
103
+
104
+ **The two-homes rule.** A type in `api.md` says what each field *means* —
105
+ trailing `//` per field, JSDoc only when a line won't do. `fields.md` says
106
+ what *happens to it* — mapping and transformation only. One fact never lives
107
+ in both places: a fact with two homes goes missing from the one being read.
108
+
109
+ **Quirk headings are `## Q<n> — Title`.** The `Q<n>` anchors are a citation
110
+ contract (`durable-citations.test.mjs` resolves them), so numbers are unique,
111
+ ascending, and never renumbered.
112
+
98
113
  - Write only files that have real content
99
- - After writing, update the `## Contents` section in `index.md` with links to the files you created
114
+ - After writing, update the `## Contents` table in `index.md` — one row per
115
+ file created, the `Answers` cell saying what a reader comes to it for
100
116
  - Replace the HTML comment the scaffold left below the title with a one-line
101
117
  `> …` note saying what the spec is for. That note is what `spec-browser`'s
102
118
  catalog shows for the spec; leave it and the spec is listed with nothing to
@@ -177,3 +193,5 @@ feedback file, if a decision is submitted).
177
193
  | Vague decisions | Each decision needs context + rationale + consequences |
178
194
  | Creating empty placeholder files | Only create a file when it has real content |
179
195
  | Forgetting to update index.md Contents | Always link to files you create |
196
+ | Prose blob where the template has a table | Fill the table layer first; prose only for reasoning the table can't carry |
197
+ | Bare interface with the field meanings in fields.md | Meaning goes on the type (`//` per field); fields.md carries only the journey |
@@ -101,6 +101,11 @@ cat > "$SPEC_DIR/index.md" <<EOF
101
101
 
102
102
  ## Contents
103
103
 
104
+ | File | Answers |
105
+ |---|---|
106
+ <!-- One row per concern file you actually create — link + what a reader
107
+ comes to it for. Files that don't exist get no row. -->
108
+
104
109
  ## Changelog
105
110
  - $DATE ($COMMIT): Initial spec
106
111
  EOF
@@ -2,6 +2,13 @@
2
2
 
3
3
  Copy-paste templates for each spec file. Omit sections that don't apply — don't leave empty headings.
4
4
 
5
+ **The layering rule, which every template below follows:** a concern file opens
6
+ with a scannable table and carries its reasoning underneath. The table serves
7
+ the reader scanning for one thing, `preflight`'s drift check, and an agent
8
+ loading the spec as context — all three read the same rows. Prose is reserved
9
+ for the one job that needs it: reasoning a future reader must be able to
10
+ follow. Write each table row as a claim someone could check, not a label.
11
+
5
12
  ---
6
13
 
7
14
  ## index.md
@@ -17,16 +24,20 @@ Copy-paste templates for each spec file. Omit sections that don't apply — don'
17
24
  **Status:** draft | review | stable
18
25
 
19
26
  ## Contents
20
- - [Decisions](decisions.md) — why we built it this way
21
- - [API](api.md) — contracts, endpoints, types
22
- - [Fields](fields.md) — field mappings: BE/3rd-party → UI
23
- - [Implementation](implementation.md) — how it works, key files
24
- - [Quirks](quirks.md) — edge cases and gotchas
27
+
28
+ | File | Answers |
29
+ |---|---|
30
+ | [Decisions](decisions.md) | why it works this way, and what was rejected |
31
+ | [Quirks](quirks.md) | what will bite you, and whether it is mitigated |
25
32
 
26
33
  ## Changelog
27
34
  - {YYYY-MM-DD} ({short-hash}): Initial spec
28
35
  ```
29
36
 
37
+ The Contents table answers *which file do I open* — one row per concern file
38
+ that actually exists, each `Answers` cell saying what a reader comes to it
39
+ for. Files that don't exist get no row.
40
+
30
41
  **Cite committed paths only.** A spec is durable; the design and plan it came
31
42
  from are working state under `.condux/`, gitignored and gone on any other
32
43
  machine. If the changelog or body needs to point at a design or a verification
@@ -41,24 +52,35 @@ rule existed and two were already dead when they were found.
41
52
  ```markdown
42
53
  # Decisions — {Feature Name}
43
54
 
44
- ## {Decision Title}
55
+ | # | Decision | Because | Status |
56
+ |---|---|---|---|
57
+ | 1 | {what was decided, as a claim} | {why, one line} | accepted |
45
58
 
46
- **Date:** {YYYY-MM-DD}
47
- **Status:** accepted | superseded | deprecated
59
+ ## 1. {Decision Title} — {YYYY-MM-DD}
48
60
 
49
- ### Context
50
- Why this decision needed to be made. What constraints or requirements drove it.
61
+ **Decided:** one sentence, stated as a claim.
62
+ **Because:** one sentence — why this option won.
51
63
 
52
- ### Decision
53
- What was decided, stated clearly.
64
+ | Alternative | Why not |
65
+ |---|---|
66
+ | {option considered} | {one line} |
54
67
 
55
- ### Rationale
56
- Why this over the alternatives. Include alternatives considered and why they were rejected.
68
+ **Consequences**
69
+ - {what changes as a result — and where it costs, named}
57
70
 
58
- ### Consequences
59
- What changes as a result — positive and negative trade-offs.
71
+ **Context** *(only when the question's origin is not obvious)*
72
+ {prose — why a decision was needed at all, when the feature doesn't make
73
+ that self-evident}
60
74
  ```
61
75
 
76
+ The summary table is mandatory and answers "which decisions exist" without
77
+ reading the file. The alternatives table is mandatory too — an empty one is
78
+ visible in a way missing prose is not, and a decision recorded without its
79
+ rejected alternatives is assertion, not rationale. `Context` is the one
80
+ conditional block: `Because` covers why *this* option won; `Context` covers
81
+ why the question arose, which is frequently self-evident. Statuses:
82
+ `accepted | superseded | deprecated`.
83
+
62
84
  ---
63
85
 
64
86
  ## api.md
@@ -75,9 +97,12 @@ What changes as a result — positive and negative trade-offs.
75
97
  ## Key Types / Schemas
76
98
 
77
99
  \`\`\`typescript
78
- interface ExampleType {
79
- id: string;
80
- // ...
100
+ // The type says what a field MEANS; fields.md says what HAPPENS to it.
101
+ interface Invoice {
102
+ id: string; // Stripe invoice id, `in_`-prefixed
103
+ amountDue: number; // minor units (cents) — never format directly
104
+ status: InvoiceStatus; // drives the badge; `draft` is hidden in the list
105
+ voidedAt: string | null; // ISO 8601; null unless voided
81
106
  }
82
107
  \`\`\`
83
108
 
@@ -88,6 +113,12 @@ interface ExampleType {
88
113
  | {Service} | {What we use it for} | {Rate limits, quirks} |
89
114
  ```
90
115
 
116
+ **Every field carries its meaning inline** — trailing `//` by default; a JSDoc
117
+ block only when a line won't do (units, constraints, nullability semantics).
118
+ This is the two-homes rule: a fact with two homes goes missing from the one
119
+ you are reading, and the un-annotated type is the home readers actually open.
120
+ `fields.md` keeps only what a type cannot express — the journey.
121
+
91
122
  ---
92
123
 
93
124
  ## fields.md
@@ -95,18 +126,20 @@ interface ExampleType {
95
126
  ```markdown
96
127
  # Fields — {Feature Name}
97
128
 
98
- Document which fields flow from source (BE or 3rd-party API) to destination (UI or your BE),
99
- with descriptions so you can glance at the spec instead of traversing the codebase.
129
+ Document which fields flow from source (BE or 3rd-party API) to destination
130
+ (UI or your BE). The Description column is about the TRANSFORMATION — what
131
+ happens to the field on the way — never about what the field means; meaning
132
+ lives on the type in api.md.
100
133
 
101
134
  ## Response Fields — {Endpoint or Source Name}
102
135
 
103
136
  > Source: `GET /api/...` or `{ThirdParty} → BE → FE`
104
137
 
105
- | Source Field | UI Label / FE Key | Type | Nullable | Description |
138
+ | Source Field | UI Label / FE Key | Type | Nullable | Transformation |
106
139
  |---|---|---|---|---|
107
- | `user_full_name` | Full Name | `string` | no | Display name shown in profile card |
108
- | `created_at` | Created | `string (ISO 8601)` | no | Formatted to `MMM D, YYYY` in UI |
109
- | `metadata.region` | Region | `string` | yes | Omitted from UI when null |
140
+ | `user_full_name` | Full Name | `string` | no | rendered as-is in profile card |
141
+ | `created_at` | Created | `string (ISO 8601)` | no | formatted to `MMM D, YYYY` in UI |
142
+ | `metadata.region` | Region | `string` | yes | omitted from UI when null |
110
143
 
111
144
  ### Forwarding Chain (3rd-party → BE → FE)
112
145
 
@@ -121,10 +154,10 @@ Use this sub-section when your BE proxies a 3rd-party API and renames fields.
121
154
 
122
155
  > Sent from FE to `POST /api/...`
123
156
 
124
- | FE Key | BE Field | Type | Required | Description |
157
+ | FE Key | BE Field | Type | Required | Transformation |
125
158
  |---|---|---|---|---|
126
- | `email` | `email` | `string` | yes | Validated client-side before send |
127
- | `preferredName` | `preferred_name` | `string` | no | Optional; omitted if empty |
159
+ | `email` | `email` | `string` | yes | validated client-side before send |
160
+ | `preferredName` | `preferred_name` | `string` | no | omitted from payload if empty |
128
161
  ```
129
162
 
130
163
  ---
@@ -134,25 +167,35 @@ Use this sub-section when your BE proxies a 3rd-party API and renames fields.
134
167
  ```markdown
135
168
  # Implementation — {Feature Name}
136
169
 
137
- ## Overview
138
- One paragraph on how the feature works end-to-end.
139
-
140
- ## Key Files
141
-
142
170
  | File | Role |
143
171
  |------|------|
144
172
  | `src/...` | ... |
145
173
 
146
- ## Data Flow
147
- Step-by-step description of how data moves through the system.
174
+ ## Data flow
175
+
176
+ 1. {step} — {what happens}
177
+ 2. {step} — {what happens}
148
178
 
149
- ## Patterns Used
150
- Non-obvious patterns, abstractions, or conventions introduced.
179
+ ## Patterns
180
+
181
+ | Pattern | Where | Why not the obvious thing |
182
+ |---|---|---|
183
+ | {pattern used} | {file or layer} | {what it displaced, one line} |
151
184
 
152
185
  ## Dependencies
186
+
153
187
  Internal modules or external packages this feature leans on heavily.
188
+
189
+ ## Overview *(only when the file table does not already tell the story)*
190
+
191
+ {prose — the end-to-end narrative, when the table and data flow leave it
192
+ untold}
154
193
  ```
155
194
 
195
+ Data flow is a numbered list — it is step-by-step by definition and was never
196
+ prose. The Patterns table's third column is mandatory: a pattern recorded
197
+ without the alternative it displaced is trivia, not guidance.
198
+
156
199
  ---
157
200
 
158
201
  ## quirks.md
@@ -160,10 +203,25 @@ Internal modules or external packages this feature leans on heavily.
160
203
  ```markdown
161
204
  # Quirks — {Feature Name}
162
205
 
163
- ## {Quirk Title}
206
+ | # | Quirk | Trigger | Severity | Mitigated |
207
+ |---|---|---|---|---|
208
+ | Q1 | {symptom, one line} | {what sets it off} | low / medium / high | yes / no / partial |
209
+
210
+ ## Q1 — {Quirk Title}
164
211
 
165
- **Severity:** low | medium | high
166
212
  **Discovered:** {date or commit}
167
213
 
168
- Description of the edge case, gotcha, or known issue. Include what triggers it and how it's handled or worked around.
214
+ **Symptom:** what a reader will observe.
215
+ **Trigger:** what sets it off.
216
+ **Cause:** why it happens.
217
+ **Mitigation:** what to do — or "none", said plainly.
218
+
219
+ {prose — only the part that genuinely needs explaining}
169
220
  ```
221
+
222
+ Quirk headings are `## Q<n> — Title`, always — `durable-citations.test.mjs`
223
+ resolves every `Q<n>` a spec cites against these headings, so the format is a
224
+ contract, not a style. Numbers are unique and ascending, never reused and
225
+ never renumbered (citations from other specs may point at them). The
226
+ `Mitigated` column is a checkable claim: `yes / no / partial`, with `partial`
227
+ explained in the body's **Mitigation** field.