okstra 0.139.0 → 0.140.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": "okstra",
3
- "version": "0.139.0",
3
+ "version": "0.140.0",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.139.0",
3
- "builtAt": "2026-07-29T09:29:56.756Z",
2
+ "package": "0.140.0",
3
+ "builtAt": "2026-07-29T15:35:26.640Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -113,6 +113,20 @@ The test: if the name appeared in a stacktrace, an autocomplete list, or a grep
113
113
 
114
114
  This rule applies most strongly to **names crossing module boundaries**: public methods, exported constants, port methods. Private helpers in a tight local scope get more slack.
115
115
 
116
+ ### One identifier, one meaning per file
117
+
118
+ A name that means two things in one file makes every reader re-derive which one they are looking at. The common shape: a new parameter holding a key takes the name a sibling signature already gives to the whole entity.
119
+
120
+ Bad:
121
+ ```ts
122
+ findFullAccount(account: string) // a primary key
123
+ update(idAccount: string, account: AccountRow) // the row — same word, four lines away
124
+ ```
125
+
126
+ Good: `findFullAccount(idAccount: string)` — the key takes the name the file already uses for keys.
127
+
128
+ The test: grep the identifier across the file. If two occurrences carry different types or different roles, rename the newer one to the distinguishing fact and follow whatever name the file already uses for that role.
129
+
116
130
  ## Functions do one thing
117
131
 
118
132
  If the name contains "and", or you must scroll to read it, split it.
@@ -180,6 +194,8 @@ Never re-read a before-field off the original object after that boundary, and ne
180
194
 
181
195
  Creating a new row and updating an existing one are not the same write authority. When reusing an existing row, update only the fields the current operation owns, and express them as an allowlist. Provenance, ownership, and externally-managed values are not overwritten without an explicit requirement saying so. Keep the create-vs-reuse answer available at the later write step — that is what tells the two authorities apart.
182
196
 
197
+ The test: for every field this change writes, grep the other sites that write the same field in the same flow — a later pass of the same loop, a final persist, a sibling handler — and put them in order. If a later write clears what an earlier one recorded, the clear needs a condition, not a default.
198
+
183
199
  ### Zero affected rows is a question, not an answer
184
200
 
185
201
  A conditional write that changed no rows has not told you whether it succeeded. Re-read the current state and separate the three cases: already in the desired state, changed to a different conflicting state, target deleted. Each gets its own success/failure handling and its own message — collapsing conflict and deletion into one error hides the case the caller must act on.
@@ -192,6 +208,22 @@ Which of several inputs wins is a business rule. It does not belong in a service
192
208
 
193
209
  Do not assert a cause the code never checked — "target not found" after a write that only reported zero rows is a guess. Failures with different remedies (deleted, state mismatch, concurrent change) get different messages, each covered by a test on that path.
194
210
 
211
+ ## Wrapping a third-party call
212
+
213
+ ### The wrapper must add something the library does not already do
214
+
215
+ Before writing recovery, retry, or fallback code around a library call, read that library's own handling of the same case in its installed source. A `catch` that repeats what the library already did — same read, same transaction, same conditions — recovers nothing; it only replaces a typed error with a weaker one.
216
+
217
+ The test: name the condition under which your branch runs and the library's does not. If you cannot name it from the library's source, the branch is dead. Delete it, or make it do something the library provably does not — a locking read where the library read without a lock, a different isolation level, a retry the library never attempts.
218
+
219
+ ### A comment may not name a condition the code does not run under
220
+
221
+ Recovery documented as "under REPEATABLE READ this re-read succeeds" is false when every caller opens READ COMMITTED. Cite the call site that establishes the condition, or drop the claim — an unverifiable comment outlives the code it excuses.
222
+
223
+ ### Rethrowing keeps the original error
224
+
225
+ Replacing a typed library error with a bare one throws away what the caller needs in order to act: which constraint fired, the driver's message, the original stack. Keep the original as the cause (`new Error(msg, { cause: err })`, `raise X from err`) and keep the type when a caller branches on it.
226
+
195
227
  ## Testing discipline
196
228
 
197
229
  These principles apply to every test file regardless of language or framework. The mechanical detection patterns (which mock library, which spy API) are in each `languages/*.md` Tests section.
@@ -237,10 +269,24 @@ Legitimately behavioral (these are fine):
237
269
 
238
270
  The judgment call: is the mocked thing an **outcome boundary** (port, external service, side-effecting adapter) or an **internal helper**? Asserting on boundaries is behavioral; asserting on helpers is implementation-coupled.
239
271
 
272
+ ### Assert the last write, not the first
273
+
274
+ When one run writes the same record more than once — an update followed by a final persist, two passes of the same loop over sibling rows — asserting the earlier call proves nothing about what the record ends up holding. The later write can drop the very field the test asserted and the test stays green, so the suite certifies a broken end state.
275
+
276
+ The test: list every write in this flow that touches the field you assert, and confirm the one you assert is the last. If it is not, assert the last one too.
277
+
240
278
  ### An effect under its own mock is not evidence
241
279
 
242
280
  When a mock replaces the code path that would produce an effect, a test above that mock has not verified the effect — it verified that the mock was reachable. Claim coverage for an effect only where the real path runs; the presence of a mock in the harness is not proof that the branch behind it works.
243
281
 
282
+ ### Every branch you add gets a test that dies with it
283
+
284
+ A new `catch`, guard, early return, or `else` is a behavior claim. Coverage for it means one test that FAILS when the branch body is deleted — not a happy-path test that merely reaches the same method.
285
+
286
+ The test: delete the branch body and run the suite. Green means the branch is untested, and an untested recovery path is indistinguishable from one that cannot fire at all.
287
+
288
+ This binds hardest on error-recovery code, where the happy path is what the library already does and the branch is the only behavior your code adds.
289
+
244
290
  ### Shared fixtures keep the ordinary default
245
291
 
246
292
  A shared fixture's defaults describe the normal path. Do not flip a default to exercise one exception — every existing caller silently changes meaning. Pass the exceptional condition explicitly in the test that needs it (`makeFamily({ familyEnabled: false })`), and search all call sites before changing any default.
@@ -251,6 +297,15 @@ The test: does the no-argument fixture call still describe the ordinary case?
251
297
 
252
298
  Two scenarios whose setup values and assertions are identical are one test with two names. Give source and destination, existing and incoming, success and conflict deliberately different values. The check: state each test's distinguishing input in one sentence, then delete that difference — if the test still passes, it never tested the path. A wrong implementation must not pass because the defaults happened to agree.
253
299
 
300
+ ### A test title names the unit and the one condition it isolates
301
+
302
+ A test title is a name, so the truthful and stand-alone rules above apply to it. From a failure line alone the reader must know which unit ran and which single condition was under test.
303
+
304
+ - Name the unit when the file covers more than one. `'returns the primary-key lookup result'` fits every by-key read in the class; `'findFullAccount returns …'`, or a `describe('findFullAccount', …)` wrapper, fits one.
305
+ - Credit only the condition the case actually isolates. A parameterised case that trips two guards at once must not name one of them in the title — split it into one case per guard.
306
+
307
+ The test: delete from the setup the input the title credits. If the case still passes, the title describes a guard the test never reached — retitle it, or fix the setup so it does.
308
+
254
309
  ### Test tooling you add is used in the same change
255
310
 
256
311
  A new mock, state setter, or repository branch that no test calls is unfinished work, not coverage. Grep every new test identifier for a use site before claiming done, and check that a repository mock does not only ever return success.
@@ -48,9 +48,10 @@ If no Stage 1 language rule matches (an unlisted language), stop and ask the use
48
48
  ## Mandatory pre-write checks (every language)
49
49
 
50
50
  - [ ] Language reference read for this turn.
51
- - [ ] `clean-code.md` principles applied: DRY, KISS, SOLID, YAGNI, meaningful naming (truthful + standalone), single-purpose functions, plain-English summary test, 50-line cap, no magic numbers, shallow nesting, comments-explain-why.
51
+ - [ ] `clean-code.md` principles applied: DRY, KISS, SOLID, YAGNI, meaningful naming (truthful + standalone + one identifier one meaning per file), single-purpose functions, plain-English summary test, 50-line cap, no magic numbers, shallow nesting, comments-explain-why.
52
52
  - [ ] Tests planned: which test(s) cover this change. New behaviour without a test is **incomplete** unless the user has explicitly opted out for this change.
53
- - [ ] **Testing discipline:** the test does not stub/spy methods on the SUT itself (collaborators are fine), and assertions are on outcomes (return values, state, events, boundary calls) — not on which internal helper was called.
53
+ - [ ] **Testing discipline:** the test does not stub/spy methods on the SUT itself (collaborators are fine), and assertions are on outcomes (return values, state, events, boundary calls) — not on which internal helper was called. Each branch this change adds (`catch`, guard, early return, `else`) has a test that fails when the branch body is deleted; assertions land on the last write to a record, not an intermediate one; each test title names its unit and the single condition it isolates.
54
+ - [ ] **Third-party wrapper (when this change wraps a library call):** the wrapper adds behaviour the library does not already provide — read the installed library source before keeping a recovery branch — no comment names a condition the call site does not establish, and a rethrow keeps the original error as `cause`.
54
55
  - [ ] **Hexagonal overlay (if loaded):** no business logic inside any port body, adapter methods are I/O only (no post-fetch JS filtering on domain state, no `findValid*`/`findActive*` adapter names hiding rules), all domain objects declared under `domain/`.
55
56
  - [ ] Existing code searched: `grep` for the symbol / file / identifier you are about to add. Do not duplicate.
56
57
  - [ ] Project conventions checked: `.editorconfig`, `CONTRIBUTING.md`, formatter config (`.prettierrc`, `rustfmt.toml`, `ktlint`, `google-java-format`, etc.). **Project rules override this resource pack on conflict.**
@@ -27,9 +27,10 @@ Do not scan holistically and stop when it "looks fine". Work the matrix exhausti
27
27
 
28
28
  1. **List every changed file:** `git diff --name-only <stage-base>..HEAD`. That list is your worklist — cover it to the end; no sampling.
29
29
  2. **Classify each file and select its rule set** from the conventions the preflight already loaded (do NOT re-derive the rules here — read them from the routed pack):
30
- - any source file → `clean-code.md`: truthful + standalone names, plain-English summary test, single-purpose ≤50-line functions, DRY (incl. scattered domain literals + documented forks), no magic numbers, shallow nesting, comments explain why.
30
+ - any source file → `clean-code.md`: truthful + standalone names, one identifier one meaning per file (a key parameter must not take the name a sibling signature gives the entity), plain-English summary test, single-purpose ≤50-line functions, DRY (incl. scattered domain literals + documented forks), no magic numbers, shallow nesting, comments explain why.
31
+ - a file that wraps a third-party / library call → `clean-code.md` "Wrapping a third-party call": the wrapper adds behaviour the library does not already provide (read the installed library source before keeping a recovery branch — a `catch` repeating the library's own retry recovers nothing), no comment names a condition the call site does not establish, and a rethrow keeps the original error as `cause`.
31
32
  - a file that decides, mutates, or persists state → `clean-code.md` "Mutation and state boundaries": decide on the direct identifier rather than a status/flag proxy, capture before-state in one snapshot ahead of the mutating boundary, update only this work's owned fields on an existing row, re-read state before calling a zero-affected-rows write success or failure, put priority-between-inputs in a named domain function, and keep error messages to what was actually observed. Also check that no state union/enum was re-declared beside an authoritative one the domain or a dependency exports.
32
- - test file (`*.spec.*` / `*.test.*` / `test_*.py` / `*_test.go` …) → `clean-code.md` "Testing discipline": no self-mocking of the SUT, behavioral (outcome) assertions not interaction-only, no tautological delegation assertion, no effect claimed under its own mock, shared-fixture defaults left on the ordinary path, setup values that actually separate the scenarios, every new test helper/mock used by a test in this same diff, and no positional mock-argument access (`rg 'mock\.calls'`).
33
+ - test file (`*.spec.*` / `*.test.*` / `test_*.py` / `*_test.go` …) → `clean-code.md` "Testing discipline": no self-mocking of the SUT, behavioral (outcome) assertions not interaction-only, no tautological delegation assertion, no effect claimed under its own mock, shared-fixture defaults left on the ordinary path, setup values that actually separate the scenarios, every new test helper/mock used by a test in this same diff, no positional mock-argument access (`rg 'mock\.calls'`), every branch this diff adds covered by a test that fails when the branch body is deleted, assertions on the last write to a record rather than an intermediate one, and test titles naming their unit plus the single condition each case isolates.
33
34
  - port / adapter / domain file, when the hexagonal overlay is loaded → `architectures/hexagonal.md`: no business logic in a port body, adapter methods are I/O only (no post-fetch filtering on domain state, no `findValid*`/`findActive*` names hiding a rule), domain objects declared under the domain boundary, no changed domain file importing an ORM / framework / adapter / service (read the import list — mechanical), and a service dependency you add or modify goes through a port rather than a concrete adapter (advisory: record it with the port sketch; the codebase already injecting concrete classes is the debt this pays down, not a reason to skip it).
34
35
  A file can hold several roles — apply every rule set that fits it.
35
36
  3. **Decide clean-or-finding for each cell.** Read the full file when a rule needs context (never judge a port/adapter/domain or a naming rule from the hunk alone).
@@ -21,7 +21,7 @@ fails, fix it or surface the violation — do not claim done on a failing item.
21
21
  - names & comments: names say what, comments say why; no obvious-restatement comments; no truthful-name violations
22
22
  - verification: ran the actual build/test — paste the exact command line and its real exit code / output tail, never a paraphrased "tests pass"
23
23
  - acceptance: each implemented plan step's `Acceptance:` condition is observably met — cite the RED→GREEN test (or the command output) proving it per step, not one global "acceptance met"; and each `Test case (success|boundary|failure)` the plan declared for this stage has a matching test in the diff (name the test), so declared edge/failure cases are not silently uncovered
24
- - mutation check: for at least one test this diff added or changed, break the implementation it covers on purpose (return the wrong value, skip the write), confirm that test FAILS, then restore cite the test name and the observed failure. A test that still passes against a broken implementation is not coverage, and "verified" claimed without this check plus the real command output above is not a verified claim
24
+ - mutation check: enumerate every branch this diff adds (`catch`, guard, early return, `else`), plus at least one test it added or changed. For each entry, break what it covers on purpose — delete the branch body, return the wrong value, skip the write confirm a test FAILS, then restore; cite the branch (or test) and the observed failure per entry, not one global "mutation verified". A branch whose deletion leaves the suite green is untested, not covered an untested recovery path is indistinguishable from one that cannot fire. "Verified" claimed without this check plus the real command output above is not a verified claim
25
25
  - cleanup: no dead or commented-out code left behind
26
26
 
27
27
  Close with a `Self-check coverage:` line naming the files you verified the
@@ -126,7 +126,7 @@ Axis scope — the `Reads` column names that group's rules, and a brief never re
126
126
  | `structural` | non-test source file × `structural` | core principles 1, 2, 3, 6; `clean-code.md` DRY / KISS / SOLID / YAGNI; the completion sweep's domain-literal and documented-fork items; plus the architecture pack when one was routed |
127
127
  | `semantic` | function × `semantic` | core principles 4, 5; `clean-code.md` meaningful naming, functions-do-one-thing, the plain-English summary test, the 50-line cap, nesting depth, magic numbers, comments-explain-why |
128
128
  | `state-and-tests` | non-test source file × `state-and-tests`, test file × `state-and-tests` | `clean-code.md` "Mutation and state boundaries" and "Testing discipline"; plus the routed language pack's self-mock signals |
129
- | `general` | non-test source file × `general` | correctness bugs, swallowed errors, security, clear performance problems, core principle 7 (fix at the cause — nothing previously green edited to silence a failure); plus the routed language pack's language-specific traps |
129
+ | `general` | non-test source file × `general` | correctness bugs, swallowed errors, security, clear performance problems, core principle 7 (fix at the cause — nothing previously green edited to silence a failure); `clean-code.md` "Wrapping a third-party call" — a recovery branch the library already performs, a comment naming a condition the call site never establishes, a rethrow that drops the original error; plus the routed language pack's language-specific traps |
130
130
 
131
131
  Say it plainly in every brief: **the census is law** — work the cells exactly as given, return a verdict for every one, never re-derive the list. There is no length budget; dropping a finding to stay brief is the failure this skill exists to prevent.
132
132