simt-dev 0.2.0__py3-none-any.whl
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.
- python/simt_dev/__init__.py +32 -0
- python/simt_dev/cli.py +225 -0
- simt_dev/__init__.py +32 -0
- simt_dev/cli.py +225 -0
- simt_dev/vendored/CHANGELOG.md +166 -0
- simt_dev/vendored/bin/simt-dev.mjs +28 -0
- simt_dev/vendored/docs/decisions/ADR-001-push-requires-approval.md +42 -0
- simt_dev/vendored/docs/decisions/ADR-002-refactor-blast-radius.md +45 -0
- simt_dev/vendored/docs/decisions/ADR-003-test-breadth.md +46 -0
- simt_dev/vendored/docs/decisions/ADR-004-hook-owns-format.md +45 -0
- simt_dev/vendored/docs/decisions/ADR-005-file-length.md +42 -0
- simt_dev/vendored/docs/decisions/ADR-006-coverage-bar.md +59 -0
- simt_dev/vendored/docs/decisions/ADR-007-section-skeleton.md +54 -0
- simt_dev/vendored/docs/decisions/ADR-008-verdict-vocabulary.md +59 -0
- simt_dev/vendored/docs/decisions/ADR-009-skill-layout.md +48 -0
- simt_dev/vendored/docs/decisions/ADR-010-runner-invocation.md +56 -0
- simt_dev/vendored/docs/decisions/ADR-011-windows-support.md +52 -0
- simt_dev/vendored/docs/decisions/ADR-012-drop-windows-support.md +59 -0
- simt_dev/vendored/docs/decisions/ADR-013-genericity-by-facts-and-local-profiles.md +114 -0
- simt_dev/vendored/docs/decisions/ADR-014-workspace-is-configuration.md +211 -0
- simt_dev/vendored/docs/decisions/ADR-015-a-source-is-content-not-a-location.md +117 -0
- simt_dev/vendored/docs/decisions/ADR-016-a-divergent-rule-is-retired-not-merged.md +180 -0
- simt_dev/vendored/docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md +89 -0
- simt_dev/vendored/docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md +183 -0
- simt_dev/vendored/docs/decisions/ADR-019-a-declared-command-is-the-runner.md +110 -0
- simt_dev/vendored/docs/decisions/ADR-020-a-rule-is-written-per-surface-and-pointed-not-copied.md +100 -0
- simt_dev/vendored/docs/decisions/ADR-021-one-surface-per-repo.md +94 -0
- simt_dev/vendored/docs/decisions/ADR-022-a-key-simt-dev-reads-is-prefixed.md +98 -0
- simt_dev/vendored/docs/decisions/ADR-023-the-gate-ships-as-a-file-the-repo-owns.md +106 -0
- simt_dev/vendored/docs/decisions/ADR-024-simt-dev-overwrites-its-own-gate-file.md +111 -0
- simt_dev/vendored/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +153 -0
- simt_dev/vendored/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +117 -0
- simt_dev/vendored/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +130 -0
- simt_dev/vendored/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +131 -0
- simt_dev/vendored/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +120 -0
- simt_dev/vendored/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +114 -0
- simt_dev/vendored/docs/decisions/ADR-031-the-suite-stages-the-environment-it-needs.md +129 -0
- simt_dev/vendored/docs/decisions/ADR-032-provenance-is-a-content-digest-not-a-commit.md +141 -0
- simt_dev/vendored/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +82 -0
- simt_dev/vendored/docs/decisions/ADR-034-a-staged-tool-refuses-a-partially-staged-file.md +91 -0
- simt_dev/vendored/docs/decisions/ADR-035-one-write-decision-and-one-json-document.md +119 -0
- simt_dev/vendored/docs/decisions/ADR-036-a-writer-that-writes-says-so-and-the-instructions-naming-it-move-with-it.md +103 -0
- simt_dev/vendored/docs/decisions/ADR-037-unchanged-means-writing-would-not-change-a-byte.md +100 -0
- simt_dev/vendored/docs/decisions/ADR-038-the-gate-filter-belongs-to-every-manifest-writer.md +95 -0
- simt_dev/vendored/docs/decisions/ADR-039-a-ci-step-runs-a-form-that-can-fail.md +101 -0
- simt_dev/vendored/docs/decisions/ADR-040-the-last-writer-that-wrote-by-default-was-not-the-one-named.md +158 -0
- simt_dev/vendored/docs/decisions/ADR-041-the-suite-installs-the-package-in-order-to-test-it.md +92 -0
- simt_dev/vendored/docs/decisions/ADR-042-a-declared-glob-must-match-a-path-built-from-it.md +94 -0
- simt_dev/vendored/docs/decisions/ADR-043-a-keyword-is-a-phrase-and-two-letters-is-a-word.md +90 -0
- simt_dev/vendored/docs/decisions/ADR-044-compatibility-is-not-evidence.md +116 -0
- simt_dev/vendored/docs/decisions/ADR-045-a-dashboard-that-counts-a-defect.md +119 -0
- simt_dev/vendored/docs/decisions/ADR-046-one-rule-one-copy.md +140 -0
- simt_dev/vendored/docs/decisions/ADR-047-a-profile-selects-on-what-a-repository-runs-on.md +141 -0
- simt_dev/vendored/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +150 -0
- simt_dev/vendored/docs/decisions/ADR-049-a-ruling-that-a-later-record-moved-says-so-where-it-is-printed.md +153 -0
- simt_dev/vendored/docs/decisions/ADR-050-a-byte-count-is-not-a-region-and-updated-is-not-a-change-of-rules.md +102 -0
- simt_dev/vendored/docs/decisions/ADR-051-a-pointer-is-the-readers-only-route-so-it-is-not-a-convenience.md +134 -0
- simt_dev/vendored/docs/decisions/ADR-052-a-dependency-is-counted-against-the-budget-and-there-is-one-budget.md +101 -0
- simt_dev/vendored/docs/decisions/ADR-053-the-node-floor-is-22-20-0.md +49 -0
- simt_dev/vendored/docs/decisions/ADR-054-the-digest-covers-every-byte-that-leaves-the-package.md +100 -0
- simt_dev/vendored/docs/decisions/ADR-055-a-status-line-travels-so-it-may-not-name-a-location.md +98 -0
- simt_dev/vendored/docs/decisions/ADR-056-three-names-one-of-which-is-cheap-to-move.md +160 -0
- simt_dev/vendored/docs/decisions/ADR-057-an-identity-is-per-developer-and-a-repository-does-not-own-it.md +115 -0
- simt_dev/vendored/docs/decisions/ADR-058-one-tool-three-channels-and-the-order-is-forced.md +148 -0
- simt_dev/vendored/docs/decisions/README.md +530 -0
- simt_dev/vendored/graph/skills.graph.json +1622 -0
- simt_dev/vendored/package.json +43 -0
- simt_dev/vendored/rules/core.md +76 -0
- simt_dev/vendored/rules/mdc/intake-questions.mdc +50 -0
- simt_dev/vendored/rules/mdc/preview-postflight.mdc +61 -0
- simt_dev/vendored/rules/mdc/slice-commit.mdc +52 -0
- simt_dev/vendored/rules/mdc/test-gate.mdc +54 -0
- simt_dev/vendored/rules/stacks/android.md +63 -0
- simt_dev/vendored/rules/stacks/cloudflare-workers.md +65 -0
- simt_dev/vendored/rules/stacks/connectiq.md +62 -0
- simt_dev/vendored/rules/stacks/container.md +31 -0
- simt_dev/vendored/rules/stacks/jvm.md +54 -0
- simt_dev/vendored/rules/stacks/node.md +45 -0
- simt_dev/vendored/rules/stacks/python.md +48 -0
- simt_dev/vendored/rules/stacks/react-native.md +58 -0
- simt_dev/vendored/rules/stacks/react-web.md +61 -0
- simt_dev/vendored/rules/stacks/typescript.md +21 -0
- simt_dev/vendored/rules/stacks/uv.md +32 -0
- simt_dev/vendored/src/argv.mjs +116 -0
- simt_dev/vendored/src/ci/dependabot.mjs +268 -0
- simt_dev/vendored/src/ci/workflow.mjs +360 -0
- simt_dev/vendored/src/commands/ci.mjs +285 -0
- simt_dev/vendored/src/commands/compare.mjs +195 -0
- simt_dev/vendored/src/commands/graph.mjs +73 -0
- simt_dev/vendored/src/commands/help.mjs +108 -0
- simt_dev/vendored/src/commands/hooks.mjs +471 -0
- simt_dev/vendored/src/commands/index.mjs +241 -0
- simt_dev/vendored/src/commands/init.mjs +632 -0
- simt_dev/vendored/src/commands/mcp.mjs +63 -0
- simt_dev/vendored/src/commands/rules.mjs +374 -0
- simt_dev/vendored/src/commands/skills.mjs +647 -0
- simt_dev/vendored/src/commands/skillsinstall.mjs +515 -0
- simt_dev/vendored/src/commands/sync.mjs +331 -0
- simt_dev/vendored/src/commands/upgrade.mjs +202 -0
- simt_dev/vendored/src/compare/discover.mjs +212 -0
- simt_dev/vendored/src/compare/hooksstate.mjs +159 -0
- simt_dev/vendored/src/compare/row.mjs +299 -0
- simt_dev/vendored/src/decisions/index.mjs +536 -0
- simt_dev/vendored/src/graph/load.mjs +541 -0
- simt_dev/vendored/src/hooks/capability.mjs +129 -0
- simt_dev/vendored/src/hooks/dispatcher.mjs +268 -0
- simt_dev/vendored/src/hooks/gates.mjs +144 -0
- simt_dev/vendored/src/hooks/resolve.mjs +194 -0
- simt_dev/vendored/src/hooks/shim.mjs +140 -0
- simt_dev/vendored/src/hooks/where.mjs +118 -0
- simt_dev/vendored/src/identity.mjs +218 -0
- simt_dev/vendored/src/locate.mjs +75 -0
- simt_dev/vendored/src/mcp/server.mjs +167 -0
- simt_dev/vendored/src/mcp/tools.mjs +410 -0
- simt_dev/vendored/src/name.mjs +95 -0
- simt_dev/vendored/src/output/index.mjs +96 -0
- simt_dev/vendored/src/projection/baselines.mjs +209 -0
- simt_dev/vendored/src/projection/conflicts.mjs +205 -0
- simt_dev/vendored/src/projection/manifest.mjs +478 -0
- simt_dev/vendored/src/projection/markers.mjs +349 -0
- simt_dev/vendored/src/projection/mdc.mjs +327 -0
- simt_dev/vendored/src/projection/project.mjs +504 -0
- simt_dev/vendored/src/projection/rules.mjs +640 -0
- simt_dev/vendored/src/projection/surfaces.mjs +398 -0
- simt_dev/vendored/src/projection/targets.mjs +221 -0
- simt_dev/vendored/src/resolve/fingerprint.mjs +553 -0
- simt_dev/vendored/src/resolve/near.mjs +61 -0
- simt_dev/vendored/src/resolve/resolve.mjs +457 -0
- simt_dev/vendored/src/resolve/source.mjs +105 -0
- simt_dev/vendored/src/toolchain.mjs +202 -0
- simt_dev/vendored/src/tsconfig.mjs +314 -0
- simt_dev/vendored/src/upgrade/plan.mjs +203 -0
- simt_dev/vendored/src/workspace.mjs +289 -0
- simt_dev-0.2.0.dist-info/METADATA +150 -0
- simt_dev-0.2.0.dist-info/RECORD +138 -0
- simt_dev-0.2.0.dist-info/WHEEL +4 -0
- simt_dev-0.2.0.dist-info/entry_points.txt +2 -0
- simt_dev-0.2.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "simt-dev",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Shared agent rules, skills and git hooks for a repository: one command adopts a repo, and every gate it declares is probed before it is written.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Said Elimam",
|
|
7
|
+
"private": false,
|
|
8
|
+
"type": "module",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=22.20.0"
|
|
11
|
+
},
|
|
12
|
+
"os": [
|
|
13
|
+
"darwin",
|
|
14
|
+
"linux"
|
|
15
|
+
],
|
|
16
|
+
"bin": {
|
|
17
|
+
"simt": "bin/simt-dev.mjs"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"bin",
|
|
21
|
+
"src",
|
|
22
|
+
"graph",
|
|
23
|
+
"rules",
|
|
24
|
+
"docs/decisions",
|
|
25
|
+
"CHANGELOG.md"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"agent",
|
|
29
|
+
"agents",
|
|
30
|
+
"ai",
|
|
31
|
+
"codex",
|
|
32
|
+
"cursor",
|
|
33
|
+
"git-hooks",
|
|
34
|
+
"lint",
|
|
35
|
+
"llm",
|
|
36
|
+
"rules",
|
|
37
|
+
"skills"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "node --test test/*.test.mjs",
|
|
41
|
+
"lint": "node bin/simt-dev.mjs doctor"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: core
|
|
3
|
+
simt-title: Shared rules
|
|
4
|
+
simt-applies: always
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Shared rules
|
|
8
|
+
|
|
9
|
+
Where a rule cites a decision, run `simt-dev rules why ADR-NNN` to read it,
|
|
10
|
+
including whether it is still in force.
|
|
11
|
+
|
|
12
|
+
These rules are stack-agnostic. Whatever is specific to your stack, your product or your
|
|
13
|
+
commands belongs in this repository, not here.
|
|
14
|
+
|
|
15
|
+
## Truth
|
|
16
|
+
|
|
17
|
+
- Never claim a test you did not run, a check you did not execute, or a deploy you did not
|
|
18
|
+
perform.
|
|
19
|
+
- Read the real runner manifest before running anything. A command absent from it is not a
|
|
20
|
+
command. (Which file that is depends on the stack: `package.json` scripts, Gradle tasks,
|
|
21
|
+
`pyproject.toml` entry points.)
|
|
22
|
+
- Report what you left alone: unrelated dirty files, skipped checks, unreviewed scope.
|
|
23
|
+
|
|
24
|
+
## Git
|
|
25
|
+
|
|
26
|
+
- Commit locally, report the SHA. **Push only on explicit approval.** (ADR-001)
|
|
27
|
+
- Before asking for push approval, state whether the push triggers a deployment, a migration
|
|
28
|
+
or any remote mutation.
|
|
29
|
+
- Never use `--no-verify`, amend, rebase, reset or rewrite history unless explicitly asked.
|
|
30
|
+
- Never commit failing, partial, secret-bearing or unrelated work.
|
|
31
|
+
|
|
32
|
+
## Refactoring
|
|
33
|
+
|
|
34
|
+
- Outside the current change's blast radius, do not refactor. (ADR-002)
|
|
35
|
+
- Inside it, a second real use extracts, and the extraction is finished: no orphaned imports,
|
|
36
|
+
exports, styles, messages or files remain.
|
|
37
|
+
- A third copy of the same logic is a defect. So is an abstraction invented for one caller.
|
|
38
|
+
|
|
39
|
+
## Validation
|
|
40
|
+
|
|
41
|
+
- Focused tests are the default gate. A full suite runs only against a named trigger: phase
|
|
42
|
+
exit, release gate, cross-system behaviour, a repo-declared numeric gate, explicit
|
|
43
|
+
instruction, or pre-push on a declared protected branch. (ADR-003)
|
|
44
|
+
- Name the exact commands and test files. Partial pass is a blocker; `6/8 pass` is the
|
|
45
|
+
canonical example of a blocked state. Never justify a skip without a pre-existing written
|
|
46
|
+
reason. (ADR-006)
|
|
47
|
+
- Evidence discipline is mandatory everywhere. A **numeric** bar is declared per repo in
|
|
48
|
+
`simt-dev.json`, never assumed centrally. (ADR-006)
|
|
49
|
+
- The pre-commit hook owns format and lint for staged files; do not re-run what it just ran.
|
|
50
|
+
Manual runs are correct when the hook is absent, when diagnosing a failure, or for a check
|
|
51
|
+
the hook does not cover. (ADR-004)
|
|
52
|
+
- Declare a gate only if this repository can run it — a script that exists, a tool that is
|
|
53
|
+
installed, a command you name. A gate that cannot run blocks every commit until someone
|
|
54
|
+
reaches for `--no-verify`. (ADR-017)
|
|
55
|
+
- Invoke tools through the repository's own runner — `npm run`, `uv run -m`, `./gradlew` —
|
|
56
|
+
never a package runner like `npx` or a bare console entrypoint. (ADR-010)
|
|
57
|
+
|
|
58
|
+
## Code and review
|
|
59
|
+
|
|
60
|
+
- No type-system escapes, no unawaited async work, no debug printing in application code.
|
|
61
|
+
- 400 lines is a review trigger; 1000 lines is a hard ceiling. Generated files are exempt
|
|
62
|
+
when the hook names the generator. (ADR-005)
|
|
63
|
+
- Never weaken CSP, CORS, auth, audit, cache or validation controls to silence a test, warning
|
|
64
|
+
or alert.
|
|
65
|
+
- Review verdicts are exactly three: `BLOCK`, `APPROVE WITH RESIDUAL RISK` (must list what was
|
|
66
|
+
not reviewed), `APPROVE`. (ADR-008)
|
|
67
|
+
- Accessibility, correctness, privacy and security are hard gates, not subjective tradeoffs.
|
|
68
|
+
- Never direct two agents to edit the same files concurrently. Select the minimum viable team.
|
|
69
|
+
- Every agent works to a stated budget and stops at a concrete blocker rather than drifting.
|
|
70
|
+
Approval by budget never converts a known failure into an approval.
|
|
71
|
+
|
|
72
|
+
## Scope of these rules
|
|
73
|
+
|
|
74
|
+
Product policy, stack-specific rule bodies, repo commands and paths stay in the repo. A rule
|
|
75
|
+
here states only what holds everywhere; the moment one repository needs an exception, it is that
|
|
76
|
+
repository's rule, not a widening of this one.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: intake-questions
|
|
3
|
+
simt-target: .cursor/rules/intake-questions.mdc
|
|
4
|
+
description: Ask only unresolved business/technical decisions before work
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
simt-applies: always
|
|
7
|
+
simt-substitutions:
|
|
8
|
+
- key: plansGlob
|
|
9
|
+
default: docs/plans/*
|
|
10
|
+
describe: glob for this repo's active plan tracker tables
|
|
11
|
+
- key: askTool
|
|
12
|
+
default: Ask the user, offering choices with the recommended one first
|
|
13
|
+
describe: how this repo asks a question; the default names no harness, because one body is written for every selected surface
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Intake Questions
|
|
17
|
+
|
|
18
|
+
When starting, resuming, or scoping a feature slice or plan task, do not jump into code
|
|
19
|
+
without resolving open material decisions.
|
|
20
|
+
|
|
21
|
+
## Agent Defaults (Do Not Ask)
|
|
22
|
+
|
|
23
|
+
Apply documented defaults without questions for:
|
|
24
|
+
|
|
25
|
+
- Operational/procedural choices (tracker updates, commit vs push, validation depth,
|
|
26
|
+
artifact shape).
|
|
27
|
+
- Slice sequencing and repository ownership when already defined in the active plan
|
|
28
|
+
tracker table (`{{plansGlob}}`).
|
|
29
|
+
- Standard engineering conventions.
|
|
30
|
+
|
|
31
|
+
## When To Ask (1-5 Questions Max)
|
|
32
|
+
|
|
33
|
+
Ask only when a functionality, business, privacy, security, or technical decision is not
|
|
34
|
+
settled in authority docs, and the answer materially changes behavior, scope, contracts,
|
|
35
|
+
migrations, or risk.
|
|
36
|
+
|
|
37
|
+
Five is a ceiling, not a quota. Ask fewer when fewer decisions are genuinely open; never pad
|
|
38
|
+
the list to reach it, and never split one decision into several questions to fit under it.
|
|
39
|
+
|
|
40
|
+
- {{askTool}}.
|
|
41
|
+
- If that is not available, present numbered choices with `(recommended)` on the first option.
|
|
42
|
+
- Do not ask operational or procedural choices; apply the best documented option.
|
|
43
|
+
|
|
44
|
+
## Required First Turn
|
|
45
|
+
|
|
46
|
+
1. State the smallest independently valuable slice, the owning repo, the active plan
|
|
47
|
+
tracker task ID (from `{{plansGlob}}`), and the acceptance checks.
|
|
48
|
+
2. Either ask the unresolved material decision questions, or state that the authority docs
|
|
49
|
+
leave no material open decision and proceed to implementation.
|
|
50
|
+
3. Proceed without waiting when no material decision remains.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: preview-postflight
|
|
3
|
+
simt-target: .cursor/rules/preview-postflight.mdc
|
|
4
|
+
description: Authenticated remote postflight uses dedicated preview Workers
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
# Opt-in, not stack-detected. Detection cannot tell a Workers repo that deploys
|
|
7
|
+
# preview Workers from one that does not, so a stack tag would hand this rule to the
|
|
8
|
+
# latter. The repo declares it instead. See ADR-016 section 3.
|
|
9
|
+
simt-applies: []
|
|
10
|
+
simt-opt-in: true
|
|
11
|
+
simt-substitutions:
|
|
12
|
+
- key: previewWorkers
|
|
13
|
+
default: the dedicated preview Workers
|
|
14
|
+
describe: the preview Worker names this family deploys
|
|
15
|
+
- key: previewUrlHost
|
|
16
|
+
default: https://<preview-worker>.<account>.workers.dev
|
|
17
|
+
describe: the preview URL shape authenticated checks must target
|
|
18
|
+
- key: productionDomains
|
|
19
|
+
default: the repo's production domains
|
|
20
|
+
describe: domains authenticated postflight must never target
|
|
21
|
+
- key: productionDomainNote
|
|
22
|
+
default: ""
|
|
23
|
+
describe: one clause naming this repo's own production domain binding
|
|
24
|
+
# These two arrived as one slot in the divergent files: one repo put a build-config
|
|
25
|
+
# instruction where another put a domain-binding note, which read as a conflict. They
|
|
26
|
+
# are not alternatives, so the union carries both — but a repo that needs one, or
|
|
27
|
+
# neither, declares that. A worker that serves derivatives and calls no API has no
|
|
28
|
+
# API base URL to set, so it declares only its own custom-domain binding.
|
|
29
|
+
- key: previewApiBaseUrlNote
|
|
30
|
+
default: "- Point the preview build at the preview API Worker: set `SOCIAL_API_BASE_URL` in the Cloudflare Workers Builds preview environment to the preview API Worker URL."
|
|
31
|
+
describe: build-config instruction, when this repo actually has a preview API base URL to set
|
|
32
|
+
- key: googleOnlyNote
|
|
33
|
+
default: "- Production and public browser flows remain Google-only."
|
|
34
|
+
describe: the identity policy this repo enforces in production
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
# Preview-Only Authenticated Postflight
|
|
38
|
+
|
|
39
|
+
Authenticated remote postflight and release checks use only {{previewWorkers}}, deployed
|
|
40
|
+
with `AUTH_PROVIDER_POLICY=preview_postflight_password` and a secret-bound Firebase password
|
|
41
|
+
identity (`POSTFLIGHT_PASSWORD_UID`).
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
- Authenticated checks must target preview URLs (`{{previewUrlHost}}`).
|
|
46
|
+
- Postflight credentials live in the operator keychain only; never in source, chat, D1, or
|
|
47
|
+
logs.
|
|
48
|
+
{{googleOnlyNote}}
|
|
49
|
+
{{previewApiBaseUrlNote}}
|
|
50
|
+
{{productionDomainNote}}
|
|
51
|
+
|
|
52
|
+
## Forbidden
|
|
53
|
+
|
|
54
|
+
- Authenticated postflight against production domains ({{productionDomains}}).
|
|
55
|
+
- Using real Social Google accounts for feature slice verification.
|
|
56
|
+
- Demanding, pasting, or storing production Google OAuth tokens in chat or tickets.
|
|
57
|
+
|
|
58
|
+
## Allowed Without Account
|
|
59
|
+
|
|
60
|
+
- Unauthenticated production probes (`/health`, `/openapi.json`, a fail-closed `401`),
|
|
61
|
+
Cloudflare API inventory, D1 migration verification, and Worker deployment status.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: slice-commit
|
|
3
|
+
simt-target: .cursor/rules/slice-commit.mdc
|
|
4
|
+
description: How a completed change becomes one commit, and what happens before anything is pushed
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
# `simt-applies: always` is what makes a rule reachable at all. Without it this rule was
|
|
7
|
+
# authored, shipped, tested for its presence on disk -- and projected into no repo,
|
|
8
|
+
# silently. Reached by running `init` against a real repo, not by reading the file.
|
|
9
|
+
simt-applies: always
|
|
10
|
+
---
|
|
11
|
+
# Slice Commit
|
|
12
|
+
|
|
13
|
+
A slice is the smallest change that is independently valuable on its own. Committing
|
|
14
|
+
one is a mechanical procedure with a fixed order, because every step after it depends
|
|
15
|
+
on the one before: you cannot review a diff you have not staged, and you cannot claim
|
|
16
|
+
evidence for a test you have not run.
|
|
17
|
+
|
|
18
|
+
## Order
|
|
19
|
+
|
|
20
|
+
1. **Run the focused test the change needs**, through the repository's own runner,
|
|
21
|
+
naming the exact command and the exact file — see `test-gate`. A full suite is
|
|
22
|
+
for a named trigger, not for progress.
|
|
23
|
+
2. **Stage only what the slice touched.** `git status` first, and leave unrelated work
|
|
24
|
+
alone. Report what you left alone; silence about it reads as "I reverted it".
|
|
25
|
+
3. **Read the staged diff.** Not the working tree — the diff. It is what will be
|
|
26
|
+
committed, and it is the last chance to notice a file that does not belong.
|
|
27
|
+
4. **Write the message.** Conventional Commits, imperative subject, no trailing period:
|
|
28
|
+
`fix(gates): declare only gates a repo can run, and report the ones it cannot`.
|
|
29
|
+
The subject says what changed; the body says why, and what was deliberately not
|
|
30
|
+
changed.
|
|
31
|
+
5. **Commit.** `git commit`, non-interactive. Report the SHA.
|
|
32
|
+
6. **Stop.**
|
|
33
|
+
|
|
34
|
+
## Pushing is a separate decision
|
|
35
|
+
|
|
36
|
+
**Never push because the work is finished.** Push is a separate act with its own
|
|
37
|
+
approval, and pushing the branch a remote treats as production can deploy it.
|
|
38
|
+
|
|
39
|
+
Before asking for approval, state what the push will do: which branch, how many
|
|
40
|
+
commits, and whether it triggers a deployment, a migration, or any other remote
|
|
41
|
+
mutation. Then wait. Approval to commit is not approval to push.
|
|
42
|
+
|
|
43
|
+
Never use `--no-verify`. A hook that fails is telling you something about the change;
|
|
44
|
+
skipping it turns a gate into a suggestion and the next person has no way to know it
|
|
45
|
+
was skipped.
|
|
46
|
+
|
|
47
|
+
## What never belongs in a commit
|
|
48
|
+
|
|
49
|
+
- Failing, partial, or unrun work. A commit is a claim; a partial one is a false one.
|
|
50
|
+
- Secrets, tokens, or generated artefacts that drift.
|
|
51
|
+
- Work nobody asked for. Unrelated changes in a slice commit are invisible in review
|
|
52
|
+
and impossible to revert on their own.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: test-gate
|
|
3
|
+
simt-target: .cursor/rules/test-gate.mdc
|
|
4
|
+
description: Full-pass tests and test deltas are mandatory before commit or reviewer approval
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
simt-applies: always
|
|
7
|
+
simt-substitutions:
|
|
8
|
+
- key: exampleRunner
|
|
9
|
+
default: your test runner, through the repository's own script or task
|
|
10
|
+
describe: how this repo actually runs one named test file
|
|
11
|
+
- key: exampleTestPath
|
|
12
|
+
default: path/to/the.changed.test.ts
|
|
13
|
+
describe: one real test file for this repo, used only as the named example
|
|
14
|
+
- key: pathKinds
|
|
15
|
+
default: feature/contract/route
|
|
16
|
+
describe: this repo's executable path vocabulary
|
|
17
|
+
- key: producerNoun
|
|
18
|
+
default: API producer
|
|
19
|
+
describe: what produces a contract this repo consumes
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Test Gate
|
|
23
|
+
|
|
24
|
+
Applies to every feature slice, bugfix, and plan tracker task. Docs/config-only chores are
|
|
25
|
+
exempt from test deltas but require named doc/format checks to pass.
|
|
26
|
+
|
|
27
|
+
## Implementer Requirements
|
|
28
|
+
|
|
29
|
+
1. **Name exact commands and test files**: {{exampleRunner}}, selecting
|
|
30
|
+
`{{exampleTestPath}}` on its own.
|
|
31
|
+
2. **100% Pass Required**: the named commands must exit 0 with every test passing. A
|
|
32
|
+
partial pass (e.g. 6/8 passing) is a hard blocker, and so is quoting the passing subset.
|
|
33
|
+
3. **Behavioral Test Deltas**:
|
|
34
|
+
- New {{pathKinds}} path -> new or extended test asserting the behavior.
|
|
35
|
+
- Bugfix -> regression test reproducing the failure mode.
|
|
36
|
+
- If no test file changed, justify why the existing tests fully prove the invariant.
|
|
37
|
+
4. **Cross-Repo Order**: commit the {{producerNoun}} (contract/migration) before any
|
|
38
|
+
consumer generates against it. Never apply remote migrations or deploy from uncommitted
|
|
39
|
+
code.
|
|
40
|
+
|
|
41
|
+
## Reviewer Mandatory Block
|
|
42
|
+
|
|
43
|
+
Return `BLOCK` when:
|
|
44
|
+
|
|
45
|
+
- Any test fails, is skipped (without pre-existing justification), or is unrun.
|
|
46
|
+
- An executable diff has no test delta and no explicit justification.
|
|
47
|
+
- The coordinator cites partial suite results or defers a failing test.
|
|
48
|
+
- **Consumer side:** a consumer requires a new API field with no backward-compatibility or
|
|
49
|
+
deploy-order evidence.
|
|
50
|
+
- **Producer side:** the {{producerNoun}} ships without a test asserting the new invariant
|
|
51
|
+
or field.
|
|
52
|
+
|
|
53
|
+
The last two are complementary, not alternatives. A reviewer that checks only one of them
|
|
54
|
+
misses half of the failure they exist to catch.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: android
|
|
3
|
+
simt-title: Android
|
|
4
|
+
# Selected on the one thing that makes a repository an Android application: a
|
|
5
|
+
# module with an `AndroidManifest.xml`. `gradle` and `kotlin` are shared with every
|
|
6
|
+
# JVM backend, which is how a Spring or Ktor service used to receive this profile
|
|
7
|
+
# (S28, ADR-047).
|
|
8
|
+
simt-applies: [has-android-manifest]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Stack profile: Android
|
|
12
|
+
|
|
13
|
+
Applies to: `has-android-manifest` — some module has a
|
|
14
|
+
`src/main/AndroidManifest.xml`. A JVM backend with no Android module does not get
|
|
15
|
+
this profile; see `jvm`.
|
|
16
|
+
|
|
17
|
+
## Build
|
|
18
|
+
|
|
19
|
+
- `./gradlew` only. Never a system `gradle`. Never `--configuration-cache` where a
|
|
20
|
+
repo documents that it breaks.
|
|
21
|
+
- `./gradlew spotlessApply` is the formatting entrypoint in repos that use Spotless.
|
|
22
|
+
- The manifest is not hand-edited for a build-level change: `applicationId`, `versionCode`
|
|
23
|
+
and `versionName` are declared in the module's build file and land in the merged
|
|
24
|
+
manifest. A value in two places is a value that will disagree.
|
|
25
|
+
|
|
26
|
+
## Modules and layers
|
|
27
|
+
|
|
28
|
+
- `:core:domain` depends on no other internal module. Enforce it; do not add an
|
|
29
|
+
import that breaks the direction.
|
|
30
|
+
- Layer suffixes: `*Repository`, `*ViewModel`, `*UiState`, `*Screen`, `*Service`.
|
|
31
|
+
- Feature modules never import `androidx.compose.ui.window.Dialog`, M3 `AlertDialog`
|
|
32
|
+
or M3 `ModalBottomSheet`. The raw `Dialog` window belongs to allowlisted `core:ui`
|
|
33
|
+
surfaces, and `checkOverlayModalPolicy` fails `./gradlew check` when a feature
|
|
34
|
+
bypasses it.
|
|
35
|
+
|
|
36
|
+
## Accessibility
|
|
37
|
+
|
|
38
|
+
- Every user-facing string is a resource, with a `cd_` content description where an
|
|
39
|
+
icon or image carries meaning. Decorative images declare `contentDescription = null`
|
|
40
|
+
explicitly.
|
|
41
|
+
- Touch targets meet the platform minimum, and the minimum is asserted rather than
|
|
42
|
+
eyeballed.
|
|
43
|
+
- Respect the system font scale and the display size the user has chosen; a fixed
|
|
44
|
+
`sp` value or a hard-coded cell height breaks both.
|
|
45
|
+
|
|
46
|
+
## Wear OS
|
|
47
|
+
|
|
48
|
+
- `applicationId` in the wear module must equal the phone app's. Divergence makes the
|
|
49
|
+
Data Layer fail silently — no error, no delivery.
|
|
50
|
+
- Wear and phone target SDKs differ by design for Play Console reasons. Do not "align"
|
|
51
|
+
them.
|
|
52
|
+
|
|
53
|
+
## Data
|
|
54
|
+
|
|
55
|
+
- Room: bump the version, add the migration, extend the migration test. Never edit an
|
|
56
|
+
applied migration.
|
|
57
|
+
|
|
58
|
+
## Testing
|
|
59
|
+
|
|
60
|
+
- Unit tests for view models, repositories and mappers; instrumentation tests for UI.
|
|
61
|
+
- `Dispatchers.setMain` in a `@BeforeTest`, restored in `@AfterTest`.
|
|
62
|
+
- Assert behaviour, not implementation. Never weaken an assertion, an accessibility
|
|
63
|
+
check or a fixture to match current behaviour.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: cloudflare-workers
|
|
3
|
+
simt-title: Cloudflare Workers (TypeScript)
|
|
4
|
+
# What the repo runs on, not what it is written in. `typescript` was here and put
|
|
5
|
+
# this profile on every TypeScript repository in the estate that had no more
|
|
6
|
+
# specific one; `hono` was here too, and Hono runs on Node, Bun and Deno as
|
|
7
|
+
# happily as on Workers. A wrangler config or the `wrangler` dependency is the
|
|
8
|
+
# only evidence that this is a Worker (S28, ADR-047).
|
|
9
|
+
simt-applies: [cloudflare-workers]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Stack profile: Cloudflare Workers (TypeScript)
|
|
13
|
+
|
|
14
|
+
Applies to: `cloudflare-workers` — a `wrangler.json`, `wrangler.jsonc` or
|
|
15
|
+
`wrangler.toml`, or a `wrangler` dependency.
|
|
16
|
+
|
|
17
|
+
## Schema and config
|
|
18
|
+
|
|
19
|
+
- `wrangler.jsonc` is the single source of binding and environment truth. Do not read
|
|
20
|
+
configuration from a second file.
|
|
21
|
+
- Bindings are the integration surface. A Worker calls a service binding, not a REST endpoint
|
|
22
|
+
over the network to its own account.
|
|
23
|
+
- Secrets come from the runtime binding. Never commit them, never log them, never accept one
|
|
24
|
+
from a query parameter.
|
|
25
|
+
|
|
26
|
+
## Data
|
|
27
|
+
|
|
28
|
+
- D1: one new sequential immutable migration per change. Never edit an applied migration.
|
|
29
|
+
Define coexistence order and forward repair; do not promise SQL rollback.
|
|
30
|
+
- Prefer D1 constraints and triggers over read-then-write in application code.
|
|
31
|
+
- Prepared, parameterised statements only. Never interpolate a value into SQL.
|
|
32
|
+
- Durable Objects: one owner per piece of state. Use alarms for scheduling, and RPC for
|
|
33
|
+
internal calls rather than self-fetch.
|
|
34
|
+
- Queues are at-least-once: consumers must be idempotent, keyed by an idempotency token that
|
|
35
|
+
is stable across retries.
|
|
36
|
+
|
|
37
|
+
## Request handling
|
|
38
|
+
|
|
39
|
+
- Validate at the edge with the schema layer. Authorization is server-authoritative; a
|
|
40
|
+
client-side role, claim, cache entry or URL parameter grants nothing.
|
|
41
|
+
- `Request`/`Response` are globals. Do not pass them through as values into domain code.
|
|
42
|
+
- Caching defaults to `private, no-store`. Never cache token, account, block, reveal, admin
|
|
43
|
+
decisions or authenticated errors.
|
|
44
|
+
- Streaming is the default for large bodies; do not buffer without a stated reason.
|
|
45
|
+
|
|
46
|
+
## Module state
|
|
47
|
+
|
|
48
|
+
- No module-scope mutable state. A Worker instance is reused across requests, so a counter or
|
|
49
|
+
cache at module scope leaks between users.
|
|
50
|
+
|
|
51
|
+
## Observability
|
|
52
|
+
|
|
53
|
+
- Structured logs through the logger interface, with an allowlist. Never log bodies, tokens,
|
|
54
|
+
UIDs, user text, exact coordinates or full URLs.
|
|
55
|
+
- Use `Math.random` never for anything security-relevant; use Web Crypto.
|
|
56
|
+
|
|
57
|
+
## Types and tests
|
|
58
|
+
|
|
59
|
+
- `strict` plus `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`,
|
|
60
|
+
`noImplicitOverride`, `noFallthroughCasesInSwitch`, `noImplicitReturns`, `isolatedModules`.
|
|
61
|
+
With `exactOptionalPropertyTypes`, pass optionals through conditional spreads or an explicit
|
|
62
|
+
`prop: value | undefined`; never `prop?: T` into a `prop: T | undefined` parameter.
|
|
63
|
+
- Generate Wrangler types from the config; do not hand-write binding types.
|
|
64
|
+
- Test with the real Workers runtime pool (`@cloudflare/vitest-pool-workers`) so Workers APIs,
|
|
65
|
+
D1 and Durable Objects behave as they will in production.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: connectiq
|
|
3
|
+
simt-title: Connect IQ (Monkey C)
|
|
4
|
+
simt-applies: [connectiq]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Stack profile: Connect IQ (Monkey C)
|
|
8
|
+
|
|
9
|
+
Applies to: `connectiq`.
|
|
10
|
+
|
|
11
|
+
## The platform is not the language you know
|
|
12
|
+
|
|
13
|
+
This profile exists because the most expensive failures in this stack are silent. Read the
|
|
14
|
+
repo's own known-defects table before assuming any platform behaviour; the entries there are
|
|
15
|
+
empirically verified and will otherwise be rediscovered one bug at a time.
|
|
16
|
+
|
|
17
|
+
Known traps to assume until verified otherwise:
|
|
18
|
+
|
|
19
|
+
- `String.toNumber()` can truncate rather than fail.
|
|
20
|
+
- `==` on two `String`s compares identity, not value.
|
|
21
|
+
- `Number` is 32-bit. Values outside that range are wrong, not approximate.
|
|
22
|
+
- `logger.error()` does not fail a test. A passing suite can contain a logged error.
|
|
23
|
+
- `getGlanceView()` is unavailable on the Simulator.
|
|
24
|
+
|
|
25
|
+
## Memory
|
|
26
|
+
|
|
27
|
+
- Memory budgets are hard limits, not targets, and they are the repo's declared gate
|
|
28
|
+
(`simt-dev.json`). Zero allocation in `onUpdate`; no allocation in a draw callback.
|
|
29
|
+
- A memory check that "currently exits 1 by design" is not a gate — it is noise that teaches
|
|
30
|
+
everyone to ignore it. Declare the budget or drop the check.
|
|
31
|
+
|
|
32
|
+
## Language and style
|
|
33
|
+
|
|
34
|
+
- Four-space indent, LF, 300-char line limit.
|
|
35
|
+
- `initialize()`, `_camelCase` for private members, `UPPER_SNAKE_CASE` for constants.
|
|
36
|
+
- `typeCheckLevel: "gradual"` — do not raise it to chase a build; fix the types instead.
|
|
37
|
+
- One responsibility per class. A class that draws, computes and persists is three classes.
|
|
38
|
+
- 400 lines is a review trigger, 1000 a ceiling (ADR-005). Generated `.mc` is exempt when the
|
|
39
|
+
generator is named.
|
|
40
|
+
|
|
41
|
+
## Data and protocol
|
|
42
|
+
|
|
43
|
+
- A model that crosses the wire is **generated, never hand-written**. Hand edits are overwritten
|
|
44
|
+
by the next codegen run and the drift is discovered in production.
|
|
45
|
+
- Validate every field at construction. A target with `bearingDeg = 400` cannot be created.
|
|
46
|
+
- A positional parser makes field order part of the contract. Reordering a field is a protocol
|
|
47
|
+
change, not a refactor.
|
|
48
|
+
- `schemaVersion` is checked before parsing, never after.
|
|
49
|
+
|
|
50
|
+
## Security
|
|
51
|
+
|
|
52
|
+
- Never commit the developer key, an app UUID hardcoded as a literal, or any credential.
|
|
53
|
+
The developer key is unrecoverable if lost — treat its loss as unrecoverable and rotate.
|
|
54
|
+
- Data minimisation: a field not needed on the device is not sent to it.
|
|
55
|
+
- No payload logging. Log the shape of a failure — field name and reason — never the data.
|
|
56
|
+
|
|
57
|
+
## Build
|
|
58
|
+
|
|
59
|
+
- `monkeyc -f monkey.jungle` with the documented product ids. Verify with the identity and
|
|
60
|
+
settings-defaults checks before every build; they catch the two mistakes that otherwise reach
|
|
61
|
+
a device.
|
|
62
|
+
- Do not pass `--configuration-cache` to Gradle.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: container
|
|
3
|
+
simt-title: Containers
|
|
4
|
+
simt-applies: [has-dockerfile]
|
|
5
|
+
simt-fallback: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Containers
|
|
9
|
+
|
|
10
|
+
A Dockerfile is the build. If a rule here and the repo's own instructions disagree, the
|
|
11
|
+
repo wins — but the disagreement is worth raising, because these are the mistakes that
|
|
12
|
+
survive to production.
|
|
13
|
+
|
|
14
|
+
- **Pin the base image by digest.** A floating `FROM node:22` means last night's build is
|
|
15
|
+
not reproducible and a compromised upstream tag is your problem. If the repo deliberately
|
|
16
|
+
tracks a tag, do not "fix" it silently.
|
|
17
|
+
- **Never bake a secret.** No `ARG` that holds a token, no `ENV` holding a key, no `COPY
|
|
18
|
+
.env`. Build args are recorded in image history, which is world-readable in most
|
|
19
|
+
registries. Read secrets from the runtime environment.
|
|
20
|
+
- **Copy as little as possible.** Copy manifests first, install, then copy source: a
|
|
21
|
+
dependency layer invalidates on every source edit otherwise. And never `COPY . .` when a
|
|
22
|
+
`.dockerignore` exists to be honoured.
|
|
23
|
+
- **Run as a non-root user**, and do not need root to write to the application directory.
|
|
24
|
+
- **One process per container.** Signals, restart policy and log collection all assume the
|
|
25
|
+
entrypoint is the app, not a shell wrapping it.
|
|
26
|
+
- **Expose only what is served.** An unpublished port is still reachable on the container
|
|
27
|
+
network; an `EXPOSE` is documentation, not a control.
|
|
28
|
+
- **Health endpoints are not debug endpoints.** A health check runs often, automatically,
|
|
29
|
+
and on every replica. It must not leak counts, versions, or stack traces.
|
|
30
|
+
- **The image is the unit of rollback.** A change you cannot rebuild byte-identically is a
|
|
31
|
+
change you cannot roll back, which is a worse position than not shipping it.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: jvm
|
|
3
|
+
simt-title: JVM backend
|
|
4
|
+
# The build tool and the language, which is what a JVM backend is identified by.
|
|
5
|
+
# This profile used to be `jvm-android` and applied on `gradle` alone, so a Spring
|
|
6
|
+
# or Ktor service received rules about Compose, Wear OS application ids and
|
|
7
|
+
# instrumentation tests — advice with nothing to apply it to (S28, ADR-047).
|
|
8
|
+
simt-applies: [gradle, has-maven-pom, kotlin, java]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Stack profile: JVM backend
|
|
12
|
+
|
|
13
|
+
Applies to: `gradle`, `has-maven-pom`, `kotlin`, `java` — a JVM build. The Android
|
|
14
|
+
rules live in the `android` profile, which selects on `has-android-manifest`.
|
|
15
|
+
|
|
16
|
+
## Build
|
|
17
|
+
|
|
18
|
+
- `./gradlew` only, never a system `gradle`; `./mvnw` where a wrapper is committed,
|
|
19
|
+
never a system `mvn`. Never `--configuration-cache` where a repo documents that it
|
|
20
|
+
breaks.
|
|
21
|
+
- `./gradlew spotlessApply` is the formatting entrypoint in repos that use Spotless.
|
|
22
|
+
- Pin the JVM toolchain in the build file (`java.toolchain`, `sourceCompatibility`).
|
|
23
|
+
A JDK on the developer's PATH is not a floor anything enforces.
|
|
24
|
+
|
|
25
|
+
## Modules and layers
|
|
26
|
+
|
|
27
|
+
- `:core:domain` depends on no other internal module. Enforce it; do not add an
|
|
28
|
+
import that breaks the direction.
|
|
29
|
+
- Layer suffixes: `*Repository`, `*Service`, `*Mapper`, `*Controller`. A class named
|
|
30
|
+
`*Repository` that talks to HTTP is not a repository.
|
|
31
|
+
|
|
32
|
+
## Kotlin
|
|
33
|
+
|
|
34
|
+
- Four-space indent, LF, 300-char line limit.
|
|
35
|
+
- No `!!`. Prefer a sealed `RepositoryResult<T>` over exceptions for expected failures.
|
|
36
|
+
- Expose `MutableStateFlow` as `StateFlow` via `.asStateFlow()`; update state with
|
|
37
|
+
`_state.update { }`.
|
|
38
|
+
- Java and Kotlin interop: `Optional` in a Java signature, a nullability annotation in
|
|
39
|
+
a Kotlin one, and never a platform type left untyped at the boundary.
|
|
40
|
+
|
|
41
|
+
## Testing
|
|
42
|
+
|
|
43
|
+
- Unit tests for services, repositories and mappers; an integration test only when the
|
|
44
|
+
behaviour is genuinely cross-system.
|
|
45
|
+
- Assert behaviour, not implementation. Never weaken an assertion to match current
|
|
46
|
+
behaviour.
|
|
47
|
+
- Test the narrowest layer that proves the behaviour.
|
|
48
|
+
|
|
49
|
+
## Data
|
|
50
|
+
|
|
51
|
+
- One new sequential immutable migration per change. Never edit an applied migration.
|
|
52
|
+
Define coexistence order and forward repair; do not promise SQL rollback.
|
|
53
|
+
- Prepared, parameterised statements only. Never interpolate a value into SQL, and
|
|
54
|
+
never assemble SQL by string concatenation from a caller-supplied identifier.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
simt-id: node
|
|
3
|
+
simt-title: Node (general)
|
|
4
|
+
simt-applies: [node]
|
|
5
|
+
simt-fallback: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Stack profile: Node (general)
|
|
9
|
+
|
|
10
|
+
Applies to: `node`, `node-web`, `typescript` when no more specific profile matches.
|
|
11
|
+
|
|
12
|
+
## Manifest
|
|
13
|
+
|
|
14
|
+
- One `packageManager` field, matching the committed lockfile. A repo with no declared
|
|
15
|
+
package manager has no enforced one.
|
|
16
|
+
- `engines.node` states a floor. A version floor nobody enforces is a comment.
|
|
17
|
+
- `.nvmrc` pins the development version and must agree with `engines` and with CI.
|
|
18
|
+
|
|
19
|
+
## Scripts
|
|
20
|
+
|
|
21
|
+
- Run through `npm run <script>` (ADR-010). Never `npx` or `npm exec` for a declared
|
|
22
|
+
dependency: `npx` will fetch a version the project does not pin.
|
|
23
|
+
- A script name is the contract. Rename a script and rename every caller, including docs,
|
|
24
|
+
hooks and CI, in the same change.
|
|
25
|
+
|
|
26
|
+
## Formatting and linting
|
|
27
|
+
|
|
28
|
+
- Prettier owns formatting. ESLint disables the stylistic rules Prettier already owns —
|
|
29
|
+
otherwise the two tools fight over every file.
|
|
30
|
+
- `format` and `format:check` are both present. `format:check` is what CI and hooks run.
|
|
31
|
+
- `.prettierignore` covers generated and vendored output: build directories, coverage,
|
|
32
|
+
generated clients, graph output. Formatting generated output is a review finding, not a fix.
|
|
33
|
+
|
|
34
|
+
## Types
|
|
35
|
+
|
|
36
|
+
- `strict` plus the full strictness set, and `noEmit` for any project that bundles elsewhere.
|
|
37
|
+
A bare `tsc` in a bundler project writes JavaScript next to the sources.
|
|
38
|
+
- `moduleResolution` spelled consistently across every repo in the workspace.
|
|
39
|
+
|
|
40
|
+
## Tests
|
|
41
|
+
|
|
42
|
+
- `vitest`, `jest` or `node --test` — one runner per repo, declared, not inherited.
|
|
43
|
+
- Focused selection is the default invocation. A full suite runs against the triggers in
|
|
44
|
+
`simt-dev.json` (ADR-003).
|
|
45
|
+
- No snapshot-only acceptance: a snapshot proves the code did not change, not that it is right.
|