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,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.
|