gennady 0.9.0-next.2 → 0.9.0-next.4

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.
@@ -0,0 +1,101 @@
1
+ <BaselineCodingRules keywords="baseline, language_agnostic, contracts, error_handling, naming, yagni, ai_to_ai" type="coding-rules" ver="1.0">
2
+ <!--
3
+ Language-agnostic coding baseline. The few things that hold in ANY language. Language rule files
4
+ (python-rules, go-rules, typescript-rules, …) inherit this and add their own idioms; do not
5
+ restate these axioms there. Activated for source code files of a scope that declares no more
6
+ specific language rule, and as the transitive parent of the language rules that reference it.
7
+ -->
8
+
9
+ <BeliefState>
10
+ <Axiom id="AX_TELEOLOGICAL_NAMING">
11
+ A name states a goal, not a pattern. Forbidden semantic-noise names: `Manager`, `Handler`,
12
+ `Data`, `Info`, `Wrapper`, `Util`, `do…`, `process…`. `OrderFulfillmentPipeline` carries intent;
13
+ `OrderManager` tells the next reader nothing. Use precise verbs (`retrieve`, `verify`, `compose`)
14
+ over vague ones (`get`, `check`, `make`).
15
+ </Axiom>
16
+
17
+ <Axiom id="AX_EXPLICIT_FAILURE">
18
+ Failures are explicit and carry cause. Raise/return a specific, typed error with a message that
19
+ says what failed and with what input; when re-raising, preserve the original cause (do not
20
+ flatten a stack into a string). Never silently swallow an error or return a zero value that hides
21
+ one.
22
+ </Axiom>
23
+
24
+ <Axiom id="AX_CONTRACT_AT_SURFACE">
25
+ The public surface documents its contract: inputs, output, and failure modes, at the declaration
26
+ — not buried in the body. A caller must be able to use the function from its signature plus one
27
+ doc block, without reading the implementation.
28
+ </Axiom>
29
+
30
+ <Axiom id="AX_INTENT_COMMENTS">
31
+ Comments carry intent the code cannot: purpose, invariant, side effect, failure mode, non-goal,
32
+ or why-not. A comment that restates the syntax on the next line is noise and must be removed.
33
+ </Axiom>
34
+
35
+ <Axiom id="AX_YAGNI_NO_DEFENSE">
36
+ Build only what the task needs. No speculative abstraction (an interface/base class for a single
37
+ caller with no confirmed second one), no dead code, and no defensive branches against states the
38
+ types or invariants already exclude. Prefer the simplest shape that satisfies the contract.
39
+ </Axiom>
40
+
41
+ <Axiom id="AX_SINGLE_RESPONSIBILITY">
42
+ A unit does one thing at one level of abstraction. A function that mixes I/O, policy, and
43
+ formatting is split. Depth of nesting and length are symptoms; name the extracted piece by its
44
+ goal.
45
+ </Axiom>
46
+ </BeliefState>
47
+
48
+ <AntiPatterns>
49
+ <AntiPattern id="AP_SWALLOWED_ERROR">
50
+ <Bad>A caught error that is logged-and-ignored, discarded, or replaced by a generic message that
51
+ drops the original cause — so the failure surfaces later with no trace of its origin.</Bad>
52
+ <Instead>Handle it (recover with a documented fallback) or propagate it with the cause attached.
53
+ One or the other, never neither.</Instead>
54
+ </AntiPattern>
55
+
56
+ <AntiPattern id="AP_PATTERN_NAMING">
57
+ <Bad>`FooManager`, `DataHandler`, `RequestWrapper`, `doWork`, `processItem` — names that describe
58
+ a code pattern rather than a business goal.</Bad>
59
+ <Instead>Name the responsibility: `SubscriptionRenewal`, `parseVcsUrl`, `retryWithBackoff`.</Instead>
60
+ </AntiPattern>
61
+
62
+ <AntiPattern id="AP_SYNTAX_COMMENT">
63
+ <Bad>`// increment i` above `i++`; a doc block that repeats parameter names and types already in
64
+ the signature.</Bad>
65
+ <Instead>Comment the why, the invariant, or the non-obvious consequence — or nothing.</Instead>
66
+ </AntiPattern>
67
+
68
+ <AntiPattern id="AP_SPECULATIVE_ABSTRACTION">
69
+ <Bad>Extracting an interface, factory, or config option for one caller "in case we need it".</Bad>
70
+ <Instead>Inline it; abstract on the second real consumer, when the variance is known.</Instead>
71
+ </AntiPattern>
72
+ </AntiPatterns>
73
+
74
+ <VerificationHooks>
75
+ <Hook id="HOOK_PROJECT_GATE">
76
+ <Purpose>The scope's own quality gate (formatter + linter + type/compile check + tests, as the
77
+ project declares them) passes on the changed files.</Purpose>
78
+ <Command>&lt;sdd-path&gt; verify --wip &lt;target-files&gt;</Command>
79
+ <Expected>Exit 0. If the scope has not declared its gate yet, that is the finding to fix first.</Expected>
80
+ </Hook>
81
+ <Hook id="HOOK_NO_SCAFFOLD_LEFTOVER">
82
+ <Purpose>No placeholder/scaffold markers survive in shipped code.</Purpose>
83
+ <Command>rg --no-heading -n "TODO|FIXME|XXX|&lt;[A-Z_]+&gt;|placeholder" &lt;target-files&gt;</Command>
84
+ <Expected>Empty on changed lines; a real deferred item belongs in a ticket, not a bare marker.</Expected>
85
+ </Hook>
86
+ </VerificationHooks>
87
+
88
+ <RewardCriteria>
89
+ ✅ Names state a goal; precise verbs; no `Manager`/`Handler`/`Data`/`Util`/`do…`/`process…`.
90
+ ✅ Every failure path raises/returns a specific error with context and preserved cause.
91
+ ✅ Public entities document inputs, output, and failure modes at the declaration.
92
+ ✅ Comments carry intent (purpose / invariant / side effect / failure / non-goal), never restate syntax.
93
+ ✅ Only what the task needs: no speculative abstraction, no dead code, no defense against impossible states.
94
+ ✅ Each unit has one responsibility at one abstraction level.
95
+ ❌ An error caught and dropped, or re-raised with the cause lost.
96
+ ❌ Pattern-shaped names that hide the business goal.
97
+ ❌ Comments that restate the code, or doc blocks duplicating the signature.
98
+ ❌ An abstraction introduced for a single caller with no confirmed second consumer.
99
+ ❌ Placeholder/scaffold markers (`TODO`, `&lt;NAME&gt;`) left in shipped code.
100
+ </RewardCriteria>
101
+ </BaselineCodingRules>
@@ -0,0 +1,94 @@
1
+ <GoCodingRules keywords="go, golang, gofmt, govet, errors, wrapping, naming, testing, ai_to_ai" type="coding-rules" ver="1.0">
2
+ <!--
3
+ Thin Go baseline. Inherits the language-agnostic coding + testing baselines; adds only the Go
4
+ idioms the community broadly agrees on. Not an exhaustive style guide — deliberately small.
5
+ Grounded in: Effective Go, Go Code Review Comments, go.dev/blog/go1.13-errors, official tooling
6
+ (gofmt, go vet, golangci-lint) — the intersection Uber/Google style guides also share.
7
+ -->
8
+ <DependsOn>
9
+ - ai/directives/coding/baseline-rules.xml
10
+ - ai/directives/testing/baseline-testing.xml
11
+ </DependsOn>
12
+
13
+ <BeliefState>
14
+ <Axiom id="AX_GO_GOFMT">
15
+ Code is `gofmt`/`goimports`-clean — non-negotiable, zero config. `goimports` is preferred (it also
16
+ groups and prunes imports). Tabs, tool-decided layout; there are no formatting debates in Go.
17
+ </Axiom>
18
+
19
+ <Axiom id="AX_GO_CHECK_AND_WRAP_ERRORS">
20
+ `error` is the last return value and every error is checked — never discarded with `_` unless the
21
+ discard is deliberate and justified. Add context by wrapping with `fmt.Errorf("doing X: %w", err)`
22
+ (`%w`, not `%v`, so callers can unwrap). Inspect with `errors.Is` / `errors.As`, not `==` or type
23
+ assertions. Define a sentinel (`var ErrNotFound = errors.New(…)`) only for a condition callers
24
+ actually branch on. Error strings are lowercase, no trailing punctuation.
25
+ </Axiom>
26
+
27
+ <Axiom id="AX_GO_NO_PANIC_IN_LIB">
28
+ Library code returns errors; it does not `panic` on ordinary or expected failures (bad input,
29
+ missing file). `panic` is for programmer bugs and truly unrecoverable state only.
30
+ </Axiom>
31
+
32
+ <Axiom id="AX_GO_NAMING">
33
+ Capitalization is visibility — export only what callers need. No stutter: in package `env`, name it
34
+ `env.Var`, not `env.EnvVar`; `New`/`Open`, not `NewEnv` inside `env`. Receiver names are short (1–2
35
+ letters) and consistent across a type's methods. Interfaces are small and `-er`-named; accept
36
+ interfaces, return concrete structs.
37
+ </Axiom>
38
+ </BeliefState>
39
+
40
+ <AntiPatterns>
41
+ <AntiPattern id="AP_GO_IGNORED_ERROR">
42
+ <Bad>`_ = doThing()` or calling a function that returns `error` and not checking it.</Bad>
43
+ <Instead>Check it: handle, or `return fmt.Errorf("…: %w", err)`. Discard only with a justifying comment.</Instead>
44
+ </AntiPattern>
45
+ <AntiPattern id="AP_GO_PANIC_IN_LIB">
46
+ <Bad>`panic("not found")` inside a reusable package for an expected condition.</Bad>
47
+ <Instead>Return an `error` the caller can handle.</Instead>
48
+ </AntiPattern>
49
+ <AntiPattern id="AP_GO_PERCENT_V_WRAP">
50
+ <Bad>`fmt.Errorf("read config: %v", err)` — `%v` flattens the error; `errors.Is/As` can no longer unwrap it.</Bad>
51
+ <Instead>`fmt.Errorf("read config: %w", err)`.</Instead>
52
+ </AntiPattern>
53
+ <AntiPattern id="AP_GO_STUTTER">
54
+ <Bad>`env.EnvVar`, `client.ClientConfig` — the package qualifier already carries the prefix.</Bad>
55
+ <Instead>`env.Var`, `client.Config`.</Instead>
56
+ </AntiPattern>
57
+ </AntiPatterns>
58
+
59
+ <VerificationHooks>
60
+ <Hook id="HOOK_GO_FMT">
61
+ <Purpose>Code is gofmt-clean.</Purpose>
62
+ <Command>gofmt -l &lt;target-files&gt;</Command>
63
+ <Expected>Empty output (no unformatted files).</Expected>
64
+ </Hook>
65
+ <Hook id="HOOK_GO_VET">
66
+ <Purpose>Vet catches the baseline bug classes.</Purpose>
67
+ <Command>go vet ./...</Command>
68
+ <Expected>Exit 0.</Expected>
69
+ </Hook>
70
+ <Hook id="HOOK_GO_LINT">
71
+ <Purpose>golangci-lint default set (govet, errcheck, staticcheck, ineffassign, unused).</Purpose>
72
+ <Command>golangci-lint run</Command>
73
+ <Expected>Exit 0 (skip if the project does not install it; go vet is the floor).</Expected>
74
+ </Hook>
75
+ <Hook id="HOOK_GO_TESTS">
76
+ <Purpose>The behavior's tests pass.</Purpose>
77
+ <Command>go test ./...</Command>
78
+ <Expected>Exit 0.</Expected>
79
+ </Hook>
80
+ </VerificationHooks>
81
+
82
+ <RewardCriteria>
83
+ ✅ `gofmt`/`goimports`-clean; `go vet ./...` passes.
84
+ ✅ Every error checked; context added with `%w`; inspected via `errors.Is`/`As`.
85
+ ✅ Library code returns errors, never panics on expected failures.
86
+ ✅ No stutter; minimal exported surface; short consistent receiver names; small `-er` interfaces.
87
+ ✅ Table-driven tests with `t.Run` subtests and `t.Helper()` in helpers; stdlib `testing` only at baseline.
88
+ ❌ An ignored/`_`-discarded error without justification.
89
+ ❌ `panic` in library code for an expected failure.
90
+ ❌ `%v` wrapping where `%w` is meant.
91
+ ❌ Stutter naming (`env.EnvVar`).
92
+ ❌ Speculatively-exported types/functions with no caller.
93
+ </RewardCriteria>
94
+ </GoCodingRules>
@@ -0,0 +1,92 @@
1
+ <PythonCodingRules keywords="python, type_hints, ruff, mypy, pytest, exceptions, pep8, ai_to_ai" type="coding-rules" ver="1.0">
2
+ <!--
3
+ Thin Python baseline. Inherits the language-agnostic coding + testing baselines; adds only the
4
+ Python idioms the community broadly agrees on. Not an exhaustive style guide — deliberately small.
5
+ Grounded in: PEP 8, Ruff/mypy docs, PyPA packaging guides (2025-2026 mainstream defaults).
6
+ -->
7
+ <DependsOn>
8
+ - ai/directives/coding/baseline-rules.xml
9
+ - ai/directives/testing/baseline-testing.xml
10
+ </DependsOn>
11
+
12
+ <BeliefState>
13
+ <Axiom id="AX_PY_TYPE_HINTS">
14
+ Public function and method signatures are type-annotated, and a checker (mypy or pyright) runs in
15
+ the project's gate. Typing is gradual, not strict, at baseline: annotate the surface, tighten over
16
+ time. Pick one checker per project; do not mix.
17
+ </Axiom>
18
+
19
+ <Axiom id="AX_PY_NARROW_EXCEPT">
20
+ Catch the narrowest exception you can actually handle (`FileNotFoundError`, `ValueError`), never a
21
+ bare `except:` or blanket `except Exception:` — those swallow `KeyboardInterrupt`/`SystemExit` and
22
+ hide bugs. Chain with `raise NewError(...) from err` to keep the cause; re-raise with a bare
23
+ `raise` after logging to keep the original traceback.
24
+ </Axiom>
25
+
26
+ <Axiom id="AX_PY_NO_MUTABLE_DEFAULT">
27
+ No mutable default arguments (`def f(x=[])` / `{}`): the default is shared across calls. Default to
28
+ `None` and construct inside the body.
29
+ </Axiom>
30
+
31
+ <Axiom id="AX_PY_FORMAT_AND_LINT">
32
+ Formatting and lint are tool-owned, not hand-argued: `ruff format` (Black-compatible, 88 columns)
33
+ plus `ruff check` with at least `F` (pyflakes), `E` (pycodestyle errors), `I` (import order), and
34
+ `B` (bugbear). All tool + packaging config lives in a single `pyproject.toml`.
35
+ </Axiom>
36
+ </BeliefState>
37
+
38
+ <AntiPatterns>
39
+ <AntiPattern id="AP_PY_MUTABLE_DEFAULT">
40
+ <Bad>`def append_to(item, target=[]):` — `target` persists and accumulates across calls.</Bad>
41
+ <Instead>`def append_to(item, target=None): target = [] if target is None else target`.</Instead>
42
+ </AntiPattern>
43
+ <AntiPattern id="AP_PY_BARE_EXCEPT">
44
+ <Bad>`try: … except: pass` or `except Exception:` as a catch-all that hides the failure.</Bad>
45
+ <Instead>Catch the specific type you handle; log-and-`raise` or `raise … from err` otherwise.</Instead>
46
+ </AntiPattern>
47
+ <AntiPattern id="AP_PY_WILDCARD_IMPORT">
48
+ <Bad>`from module import *` — pollutes the namespace and defeats static analysis (Ruff F403/F405).</Bad>
49
+ <Instead>Import the names you use explicitly.</Instead>
50
+ </AntiPattern>
51
+ <AntiPattern id="AP_PY_IDENTITY_COMPARE">
52
+ <Bad>`if x == None:` / `== True` — value comparison to a singleton (Ruff E711/E712).</Bad>
53
+ <Instead>`if x is None:` / `if flag:`.</Instead>
54
+ </AntiPattern>
55
+ </AntiPatterns>
56
+
57
+ <VerificationHooks>
58
+ <Hook id="HOOK_PY_FORMAT">
59
+ <Purpose>Code matches the canonical format.</Purpose>
60
+ <Command>ruff format --check &lt;target-files&gt;</Command>
61
+ <Expected>Exit 0; no files would be reformatted.</Expected>
62
+ </Hook>
63
+ <Hook id="HOOK_PY_LINT">
64
+ <Purpose>No lint findings from the baseline rule sets.</Purpose>
65
+ <Command>ruff check &lt;target-files&gt;</Command>
66
+ <Expected>Exit 0 (F/E/I/B clean).</Expected>
67
+ </Hook>
68
+ <Hook id="HOOK_PY_TYPES">
69
+ <Purpose>Annotations type-check.</Purpose>
70
+ <Command>mypy &lt;target-files&gt;</Command>
71
+ <Expected>Exit 0 (or the project's configured pyright equivalent).</Expected>
72
+ </Hook>
73
+ <Hook id="HOOK_PY_TESTS">
74
+ <Purpose>The behavior's tests pass under pytest.</Purpose>
75
+ <Command>pytest -q</Command>
76
+ <Expected>Exit 0.</Expected>
77
+ </Hook>
78
+ </VerificationHooks>
79
+
80
+ <RewardCriteria>
81
+ ✅ Public signatures are type-annotated; a checker runs in the gate.
82
+ ✅ Exceptions caught are the narrowest handled; re-raise preserves the cause (`raise … from`) or the traceback (bare `raise`).
83
+ ✅ No mutable default arguments; `None` + in-body construction.
84
+ ✅ `ruff format` clean at 88 cols; `ruff check` clean on F/E/I/B; single `pyproject.toml`.
85
+ ✅ Tests use pytest with `@pytest.mark.parametrize` for input variants (one behavior per test).
86
+ ❌ Bare `except:` / blanket `except Exception:` that swallows the error.
87
+ ❌ Mutable default argument.
88
+ ❌ Wildcard import (`from x import *`).
89
+ ❌ `== None` / `== True` singleton comparison.
90
+ ❌ Re-raising a new exception without `from`, losing the original cause.
91
+ </RewardCriteria>
92
+ </PythonCodingRules>
@@ -64,14 +64,43 @@
64
64
  <CheckPhase>lint</CheckPhase>
65
65
  <RequiresVerification>check-command</RequiresVerification>
66
66
  </Rule>
67
+ <Rule id="baseline-rules">
68
+ <File>ai/directives/coding/baseline-rules.xml</File>
69
+ <Purpose>Language-agnostic coding baseline: teleological naming, explicit failures with cause, contract at the surface, intent comments, YAGNI, single responsibility. Parent of the language rule files.</Purpose>
70
+ <Triggers>Target Files include source code files (not config) of any language</Triggers>
71
+ <SkipWhen>Config-only task; a more specific language rule already covers the file and inherits this</SkipWhen>
72
+ <ActivationHint>Before editing any source file. Language rules (python-rules, go-rules, typescript-rules) inherit this — read the specific one too.</ActivationHint>
73
+ <CheckPhase>lint</CheckPhase>
74
+ <RequiresVerification>check-command</RequiresVerification>
75
+ </Rule>
67
76
  <Rule id="typescript-rules">
68
77
  <File>ai/directives/coding/typescript-rules.xml</File>
69
- <Purpose>Writing code in the chosen language: typing, DbC, patterns, anti-patterns.</Purpose>
70
- <Triggers>Target Files include source code files (not config)</Triggers>
71
- <SkipWhen>Config-only task; infra-setup task without code files</SkipWhen>
72
- <ActivationHint>Before editing or creating any source code file</ActivationHint>
78
+ <Purpose>Writing TypeScript: typing, DbC, patterns, anti-patterns. The TS language baseline.</Purpose>
79
+ <Triggers>Target Files include .ts / .tsx source files</Triggers>
80
+ <SkipWhen>Config-only task; non-TypeScript language (see python-rules / go-rules); infra-setup without code files</SkipWhen>
81
+ <ActivationHint>Before editing or creating any .ts / .tsx source file</ActivationHint>
82
+ <CheckPhase>typecheck</CheckPhase>
83
+ <RequiresVerification>check-command</RequiresVerification>
84
+ </Rule>
85
+ <Rule id="python-rules">
86
+ <File>ai/directives/coding/python-rules.xml</File>
87
+ <Purpose>Writing Python: type hints + checker, narrow exceptions with cause, no mutable defaults, ruff format/lint. Inherits baseline-rules.</Purpose>
88
+ <Triggers>Target Files include .py source files</Triggers>
89
+ <SkipWhen>Config-only task; non-Python language</SkipWhen>
90
+ <ActivationHint>Before editing or creating any .py file. Inherits baseline-rules — read both.</ActivationHint>
73
91
  <CheckPhase>typecheck</CheckPhase>
74
92
  <RequiresVerification>check-command</RequiresVerification>
93
+ <CrossRef id="baseline-rules">Parent directive: inherits the language-agnostic coding baseline.</CrossRef>
94
+ </Rule>
95
+ <Rule id="go-rules">
96
+ <File>ai/directives/coding/go-rules.xml</File>
97
+ <Purpose>Writing Go: gofmt/vet, check-and-wrap errors with %w, no panic in libraries, no stutter naming. Inherits baseline-rules.</Purpose>
98
+ <Triggers>Target Files include .go source files</Triggers>
99
+ <SkipWhen>Config-only task; non-Go language</SkipWhen>
100
+ <ActivationHint>Before editing or creating any .go file. Inherits baseline-rules — read both.</ActivationHint>
101
+ <CheckPhase>typecheck</CheckPhase>
102
+ <RequiresVerification>check-command</RequiresVerification>
103
+ <CrossRef id="baseline-rules">Parent directive: inherits the language-agnostic coding baseline.</CrossRef>
75
104
  </Rule>
76
105
  <Rule id="svelte5-runes">
77
106
  <File>ai/directives/coding/svelte5-runes.xml</File>
@@ -97,6 +126,15 @@
97
126
  </Rule>
98
127
  </Coding>
99
128
  <Testing>
129
+ <Rule id="baseline-testing">
130
+ <File>ai/directives/testing/baseline-testing.xml</File>
131
+ <Purpose>Language-agnostic testing baseline: one behavior per named test, determinism, isolation, arrange-act-assert, failure paths covered. Parent of runner-specific testing rules.</Purpose>
132
+ <Triggers>Target Files include test files of any language</Triggers>
133
+ <SkipWhen>no test files in scope; a runner-specific rule already covers them and inherits this</SkipWhen>
134
+ <ActivationHint>Before writing tests in any language. Runner-specific rules inherit this — read the specific one too.</ActivationHint>
135
+ <CheckPhase>test</CheckPhase>
136
+ <RequiresVerification>check-command</RequiresVerification>
137
+ </Rule>
100
138
  <Rule id="testing-common">
101
139
  <File>ai/directives/testing/common.xml</File>
102
140
  <Purpose>Shared testing core inherited by every runner-specific directive: contract boundary, case flow, phase anchors, unified context + factory, BDD mapping, snapshot operator-confirm, file budget.</Purpose>
@@ -278,6 +278,7 @@
278
278
  Missing ticket → `TASK_ID_DRIFT` (`MAJOR`).
279
279
  - Two ticket files declaring same Task-ID → `TASK_ID_DRIFT` (`BLOCKER`).
280
280
  - Compare current `@tasks:` field values against pre-task git ref. Prior IDs removed → `TASK_ID_DRIFT` (`MAJOR`).
281
+ - **Spec-anchor references resolve (SSOT, advisory).** Per scaffold `AX_SSOT_TRACEABILITY` a ticket references spec facts by anchor rather than restating them. Each Markdown anchor link from the ticket into a spec must resolve to a real section/heading. A dangling reference (spec anchor renamed or removed) → `INFO` tagged `dangling-spec-ref`: the reader is sent to a fact that no longer exists. Structural check only — verify the anchor exists; never compare the referenced value against a restated copy (by design there is none).
281
282
  - **Ticket section-anchor coverage** (per scaffold `AX_TICKET_SECTION_NAMES_NORMATIVE`). For
282
283
  each anchor name templated in `TASK_TICKET_STRUCTURE` invoke
283
284
  `<sdd-path> extract <ticket> <NAME>`, using the exact absolute tool path supplied by the
@@ -142,6 +142,7 @@
142
142
  - At least one phase of kind `test` MUST exist whose Target Files contains the test file, unless every scenario has `Deferred Test Ownership`.
143
143
  - Canonical case names are normative: phase-subagent uses verbatim or updates the ticket before phase DONE.
144
144
  - BDD describes behavior, not language-specific syntax.
145
+ - A scenario's expected outcome REFERENCES the spec's canonical fact by anchor (e.g. "error per spec §Error Format"); it does not paste the literal message or value (per `AX_SSOT_TRACEABILITY`). The verifying test carries the literal; the BDD carries the intent and the reference. Concrete input instances in `Given` stay literal — they are the scenario's own data, not a restated spec fact.
145
146
  - For every DbC contract in Spec References (Port / Adapter / Value Object / branded type / discriminated union): one `contract`-level typing scenario MUST exist — covers input/output shape, branded-type rejection at boundary, union exhaustiveness. `Deferred Test Ownership` not allowed for typing scenarios — they ship with the types.
146
147
  </Axiom>
147
148
 
@@ -264,7 +265,20 @@
264
265
  </Axiom>
265
266
 
266
267
  <Axiom id="AX_SSOT_TRACEABILITY">
267
- **No contract duplication.** Tickets link to spec sections via Markdown anchors. Module spec for contracts/consumers/invariants; scope spec 4 only for cross-cutting constraints.
268
+ **No duplication of a canonical fact reference it, do not restate it.** A fact fixed in the
269
+ spec (error-message format, behavior rule, requirement text, signature, contract) has ONE home:
270
+ the spec. Tickets link to it by Markdown anchor; they never paste the literal. A BDD `Then` names
271
+ the spec fact ("error matches spec §Error Format"), it does not re-type the message string.
272
+ Module spec for contracts/consumers/invariants; scope spec 4 only for cross-cutting constraints.
273
+
274
+ The executable literal (the actual error string, the real signature) lives ONLY in the code and
275
+ its test — that pair is the executable projection, kept honest by the test run, not by hand. A
276
+ doc that restates such a literal is drift waiting to happen: an ordinary narrow edit updates the
277
+ spec and code but leaves the doc copy stale, silently contradicting both. Distinct projections
278
+ are NOT duplication and stay: a scenario's concrete input instance (`MISSING_VAR`), the BDD shape
279
+ itself, the diagram, each type's own test. Referencing carries one risk — a dangling anchor — so
280
+ a reference must resolve to a real spec section; that is checkable structurally (does the anchor
281
+ exist), never by matching the value string (matching templates against instances cries wolf).
268
282
  </Axiom>
269
283
 
270
284
  <Axiom id="AX_DIALOGUE_DISCIPLINE">
@@ -0,0 +1,89 @@
1
+ <BaselineTestingRules keywords="baseline, language_agnostic, testing, determinism, isolation, behavior_named, ai_to_ai" type="testing-rules" ver="1.0">
2
+ <!--
3
+ Language-agnostic testing baseline. What a good unit test is in ANY language. Language testing
4
+ rule files (pytest-rules, gotest-rules, vitest-rules, …) inherit this and add framework idioms;
5
+ do not restate these axioms there.
6
+ -->
7
+
8
+ <BeliefState>
9
+ <Axiom id="AX_TEST_ONE_BEHAVIOR">
10
+ One test proves one behavior, named after that behavior — not after the function. A test named
11
+ `retries_until_success_then_stops` documents intent; `test_retry_2` documents nothing. The name
12
+ is the specification a reader trusts without opening the body.
13
+ </Axiom>
14
+
15
+ <Axiom id="AX_TEST_DETERMINISM">
16
+ A test gives the same verdict on every run. No dependence on wall-clock time, random seeds,
17
+ ordering between tests, or ambient environment. Inject the clock, fix the seed, control the
18
+ input. A flaky test is a failing test.
19
+ </Axiom>
20
+
21
+ <Axiom id="AX_TEST_ISOLATION">
22
+ A unit test touches no network, no real filesystem outside a temp dir, no shared mutable global,
23
+ and no other test's state. External collaborators are faked at the boundary. What needs the real
24
+ world is an integration test, named and gated as one.
25
+ </Axiom>
26
+
27
+ <Axiom id="AX_TEST_ARRANGE_ACT_ASSERT">
28
+ A test reads as arrange → act → assert, with the assertion on the behavior's observable outcome,
29
+ not on internal calls. Assert the returned value / raised error / recorded effect — not "method X
30
+ was invoked", unless the interaction itself is the contract.
31
+ </Axiom>
32
+
33
+ <Axiom id="AX_TEST_COVERS_FAILURE">
34
+ The failure paths are tested, not only the happy path. For each documented error mode there is a
35
+ test that provokes it and asserts the specific error. Untested error handling is unverified
36
+ error handling.
37
+ </Axiom>
38
+ </BeliefState>
39
+
40
+ <AntiPatterns>
41
+ <AntiPattern id="AP_TEST_NAMED_AFTER_FUNCTION">
42
+ <Bad>`test_parse`, `test_parse_2`, `test_parse_edge` — names that index the function, not the
43
+ behavior under test.</Bad>
44
+ <Instead>`rejects_empty_input`, `parses_nested_path`, `keeps_last_value_on_repeat`.</Instead>
45
+ </AntiPattern>
46
+
47
+ <AntiPattern id="AP_TEST_NONDETERMINISM">
48
+ <Bad>Asserting on `now()`, real `sleep`, unseeded randomness, or the order tests happen to run in.</Bad>
49
+ <Instead>Inject the clock/seed; assert bounds or exact controlled values.</Instead>
50
+ </AntiPattern>
51
+
52
+ <AntiPattern id="AP_TEST_HITS_WORLD">
53
+ <Bad>A unit test that opens a socket, calls a live API, or writes outside a temp directory.</Bad>
54
+ <Instead>Fake the boundary; move a genuine end-to-end check into a separate, gated integration test.</Instead>
55
+ </AntiPattern>
56
+
57
+ <AntiPattern id="AP_TEST_ASSERTS_MECHANICS">
58
+ <Bad>Asserting a private helper was called, or snapshotting an internal structure, so a valid
59
+ refactor breaks the test.</Bad>
60
+ <Instead>Assert the observable outcome the caller depends on.</Instead>
61
+ </AntiPattern>
62
+ </AntiPatterns>
63
+
64
+ <VerificationHooks>
65
+ <Hook id="HOOK_TESTS_PASS">
66
+ <Purpose>The scope's test suite passes via the project's declared test runner.</Purpose>
67
+ <Command>&lt;sdd-path&gt; verify --wip &lt;target-files&gt;</Command>
68
+ <Expected>Exit 0; the changed behavior's tests present and green.</Expected>
69
+ </Hook>
70
+ <Hook id="HOOK_NO_SKIPS">
71
+ <Purpose>No silently disabled tests shipped as coverage.</Purpose>
72
+ <Command>rg --no-heading -n "skip|xfail|t\.Skip|it\.only|fdescribe|\.only\(" &lt;target-files&gt;</Command>
73
+ <Expected>Empty on changed lines, or each skip carries a comment with a ticket reference.</Expected>
74
+ </Hook>
75
+ </VerificationHooks>
76
+
77
+ <RewardCriteria>
78
+ ✅ Each test proves one behavior and is named after that behavior.
79
+ ✅ Deterministic: clock/seed injected, no cross-test ordering dependence.
80
+ ✅ Isolated: no network, no real FS outside a temp dir, no shared global; boundaries faked.
81
+ ✅ Arrange → act → assert; assertion on the observable outcome.
82
+ ✅ Every documented failure mode has a test that provokes it and asserts the specific error.
83
+ ❌ Tests named after the function (`test_parse_2`) rather than the behavior.
84
+ ❌ Dependence on real time, unseeded randomness, or run order.
85
+ ❌ A unit test that touches the network, live services, or the real filesystem.
86
+ ❌ Assertions on internal mechanics that a valid refactor would break.
87
+ ❌ Disabled/skipped tests shipped without a referenced reason.
88
+ </RewardCriteria>
89
+ </BaselineTestingRules>
@@ -0,0 +1,101 @@
1
+ <BaselineCodingRules keywords="baseline, language_agnostic, contracts, error_handling, naming, yagni, ai_to_ai" type="coding-rules" ver="1.0">
2
+ <!--
3
+ Language-agnostic coding baseline. The few things that hold in ANY language. Language rule files
4
+ (python-rules, go-rules, typescript-rules, …) inherit this and add their own idioms; do not
5
+ restate these axioms there. Activated for source code files of a scope that declares no more
6
+ specific language rule, and as the transitive parent of the language rules that reference it.
7
+ -->
8
+
9
+ <BeliefState>
10
+ <Axiom id="AX_TELEOLOGICAL_NAMING">
11
+ A name states a goal, not a pattern. Forbidden semantic-noise names: `Manager`, `Handler`,
12
+ `Data`, `Info`, `Wrapper`, `Util`, `do…`, `process…`. `OrderFulfillmentPipeline` carries intent;
13
+ `OrderManager` tells the next reader nothing. Use precise verbs (`retrieve`, `verify`, `compose`)
14
+ over vague ones (`get`, `check`, `make`).
15
+ </Axiom>
16
+
17
+ <Axiom id="AX_EXPLICIT_FAILURE">
18
+ Failures are explicit and carry cause. Raise/return a specific, typed error with a message that
19
+ says what failed and with what input; when re-raising, preserve the original cause (do not
20
+ flatten a stack into a string). Never silently swallow an error or return a zero value that hides
21
+ one.
22
+ </Axiom>
23
+
24
+ <Axiom id="AX_CONTRACT_AT_SURFACE">
25
+ The public surface documents its contract: inputs, output, and failure modes, at the declaration
26
+ — not buried in the body. A caller must be able to use the function from its signature plus one
27
+ doc block, without reading the implementation.
28
+ </Axiom>
29
+
30
+ <Axiom id="AX_INTENT_COMMENTS">
31
+ Comments carry intent the code cannot: purpose, invariant, side effect, failure mode, non-goal,
32
+ or why-not. A comment that restates the syntax on the next line is noise and must be removed.
33
+ </Axiom>
34
+
35
+ <Axiom id="AX_YAGNI_NO_DEFENSE">
36
+ Build only what the task needs. No speculative abstraction (an interface/base class for a single
37
+ caller with no confirmed second one), no dead code, and no defensive branches against states the
38
+ types or invariants already exclude. Prefer the simplest shape that satisfies the contract.
39
+ </Axiom>
40
+
41
+ <Axiom id="AX_SINGLE_RESPONSIBILITY">
42
+ A unit does one thing at one level of abstraction. A function that mixes I/O, policy, and
43
+ formatting is split. Depth of nesting and length are symptoms; name the extracted piece by its
44
+ goal.
45
+ </Axiom>
46
+ </BeliefState>
47
+
48
+ <AntiPatterns>
49
+ <AntiPattern id="AP_SWALLOWED_ERROR">
50
+ <Bad>A caught error that is logged-and-ignored, discarded, or replaced by a generic message that
51
+ drops the original cause — so the failure surfaces later with no trace of its origin.</Bad>
52
+ <Instead>Handle it (recover with a documented fallback) or propagate it with the cause attached.
53
+ One or the other, never neither.</Instead>
54
+ </AntiPattern>
55
+
56
+ <AntiPattern id="AP_PATTERN_NAMING">
57
+ <Bad>`FooManager`, `DataHandler`, `RequestWrapper`, `doWork`, `processItem` — names that describe
58
+ a code pattern rather than a business goal.</Bad>
59
+ <Instead>Name the responsibility: `SubscriptionRenewal`, `parseVcsUrl`, `retryWithBackoff`.</Instead>
60
+ </AntiPattern>
61
+
62
+ <AntiPattern id="AP_SYNTAX_COMMENT">
63
+ <Bad>`// increment i` above `i++`; a doc block that repeats parameter names and types already in
64
+ the signature.</Bad>
65
+ <Instead>Comment the why, the invariant, or the non-obvious consequence — or nothing.</Instead>
66
+ </AntiPattern>
67
+
68
+ <AntiPattern id="AP_SPECULATIVE_ABSTRACTION">
69
+ <Bad>Extracting an interface, factory, or config option for one caller "in case we need it".</Bad>
70
+ <Instead>Inline it; abstract on the second real consumer, when the variance is known.</Instead>
71
+ </AntiPattern>
72
+ </AntiPatterns>
73
+
74
+ <VerificationHooks>
75
+ <Hook id="HOOK_PROJECT_GATE">
76
+ <Purpose>The scope's own quality gate (formatter + linter + type/compile check + tests, as the
77
+ project declares them) passes on the changed files.</Purpose>
78
+ <Command>&lt;sdd-path&gt; verify --wip &lt;target-files&gt;</Command>
79
+ <Expected>Exit 0. If the scope has not declared its gate yet, that is the finding to fix first.</Expected>
80
+ </Hook>
81
+ <Hook id="HOOK_NO_SCAFFOLD_LEFTOVER">
82
+ <Purpose>No placeholder/scaffold markers survive in shipped code.</Purpose>
83
+ <Command>rg --no-heading -n "TODO|FIXME|XXX|&lt;[A-Z_]+&gt;|placeholder" &lt;target-files&gt;</Command>
84
+ <Expected>Empty on changed lines; a real deferred item belongs in a ticket, not a bare marker.</Expected>
85
+ </Hook>
86
+ </VerificationHooks>
87
+
88
+ <RewardCriteria>
89
+ ✅ Names state a goal; precise verbs; no `Manager`/`Handler`/`Data`/`Util`/`do…`/`process…`.
90
+ ✅ Every failure path raises/returns a specific error with context and preserved cause.
91
+ ✅ Public entities document inputs, output, and failure modes at the declaration.
92
+ ✅ Comments carry intent (purpose / invariant / side effect / failure / non-goal), never restate syntax.
93
+ ✅ Only what the task needs: no speculative abstraction, no dead code, no defense against impossible states.
94
+ ✅ Each unit has one responsibility at one abstraction level.
95
+ ❌ An error caught and dropped, or re-raised with the cause lost.
96
+ ❌ Pattern-shaped names that hide the business goal.
97
+ ❌ Comments that restate the code, or doc blocks duplicating the signature.
98
+ ❌ An abstraction introduced for a single caller with no confirmed second consumer.
99
+ ❌ Placeholder/scaffold markers (`TODO`, `&lt;NAME&gt;`) left in shipped code.
100
+ </RewardCriteria>
101
+ </BaselineCodingRules>
@@ -0,0 +1,94 @@
1
+ <GoCodingRules keywords="go, golang, gofmt, govet, errors, wrapping, naming, testing, ai_to_ai" type="coding-rules" ver="1.0">
2
+ <!--
3
+ Thin Go baseline. Inherits the language-agnostic coding + testing baselines; adds only the Go
4
+ idioms the community broadly agrees on. Not an exhaustive style guide — deliberately small.
5
+ Grounded in: Effective Go, Go Code Review Comments, go.dev/blog/go1.13-errors, official tooling
6
+ (gofmt, go vet, golangci-lint) — the intersection Uber/Google style guides also share.
7
+ -->
8
+ <DependsOn>
9
+ - ai/directives/coding/baseline-rules.xml
10
+ - ai/directives/testing/baseline-testing.xml
11
+ </DependsOn>
12
+
13
+ <BeliefState>
14
+ <Axiom id="AX_GO_GOFMT">
15
+ Code is `gofmt`/`goimports`-clean — non-negotiable, zero config. `goimports` is preferred (it also
16
+ groups and prunes imports). Tabs, tool-decided layout; there are no formatting debates in Go.
17
+ </Axiom>
18
+
19
+ <Axiom id="AX_GO_CHECK_AND_WRAP_ERRORS">
20
+ `error` is the last return value and every error is checked — never discarded with `_` unless the
21
+ discard is deliberate and justified. Add context by wrapping with `fmt.Errorf("doing X: %w", err)`
22
+ (`%w`, not `%v`, so callers can unwrap). Inspect with `errors.Is` / `errors.As`, not `==` or type
23
+ assertions. Define a sentinel (`var ErrNotFound = errors.New(…)`) only for a condition callers
24
+ actually branch on. Error strings are lowercase, no trailing punctuation.
25
+ </Axiom>
26
+
27
+ <Axiom id="AX_GO_NO_PANIC_IN_LIB">
28
+ Library code returns errors; it does not `panic` on ordinary or expected failures (bad input,
29
+ missing file). `panic` is for programmer bugs and truly unrecoverable state only.
30
+ </Axiom>
31
+
32
+ <Axiom id="AX_GO_NAMING">
33
+ Capitalization is visibility — export only what callers need. No stutter: in package `env`, name it
34
+ `env.Var`, not `env.EnvVar`; `New`/`Open`, not `NewEnv` inside `env`. Receiver names are short (1–2
35
+ letters) and consistent across a type's methods. Interfaces are small and `-er`-named; accept
36
+ interfaces, return concrete structs.
37
+ </Axiom>
38
+ </BeliefState>
39
+
40
+ <AntiPatterns>
41
+ <AntiPattern id="AP_GO_IGNORED_ERROR">
42
+ <Bad>`_ = doThing()` or calling a function that returns `error` and not checking it.</Bad>
43
+ <Instead>Check it: handle, or `return fmt.Errorf("…: %w", err)`. Discard only with a justifying comment.</Instead>
44
+ </AntiPattern>
45
+ <AntiPattern id="AP_GO_PANIC_IN_LIB">
46
+ <Bad>`panic("not found")` inside a reusable package for an expected condition.</Bad>
47
+ <Instead>Return an `error` the caller can handle.</Instead>
48
+ </AntiPattern>
49
+ <AntiPattern id="AP_GO_PERCENT_V_WRAP">
50
+ <Bad>`fmt.Errorf("read config: %v", err)` — `%v` flattens the error; `errors.Is/As` can no longer unwrap it.</Bad>
51
+ <Instead>`fmt.Errorf("read config: %w", err)`.</Instead>
52
+ </AntiPattern>
53
+ <AntiPattern id="AP_GO_STUTTER">
54
+ <Bad>`env.EnvVar`, `client.ClientConfig` — the package qualifier already carries the prefix.</Bad>
55
+ <Instead>`env.Var`, `client.Config`.</Instead>
56
+ </AntiPattern>
57
+ </AntiPatterns>
58
+
59
+ <VerificationHooks>
60
+ <Hook id="HOOK_GO_FMT">
61
+ <Purpose>Code is gofmt-clean.</Purpose>
62
+ <Command>gofmt -l &lt;target-files&gt;</Command>
63
+ <Expected>Empty output (no unformatted files).</Expected>
64
+ </Hook>
65
+ <Hook id="HOOK_GO_VET">
66
+ <Purpose>Vet catches the baseline bug classes.</Purpose>
67
+ <Command>go vet ./...</Command>
68
+ <Expected>Exit 0.</Expected>
69
+ </Hook>
70
+ <Hook id="HOOK_GO_LINT">
71
+ <Purpose>golangci-lint default set (govet, errcheck, staticcheck, ineffassign, unused).</Purpose>
72
+ <Command>golangci-lint run</Command>
73
+ <Expected>Exit 0 (skip if the project does not install it; go vet is the floor).</Expected>
74
+ </Hook>
75
+ <Hook id="HOOK_GO_TESTS">
76
+ <Purpose>The behavior's tests pass.</Purpose>
77
+ <Command>go test ./...</Command>
78
+ <Expected>Exit 0.</Expected>
79
+ </Hook>
80
+ </VerificationHooks>
81
+
82
+ <RewardCriteria>
83
+ ✅ `gofmt`/`goimports`-clean; `go vet ./...` passes.
84
+ ✅ Every error checked; context added with `%w`; inspected via `errors.Is`/`As`.
85
+ ✅ Library code returns errors, never panics on expected failures.
86
+ ✅ No stutter; minimal exported surface; short consistent receiver names; small `-er` interfaces.
87
+ ✅ Table-driven tests with `t.Run` subtests and `t.Helper()` in helpers; stdlib `testing` only at baseline.
88
+ ❌ An ignored/`_`-discarded error without justification.
89
+ ❌ `panic` in library code for an expected failure.
90
+ ❌ `%v` wrapping where `%w` is meant.
91
+ ❌ Stutter naming (`env.EnvVar`).
92
+ ❌ Speculatively-exported types/functions with no caller.
93
+ </RewardCriteria>
94
+ </GoCodingRules>
@@ -0,0 +1,92 @@
1
+ <PythonCodingRules keywords="python, type_hints, ruff, mypy, pytest, exceptions, pep8, ai_to_ai" type="coding-rules" ver="1.0">
2
+ <!--
3
+ Thin Python baseline. Inherits the language-agnostic coding + testing baselines; adds only the
4
+ Python idioms the community broadly agrees on. Not an exhaustive style guide — deliberately small.
5
+ Grounded in: PEP 8, Ruff/mypy docs, PyPA packaging guides (2025-2026 mainstream defaults).
6
+ -->
7
+ <DependsOn>
8
+ - ai/directives/coding/baseline-rules.xml
9
+ - ai/directives/testing/baseline-testing.xml
10
+ </DependsOn>
11
+
12
+ <BeliefState>
13
+ <Axiom id="AX_PY_TYPE_HINTS">
14
+ Public function and method signatures are type-annotated, and a checker (mypy or pyright) runs in
15
+ the project's gate. Typing is gradual, not strict, at baseline: annotate the surface, tighten over
16
+ time. Pick one checker per project; do not mix.
17
+ </Axiom>
18
+
19
+ <Axiom id="AX_PY_NARROW_EXCEPT">
20
+ Catch the narrowest exception you can actually handle (`FileNotFoundError`, `ValueError`), never a
21
+ bare `except:` or blanket `except Exception:` — those swallow `KeyboardInterrupt`/`SystemExit` and
22
+ hide bugs. Chain with `raise NewError(...) from err` to keep the cause; re-raise with a bare
23
+ `raise` after logging to keep the original traceback.
24
+ </Axiom>
25
+
26
+ <Axiom id="AX_PY_NO_MUTABLE_DEFAULT">
27
+ No mutable default arguments (`def f(x=[])` / `{}`): the default is shared across calls. Default to
28
+ `None` and construct inside the body.
29
+ </Axiom>
30
+
31
+ <Axiom id="AX_PY_FORMAT_AND_LINT">
32
+ Formatting and lint are tool-owned, not hand-argued: `ruff format` (Black-compatible, 88 columns)
33
+ plus `ruff check` with at least `F` (pyflakes), `E` (pycodestyle errors), `I` (import order), and
34
+ `B` (bugbear). All tool + packaging config lives in a single `pyproject.toml`.
35
+ </Axiom>
36
+ </BeliefState>
37
+
38
+ <AntiPatterns>
39
+ <AntiPattern id="AP_PY_MUTABLE_DEFAULT">
40
+ <Bad>`def append_to(item, target=[]):` — `target` persists and accumulates across calls.</Bad>
41
+ <Instead>`def append_to(item, target=None): target = [] if target is None else target`.</Instead>
42
+ </AntiPattern>
43
+ <AntiPattern id="AP_PY_BARE_EXCEPT">
44
+ <Bad>`try: … except: pass` or `except Exception:` as a catch-all that hides the failure.</Bad>
45
+ <Instead>Catch the specific type you handle; log-and-`raise` or `raise … from err` otherwise.</Instead>
46
+ </AntiPattern>
47
+ <AntiPattern id="AP_PY_WILDCARD_IMPORT">
48
+ <Bad>`from module import *` — pollutes the namespace and defeats static analysis (Ruff F403/F405).</Bad>
49
+ <Instead>Import the names you use explicitly.</Instead>
50
+ </AntiPattern>
51
+ <AntiPattern id="AP_PY_IDENTITY_COMPARE">
52
+ <Bad>`if x == None:` / `== True` — value comparison to a singleton (Ruff E711/E712).</Bad>
53
+ <Instead>`if x is None:` / `if flag:`.</Instead>
54
+ </AntiPattern>
55
+ </AntiPatterns>
56
+
57
+ <VerificationHooks>
58
+ <Hook id="HOOK_PY_FORMAT">
59
+ <Purpose>Code matches the canonical format.</Purpose>
60
+ <Command>ruff format --check &lt;target-files&gt;</Command>
61
+ <Expected>Exit 0; no files would be reformatted.</Expected>
62
+ </Hook>
63
+ <Hook id="HOOK_PY_LINT">
64
+ <Purpose>No lint findings from the baseline rule sets.</Purpose>
65
+ <Command>ruff check &lt;target-files&gt;</Command>
66
+ <Expected>Exit 0 (F/E/I/B clean).</Expected>
67
+ </Hook>
68
+ <Hook id="HOOK_PY_TYPES">
69
+ <Purpose>Annotations type-check.</Purpose>
70
+ <Command>mypy &lt;target-files&gt;</Command>
71
+ <Expected>Exit 0 (or the project's configured pyright equivalent).</Expected>
72
+ </Hook>
73
+ <Hook id="HOOK_PY_TESTS">
74
+ <Purpose>The behavior's tests pass under pytest.</Purpose>
75
+ <Command>pytest -q</Command>
76
+ <Expected>Exit 0.</Expected>
77
+ </Hook>
78
+ </VerificationHooks>
79
+
80
+ <RewardCriteria>
81
+ ✅ Public signatures are type-annotated; a checker runs in the gate.
82
+ ✅ Exceptions caught are the narrowest handled; re-raise preserves the cause (`raise … from`) or the traceback (bare `raise`).
83
+ ✅ No mutable default arguments; `None` + in-body construction.
84
+ ✅ `ruff format` clean at 88 cols; `ruff check` clean on F/E/I/B; single `pyproject.toml`.
85
+ ✅ Tests use pytest with `@pytest.mark.parametrize` for input variants (one behavior per test).
86
+ ❌ Bare `except:` / blanket `except Exception:` that swallows the error.
87
+ ❌ Mutable default argument.
88
+ ❌ Wildcard import (`from x import *`).
89
+ ❌ `== None` / `== True` singleton comparison.
90
+ ❌ Re-raising a new exception without `from`, losing the original cause.
91
+ </RewardCriteria>
92
+ </PythonCodingRules>
@@ -64,14 +64,43 @@
64
64
  <CheckPhase>lint</CheckPhase>
65
65
  <RequiresVerification>check-command</RequiresVerification>
66
66
  </Rule>
67
+ <Rule id="baseline-rules">
68
+ <File>ai/directives/coding/baseline-rules.xml</File>
69
+ <Purpose>Language-agnostic coding baseline: teleological naming, explicit failures with cause, contract at the surface, intent comments, YAGNI, single responsibility. Parent of the language rule files.</Purpose>
70
+ <Triggers>Target Files include source code files (not config) of any language</Triggers>
71
+ <SkipWhen>Config-only task; a more specific language rule already covers the file and inherits this</SkipWhen>
72
+ <ActivationHint>Before editing any source file. Language rules (python-rules, go-rules, typescript-rules) inherit this — read the specific one too.</ActivationHint>
73
+ <CheckPhase>lint</CheckPhase>
74
+ <RequiresVerification>check-command</RequiresVerification>
75
+ </Rule>
67
76
  <Rule id="typescript-rules">
68
77
  <File>ai/directives/coding/typescript-rules.xml</File>
69
- <Purpose>Writing code in the chosen language: typing, DbC, patterns, anti-patterns.</Purpose>
70
- <Triggers>Target Files include source code files (not config)</Triggers>
71
- <SkipWhen>Config-only task; infra-setup task without code files</SkipWhen>
72
- <ActivationHint>Before editing or creating any source code file</ActivationHint>
78
+ <Purpose>Writing TypeScript: typing, DbC, patterns, anti-patterns. The TS language baseline.</Purpose>
79
+ <Triggers>Target Files include .ts / .tsx source files</Triggers>
80
+ <SkipWhen>Config-only task; non-TypeScript language (see python-rules / go-rules); infra-setup without code files</SkipWhen>
81
+ <ActivationHint>Before editing or creating any .ts / .tsx source file</ActivationHint>
82
+ <CheckPhase>typecheck</CheckPhase>
83
+ <RequiresVerification>check-command</RequiresVerification>
84
+ </Rule>
85
+ <Rule id="python-rules">
86
+ <File>ai/directives/coding/python-rules.xml</File>
87
+ <Purpose>Writing Python: type hints + checker, narrow exceptions with cause, no mutable defaults, ruff format/lint. Inherits baseline-rules.</Purpose>
88
+ <Triggers>Target Files include .py source files</Triggers>
89
+ <SkipWhen>Config-only task; non-Python language</SkipWhen>
90
+ <ActivationHint>Before editing or creating any .py file. Inherits baseline-rules — read both.</ActivationHint>
73
91
  <CheckPhase>typecheck</CheckPhase>
74
92
  <RequiresVerification>check-command</RequiresVerification>
93
+ <CrossRef id="baseline-rules">Parent directive: inherits the language-agnostic coding baseline.</CrossRef>
94
+ </Rule>
95
+ <Rule id="go-rules">
96
+ <File>ai/directives/coding/go-rules.xml</File>
97
+ <Purpose>Writing Go: gofmt/vet, check-and-wrap errors with %w, no panic in libraries, no stutter naming. Inherits baseline-rules.</Purpose>
98
+ <Triggers>Target Files include .go source files</Triggers>
99
+ <SkipWhen>Config-only task; non-Go language</SkipWhen>
100
+ <ActivationHint>Before editing or creating any .go file. Inherits baseline-rules — read both.</ActivationHint>
101
+ <CheckPhase>typecheck</CheckPhase>
102
+ <RequiresVerification>check-command</RequiresVerification>
103
+ <CrossRef id="baseline-rules">Parent directive: inherits the language-agnostic coding baseline.</CrossRef>
75
104
  </Rule>
76
105
  <Rule id="svelte5-runes">
77
106
  <File>ai/directives/coding/svelte5-runes.xml</File>
@@ -97,6 +126,15 @@
97
126
  </Rule>
98
127
  </Coding>
99
128
  <Testing>
129
+ <Rule id="baseline-testing">
130
+ <File>ai/directives/testing/baseline-testing.xml</File>
131
+ <Purpose>Language-agnostic testing baseline: one behavior per named test, determinism, isolation, arrange-act-assert, failure paths covered. Parent of runner-specific testing rules.</Purpose>
132
+ <Triggers>Target Files include test files of any language</Triggers>
133
+ <SkipWhen>no test files in scope; a runner-specific rule already covers them and inherits this</SkipWhen>
134
+ <ActivationHint>Before writing tests in any language. Runner-specific rules inherit this — read the specific one too.</ActivationHint>
135
+ <CheckPhase>test</CheckPhase>
136
+ <RequiresVerification>check-command</RequiresVerification>
137
+ </Rule>
100
138
  <Rule id="testing-common">
101
139
  <File>ai/directives/testing/common.xml</File>
102
140
  <Purpose>Shared testing core inherited by every runner-specific directive: contract boundary, case flow, phase anchors, unified context + factory, BDD mapping, snapshot operator-confirm, file budget.</Purpose>
@@ -278,6 +278,7 @@
278
278
  Missing ticket → `TASK_ID_DRIFT` (`MAJOR`).
279
279
  - Two ticket files declaring same Task-ID → `TASK_ID_DRIFT` (`BLOCKER`).
280
280
  - Compare current `@tasks:` field values against pre-task git ref. Prior IDs removed → `TASK_ID_DRIFT` (`MAJOR`).
281
+ - **Spec-anchor references resolve (SSOT, advisory).** Per scaffold `AX_SSOT_TRACEABILITY` a ticket references spec facts by anchor rather than restating them. Each Markdown anchor link from the ticket into a spec must resolve to a real section/heading. A dangling reference (spec anchor renamed or removed) → `INFO` tagged `dangling-spec-ref`: the reader is sent to a fact that no longer exists. Structural check only — verify the anchor exists; never compare the referenced value against a restated copy (by design there is none).
281
282
  - **Ticket section-anchor coverage** (per scaffold `AX_TICKET_SECTION_NAMES_NORMATIVE`). For
282
283
  each anchor name templated in `TASK_TICKET_STRUCTURE` invoke
283
284
  `<sdd-path> extract <ticket> <NAME>`, using the exact absolute tool path supplied by the
@@ -142,6 +142,7 @@
142
142
  - At least one phase of kind `test` MUST exist whose Target Files contains the test file, unless every scenario has `Deferred Test Ownership`.
143
143
  - Canonical case names are normative: phase-subagent uses verbatim or updates the ticket before phase DONE.
144
144
  - BDD describes behavior, not language-specific syntax.
145
+ - A scenario's expected outcome REFERENCES the spec's canonical fact by anchor (e.g. "error per spec §Error Format"); it does not paste the literal message or value (per `AX_SSOT_TRACEABILITY`). The verifying test carries the literal; the BDD carries the intent and the reference. Concrete input instances in `Given` stay literal — they are the scenario's own data, not a restated spec fact.
145
146
  - For every DbC contract in Spec References (Port / Adapter / Value Object / branded type / discriminated union): one `contract`-level typing scenario MUST exist — covers input/output shape, branded-type rejection at boundary, union exhaustiveness. `Deferred Test Ownership` not allowed for typing scenarios — they ship with the types.
146
147
  </Axiom>
147
148
 
@@ -264,7 +265,20 @@
264
265
  </Axiom>
265
266
 
266
267
  <Axiom id="AX_SSOT_TRACEABILITY">
267
- **No contract duplication.** Tickets link to spec sections via Markdown anchors. Module spec for contracts/consumers/invariants; scope spec 4 only for cross-cutting constraints.
268
+ **No duplication of a canonical fact reference it, do not restate it.** A fact fixed in the
269
+ spec (error-message format, behavior rule, requirement text, signature, contract) has ONE home:
270
+ the spec. Tickets link to it by Markdown anchor; they never paste the literal. A BDD `Then` names
271
+ the spec fact ("error matches spec §Error Format"), it does not re-type the message string.
272
+ Module spec for contracts/consumers/invariants; scope spec 4 only for cross-cutting constraints.
273
+
274
+ The executable literal (the actual error string, the real signature) lives ONLY in the code and
275
+ its test — that pair is the executable projection, kept honest by the test run, not by hand. A
276
+ doc that restates such a literal is drift waiting to happen: an ordinary narrow edit updates the
277
+ spec and code but leaves the doc copy stale, silently contradicting both. Distinct projections
278
+ are NOT duplication and stay: a scenario's concrete input instance (`MISSING_VAR`), the BDD shape
279
+ itself, the diagram, each type's own test. Referencing carries one risk — a dangling anchor — so
280
+ a reference must resolve to a real spec section; that is checkable structurally (does the anchor
281
+ exist), never by matching the value string (matching templates against instances cries wolf).
268
282
  </Axiom>
269
283
 
270
284
  <Axiom id="AX_DIALOGUE_DISCIPLINE">
@@ -0,0 +1,89 @@
1
+ <BaselineTestingRules keywords="baseline, language_agnostic, testing, determinism, isolation, behavior_named, ai_to_ai" type="testing-rules" ver="1.0">
2
+ <!--
3
+ Language-agnostic testing baseline. What a good unit test is in ANY language. Language testing
4
+ rule files (pytest-rules, gotest-rules, vitest-rules, …) inherit this and add framework idioms;
5
+ do not restate these axioms there.
6
+ -->
7
+
8
+ <BeliefState>
9
+ <Axiom id="AX_TEST_ONE_BEHAVIOR">
10
+ One test proves one behavior, named after that behavior — not after the function. A test named
11
+ `retries_until_success_then_stops` documents intent; `test_retry_2` documents nothing. The name
12
+ is the specification a reader trusts without opening the body.
13
+ </Axiom>
14
+
15
+ <Axiom id="AX_TEST_DETERMINISM">
16
+ A test gives the same verdict on every run. No dependence on wall-clock time, random seeds,
17
+ ordering between tests, or ambient environment. Inject the clock, fix the seed, control the
18
+ input. A flaky test is a failing test.
19
+ </Axiom>
20
+
21
+ <Axiom id="AX_TEST_ISOLATION">
22
+ A unit test touches no network, no real filesystem outside a temp dir, no shared mutable global,
23
+ and no other test's state. External collaborators are faked at the boundary. What needs the real
24
+ world is an integration test, named and gated as one.
25
+ </Axiom>
26
+
27
+ <Axiom id="AX_TEST_ARRANGE_ACT_ASSERT">
28
+ A test reads as arrange → act → assert, with the assertion on the behavior's observable outcome,
29
+ not on internal calls. Assert the returned value / raised error / recorded effect — not "method X
30
+ was invoked", unless the interaction itself is the contract.
31
+ </Axiom>
32
+
33
+ <Axiom id="AX_TEST_COVERS_FAILURE">
34
+ The failure paths are tested, not only the happy path. For each documented error mode there is a
35
+ test that provokes it and asserts the specific error. Untested error handling is unverified
36
+ error handling.
37
+ </Axiom>
38
+ </BeliefState>
39
+
40
+ <AntiPatterns>
41
+ <AntiPattern id="AP_TEST_NAMED_AFTER_FUNCTION">
42
+ <Bad>`test_parse`, `test_parse_2`, `test_parse_edge` — names that index the function, not the
43
+ behavior under test.</Bad>
44
+ <Instead>`rejects_empty_input`, `parses_nested_path`, `keeps_last_value_on_repeat`.</Instead>
45
+ </AntiPattern>
46
+
47
+ <AntiPattern id="AP_TEST_NONDETERMINISM">
48
+ <Bad>Asserting on `now()`, real `sleep`, unseeded randomness, or the order tests happen to run in.</Bad>
49
+ <Instead>Inject the clock/seed; assert bounds or exact controlled values.</Instead>
50
+ </AntiPattern>
51
+
52
+ <AntiPattern id="AP_TEST_HITS_WORLD">
53
+ <Bad>A unit test that opens a socket, calls a live API, or writes outside a temp directory.</Bad>
54
+ <Instead>Fake the boundary; move a genuine end-to-end check into a separate, gated integration test.</Instead>
55
+ </AntiPattern>
56
+
57
+ <AntiPattern id="AP_TEST_ASSERTS_MECHANICS">
58
+ <Bad>Asserting a private helper was called, or snapshotting an internal structure, so a valid
59
+ refactor breaks the test.</Bad>
60
+ <Instead>Assert the observable outcome the caller depends on.</Instead>
61
+ </AntiPattern>
62
+ </AntiPatterns>
63
+
64
+ <VerificationHooks>
65
+ <Hook id="HOOK_TESTS_PASS">
66
+ <Purpose>The scope's test suite passes via the project's declared test runner.</Purpose>
67
+ <Command>&lt;sdd-path&gt; verify --wip &lt;target-files&gt;</Command>
68
+ <Expected>Exit 0; the changed behavior's tests present and green.</Expected>
69
+ </Hook>
70
+ <Hook id="HOOK_NO_SKIPS">
71
+ <Purpose>No silently disabled tests shipped as coverage.</Purpose>
72
+ <Command>rg --no-heading -n "skip|xfail|t\.Skip|it\.only|fdescribe|\.only\(" &lt;target-files&gt;</Command>
73
+ <Expected>Empty on changed lines, or each skip carries a comment with a ticket reference.</Expected>
74
+ </Hook>
75
+ </VerificationHooks>
76
+
77
+ <RewardCriteria>
78
+ ✅ Each test proves one behavior and is named after that behavior.
79
+ ✅ Deterministic: clock/seed injected, no cross-test ordering dependence.
80
+ ✅ Isolated: no network, no real FS outside a temp dir, no shared global; boundaries faked.
81
+ ✅ Arrange → act → assert; assertion on the observable outcome.
82
+ ✅ Every documented failure mode has a test that provokes it and asserts the specific error.
83
+ ❌ Tests named after the function (`test_parse_2`) rather than the behavior.
84
+ ❌ Dependence on real time, unseeded randomness, or run order.
85
+ ❌ A unit test that touches the network, live services, or the real filesystem.
86
+ ❌ Assertions on internal mechanics that a valid refactor would break.
87
+ ❌ Disabled/skipped tests shipped without a referenced reason.
88
+ </RewardCriteria>
89
+ </BaselineTestingRules>
package/dist/gennady.js CHANGED
@@ -3,7 +3,7 @@ import { c as s } from "./chunks/shared-CCgxBhk_.js";
3
3
  import "node:fs";
4
4
  import "node:path";
5
5
  import "node:url";
6
- const r = "0.9.0-next.2", i = /* @__PURE__ */ new Set(["help", "--help", "-h"]), p = /* @__PURE__ */ new Set(["--version", "-v"]), t = process.argv[2];
6
+ const r = "0.9.0-next.4", i = /* @__PURE__ */ new Set(["help", "--help", "-h"]), p = /* @__PURE__ */ new Set(["--version", "-v"]), t = process.argv[2];
7
7
  p.has(t) && (console.log(r), process.exit(0));
8
8
  (!t || i.has(t)) && (await import("./chunks/help.cmd-Bz1YqU2V.js"), process.exit(0));
9
9
  s({ name: "gennady", version: r });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gennady",
3
- "version": "0.9.0-next.2",
3
+ "version": "0.9.0-next.4",
4
4
  "author": "Konstantin Lebedev <ibnrubaxa@gmail.com>",
5
5
  "description": "Gennady — General Extensible Neural Network Adaptive Data Yntelligence",
6
6
  "keywords": [