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.
Files changed (138) hide show
  1. python/simt_dev/__init__.py +32 -0
  2. python/simt_dev/cli.py +225 -0
  3. simt_dev/__init__.py +32 -0
  4. simt_dev/cli.py +225 -0
  5. simt_dev/vendored/CHANGELOG.md +166 -0
  6. simt_dev/vendored/bin/simt-dev.mjs +28 -0
  7. simt_dev/vendored/docs/decisions/ADR-001-push-requires-approval.md +42 -0
  8. simt_dev/vendored/docs/decisions/ADR-002-refactor-blast-radius.md +45 -0
  9. simt_dev/vendored/docs/decisions/ADR-003-test-breadth.md +46 -0
  10. simt_dev/vendored/docs/decisions/ADR-004-hook-owns-format.md +45 -0
  11. simt_dev/vendored/docs/decisions/ADR-005-file-length.md +42 -0
  12. simt_dev/vendored/docs/decisions/ADR-006-coverage-bar.md +59 -0
  13. simt_dev/vendored/docs/decisions/ADR-007-section-skeleton.md +54 -0
  14. simt_dev/vendored/docs/decisions/ADR-008-verdict-vocabulary.md +59 -0
  15. simt_dev/vendored/docs/decisions/ADR-009-skill-layout.md +48 -0
  16. simt_dev/vendored/docs/decisions/ADR-010-runner-invocation.md +56 -0
  17. simt_dev/vendored/docs/decisions/ADR-011-windows-support.md +52 -0
  18. simt_dev/vendored/docs/decisions/ADR-012-drop-windows-support.md +59 -0
  19. simt_dev/vendored/docs/decisions/ADR-013-genericity-by-facts-and-local-profiles.md +114 -0
  20. simt_dev/vendored/docs/decisions/ADR-014-workspace-is-configuration.md +211 -0
  21. simt_dev/vendored/docs/decisions/ADR-015-a-source-is-content-not-a-location.md +117 -0
  22. simt_dev/vendored/docs/decisions/ADR-016-a-divergent-rule-is-retired-not-merged.md +180 -0
  23. simt_dev/vendored/docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md +89 -0
  24. simt_dev/vendored/docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md +183 -0
  25. simt_dev/vendored/docs/decisions/ADR-019-a-declared-command-is-the-runner.md +110 -0
  26. simt_dev/vendored/docs/decisions/ADR-020-a-rule-is-written-per-surface-and-pointed-not-copied.md +100 -0
  27. simt_dev/vendored/docs/decisions/ADR-021-one-surface-per-repo.md +94 -0
  28. simt_dev/vendored/docs/decisions/ADR-022-a-key-simt-dev-reads-is-prefixed.md +98 -0
  29. simt_dev/vendored/docs/decisions/ADR-023-the-gate-ships-as-a-file-the-repo-owns.md +106 -0
  30. simt_dev/vendored/docs/decisions/ADR-024-simt-dev-overwrites-its-own-gate-file.md +111 -0
  31. simt_dev/vendored/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +153 -0
  32. simt_dev/vendored/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +117 -0
  33. simt_dev/vendored/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +130 -0
  34. 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
  35. simt_dev/vendored/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +120 -0
  36. simt_dev/vendored/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +114 -0
  37. simt_dev/vendored/docs/decisions/ADR-031-the-suite-stages-the-environment-it-needs.md +129 -0
  38. simt_dev/vendored/docs/decisions/ADR-032-provenance-is-a-content-digest-not-a-commit.md +141 -0
  39. simt_dev/vendored/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +82 -0
  40. simt_dev/vendored/docs/decisions/ADR-034-a-staged-tool-refuses-a-partially-staged-file.md +91 -0
  41. simt_dev/vendored/docs/decisions/ADR-035-one-write-decision-and-one-json-document.md +119 -0
  42. simt_dev/vendored/docs/decisions/ADR-036-a-writer-that-writes-says-so-and-the-instructions-naming-it-move-with-it.md +103 -0
  43. simt_dev/vendored/docs/decisions/ADR-037-unchanged-means-writing-would-not-change-a-byte.md +100 -0
  44. simt_dev/vendored/docs/decisions/ADR-038-the-gate-filter-belongs-to-every-manifest-writer.md +95 -0
  45. simt_dev/vendored/docs/decisions/ADR-039-a-ci-step-runs-a-form-that-can-fail.md +101 -0
  46. simt_dev/vendored/docs/decisions/ADR-040-the-last-writer-that-wrote-by-default-was-not-the-one-named.md +158 -0
  47. simt_dev/vendored/docs/decisions/ADR-041-the-suite-installs-the-package-in-order-to-test-it.md +92 -0
  48. simt_dev/vendored/docs/decisions/ADR-042-a-declared-glob-must-match-a-path-built-from-it.md +94 -0
  49. simt_dev/vendored/docs/decisions/ADR-043-a-keyword-is-a-phrase-and-two-letters-is-a-word.md +90 -0
  50. simt_dev/vendored/docs/decisions/ADR-044-compatibility-is-not-evidence.md +116 -0
  51. simt_dev/vendored/docs/decisions/ADR-045-a-dashboard-that-counts-a-defect.md +119 -0
  52. simt_dev/vendored/docs/decisions/ADR-046-one-rule-one-copy.md +140 -0
  53. simt_dev/vendored/docs/decisions/ADR-047-a-profile-selects-on-what-a-repository-runs-on.md +141 -0
  54. simt_dev/vendored/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +150 -0
  55. simt_dev/vendored/docs/decisions/ADR-049-a-ruling-that-a-later-record-moved-says-so-where-it-is-printed.md +153 -0
  56. 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
  57. simt_dev/vendored/docs/decisions/ADR-051-a-pointer-is-the-readers-only-route-so-it-is-not-a-convenience.md +134 -0
  58. simt_dev/vendored/docs/decisions/ADR-052-a-dependency-is-counted-against-the-budget-and-there-is-one-budget.md +101 -0
  59. simt_dev/vendored/docs/decisions/ADR-053-the-node-floor-is-22-20-0.md +49 -0
  60. simt_dev/vendored/docs/decisions/ADR-054-the-digest-covers-every-byte-that-leaves-the-package.md +100 -0
  61. simt_dev/vendored/docs/decisions/ADR-055-a-status-line-travels-so-it-may-not-name-a-location.md +98 -0
  62. simt_dev/vendored/docs/decisions/ADR-056-three-names-one-of-which-is-cheap-to-move.md +160 -0
  63. simt_dev/vendored/docs/decisions/ADR-057-an-identity-is-per-developer-and-a-repository-does-not-own-it.md +115 -0
  64. simt_dev/vendored/docs/decisions/ADR-058-one-tool-three-channels-and-the-order-is-forced.md +148 -0
  65. simt_dev/vendored/docs/decisions/README.md +530 -0
  66. simt_dev/vendored/graph/skills.graph.json +1622 -0
  67. simt_dev/vendored/package.json +43 -0
  68. simt_dev/vendored/rules/core.md +76 -0
  69. simt_dev/vendored/rules/mdc/intake-questions.mdc +50 -0
  70. simt_dev/vendored/rules/mdc/preview-postflight.mdc +61 -0
  71. simt_dev/vendored/rules/mdc/slice-commit.mdc +52 -0
  72. simt_dev/vendored/rules/mdc/test-gate.mdc +54 -0
  73. simt_dev/vendored/rules/stacks/android.md +63 -0
  74. simt_dev/vendored/rules/stacks/cloudflare-workers.md +65 -0
  75. simt_dev/vendored/rules/stacks/connectiq.md +62 -0
  76. simt_dev/vendored/rules/stacks/container.md +31 -0
  77. simt_dev/vendored/rules/stacks/jvm.md +54 -0
  78. simt_dev/vendored/rules/stacks/node.md +45 -0
  79. simt_dev/vendored/rules/stacks/python.md +48 -0
  80. simt_dev/vendored/rules/stacks/react-native.md +58 -0
  81. simt_dev/vendored/rules/stacks/react-web.md +61 -0
  82. simt_dev/vendored/rules/stacks/typescript.md +21 -0
  83. simt_dev/vendored/rules/stacks/uv.md +32 -0
  84. simt_dev/vendored/src/argv.mjs +116 -0
  85. simt_dev/vendored/src/ci/dependabot.mjs +268 -0
  86. simt_dev/vendored/src/ci/workflow.mjs +360 -0
  87. simt_dev/vendored/src/commands/ci.mjs +285 -0
  88. simt_dev/vendored/src/commands/compare.mjs +195 -0
  89. simt_dev/vendored/src/commands/graph.mjs +73 -0
  90. simt_dev/vendored/src/commands/help.mjs +108 -0
  91. simt_dev/vendored/src/commands/hooks.mjs +471 -0
  92. simt_dev/vendored/src/commands/index.mjs +241 -0
  93. simt_dev/vendored/src/commands/init.mjs +632 -0
  94. simt_dev/vendored/src/commands/mcp.mjs +63 -0
  95. simt_dev/vendored/src/commands/rules.mjs +374 -0
  96. simt_dev/vendored/src/commands/skills.mjs +647 -0
  97. simt_dev/vendored/src/commands/skillsinstall.mjs +515 -0
  98. simt_dev/vendored/src/commands/sync.mjs +331 -0
  99. simt_dev/vendored/src/commands/upgrade.mjs +202 -0
  100. simt_dev/vendored/src/compare/discover.mjs +212 -0
  101. simt_dev/vendored/src/compare/hooksstate.mjs +159 -0
  102. simt_dev/vendored/src/compare/row.mjs +299 -0
  103. simt_dev/vendored/src/decisions/index.mjs +536 -0
  104. simt_dev/vendored/src/graph/load.mjs +541 -0
  105. simt_dev/vendored/src/hooks/capability.mjs +129 -0
  106. simt_dev/vendored/src/hooks/dispatcher.mjs +268 -0
  107. simt_dev/vendored/src/hooks/gates.mjs +144 -0
  108. simt_dev/vendored/src/hooks/resolve.mjs +194 -0
  109. simt_dev/vendored/src/hooks/shim.mjs +140 -0
  110. simt_dev/vendored/src/hooks/where.mjs +118 -0
  111. simt_dev/vendored/src/identity.mjs +218 -0
  112. simt_dev/vendored/src/locate.mjs +75 -0
  113. simt_dev/vendored/src/mcp/server.mjs +167 -0
  114. simt_dev/vendored/src/mcp/tools.mjs +410 -0
  115. simt_dev/vendored/src/name.mjs +95 -0
  116. simt_dev/vendored/src/output/index.mjs +96 -0
  117. simt_dev/vendored/src/projection/baselines.mjs +209 -0
  118. simt_dev/vendored/src/projection/conflicts.mjs +205 -0
  119. simt_dev/vendored/src/projection/manifest.mjs +478 -0
  120. simt_dev/vendored/src/projection/markers.mjs +349 -0
  121. simt_dev/vendored/src/projection/mdc.mjs +327 -0
  122. simt_dev/vendored/src/projection/project.mjs +504 -0
  123. simt_dev/vendored/src/projection/rules.mjs +640 -0
  124. simt_dev/vendored/src/projection/surfaces.mjs +398 -0
  125. simt_dev/vendored/src/projection/targets.mjs +221 -0
  126. simt_dev/vendored/src/resolve/fingerprint.mjs +553 -0
  127. simt_dev/vendored/src/resolve/near.mjs +61 -0
  128. simt_dev/vendored/src/resolve/resolve.mjs +457 -0
  129. simt_dev/vendored/src/resolve/source.mjs +105 -0
  130. simt_dev/vendored/src/toolchain.mjs +202 -0
  131. simt_dev/vendored/src/tsconfig.mjs +314 -0
  132. simt_dev/vendored/src/upgrade/plan.mjs +203 -0
  133. simt_dev/vendored/src/workspace.mjs +289 -0
  134. simt_dev-0.2.0.dist-info/METADATA +150 -0
  135. simt_dev-0.2.0.dist-info/RECORD +138 -0
  136. simt_dev-0.2.0.dist-info/WHEEL +4 -0
  137. simt_dev-0.2.0.dist-info/entry_points.txt +2 -0
  138. simt_dev-0.2.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,148 @@
1
+ # One tool, three channels, and the order is forced
2
+
3
+ Status: **accepted** · 2026-10-11
4
+
5
+ Affects: `pyproject.toml`, `python/simt_dev/`, `Formula/simt-dev.rb`,
6
+ `.github/workflows/publish.yml`, `scripts/release-formula.mjs`,
7
+ `scripts/verify-wheel.mjs`, `test/packaging.test.mjs`
8
+
9
+ ## Context
10
+
11
+ simt-dev is a Node program. It is also a *repository* tool: somebody adopting a
12
+ repository runs `simt init` inside it, and the places people run that from are
13
+ not all Node projects. A python developer in a repository that has no
14
+ `package.json` has no reason to install a package manager to get a command that
15
+ adopts their repository, and a Homebrew user has no reason to install Node to
16
+ install a tool that needs Node.
17
+
18
+ So the tool is published to three channels:
19
+
20
+ | Channel | What it installs |
21
+ | --- | --- |
22
+ | npm | a tarball of `.mjs` files |
23
+ | PyPI | a wheel carrying those same files, plus a `simt` entry point |
24
+ | Homebrew | a formula that downloads the npm tarball at install time |
25
+
26
+ The design constraint that decided all of it: the tool has **zero runtime
27
+ dependencies and no build step**. The whole thing is ~1.3 MB of text. That makes
28
+ the "packaging for a second language" problem a *copying* problem rather than a
29
+ build one, and copying is a problem with a right answer.
30
+
31
+ ## Ruling
32
+
33
+ 1. **The Python wheel carries the tool's own source, byte for byte.** `files` in
34
+ `package.json` and `force-include` in `pyproject.toml` name the same paths,
35
+ and `scripts/verify-wheel.mjs` builds both artefacts and hashes every file in
36
+ both, failing the release when a single byte differs.
37
+
38
+ The wheel is not a wrapper around `npm install`. A wrapper would fetch code
39
+ at install time - unpinned, unsigned by anything the installer can check, and
40
+ absent the moment somebody runs `pip install` on a train.
41
+
42
+ 2. **The version is one number, read rather than written.**
43
+ `pyproject.toml` declares `dynamic = ["version"]` and reads it from
44
+ `package.json` with a regex. A second literal would drift, and the drift
45
+ would be invisible until somebody ran `pip show` beside `simt --version` and
46
+ got two answers about which release they are running.
47
+
48
+ 3. **The Python launcher is standard library only, and has no required
49
+ dependencies.** The `[node]` extra is optional and imports `nodejs_wheel`
50
+ *inside a `try`, at the point of use* - never at module level - so the
51
+ default install cannot fail on a package it never asked for.
52
+
53
+ ADR-012 rules out runtime dependencies for the npm package. The reasoning
54
+ transfers without modification: a dependency is a runtime this package cannot
55
+ verify and its owner cannot patch, and this package exists to be the thing
56
+ people run in a repository rather than the thing they have to curate.
57
+
58
+ 4. **`simt` is the one command, on every channel.** `bin` in `package.json` and
59
+ `[project.scripts]` in `pyproject.toml` are asserted equal, and the formula's
60
+ `test do` block runs it. A second name under the package name would be a
61
+ convenience that forks the documentation the first time somebody documents
62
+ one channel and not the other.
63
+
64
+ 5. **Homebrew installs the npm tarball, and does not use `std_npm_args`.**
65
+ `depends_on "node"` declares the runtime; the entry point's shebang is
66
+ rewritten to that node rather than left at `#!/usr/bin/env node`, so the
67
+ command works from a cron job and from a shell that has moved node out of
68
+ the way, both of which look identical to a broken install.
69
+
70
+ `std_npm_args` invokes npm at build time, which needs the registry at build
71
+ time. This package has nothing to install, so that would add a network fetch
72
+ to installing the one thing in the estate that provably needs none.
73
+
74
+ 6. **`Formula/simt-dev.rb` is the source of truth, and `scripts/release-formula.mjs`
75
+ patches two lines in it.** It does not regenerate the formula from a
76
+ template: a template would mean the bytes a reader sees in this repository
77
+ are not the bytes a tap serves, which is the defect ADR-023 ruled out for the
78
+ gate file. The two lines are matched by content, not by line number, because
79
+ a line number would make the formula a file that cannot be edited - adding a
80
+ comment above the `sha256` line would silently write the checksum to the
81
+ wrong place.
82
+
83
+ 7. **The npm version must be live before `publish-pypi` and `publish-brew`
84
+ succeed, and both jobs say so when it is not.** The order is not a
85
+ preference. Homebrew downloads from the npm registry at install time, so a
86
+ formula naming a version that is still in npm's staging area installs a
87
+ 404 - on somebody else's machine, at a tap, from a release this workflow
88
+ already declared done.
89
+
90
+ Those two jobs are the ones a maintainer runs again after
91
+ `npm stage approve`. "Re-run failed jobs" carries the tag context, which a
92
+ `workflow_dispatch` does not: on a dispatch, `github.ref_name` is the branch,
93
+ and the version guard would compare a tag against a branch.
94
+
95
+ ## Consequences
96
+
97
+ The invariants split in two, and the split is deliberate.
98
+
99
+ `test/packaging.test.mjs` holds the **static** ones: that `files` and
100
+ `force-include` name the same paths, that the version is not written twice, that
101
+ the command is one name, that the launcher imports nothing outside the standard
102
+ library, that no second build system sits at the repository root. Every one of
103
+ those is checkable from this checkout with no network and no interpreter, which
104
+ is what lets it run in `npm test`.
105
+
106
+ `scripts/verify-wheel.mjs` holds the one that is not: that the paths, once
107
+ built, contain the same bytes. It runs in the release workflow, where a Python
108
+ and a registry are available by construction.
109
+
110
+ That split is ADR-031 applied. A test that needed `pip` would not run for the
111
+ next person who clones this repository, and would run differently on the
112
+ machine that wrote it - which is the failure that suite is about.
113
+
114
+ Two findings from building it are recorded because they are the kind that only
115
+ appear when a check is real:
116
+
117
+ - The first run of `verify-wheel.mjs` reported `LICENSE` and `README.md` as
118
+ differences. They are not defects: npm adds both to every tarball, and the
119
+ wheel carries them as *distribution metadata* rather than as files. The
120
+ comparison is now scoped to what `files` names, which is the content the tool
121
+ reads.
122
+ - The first run of that same comparison reported every one of `bin`, `src`,
123
+ `graph` and `rules` as missing from both, because `files` names **directories**
124
+ and npm includes them recursively. The expansion happens in the script now,
125
+ with the reason stated where the code is.
126
+
127
+ The check was also tampered with deliberately - a wheel-only `README.md` was
128
+ injected into `force-include` and it failed - because a parity check that has
129
+ never failed is not evidence that it works.
130
+
131
+ ## Rejected
132
+
133
+ - **A Python wrapper that runs `npm install -g simt-dev` on first use.** This is
134
+ what the unofficial `pyright` package does, and it has the properties it is
135
+ reputed to have: a download on first run, unpinned, uncacheable, and a failure
136
+ mode that reads as a broken package rather than as a missing runtime.
137
+ - **Requiring node, with no bundled fallback.** The reader who installs from
138
+ PyPI is frequently the reader with no node, which is the whole reason the
139
+ channel exists. The failure message therefore names the `[node]` extra first,
140
+ because it is the one remedy that is the same command on every platform.
141
+ - **Guessing where node is.** A hardcoded list of `/opt/homebrew/bin/node`,
142
+ `/usr/local/bin/node` and `~/.nvm/...` is three guesses and a surprise: it
143
+ prefers a guess over the reader's own `PATH`, and it is already out of date
144
+ for anyone who installs node differently. `shutil.which` asks the question
145
+ instead of answering it.
146
+ - **`std_npm_args`.** See ruling 5.
147
+ - **Publishing all three channels simultaneously.** Rejected by the ordering in
148
+ ruling 7 - Homebrew cannot name a version npm has not released.