simt-dev 0.2.1__tar.gz → 0.3.0__tar.gz

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 (202) hide show
  1. {simt_dev-0.2.1 → simt_dev-0.3.0}/.gitignore +7 -1
  2. {simt_dev-0.2.1 → simt_dev-0.3.0}/CHANGELOG.md +239 -0
  3. {simt_dev-0.2.1 → simt_dev-0.3.0}/PKG-INFO +2 -2
  4. simt_dev-0.3.0/bin/simt-dev.mjs +47 -0
  5. simt_dev-0.3.0/docs/decisions/ADR-064-the-context-meter-counts-what-the-tool-wrote.md +107 -0
  6. simt_dev-0.3.0/docs/decisions/ADR-065-a-pack-is-one-shared-rule-set-pinned-by-digest.md +97 -0
  7. simt_dev-0.3.0/docs/decisions/ADR-066-a-monorepo-projects-once-per-package.md +115 -0
  8. simt_dev-0.3.0/docs/decisions/ADR-067-which-harnesses-read-agents-md-is-a-column.md +110 -0
  9. simt_dev-0.3.0/docs/decisions/ADR-068-the-mcp-revision-list-cites-what-was-read.md +111 -0
  10. simt_dev-0.3.0/docs/decisions/ADR-069-the-conformance-lint-checks-the-published-specification.md +90 -0
  11. simt_dev-0.3.0/docs/decisions/ADR-070-the-json-payload-carries-its-own-version.md +91 -0
  12. simt_dev-0.3.0/docs/decisions/ADR-071-a-rule-that-can-be-narrowed-is-written-narrow.md +106 -0
  13. simt_dev-0.3.0/docs/decisions/ADR-072-the-budget-is-a-declaration-beside-the-gates.md +104 -0
  14. simt_dev-0.3.0/docs/decisions/ADR-073-a-kernel-is-a-smaller-projection-the-repository-asks-for.md +92 -0
  15. simt_dev-0.3.0/docs/decisions/ADR-074-a-rule-and-a-decision-are-readable-by-uri.md +99 -0
  16. simt_dev-0.3.0/docs/decisions/ADR-075-nothing-leaves-the-machine.md +56 -0
  17. simt_dev-0.3.0/docs/decisions/ADR-076-a-change-to-what-agents-read-is-measured.md +70 -0
  18. simt_dev-0.3.0/docs/decisions/ADR-077-the-command-card-leads-the-region-and-reads-committed-evidence.md +91 -0
  19. simt_dev-0.3.0/docs/decisions/ADR-078-a-line-the-repository-already-proves-does-not-ship.md +117 -0
  20. simt_dev-0.3.0/docs/decisions/ADR-079-the-rules-are-enforced-at-the-tool-call-from-one-policy-table.md +162 -0
  21. simt_dev-0.3.0/docs/decisions/ADR-080-the-tools-own-names-are-not-repositories-and-neither-reader-decides-by-shape.md +99 -0
  22. simt_dev-0.3.0/docs/decisions/ADR-081-a-tracker-is-read-through-the-repositorys-template.md +36 -0
  23. simt_dev-0.3.0/docs/decisions/ADR-082-the-tracker-override-is-a-manifest-key.md +35 -0
  24. {simt_dev-0.2.1 → simt_dev-0.3.0}/package.json +2 -1
  25. {simt_dev-0.2.1 → simt_dev-0.3.0}/pyproject.toml +2 -0
  26. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/core.md +1 -0
  27. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/android.md +2 -0
  28. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/cloudflare-workers.md +2 -0
  29. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/connectiq.md +1 -0
  30. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/container.md +2 -0
  31. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/dart.md +2 -0
  32. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/deno.md +2 -0
  33. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/dotnet.md +2 -0
  34. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/elixir.md +2 -0
  35. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/go.md +2 -0
  36. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/jvm.md +2 -0
  37. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/node.md +2 -0
  38. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/python.md +2 -0
  39. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/react-native.md +2 -0
  40. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/react-web.md +2 -0
  41. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/ruby.md +2 -0
  42. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/rust.md +2 -0
  43. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/swift.md +2 -0
  44. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/typescript.md +2 -0
  45. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/stacks/uv.md +2 -0
  46. simt_dev-0.3.0/src/agenthooks/decide.mjs +123 -0
  47. simt_dev-0.3.0/src/agenthooks/policy.mjs +78 -0
  48. simt_dev-0.3.0/src/agenthooks/render.mjs +240 -0
  49. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/argv.mjs +55 -1
  50. simt_dev-0.3.0/src/bench/ablate.mjs +630 -0
  51. simt_dev-0.3.0/src/bench/adapters.json +149 -0
  52. simt_dev-0.3.0/src/bench/grade.mjs +229 -0
  53. simt_dev-0.3.0/src/bench/mine.mjs +811 -0
  54. simt_dev-0.3.0/src/bench/probes.mjs +1350 -0
  55. simt_dev-0.3.0/src/bench/report.mjs +899 -0
  56. simt_dev-0.3.0/src/bench/run.mjs +1002 -0
  57. simt_dev-0.3.0/src/commands/agenthook.mjs +282 -0
  58. simt_dev-0.3.0/src/commands/bench.mjs +359 -0
  59. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/compare.mjs +15 -0
  60. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/graph.mjs +45 -21
  61. simt_dev-0.3.0/src/commands/harness.mjs +301 -0
  62. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/help.mjs +73 -2
  63. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/index.mjs +67 -25
  64. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/init.mjs +12 -8
  65. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/mcp.mjs +20 -5
  66. simt_dev-0.3.0/src/commands/plan.mjs +99 -0
  67. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/rules.mjs +42 -57
  68. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/skills.mjs +67 -92
  69. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/skillsinstall.mjs +17 -2
  70. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/sync.mjs +62 -6
  71. simt_dev-0.3.0/src/commands/tokens.mjs +169 -0
  72. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/compare/row.mjs +73 -2
  73. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/decisions/index.mjs +23 -5
  74. simt_dev-0.3.0/src/graph/spec.mjs +227 -0
  75. simt_dev-0.3.0/src/graph/speccheck.mjs +68 -0
  76. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/hooks/dispatcher.mjs +35 -3
  77. simt_dev-0.3.0/src/kernel/card.mjs +254 -0
  78. simt_dev-0.3.0/src/kernel/inferability.mjs +188 -0
  79. simt_dev-0.3.0/src/mcp/resources.mjs +157 -0
  80. simt_dev-0.3.0/src/mcp/schema.mjs +139 -0
  81. simt_dev-0.3.0/src/mcp/server.mjs +375 -0
  82. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/mcp/tools.mjs +372 -1
  83. simt_dev-0.3.0/src/measure/context.mjs +542 -0
  84. simt_dev-0.3.0/src/measure/loaderView.mjs +435 -0
  85. simt_dev-0.3.0/src/measure/session.mjs +267 -0
  86. simt_dev-0.3.0/src/monorepo.mjs +339 -0
  87. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/output/index.mjs +78 -2
  88. simt_dev-0.3.0/src/packs.mjs +182 -0
  89. simt_dev-0.3.0/src/plan/index.mjs +650 -0
  90. simt_dev-0.3.0/src/plan/template.mjs +331 -0
  91. simt_dev-0.3.0/src/projection/loaders.mjs +689 -0
  92. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/projection/manifest.mjs +114 -3
  93. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/projection/project.mjs +140 -9
  94. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/projection/rules.mjs +365 -22
  95. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/projection/surfaces.mjs +155 -23
  96. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/projection/targets.mjs +37 -0
  97. simt_dev-0.3.0/src/toolwords.mjs +69 -0
  98. simt_dev-0.3.0/templates/plan/tracker.json +36 -0
  99. simt_dev-0.2.1/bin/simt-dev.mjs +0 -28
  100. simt_dev-0.2.1/src/mcp/server.mjs +0 -167
  101. {simt_dev-0.2.1 → simt_dev-0.3.0}/LICENSE +0 -0
  102. {simt_dev-0.2.1 → simt_dev-0.3.0}/README.md +0 -0
  103. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-001-push-requires-approval.md +0 -0
  104. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-002-refactor-blast-radius.md +0 -0
  105. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-003-test-breadth.md +0 -0
  106. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-004-hook-owns-format.md +0 -0
  107. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-005-file-length.md +0 -0
  108. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-006-coverage-bar.md +0 -0
  109. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-007-section-skeleton.md +0 -0
  110. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-008-verdict-vocabulary.md +0 -0
  111. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-009-skill-layout.md +0 -0
  112. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-010-runner-invocation.md +0 -0
  113. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-011-windows-support.md +0 -0
  114. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-012-drop-windows-support.md +0 -0
  115. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-013-genericity-by-facts-and-local-profiles.md +0 -0
  116. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-014-workspace-is-configuration.md +0 -0
  117. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-015-a-source-is-content-not-a-location.md +0 -0
  118. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-016-a-divergent-rule-is-retired-not-merged.md +0 -0
  119. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md +0 -0
  120. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md +0 -0
  121. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-019-a-declared-command-is-the-runner.md +0 -0
  122. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-020-a-rule-is-written-per-surface-and-pointed-not-copied.md +0 -0
  123. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-021-one-surface-per-repo.md +0 -0
  124. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-022-a-key-simt-dev-reads-is-prefixed.md +0 -0
  125. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-023-the-gate-ships-as-a-file-the-repo-owns.md +0 -0
  126. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-024-simt-dev-overwrites-its-own-gate-file.md +0 -0
  127. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +0 -0
  128. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +0 -0
  129. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +0 -0
  130. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +0 -0
  131. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +0 -0
  132. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +0 -0
  133. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-031-the-suite-stages-the-environment-it-needs.md +0 -0
  134. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-032-provenance-is-a-content-digest-not-a-commit.md +0 -0
  135. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +0 -0
  136. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-034-a-staged-tool-refuses-a-partially-staged-file.md +0 -0
  137. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-035-one-write-decision-and-one-json-document.md +0 -0
  138. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-036-a-writer-that-writes-says-so-and-the-instructions-naming-it-move-with-it.md +0 -0
  139. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-037-unchanged-means-writing-would-not-change-a-byte.md +0 -0
  140. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-038-the-gate-filter-belongs-to-every-manifest-writer.md +0 -0
  141. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-039-a-ci-step-runs-a-form-that-can-fail.md +0 -0
  142. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-040-the-last-writer-that-wrote-by-default-was-not-the-one-named.md +0 -0
  143. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-041-the-suite-installs-the-package-in-order-to-test-it.md +0 -0
  144. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-042-a-declared-glob-must-match-a-path-built-from-it.md +0 -0
  145. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-043-a-keyword-is-a-phrase-and-two-letters-is-a-word.md +0 -0
  146. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-044-compatibility-is-not-evidence.md +0 -0
  147. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-045-a-dashboard-that-counts-a-defect.md +0 -0
  148. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-046-one-rule-one-copy.md +0 -0
  149. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-047-a-profile-selects-on-what-a-repository-runs-on.md +0 -0
  150. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +0 -0
  151. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-049-a-ruling-that-a-later-record-moved-says-so-where-it-is-printed.md +0 -0
  152. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-050-a-byte-count-is-not-a-region-and-updated-is-not-a-change-of-rules.md +0 -0
  153. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-051-a-pointer-is-the-readers-only-route-so-it-is-not-a-convenience.md +0 -0
  154. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-052-a-dependency-is-counted-against-the-budget-and-there-is-one-budget.md +0 -0
  155. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-053-the-node-floor-is-22-20-0.md +0 -0
  156. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-054-the-digest-covers-every-byte-that-leaves-the-package.md +0 -0
  157. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-055-a-status-line-travels-so-it-may-not-name-a-location.md +0 -0
  158. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-056-three-names-one-of-which-is-cheap-to-move.md +0 -0
  159. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-057-an-identity-is-per-developer-and-a-repository-does-not-own-it.md +0 -0
  160. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-058-one-tool-three-channels-and-the-order-is-forced.md +0 -0
  161. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-059-the-version-a-repository-runs-is-the-one-it-pinned.md +0 -0
  162. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-060-a-repository-with-nothing-to-run-is-adopted-and-says-so.md +0 -0
  163. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-061-the-skill-registry-is-workspace-data.md +0 -0
  164. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-062-a-fresh-adoption-seeds-the-gates-it-can-run.md +0 -0
  165. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/ADR-063-the-adoption-escape-hatch-is-opt-in.md +0 -0
  166. {simt_dev-0.2.1 → simt_dev-0.3.0}/docs/decisions/README.md +0 -0
  167. {simt_dev-0.2.1 → simt_dev-0.3.0}/python/simt_dev/__init__.py +0 -0
  168. {simt_dev-0.2.1 → simt_dev-0.3.0}/python/simt_dev/cli.py +0 -0
  169. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/mdc/intake-questions.mdc +0 -0
  170. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/mdc/preview-postflight.mdc +0 -0
  171. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/mdc/slice-commit.mdc +0 -0
  172. {simt_dev-0.2.1 → simt_dev-0.3.0}/rules/mdc/test-gate.mdc +0 -0
  173. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/ci/dependabot.mjs +0 -0
  174. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/ci/workflow.mjs +0 -0
  175. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/ci.mjs +0 -0
  176. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/hooks.mjs +0 -0
  177. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/commands/upgrade.mjs +0 -0
  178. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/compare/discover.mjs +0 -0
  179. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/compare/hooksstate.mjs +0 -0
  180. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/graph/load.mjs +0 -0
  181. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/hooks/capability.mjs +0 -0
  182. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/hooks/gates.mjs +0 -0
  183. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/hooks/resolve.mjs +0 -0
  184. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/hooks/shim.mjs +0 -0
  185. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/hooks/where.mjs +0 -0
  186. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/identity.mjs +0 -0
  187. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/locate.mjs +0 -0
  188. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/name.mjs +0 -0
  189. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/package-version.mjs +0 -0
  190. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/projection/baselines.mjs +0 -0
  191. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/projection/conflicts.mjs +0 -0
  192. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/projection/markers.mjs +0 -0
  193. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/projection/mdc.mjs +0 -0
  194. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/resolve/eval.mjs +0 -0
  195. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/resolve/fingerprint.mjs +0 -0
  196. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/resolve/near.mjs +0 -0
  197. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/resolve/resolve.mjs +0 -0
  198. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/resolve/source.mjs +0 -0
  199. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/toolchain.mjs +0 -0
  200. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/tsconfig.mjs +0 -0
  201. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/upgrade/plan.mjs +0 -0
  202. {simt_dev-0.2.1 → simt_dev-0.3.0}/src/workspace.mjs +0 -0
@@ -7,4 +7,10 @@
7
7
  git.config
8
8
 
9
9
  .pybuild/
10
- dist/
10
+ dist/
11
+
12
+ # Local index, evidence ledger and benchmark run outputs (plan 0003 §4).
13
+ # Task descriptors in .simt-dev/bench/tasks/*.json are committed; runs are not.
14
+ .simt-dev/index.sqlite
15
+ .simt-dev/ledger.jsonl
16
+ .simt-dev/bench/runs/
@@ -6,6 +6,242 @@ All notable changes to this package are recorded here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.0] — 2026-10-17
10
+
11
+ ### The rules and the decisions are readable as MCP resources
12
+
13
+ A client that wants to browse this tool's rules and rulings had no way to: every answer came back from a tool call with an argument in it. They are addressable now, as one URI per rule and one per decision record, with the byte size each will cost stated up front — the one number a client cannot get without spending the read.
14
+
15
+ Both resource revisions of the specification were read rather than recalled, and they disagree about the behaviour that decides whether a client retries or gives up: a missing resource is `-32602` on the newest revision and `-32002` on the two older ones, and neither lets a server answer with an empty body. A decision record is redacted on the way out, because a resource travels further than the CLI did (ADR-074).
16
+
17
+ ### A plan is resumed one slice at a time
18
+
19
+ `simt plan next` returns the next ready slice and only its section. `simt plan done` rewrites that one row, and previews the change unless `--yes` is given. The directories and the table format come from the `plans` key in `simt.json` when a repository sets one; otherwise the tracker template delivered with the tool is used, which reads `docs/plans` and `docs/reviews` (ADR-081, ADR-082). `init` keeps a manifest key it does not write, which is what lets `plans` survive a re-init.
20
+
21
+ ### The rules are enforced at the tool call
22
+
23
+ Three rules that text does not hold — a `git push`, a `--no-verify`, an edit inside a generated region — are now a decision before the tool call. `simt agent-hooks install` writes one policy into the harnesses that read a hooks file, and keeps every key it does not own. `simt agent-hook` is the entry point those harnesses call (ADR-079).
24
+
25
+ ### A line the repository already proves does not ship
26
+
27
+ A rule line may say when the repository already proves it. `simt sync` drops that line, and `--explain` names the line and the fact, so a removal is visible rather than silent (ADR-078).
28
+
29
+ ### The region leads with this repository's commands
30
+
31
+ The projected region opens with the commands this repository actually runs, derived from its committed gates and runner, in both the full projection and kernel mode (ADR-077).
32
+
33
+ ### A change to what an agent reads can be measured
34
+
35
+ `simt bench mine`, `run`, `report`, `ablate` and `probes` measure a change to projected context against tasks taken from this repository's own history. A change that cannot be run says `unmeasured` in its record rather than implying a result (ADR-076).
36
+
37
+ ### What each reader loads, and a transcript split
38
+
39
+ `simt harness loaders` reports what each reader loads at session start, in bytes, and what its size cap drops. DeepSeek is one of those readers. `simt tokens --session` splits a local Claude Code or Codex transcript into rules, file reads and tool output.
40
+
41
+ ### A name this tool owns is not reported as a repository
42
+
43
+ `simt rules why` keeps this tool's own names (`simt-priority`, `simt-hooks`, and the rest) and still redacts a repository the reader cannot open (ADR-080).
44
+
45
+ ## [0.2.2] — 2026-10-14
46
+
47
+ ### A repository can ask for a smaller projection
48
+
49
+ `simt tokens` measured the cost and `check` now holds the line the repository draws: `budget.bytes`
50
+ in `simt.json` is how many always-on bytes that repository has agreed to carry, and both the meter and
51
+ the gate report the overflow with the two numbers in them. It sits beside `gates` rather than inside
52
+ `projection`, because that key belongs to this tool and an upgrade replaces it.
53
+
54
+ `mode: kernel` is the other half. It keeps the rules a reader cannot do without — the ones a hard
55
+ constraint or a command depends on — and defers the rest behind a `## Deferred rules` section that
56
+ names each one and the command that shows it. A Python fixture projects 16% smaller. `full` is still
57
+ the default, because nobody has measured yet whether the smaller projection loses compliance, and a
58
+ tool that made that trade silently would be deciding it on the reader's behalf (ADR-073).
59
+
60
+ ### A repository can declare how many always-on bytes it is willing to carry
61
+
62
+ `simt tokens` has reported that number since the meter landed, against a constant nobody
63
+ could argue with. A repository can now say what it will carry — `budget: { bytes: N }`
64
+ beside `limits` and `gates` in the manifest — and both `simt tokens` and `simt-dev check`
65
+ measure against that line. One declaration, one meter, one function that words the finding.
66
+
67
+ The declaration is deliberately *not* inside `projection`, which is the one block this tool
68
+ owns and `upgrade` is allowed to replace: a number written there would be deleted silently
69
+ on the next upgrade and never reported as drift. And a repository that declares nothing is
70
+ reported as declaring nothing, rather than being quietly handed the default as though it
71
+ had chosen it.
72
+
73
+ Over budget is reported and never fails the gate. Nothing was cut — the projection is
74
+ still full mode, every rule loaded as written — so the honest thing to say is "you are
75
+ over your line", not to fail a repository over a ceiling it wrote down itself. Which rules
76
+ move behind skills stays with the evaluation that has to decide it (ADR-072).
77
+
78
+ ### A repository can declare the always-on bytes it intends to carry
79
+
80
+ `simt tokens` measured the number; it could not be held to one. A manifest now carries
81
+ `budget.bytes` — a declaration beside `limits` and `gates`, not inside `projection`,
82
+ because that block is regenerated on every upgrade and nothing in the repair would notice
83
+ its budget disappearing. A repository that declares nothing is measured against the
84
+ review's 30 KB and reports `declared: false` rather than looking like it chose it.
85
+
86
+ Over budget is reported, never enforced, and the report says what was not done: nothing
87
+ was cut, because the projection is still in full mode. Which rules move behind skills is a
88
+ measurement that has not been taken yet, and a budget that quietly dropped rules would be
89
+ enforcing a decision nobody made (ADR-072).
90
+
91
+ ### A stack profile loads only when a matching file is in play
92
+
93
+ A stack profile used to be always-on everywhere, because profiles reached a reader through
94
+ the generated region in AGENTS.md and through nothing else. A profile can now declare
95
+ `simt-scope`, and a repository on Cursor, Copilot or Antigravity gets it as a file the
96
+ surface attaches on patterns: `.cursor/rules/python.mdc` with `globs` and
97
+ `alwaysApply: false`, an Antigravity rule with `trigger: glob`, a Copilot
98
+ `.instructions.md` with `applyTo`. AGENTS.md keeps one line naming the profile, the
99
+ patterns and the file it moved to — a profile that vanishes from the region reads as one
100
+ that was deleted.
101
+
102
+ The narrowing happens only when every selected surface can do it. Claude Code's `paths:`
103
+ key is documented and silently fails to lazy-load for quoted values, and opencode has no
104
+ per-rule channel at all, so a repository carrying either keeps its profiles in the region
105
+ and is told which surface stopped it. A rule that never loads is worse than a rule that
106
+ loads eagerly.
107
+
108
+ Reading the two vendors' docs changed the shape of this: Antigravity requires patterns
109
+ starting with `*` to be quoted or YAML reads them as an alias anchor and the rule is
110
+ discarded with no message, and it documents a 20,000-token budget for always_on rules past
111
+ which they are demoted to pointers — so eager profiles cost the reader a real budget. A
112
+ `simt-scope` that matches everything is now refused, since that is the same behaviour with
113
+ a narrowing-looking spelling (ADR-071).
114
+
115
+ ### Every `--json` payload says which version it is
116
+
117
+ An agent that parses `--json` had no way to notice that a field had moved: the parse
118
+ succeeds, the lookup returns `undefined`, and the answer is quietly wrong. Every payload now
119
+ opens with `schemaVersion`, and a version in a comment is not a version, so it travels in
120
+ the document every reader is guaranteed to see.
121
+
122
+ The same document is now one line rather than four columns of whitespace, because the reader
123
+ of `--json` is a program and it pays for every byte. `--pretty` asks for the other form, and
124
+ a terminal gets it without being asked, because there the reader is a person.
125
+
126
+ Writing the contract test found two things the review that asked for this missed. Eight
127
+ commands — `doctor`, `mcp --list`, `graph lint`, `rules list`, and the `skills` ones — had a
128
+ private `emit(JSON.stringify(..., null, 2))` instead of the shared helper, so `--json` meant
129
+ something different depending on which command you asked. And a command that *failed* under
130
+ `--json` wrote only to stderr, so a scripted caller got an empty document and a non-zero
131
+ exit and no way to learn what went wrong. It now answers in the shape it was asked in (ADR-070).
132
+
133
+ ### `graph lint` now checks skills against the published specification
134
+
135
+ The Agent Skills specification sets limits nobody here was checking: a name of lowercase
136
+ letters, digits and single hyphens that equals the directory it lives in; a description
137
+ under 1,024 characters; a compatibility note under 500; a body recommended under 5,000
138
+ tokens because an agent loads all of it once the skill activates. `graph lint --spec
139
+ agentskills` checks every skill against those, and the specification was fetched rather
140
+ than recalled — reading it turned up four rules the review that asked for this did not
141
+ list, including the one that breaks installs.
142
+
143
+ What the specification calls a must fails the exit code; what it calls a recommendation
144
+ warns. A lint that fails on advice is a lint that gets skipped. `skills install` now asks
145
+ the same name rules, since the directory is created from the name and an unaddressable
146
+ skill would be the last thing anyone notices.
147
+
148
+ ### The MCP surface speaks three protocol revisions, and answers in the shape each asks for
149
+
150
+ The server was pinned to one revision and returned pretty JSON in a text block, so a
151
+ client could read the answer but could not validate it, could not cache it, and could not
152
+ tell a read-only tool from one that writes. All three published revisions were read rather
153
+ than guessed at, and each is answered in its own shape: the newest drops the handshake for
154
+ a version carried on every request, adds a discovery method servers must implement,
155
+ requires a result-type field and a cache lifetime on list results, and deleted the liveness
156
+ method — which older clients still get, and newest clients are told is gone by name.
157
+
158
+ Every tool now declares what it returns and says it is a reader, and every call returns
159
+ the answer in both shapes: the text block for a client that predates structured content,
160
+ the structured block for one that validates against the declared schema. The byte ceiling
161
+ bounds the text only, so the block a client can check still checks. The schema checker
162
+ that makes this testable lives in the package rather than in a dependency.
163
+
164
+ ### Which harnesses a repository is set up for is now answerable
165
+
166
+ `simt-dev harness detect` looks at the configuration directories that exist and says
167
+ which harnesses this repository — and, with `--user`, its reader — is set up for. It
168
+ claims presence and nothing else: a directory found is not a claim about what its owner
169
+ reads, because the tool cannot answer that without a vendor document, and a guess here
170
+ would be the reader's problem the first time it was wrong. A harness that reads
171
+ AGENTS.md is served by the standard surface whatever else it reads, and every surface row
172
+ now records that in three values rather than two, because "reads AGENTS.md" and "is
173
+ covered for this harness" are not the same sentence — Copilot's CLI reads it and its
174
+ editor, code review and cloud agent do not.
175
+
176
+ No new harness rows ship with it. The candidates are recorded as directory names, which
177
+ claim presence and not coverage; a row still requires the vendor document it cites, and
178
+ one that reads nothing has to be added on purpose.
179
+
180
+ ### A monorepo gets rules for each of its packages
181
+
182
+ One repository, one projection: every package under the root was given the root's rules,
183
+ so a React application inside a node workspace was handed the node profile and nothing
184
+ else, and the profile that should have applied to it was applied by nobody. The rule that
185
+ package needed was not wrong — it was missing.
186
+
187
+ Packages are now discovered from the declarations that already list them (npm and pnpm
188
+ workspaces, turbo, Cargo members, `go.work`, Gradle includes) rather than by walking the
189
+ tree, because a declaration states which directories are members and a marker file only
190
+ says there is a build here. The root keeps what every package shares; each package that
191
+ selects something new gets a generated region carrying exactly its own delta, and a
192
+ package that selects nothing new gets no file at all. `check` reports every package
193
+ region and fails on any one of them, and `compare` counts the drifted regions rather than
194
+ the repositories that hold them.
195
+
196
+ A gate can also name the directory it runs in, which is what a monorepo with no root test
197
+ script needs: `gates.cwd` maps a gate to a directory, a directory that does not exist or
198
+ resolves outside the repository fails the event and is reported by `check` before the
199
+ commit rather than at it, and a directory named for a gate that is not declared is
200
+ reported as the inert entry it is.
201
+
202
+ ### One rule set, shared by the repositories that agreed on it
203
+
204
+ Several repositories want the same rules and each kept its own copy, so a policy change
205
+ was several edits that could be several-minus-one, and the repository that missed it
206
+ carried instructions nobody there wrote. A pack is that shared set: `packs` in the manifest
207
+ declares it, the bytes live in the repository, and a digest of them is recorded so nobody
208
+ has to trust that the two agree.
209
+
210
+ A pack's rules are selected by the same matcher as any profile and appear in the region's
211
+ source list under the pack's own directory. Precedence is the repository, then a pack it
212
+ declares, then what the tool ships, and every replacement is reported rather than silent.
213
+
214
+ A pack that disagrees with its pin still projects — the gate blocks and prints both
215
+ digests, because dropping it would let the repair delete the rules it is reporting. One
216
+ that is missing blocks with a remedy that restores it, and one with no digest is reported
217
+ without failing anything, since its rules render correctly. `compare` lists every pack and
218
+ the digest it is pinned to, so a repository behind a shared change is visible before
219
+ anyone follows stale instructions. Nothing is fetched: the bytes are in the repository,
220
+ which is what makes the rendered region reproducible from what the reader has (ADR-065).
221
+
222
+ ### `simt tokens` counts what a reader loads
223
+
224
+ Review 0002 filed the estate's always-on context at 90 KB per turn, 72% of it a paragraph
225
+ an earlier repository had already loaded — a number produced by reading seven
226
+ repositories by hand, which ages the moment a rule is edited and cannot be re-derived by
227
+ the reader who has to act on it.
228
+
229
+ `simt tokens` is that reading, as a command. It reports the bytes this tool wrote per
230
+ channel (region, rule files, skill metadata), an approximate token figure, and — with
231
+ `--repos` — the same figure across a workspace, with every repeated paragraph attributed
232
+ to the first root that carries it. A region that ends past a harness's read limit is
233
+ reported with its byte offset, using the same numbers the existing check uses.
234
+
235
+ Three choices keep the figure honest, and all three are recorded in ADR-064: the meter
236
+ counts what the projection wrote and reports hand-written bytes beside it rather than
237
+ folding them into the budget; a region is the span between its two markers, so the blank
238
+ line a person left above the marker is billed to nobody; and an over-budget repository is
239
+ reported rather than failed, because a meter that went red would train people to ignore
240
+ the number.
241
+
242
+ S38 stays open — this is the instrument. Whether the estate gets under 30 KB is R07's
243
+ kernel, R08's budget field and R11's dedupe, measured with this.
244
+
9
245
  ## [0.2.1] — 2026-10-13
10
246
 
11
247
  ### `init` writes in place; the backup tag and adopt branch are opt-in
@@ -327,6 +563,9 @@ never ran `simt hooks install` again will find nothing until the dependency is r
327
563
  and `simt hooks install --yes` is re-run. That is a real upgrade step and it is called out
328
564
  here rather than discovered.
329
565
 
566
+ [0.3.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.3.0
567
+ [0.2.2]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.2
568
+ [0.2.1]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.1
330
569
  [0.2.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.2.0
331
570
  [0.1.1]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.1.1
332
571
  [0.1.0]: https://github.com/QuazarMedia/simt-dev/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: simt-dev
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: 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
5
  Project-URL: Homepage, https://github.com/QuazarMedia/simt-dev
6
6
  Project-URL: Repository, https://github.com/QuazarMedia/simt-dev
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ // simt-dev - developer and agent tooling for the SIMT ecosystem.
3
+ //
4
+ // POSIX only (ADR-012). package.json declares os: [darwin, linux], so npm refuses
5
+ // the install elsewhere rather than letting a hook fail obscurely.
6
+
7
+ import { parse } from '../src/argv.mjs';
8
+ import { EXIT, fail, jsonFailMode, jsonIndentDefault } from '../src/output/index.mjs';
9
+
10
+ try {
11
+ const argv = process.argv.slice(2);
12
+ const parsed = parse(argv);
13
+ if (!parsed.ok) {
14
+ process.exit(parsed.code);
15
+ }
16
+ const { flags, positional } = parsed.value;
17
+
18
+ // No arguments at all is a usage error, not a silent help dump: an agent that
19
+ // ran the wrong thing should be told so rather than handed 40 lines of prose.
20
+ if (positional.length === 0 && !flags.help && !flags.version) {
21
+ process.exit(fail('no command given', EXIT.USAGE));
22
+ }
23
+
24
+ jsonIndentDefault(flags.pretty === true);
25
+ jsonFailMode(flags.json === true);
26
+
27
+ // The one command that runs on somebody else's hot path, and therefore the one that
28
+ // does not load the registry. `agent-hook` is called before every tool call an agent
29
+ // makes, so its latency is a product requirement rather than a nicety (plan 0003
30
+ // R15), and the registry's imports - detection, projection, the bench corpus - cost
31
+ // tens of milliseconds before the command itself starts. Measured on the machine
32
+ // this was written on: 127.7 ms through the registry against 91.5 ms with only this
33
+ // module's imports, where the bare runtime is 86.9 ms of it.
34
+ //
35
+ // Only the exact spelling the renderers write takes this path. Anything else -
36
+ // including `agent-hook --help` - loads the registry, so help stays the one list of
37
+ // every command and this shortcut cannot become a second, thinner command surface.
38
+ if (positional[0] === 'agent-hook' && !flags.help && !flags.version) {
39
+ const { agentHookCommand } = await import('../src/commands/agenthook.mjs');
40
+ process.exit((await agentHookCommand(positional.slice(1), flags)) ?? EXIT.OK);
41
+ }
42
+
43
+ const { dispatch } = await import('../src/commands/index.mjs');
44
+ process.exit((await dispatch(argv)) ?? EXIT.OK);
45
+ } catch (error) {
46
+ process.exit(fail(error?.message ?? String(error), EXIT.FAIL));
47
+ }
@@ -0,0 +1,107 @@
1
+ # ADR-064 — The context meter counts what the tool wrote, and says what it did not
2
+
3
+ Status: **accepted** · 2026-10-13 · filed after review 0002 S38
4
+
5
+ **Affects:** the `tokens` command, the new context meter in `src/measure/context.mjs`, and
6
+ the structure field `readLimitFindings` now returns.
7
+
8
+ ## Context
9
+
10
+ Review 0002 S38 measured the estate by hand and filed a number: 90,042 bytes of always-on
11
+ context per turn across seven workspace roots, of which 64,738 bytes — 72% — were a
12
+ paragraph an earlier root had already loaded. It also found a repository whose region
13
+ ended past the 32,768 bytes Codex reads before it stops.
14
+
15
+ Two things are wrong with a finding that arrives as a hand count. The count ages: the
16
+ moment a rule is edited, the number is a description of a version nobody has any more.
17
+ And it cannot be re-derived by the reader who has to act on it — it was produced by
18
+ reading seven repositories, which is the work the tool exists to do.
19
+
20
+ Plan 0003 R06 asks for the same measurement as a command, with an acceptance criterion
21
+ that names the figures it must reproduce: the 90,042 bytes, the 64,738 redundant, and a
22
+ repository beyond Codex's limit. Reproducing a hand count is only possible if the meter
23
+ counts what the hand counted — which means settling what "always-on" means before
24
+ writing the code, because every choice here changes the number the criterion will be
25
+ checked against.
26
+
27
+ The answer is the hardest question in the command, and it is not "bytes".
28
+
29
+ ## Rulings
30
+
31
+ 1. **The meter counts what this tool wrote, and reports hand-written bytes beside it
32
+ rather than folding them in.** The budget being defended is the projection's: a
33
+ repository's own 40 KB of prose in AGENTS.md is an authoring decision the tool did not
34
+ make and cannot shrink. Billing it to a projection budget would make the budget
35
+ unachievable for reasons outside the projection, which is how a budget stops being
36
+ read. So every channel reports `generatedBytes` and `readerBytes` separately, and the
37
+ headline figure is the generated one.
38
+
39
+ 2. **A token count is an approximation, and it is labelled as one every time it is
40
+ printed.** Four bytes per token, stated in the payload and in the text, with a `~`
41
+ in front of the number. Nobody has measured this corpus, and a figure that looks exact
42
+ invites the reader to spend it as though it were.
43
+
44
+ 3. **A region is the span between its two markers, and nothing outside it counts as
45
+ generated — not even the blank line above the marker.** This is the defect the first
46
+ test run caught: subtracting the hand-written prefix from the file billed the
47
+ projection for a newline a person typed. In a command whose whole claim is that it
48
+ counts exactly, that byte is the whole argument.
49
+
50
+ 4. **Redundancy is byte-exact, blank-line-paragraph by blank-line-paragraph, measured
51
+ across roots in sorted path order.** That is the method review 0002 used by hand, and
52
+ it is reproducible: the same corpus measured twice gives the same figure, which is
53
+ what lets the number appear in a commit message. Order is therefore load-bearing —
54
+ `discover` sorts, and the first root to carry a paragraph owns it. Reordering the walk
55
+ would move bytes between roots, so nothing downstream re-sorts them.
56
+
57
+ 5. **The budget is reported, not enforced, and the command exits 0 for it.** A
58
+ repository over budget is a fact about the repository. A meter that went red for it
59
+ would train people to ignore the number it exists to print. Enforcement is R08's
60
+ business, and it is a manifest field rather than a constant so a repository can argue
61
+ with its own budget.
62
+
63
+ 6. **A pointer is counted once, as the file it points at.** One reader, one copy of the
64
+ text; enumerating the target as well would double every rule the projection replaced
65
+ with a pointer, which is precisely the saving the pointer was made for.
66
+
67
+ 7. **MCP `tools/list` bytes are reported once, outside every repository total.** The
68
+ same tool list is loaded whatever the repository, so summing it into each root would
69
+ multiply one number by the number of roots and call the result a workspace cost.
70
+
71
+ 8. **The flag is `--repos`, not `--workspace`.** Plan 0003 R06 wrote `--workspace`, but
72
+ `--workspace <path>` has been a value flag since plan 0002, where it names a
73
+ directory of skill sources. One flag cannot mean two things depending on which
74
+ command read it — that is the silent-ignore defect the argument parser's own header
75
+ refuses — so the new one is `--repos`, and it says what it measures: every repository
76
+ under the root, in sorted order.
77
+
78
+ 9. **No MCP tool exposes this yet.** The byte counts are already in the payload, but a
79
+ tool that returns a per-repository context cost is a second public surface to keep
80
+ honest. It arrives when there is a second caller for the logic to be wrong in.
81
+
82
+ ## Consequences
83
+
84
+ - `simt tokens` answers "what does a turn pay for this repository", and
85
+ `simt tokens --repos` answers "what does a turn pay for the workspace". Both print
86
+ bytes and a labelled approximation; `--json` carries the same figures, because two
87
+ renderings of one question must not be able to disagree (ADR-050).
88
+ - The command refuses two shapes of path — a directory holding repositories when
89
+ `--repos` was not passed, and a directory holding none — with remedies that are
90
+ commands rather than locations.
91
+ - `readLimitFindings` grew structured fields (`reader`, `limitBytes`, `endByte`,
92
+ `overBy`) beside its prose, so the meter and the check that already reported the
93
+ Codex limit are reading the same numbers rather than each re-deriving them.
94
+ - S38 stays open. What landed is the instrument. The 30 KB figure it wants needs R07's
95
+ kernel, R08's budget field, R11's dedupe, and R01–R05's evaluation of whether moving
96
+ the process rules behind skills costs compliance — and this meter's job is to report
97
+ whether that work succeeded.
98
+
99
+ ## Rejected
100
+
101
+ - **Failing the run when a repository is over budget.** The measurement would become
102
+ something people route around, and the number would go with it.
103
+ - **Treating a pointer as free rather than counted once.** Its target is still loaded;
104
+ calling that zero is the sort of accounting that makes a report unfalsifiable.
105
+ - **`--workspace` for the new flag, on the grounds that the plan says so.** The plan
106
+ wrote that before `--workspace` was a value flag for something else, and a plan is a
107
+ record of an intention, not a reason to ship two meanings for one name.
@@ -0,0 +1,97 @@
1
+ # ADR-065 — A pack is one shared rule set, vendored into the repository and pinned by digest
2
+
3
+ Status: **accepted** · 2026-10-13 · filed as S39
4
+ **Affects:** `src/packs.mjs`, `src/projection/rules.mjs` (`loadRules`, `select`), `src/projection/manifest.mjs` (`packs` field), `src/commands/sync.mjs` (`check`), `src/compare/row.mjs` (`packs`, `problems`), `test/packs.test.mjs`
5
+
6
+ ## Context
7
+
8
+ S39 found estate product policy shipping as if it were universal: a profile that named
9
+ one company's domain layer, a rule file that named its Firebase environment variables, and
10
+ a `gender` sentinel in a map profile. Fourteen repositories shared one set of those rules
11
+ and each carried its own copy, so a policy change was fourteen edits that could be thirteen.
12
+
13
+ Two answers were available. The first is a remote: a pack published somewhere, a `source`
14
+ in the manifest, and the renderer fetching it. The second is a directory in the repository
15
+ holding the bytes, with a digest recorded so nobody has to trust that the two agree.
16
+
17
+ The remote is the one that reads better in a design document and is worse for the reader
18
+ this tool is for. A region rendered from a fetched pack is a region whose bytes depend on
19
+ something the receiving repository does not contain: a reader who clones the repo, deletes
20
+ their network, and runs the gate gets a different answer, and a reviewer approving a diff
21
+ is approving text they cannot produce from what is in front of them. That is the single
22
+ failure this repository exists to prevent, and a shared-rules feature is exactly where it
23
+ would arrive.
24
+
25
+ The vendored answer has one obvious weakness: updating a pack is a commit in every
26
+ repository that declares it, not one push somewhere. That is the cost of the property
27
+ above, and it is the right trade — a rule a reader cannot account for is worth less than a
28
+ rule that is one merge behind.
29
+
30
+ ADR-061 already settled half of P4. A pack may have carried the skill registry, and the
31
+ review's fix line says it should; ADR-061 ruled the registry to be workspace data that the
32
+ package no longer ships. A second source for it would reopen that ruling by accident, from
33
+ a feature that is not about skills at all.
34
+
35
+ ## Rulings
36
+
37
+ 1. **A pack's bytes live in the adopting repository, and the manifest records a digest of
38
+ them.** The manifest gains a `packs` list of `{name, source, version, digest}`; the
39
+ directory the tool reads is a fixed one, and `source` and `version` record where the
40
+ bytes came from for a human and for a future fetch. Nothing in a projection reads a
41
+ path outside the repository it is rendering into.
42
+
43
+ 2. **Precedence is ranked, not special-cased: the repository's own profile, then a pack it
44
+ declares, then what the tool ships.** The ranking is one table read by the selector, so
45
+ the precedence and the order the files are collected in cannot disagree. A replacement
46
+ is reported in the existing `shadowed` list, because a profile that silently replaced
47
+ another leaves a reader unable to tell which of two texts they are being held to.
48
+
49
+ 3. **A pack that is present but disagrees with its pin still projects.** What is
50
+ questioned is the pin, and the gate blocks and prints both digests. Dropping the pack
51
+ instead would let the remedy the finding names — `sync` — delete every rule the pack
52
+ carries, which is the opposite of what a reader who ran it meant. A pack that is absent
53
+ has nothing to project and blocks with a remedy that restores it; a pack with no digest
54
+ is reported and does not block, because its rules render correctly and only the record of
55
+ which bytes they were is missing.
56
+
57
+ 4. **A pack carries rule files, not a skill registry.** A profile it ships is selected by
58
+ the same matcher as any other and appears in the region's source list under its pack's
59
+ directory. A registry inside a pack is read by nothing, and that is the ruling rather
60
+ than an oversight: ADR-061 makes the registry workspace data, and a second source for
61
+ it needs its own decision about which of two registries answers.
62
+
63
+ 5. **`compare` reports every pack and the digest it is pinned to, per repository.** The
64
+ pinned digest, not the computed one — it answers "which bytes was this declared at", and
65
+ a row reporting the new digest would hide the mismatch from anything reading the JSON.
66
+ A pack that cannot be used is a problem on the row; one that is merely unpinned is a
67
+ note, which is the same division the gate draws.
68
+
69
+ 6. **`packs` is repository-owned content, not a generated field.** Which packs a repository
70
+ uses is its decision, so a re-init must not empty it, and the drift check does not
71
+ compare it. What is compared is the directory's digest against the pin.
72
+
73
+ ## Consequences
74
+
75
+ - A group of repositories sharing one rule set declares the same pack and vendors the same
76
+ bytes; a policy change is a commit per repository, and the digest is what makes an
77
+ unmerged one visible before a reader follows stale instructions.
78
+ - Nothing fetches anything. A pack cannot be pulled by name, cannot be published by this
79
+ tool, and cannot be refreshed by `upgrade`. That is the deliberate half of the answer,
80
+ and it leaves the fetch as a later ruling rather than a missing feature.
81
+ - The rule provenance digest does not cover pack bytes, so `projection.simtDevSha` still
82
+ answers "what did the tool render" and the pack digest answers "which bytes were these".
83
+ Two answers to two questions, both in the manifest.
84
+
85
+ ## Rejected
86
+
87
+ - **Fetching a pack from a remote during projection.** It makes the rendered bytes depend
88
+ on something the receiving repository does not carry, and it puts the network inside a
89
+ command whose defining property is that it is deterministic and offline.
90
+ - **Refusing to project a pack whose digest does not match.** The remedy would delete the
91
+ rules it reports, so the tool would cause the damage it exists to report.
92
+ - **One flag per pack state**, so a reader could choose what blocks the gate. Three flags
93
+ for one directory's worth of state is a configuration surface nobody will read twice, and
94
+ the division that matters — does the region account for its rules — has one defensible
95
+ answer.
96
+ - **A pack carrying the skill registry**, which proposal P4 asked for before ADR-061
97
+ settled where the registry comes from.
@@ -0,0 +1,115 @@
1
+ # ADR-066 — A monorepo projects once per package
2
+
3
+ Status: **accepted** · 2026-10-13 · filed as S41
4
+ **Affects:** `init`, `sync`, `check`, `compare`, `hooks run`, `src/monorepo.mjs`,
5
+ `src/projection/rules.mjs`, `src/projection/project.mjs`, `src/hooks/dispatcher.mjs`,
6
+ the `gates.cwd` manifest field
7
+
8
+ ## Context
9
+
10
+ A monorepo received exactly one projection: the root's `AGENTS.md`, fingerprinted from
11
+ the root. Everything below the root was invisible to the detector, so a React
12
+ application inside a node workspace was handed the node profile and nothing else, while
13
+ a Workers service beside it was handed the same. Neither the repository's rules nor the
14
+ tool's output said another package existed with different rules.
15
+
16
+ The estate's own monorepos made this expensive rather than merely wrong. Fourteen
17
+ repositories hold between them hundreds of packages, and one rule set per repository
18
+ means every package is told the union of what the root can see and nothing about what it
19
+ is. The profile that should have applied to a package is applied by nobody, so the rule
20
+ that package needed was simply missing — not wrong, missing.
21
+
22
+ Three shapes of answer were available, and the cheapest is the one that was not taken.
23
+ Writing every package's full profile set into the root duplicates text the reader loads
24
+ once per turn (review 0002 S38 measured that duplication at 72% of the workspace's
25
+ always-on bytes). Writing the root's set into each package does the same and worse.
26
+ Projecting the root once and each package once, with each package carrying only what the
27
+ root does not, adds bytes proportional to the number of packages that actually differ,
28
+ and nothing at all to the ones that do not.
29
+
30
+ The harder half is that `check` is the gate. A projection the tool writes but does not
31
+ check is a projection nobody is held to, and a monorepo that drifts in one package would
32
+ report `current` while CI is green and the reader is wrong.
33
+
34
+ ## Rulings
35
+
36
+ 1. **A workspace is read from its declarations, never from a directory scan.** npm
37
+ workspaces, pnpm's package list, turbo's workspace list, Cargo members, a go.work use
38
+ list and Gradle includes are all read, because each one is a file that already states
39
+ which directories are members and why. Finding a marker file by walking the tree is
40
+ the alternative, and it is the one this repository's rules forbid: a heuristic that
41
+ cannot be explained by a declaration is a heuristic a reader has to reverse-engineer
42
+ from its output, and the only way to find out why a directory was or was not given
43
+ rules is to read this tool's source. A declaration this tool cannot read is a
44
+ directory it does not discover, which is an honest absence rather than a guess.
45
+
46
+ 2. **The root keeps the shared rules; a package carries its delta.** The root is
47
+ fingerprinted as before and renders the core plus every profile the root's own signals
48
+ select. A package is fingerprinted the same way, and renders only the profiles the
49
+ root did not already select. A reader opening the package file is therefore never
50
+ shown a rule the root already gave them, and a reader of the root file is never shown
51
+ a rule that belongs to one package. The duplication the workspace reader pays for is
52
+ one sentence of orientation per package, and the bytes it pays for scale with the
53
+ packages that differ rather than with the repository count.
54
+
55
+ 3. **An empty delta writes nothing.** Two node packages under one workspace select the
56
+ same profiles, so the delta is empty and there is no file. A file whose entire content
57
+ is a claim about rules it does not contain is always-on bytes spent on nothing, which
58
+ is the cost S38 exists to measure, and the reader is left looking for rules that are
59
+ not there.
60
+
61
+ 4. **A gate names its directory in a map, not in a new shape for the command.** The
62
+ manifest's gate commands stay name-to-command-line strings, and an added map gives
63
+ each gate a directory to run in. Re-reading a command string to discover where it runs
64
+ would mean the answer to "where does anything run" lives inside a value whose purpose
65
+ is something else, and a manifest written by an earlier release would have to be
66
+ rewritten to gain the capability. A directory that does not exist, or that resolves
67
+ outside the repository, fails the event with a message naming which of the two things
68
+ is wrong, because they are repaired by different edits. Containment is checked on the
69
+ resolved path rather than on the text: a relative traversal and a symlink both pass a
70
+ prefix test applied to a string.
71
+
72
+ 5. **`check` reports a bad directory before a commit, not at one.** The dispatcher fails
73
+ closed on the same condition, so a reader would otherwise find out by being unable to
74
+ commit — after writing a commit, which is the wrong order for the news. The same
75
+ applies to a directory entry naming a gate that is not declared at all: the entry is
76
+ inert, the reader believes the gate runs where it says, and nothing anywhere says
77
+ otherwise.
78
+
79
+ 6. **`compare` counts findings, not repositories.** One monorepo with three drifted
80
+ packages is one repository with three findings. A summary that said one problem would
81
+ understate the work by a factor of three, and a reader comparing two runs would see a
82
+ single count that moves by three when the underlying state changed by three. The
83
+ repository still counts once among repositories with problems, because that count is
84
+ about how much work a person has.
85
+
86
+ ## Consequences
87
+
88
+ - A workspace whose members are declared in a form this tool does not read gets the root
89
+ projection only, exactly as it did before. That is a gap to close by adding a row to
90
+ the declaration table, not a behaviour to document.
91
+ - Nested regions are checked, and their findings are printed with their paths. A reader
92
+ who edits a package file by hand is told which file, which is the same information the
93
+ root's own drift finding carries.
94
+ - The package file is a generated region like any other: the markers, the source list and
95
+ the repair command all behave identically, so there is no second kind of generated file
96
+ for a reader to learn.
97
+
98
+ ## Rejected
99
+
100
+ - **Scanning for marker files instead of reading declarations.** Rejected for ruling 1.
101
+ A marker file answers "is there a build here", which is not the question; a declaration
102
+ answers "is this a member of the workspace", which is.
103
+ - **Folding every package's rules into the root region.** Rejected for ruling 2. It is
104
+ the smallest change to make and it makes every reader in the workspace carry every
105
+ package's rules, which is the cost S38 measured and S38's kernel exists to remove.
106
+ - **A directory field on each declared command.** Rejected for ruling 4, for the reason
107
+ recorded there: it puts where a gate runs inside the value that says what it runs, and
108
+ it makes an old manifest unreadable rather than merely older.
109
+ - **Skipping the directory check because the dispatcher already fails closed.**
110
+ Rejected for ruling 5. The dispatcher's refusal is correct but late; the reader who
111
+ learns about it from a failed commit has already done the work the commit would have
112
+ saved them.
113
+ - **Refusing to project a monorepo at all until every package is understood.** Rejected:
114
+ a root-only projection is a strictly smaller claim than it was, and refusing to improve
115
+ it because the improvement is incomplete makes the reader worse off than the tool.