grift-cli 0.5.2__tar.gz → 0.5.4__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.

Potentially problematic release.


This version of grift-cli might be problematic. Click here for more details.

Files changed (116) hide show
  1. {grift_cli-0.5.2 → grift_cli-0.5.4}/CHANGELOG.md +24 -0
  2. {grift_cli-0.5.2 → grift_cli-0.5.4}/PKG-INFO +6 -4
  3. {grift_cli-0.5.2 → grift_cli-0.5.4}/README.en.md +7 -3
  4. {grift_cli-0.5.2 → grift_cli-0.5.4}/README.md +5 -3
  5. {grift_cli-0.5.2 → grift_cli-0.5.4}/action.yml +1 -1
  6. grift_cli-0.5.4/docs/metrics-guide.md +149 -0
  7. {grift_cli-0.5.2 → grift_cli-0.5.4}/pyproject.toml +1 -1
  8. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/hygiene_grep.py +4 -0
  9. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_cli/__main__.py +76 -9
  10. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/version.py +2 -2
  11. grift_cli-0.5.4/tests/test_default_report_path.py +134 -0
  12. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_hygiene.py +4 -0
  13. {grift_cli-0.5.2 → grift_cli-0.5.4}/.github/workflows/ci.yml +0 -0
  14. {grift_cli-0.5.2 → grift_cli-0.5.4}/.github/workflows/kpi-monthly.yml +0 -0
  15. {grift_cli-0.5.2 → grift_cli-0.5.4}/.github/workflows/publish-testpypi.yml +0 -0
  16. {grift_cli-0.5.2 → grift_cli-0.5.4}/.github/workflows/publish.yml +0 -0
  17. {grift_cli-0.5.2 → grift_cli-0.5.4}/.gitignore +0 -0
  18. {grift_cli-0.5.2 → grift_cli-0.5.4}/LICENSE +0 -0
  19. {grift_cli-0.5.2 → grift_cli-0.5.4}/corpus/AD-PILOT-20260822.md +0 -0
  20. {grift_cli-0.5.2 → grift_cli-0.5.4}/corpus/DISCRIMINANT-v2026.09.md +0 -0
  21. {grift_cli-0.5.2 → grift_cli-0.5.4}/corpus/DISCRIMINANT-v2026.11.md +0 -0
  22. {grift_cli-0.5.2 → grift_cli-0.5.4}/corpus/ad-pilot.json +0 -0
  23. {grift_cli-0.5.2 → grift_cli-0.5.4}/corpus/manifest.toml +0 -0
  24. {grift_cli-0.5.2 → grift_cli-0.5.4}/corpus/pins-v2026.09.toml +0 -0
  25. {grift_cli-0.5.2 → grift_cli-0.5.4}/corpus/pins-v2026.11.toml +0 -0
  26. {grift_cli-0.5.2 → grift_cli-0.5.4}/corpus/v2026.09/distributions.json +0 -0
  27. {grift_cli-0.5.2 → grift_cli-0.5.4}/corpus/v2026.11/distributions.json +0 -0
  28. {grift_cli-0.5.2 → grift_cli-0.5.4}/docs/corpus-protocol.md +0 -0
  29. {grift_cli-0.5.2 → grift_cli-0.5.4}/docs/coverage-map.md +0 -0
  30. {grift_cli-0.5.2 → grift_cli-0.5.4}/docs/export-schema.md +0 -0
  31. {grift_cli-0.5.2 → grift_cli-0.5.4}/docs/identity-schema.md +0 -0
  32. {grift_cli-0.5.2 → grift_cli-0.5.4}/docs/norms.md +0 -0
  33. {grift_cli-0.5.2 → grift_cli-0.5.4}/docs/persona-answers.md +0 -0
  34. {grift_cli-0.5.2 → grift_cli-0.5.4}/docs/report-schema.md +0 -0
  35. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/README.md +0 -0
  36. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/coverage.md +0 -0
  37. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G1-click.json +0 -0
  38. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G10-scvi-tools.json +0 -0
  39. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G11-kilo.json +0 -0
  40. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G12-httpx.json +0 -0
  41. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G13-voicevox.json +0 -0
  42. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G2-gitignore.json +0 -0
  43. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G3-spoon-knife.json +0 -0
  44. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G4-express.json +0 -0
  45. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G5-typer.json +0 -0
  46. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G6-claude-code.json +0 -0
  47. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G7-axios.json +0 -0
  48. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G8-co.json +0 -0
  49. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/expected/G9-pyscript.json +0 -0
  50. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/identity/G1-click.toml +0 -0
  51. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/identity/example.toml +0 -0
  52. {grift_cli-0.5.2 → grift_cli-0.5.4}/golden/pins.toml +0 -0
  53. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/admission_v2026_11.py +0 -0
  54. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/build_dist_v2026_11.py +0 -0
  55. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/build_distributions.py +0 -0
  56. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/check_forbidden_vocab.py +0 -0
  57. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/corpus_ad_pilot.py +0 -0
  58. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/corpus_run.py +0 -0
  59. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/discriminant_v2026.py +0 -0
  60. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/discriminant_v2026_11.py +0 -0
  61. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/gen_coverage_table.py +0 -0
  62. {grift_cli-0.5.2 → grift_cli-0.5.4}/scripts/gen_golden_expected.py +0 -0
  63. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_cli/__init__.py +0 -0
  64. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/__init__.py +0 -0
  65. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/activity.py +0 -0
  66. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/analyze.py +0 -0
  67. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/cochange.py +0 -0
  68. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/context_profile.py +0 -0
  69. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/contribute.py +0 -0
  70. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/core_period.py +0 -0
  71. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/data/v2026.09/distributions.json +0 -0
  72. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/data/v2026.11/distributions.json +0 -0
  73. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/export.py +0 -0
  74. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/gitutil.py +0 -0
  75. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/identity.py +0 -0
  76. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/lineage.py +0 -0
  77. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/observation.py +0 -0
  78. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/origin.py +0 -0
  79. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/paths.py +0 -0
  80. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/reference.py +0 -0
  81. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/report.py +0 -0
  82. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/rework.py +0 -0
  83. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/schema.py +0 -0
  84. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/scope.py +0 -0
  85. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/survival.py +0 -0
  86. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/tests_observed.py +0 -0
  87. {grift_cli-0.5.2 → grift_cli-0.5.4}/src/tep_core/verify.py +0 -0
  88. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/conftest.py +0 -0
  89. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/coverage_matrix.py +0 -0
  90. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/git_fixture.py +0 -0
  91. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_activity.py +0 -0
  92. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_cli.py +0 -0
  93. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_cochange.py +0 -0
  94. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_context_profile.py +0 -0
  95. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_contribute.py +0 -0
  96. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_coverage.py +0 -0
  97. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_discriminant_claims.py +0 -0
  98. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_export.py +0 -0
  99. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_export_recipe.py +0 -0
  100. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_forbidden_vocab.py +0 -0
  101. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_gitutil.py +0 -0
  102. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_golden.py +0 -0
  103. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_h5_swarm.py +0 -0
  104. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_identity_validation.py +0 -0
  105. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_lifecycle_v2.py +0 -0
  106. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_not_observed.py +0 -0
  107. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_origin.py +0 -0
  108. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_persona_answers.py +0 -0
  109. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_release_gate.py +0 -0
  110. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_report.py +0 -0
  111. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_rework.py +0 -0
  112. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_schema.py +0 -0
  113. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_scope.py +0 -0
  114. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_shared_block.py +0 -0
  115. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_survival.py +0 -0
  116. {grift_cli-0.5.2 → grift_cli-0.5.4}/tests/test_verify.py +0 -0
@@ -4,6 +4,30 @@
4
4
 
5
5
  タグ・PyPI・リポ public 化は本ファイルの記載対象外(人間ゲート)。
6
6
 
7
+ ## [0.5.4] — 2026-08-23
8
+
9
+ `grift report` の stale 出力問題を解消 + 表示の版残存を一掃。
10
+
11
+ ### Fixed
12
+
13
+ - **`grift report`(引数なし)は既存の `./out/report.json` があっても常に現在の HEAD を再分析**する(提唱者実測で検出: 旧仕様は既存 JSON を再表示し、2コミット目以降の SHA が古いまま残った — 自己証明の罹患として重大)。再分析なしの再レンダリングは引数指定時のみ
14
+ - README 日英: Action 使用例を `@v0.5.4` へ・**PyPI 欄を版に依存しない表記に変更**(「最新 0.5.2」等の古い版番号が残り続ける問題を構造的に解消)・`grift report` を「初回の簡単導線」として条件付きで説明(主 CTA は `grift analyze . --scope repo --out ./out` を維持)
15
+ - action.yml 既定ピンを 0.5.4 へ
16
+
17
+ ## [0.5.3] — 2026-08-23
18
+
19
+ UX 改善(代表フィードバック)+ 公開 README の整合。
20
+
21
+ ### Changed
22
+
23
+ - **`grift report`(引数なし)= そのディレクトリを分析して `./out/report.{json,md}` を自動作成**(`out/` がなければ作る)。引数を渡した場合は従来どおり既存 JSON からの再レンダリング(再分析なし)
24
+ - `grift contribute`(引数なし)は `./out/report.json` → `.grift-out/report.json` → `./report.json` を自動探索。無ければ「先に `grift report` を」と案内
25
+ - GitHub Action の使用例を `@v0.5.2` ぼ更新・「本公開は別ゲート」注記を公開済み表示に更新(README 日英)
26
+
27
+ ### Fixed
28
+
29
+ - hygiene 除外リストに内部調査文書プレフィックス(SMOKE / REREVIEW / RELEASE-REHEARSAL / INTERNAL-PILOT)を追加
30
+
7
31
  ## [0.5.2] — 2026-08-23
8
32
 
9
33
  基準の憲法(norms / reader's guide / verify)+ 文脈層(context_profile v2・lifecycle-v2)+ corpus v2026.11 + contribute。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: grift-cli
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: Deterministic TEP evidence metrics from a single git repository. No LLM.
5
5
  Project-URL: Homepage, https://github.com/Cor-Incorporated/grift-cli
6
6
  Author: TEP contributors
@@ -40,12 +40,14 @@ Named after the Grift product line.
40
40
 
41
41
  ```bash
42
42
  pipx install grift-cli
43
- grift analyze ./my-repo --scope repo
43
+ grift analyze ./my-repo --scope repo --out ./out
44
44
  ```
45
45
 
46
46
  - `--scope tenant`(既定): `.tep/identity.toml` に載った人の仕事 = **証拠用**
47
47
  - `--scope repo`: bot 以外の全人間コミット = **プロセス観測・参照分布用**
48
48
 
49
+ **初回の簡単導線**: リポジトリ内で `grift report` だけでも現在の HEAD を分析して `./out/report.md` と `report.json` を書き出します(既存の `./out/report.json` があっても常に再分析します)。**確実に指定条件で測りたいとき・CI では `grift analyze . --scope repo --out ./out` を使ってください**(`report` は repo スコープ固定の省略形です)。
50
+
49
51
  参照分布 v2026.09 は **repo スコープ同士**でのみ照合する。tenant の値を repo 分布に載せない(混ぜたら分布が嘘になる)。
50
52
 
51
53
  ## 実レポート例
@@ -107,7 +109,7 @@ v2026.09 の記録(Run 2: A vs C `separated` ほか)は `corpus/DISCRIMINANT
107
109
  ## GitHub Action(観測のみ)
108
110
 
109
111
  ```yaml
110
- uses: Cor-Incorporated/grift-cli@v0.5.1
112
+ uses: Cor-Incorporated/grift-cli@v0.5.4
111
113
  with:
112
114
  scope: repo
113
115
  ```
@@ -128,7 +130,7 @@ with:
128
130
 
129
131
  - 公開リポ: https://github.com/Cor-Incorporated/grift-cli
130
132
  - コマンド名: **`grift`**(方法論名 TEP はレポートに残す)
131
- - PyPI: 配布名 **`grift-cli`**。本公開は別ゲート(未了ならソースから `pipx install .`)
133
+ - PyPI: 配布名 **`grift-cli`**(公開済み: `pipx install grift-cli`・最新版は [PyPI](https://pypi.org/project/grift-cli/) を参照)
132
134
 
133
135
  ## License
134
136
 
@@ -17,10 +17,14 @@ Commit counts, line counts, and “activity” inflate easily once agents and ge
17
17
 
18
18
  ```bash
19
19
  pipx install grift-cli
20
- grift analyze ./my-repo --scope repo
20
+ grift analyze ./my-repo --scope repo --out ./out
21
21
  ```
22
22
 
23
- If the package is not on PyPI yet: `pipx install .`
23
+ **Quickest first run**: inside a repository, `grift report` alone analyzes the
24
+ current HEAD and writes `./out/report.md` and `report.json` (it always
25
+ re-analyzes, even if `./out/report.json` already exists). For explicit
26
+ control and CI, use `grift analyze . --scope repo --out ./out` (`report` is a
27
+ fixed repo-scope shorthand).
24
28
 
25
29
  - `--scope tenant` (default): work matched in `.tep/identity.toml` = **evidence**
26
30
  - `--scope repo`: all non-bot human commits = **process observation and reference distributions**
@@ -82,7 +86,7 @@ The v2026.09 record (Run 2: A vs C `separated` etc.) remains as history in `corp
82
86
  ## GitHub Action (observation only)
83
87
 
84
88
  ```yaml
85
- uses: Cor-Incorporated/grift-cli@v0.5.1
89
+ uses: Cor-Incorporated/grift-cli@v0.5.4
86
90
  with:
87
91
  scope: repo
88
92
  ```
@@ -17,12 +17,14 @@ Named after the Grift product line.
17
17
 
18
18
  ```bash
19
19
  pipx install grift-cli
20
- grift analyze ./my-repo --scope repo
20
+ grift analyze ./my-repo --scope repo --out ./out
21
21
  ```
22
22
 
23
23
  - `--scope tenant`(既定): `.tep/identity.toml` に載った人の仕事 = **証拠用**
24
24
  - `--scope repo`: bot 以外の全人間コミット = **プロセス観測・参照分布用**
25
25
 
26
+ **初回の簡単導線**: リポジトリ内で `grift report` だけでも現在の HEAD を分析して `./out/report.md` と `report.json` を書き出します(既存の `./out/report.json` があっても常に再分析します)。**確実に指定条件で測りたいとき・CI では `grift analyze . --scope repo --out ./out` を使ってください**(`report` は repo スコープ固定の省略形です)。
27
+
26
28
  参照分布 v2026.09 は **repo スコープ同士**でのみ照合する。tenant の値を repo 分布に載せない(混ぜたら分布が嘘になる)。
27
29
 
28
30
  ## 実レポート例
@@ -84,7 +86,7 @@ v2026.09 の記録(Run 2: A vs C `separated` ほか)は `corpus/DISCRIMINANT
84
86
  ## GitHub Action(観測のみ)
85
87
 
86
88
  ```yaml
87
- uses: Cor-Incorporated/grift-cli@v0.5.1
89
+ uses: Cor-Incorporated/grift-cli@v0.5.4
88
90
  with:
89
91
  scope: repo
90
92
  ```
@@ -105,7 +107,7 @@ with:
105
107
 
106
108
  - 公開リポ: https://github.com/Cor-Incorporated/grift-cli
107
109
  - コマンド名: **`grift`**(方法論名 TEP はレポートに残す)
108
- - PyPI: 配布名 **`grift-cli`**。本公開は別ゲート(未了ならソースから `pipx install .`)
110
+ - PyPI: 配布名 **`grift-cli`**(公開済み: `pipx install grift-cli`・最新版は [PyPI](https://pypi.org/project/grift-cli/) を参照)
109
111
 
110
112
  ## License
111
113
 
@@ -16,7 +16,7 @@ inputs:
16
16
  version:
17
17
  description: "grift-cli version to install (pinned)"
18
18
  required: false
19
- default: "0.5.1"
19
+ default: "0.5.4"
20
20
  comment:
21
21
  description: "Opt-in: post the shared block as a PR comment (default off)"
22
22
  required: false
@@ -0,0 +1,149 @@
1
+ # grift レポートの読み方 — 指標ごとの平易な解説(v1・2026-08-23)
2
+
3
+ **対象**: grift 0.5.4 以降(指標の定義は全ての 0.5.x で共通)。非エンジニアの方向け。
4
+ **原則の再掲**: すべての数値は**観測された事実の記録**であって、優劣の評価ではありません。単独の数値での合否判断・人と人の比較は TEP 非準拠です([docs/norms.md](norms.md))。書かれていないこと(= not observed)は「無かったこと」を意味しません。
5
+ **参考値の出典**: 特に断らない限り、公開リポジトリ 117 件のコーパス(v2026.11・repo スコープ)と v0.5.2 実地調査(requests / flask / GitHub CLI / chalk ほか)に基づきます。参考値は「だいたいの目安」であり、分布は成長とともに新版として出ます。
6
+
7
+ ---
8
+
9
+ ## まず全体の流れ(report.md の構成)
10
+
11
+ 1. **Shared block**: 3-5 行の要約(コピペで共有できる形式)
12
+ 2. **Provenance**: いつ・どのコミット・どの定義版で測ったか(再現に必要な情報)
13
+ 3. **証拠系**: test co-change(検証を伴った変更の割合)
14
+ 4. **観測系**: rework(手戻りの傾向)— 証拠主張ではない
15
+ 5. **文脈**: context profile(どんな repo か)
16
+ 6. **読み方の注意**(全レポート末尾に自動付与)
17
+
18
+ 2種類のスコープがあります:
19
+ - **tenant スコープ**: identity.toml に載った特定の人の仕事(= 自己証明用)
20
+ - **repo スコープ**: bot 以外の全員のコミット(= repo 全体の姿・参考分布との比較はこちらのみ)
21
+
22
+ ---
23
+
24
+ ## 証拠系: test co-change(テスト同期変更率)
25
+
26
+ **単位**: ratio(0.0〜1.0)/**母数**: 本番コードを変えたコミット数
27
+
28
+ ### 何を測っているか
29
+
30
+ 「本番のコード(機能・ロジック)を変えたコミットのうち、**同じコミットでテストも変更した**割合」。テストを書く習慣が実際の変更に伴っているかを、申告ではなく git の記録から数えます。ドキュメントだけの変更は母数から除外されます。
31
+
32
+ ### 読み方
33
+
34
+ - **高い(目安: 0.23 以上 ≒ 分布の上位30%)**: 本番変更にテストが伴う習慣が定着している。変更の意図を後から検証できる状態が保たれやすい
35
+ - **中程度(目安: 0.07〜0.23)**: 多くの公開リポジトリがここにいます(中央値はおよそ 0.11〜0.16)
36
+ - **低い / 0**: テスト基盤が無い・あっても本番変更と同期していない。**ただし「テストを書いていない」=「悪い」ではありません** — ドキュメント中心のリポジトリ、设计のみの期間、ツール類など、テストが前提でない仕事は正当に低くなります
37
+ - **not observed**: テスト基盤そのものが検出できない(framework も tests ディレクトリも無い)場合。観測不能であって 0 ではありません
38
+
39
+ ### 分布の目安(v2026.11・n=68・repo スコープ)
40
+
41
+ | 位置 | 値の目安 |
42
+ |---|---|
43
+ | 中央値付近(第5十分位) | 約 0.11〜0.16 |
44
+ | 上位30%に入る | 0.23 以上 |
45
+ | 上位10%(第9-10十分位) | 0.49 以上 |
46
+
47
+ 実例: GitHub CLI 0.37 / flask 0.20 / requests 0.12 / chalk 0.18(2026-08 実測)。検証文化で選んだ A クラス repo の中央値は 0.24。
48
+
49
+ ### 十分位(decile)の読み方
50
+
51
+ `interpretation` に「第N十分位」が出るとき、それは**同じ repo スコープの公開コーパスの中での位置**です(例: 第7十分位 = 下から 70% の位置)。「優秀」の意味ではありません。n≥30 のときだけ表示されます。
52
+
53
+ ---
54
+
55
+ ## 観測系: corrective rework(修正的リワーク率)と path retouch
56
+
57
+ **単位**: ratio/**母数**: 本番変更コミット数/**注意**: **証拠主張ではありません**(観測専用)
58
+
59
+ ### 何を測っているか
60
+
61
+ - **corrective rework**: 直近21日以内に触ったファイルを、`fix` / `revert` 等の件名で**すぐにやり直した**コミットの割合。「作った直後に手直しが必要になった頻度」の観測
62
+ - **path retouch**: 同じファイルを21日窓で再び触れた割合(手戻りというより「同じ場所を編集し続けている」傾向)
63
+
64
+ ### 読み方(これは「数値が低い=優秀」では読まない)
65
+
66
+ - commit メッセージの慣習(fix を書く文化か)で出方が変わるため、**人と人の比較・合否に使えません**(レポートにも明記されます)
67
+ - 高い値の読み方の例: 検証が無いまま進めて後から直している/活発に試しては直しているフェーズにある — の**いずれとも取れる**ので、面接などでの質問の種に使います(「この期間に何がありましたか?」)
68
+ - 低くても「バグが少ない」の証明にはなりません(バグ件数ではありません)
69
+
70
+ ### 分布の目安(v2026.11・n=101・repo スコープ)
71
+
72
+ 中央値付近 約 0.07〜0.10 / 上位側(0.19 以上)で第8-9十分位。A クラス(検証文化)の中央値は 0.05 と低めですが、因果の主張はできません。
73
+
74
+ ---
75
+
76
+ ## survival(行の生存率)— 0.5.x では任意指定
77
+
78
+ `--survival` を付けたときだけ計算(τ=180日)。**「今も残っている行の割合」**を指数で出します(1.0 に近い = 6ヶ月経ってもほぼ残っている)。
79
+ - 実測例: 小規模運用中 repo で 0.94 — ほぼ全行が残存
80
+ - 参照分布は **v2027 予定**(現時点で高低の判断基準は提供しません)
81
+
82
+ ---
83
+
84
+ ## 文脈: context profile(どんな repo か)
85
+
86
+ 数値の意味は文脈で変わるため、repo の姿を並記します。**すべて分類であって序列ではありません。**
87
+
88
+ ### collaboration_class(協働のかたち)
89
+
90
+ | クラス | 定義 | 読み方の例 |
91
+ |---|---|---|
92
+ | solo | 最多作成者の比率 90% 以上 | 個人项目。一貫性は保たれやすいが、バス係数(本人がいないと進まない度)は構造的に高い |
93
+ | small_team | 関与者 5 人以下 | 少人数で役割が重なる協働 |
94
+ | community | それ以上 | 多人数。審査プロセス(PR flow)や文書化が効いてきやすい |
95
+
96
+ 実測例: requests 802人 / flask 870人 / GitHub CLI 722人は community。個人開発は solo。
97
+
98
+ ### lifecycle_stage(活動密度)— 一次観測2つが本体
99
+
100
+ **クラスは便宜であり、必ず生の2数値と併せて読みます**:
101
+
102
+ - **active_days_180d**: 直近180日のうち、人がコミットした日が何日あったか(≥12 で active)
103
+ - **days_since_last_human_commit**: 最後のコミットから何日経ったか
104
+
105
+ | クラス | active_days_180d | 読み方の例 |
106
+ |---|---|---|
107
+ | active | 12日以上 | 現在も開発が続いている密度 |
108
+ | maintained | 3〜11日 | 維持運用は続いている(改修・追従) |
109
+ | dormant | 2日以下 | 180日窓ではほぼ静止している |
110
+
111
+ 注意: **密度は「開発 vs 保守」の区別を測れません**(高密度の保守運用も active になります)。一斉ハウスキーピングで「新しそうに見える」のを防ぐため、新しさでなく密度を採っています。
112
+
113
+ ### その他の文脈観測(抜粋)
114
+
115
+ - **pr_flow_share**: merge コミット(PR マージ)の割合。高い = レビュー流程を経る文化
116
+ - **conventional_commit_share**: `feat:` `fix:` のような定型件名の割合。高い = 変更種別が機械可読な管理
117
+ - **language_composition**: 言語別のファイル接触比率(多言語 repo の判別に)
118
+ - **dependency_manifests**: 依存管理ファイルの種類(複数 = polyglot / monorepo の兆候)
119
+ - **top_actor_share**: 最多作成者の比率(単独性の程度。名前は出ません)
120
+
121
+ ---
122
+
123
+ ## よくある誤読と、してはいけない読み方
124
+
125
+ | 誤読 | 正しくは |
126
+ |---|---|
127
+ | 「co-change が低い人はテストを書けない人」 | テストが前提でない仕事(docs・設計・ツール)は正当に低くなる。不在・低値を能力の否定として読むのは非準拠(norms 1条) |
128
+ | 「corrective rework が高い = バグが多い人」 | 件名慣習への依存が強く、バグ件数でも品質でもない(観測専用) |
129
+ | 「dormant = 捨てられた repo」 | 保守周期が長いだけの成熟 repo もある。復帰の履歴は gap として可視化されるが、gap を負に読むのは非準拠 |
130
+ | 「第9十分位の人は第5十分位の人より優秀」 | 十分位は同スコープ repo 間の位置のみ。単独足切り・比較表は非準拠(norms 2条) |
131
+ | 「decile が無い = 隠している」 | 母数 <20 か分布 n<30 では出力規則により非表示(insufficient_population / reference_too_small)です |
132
+
133
+ ---
134
+
135
+ ## 数値が出ない(not observed)とき
136
+
137
+ 「0 だった」と「測れなかった」は別物です。主な理由コード:
138
+
139
+ | reason | 意味 |
140
+ |---|---|
141
+ | `no_test_framework_or_directory` | テスト基盤が検出できない |
142
+ | `insufficient_population` | 母数が 20 コミット未満(率を出さず件数のみ) |
143
+ | `pending_attribution` | identity(誰の仕事か)が未設定 |
144
+ | `scope_is_tenant` | 参照分布は repo スコープ専用のため tenant 実行では付かない |
145
+ | `survival_scan_disabled` | survival は `--survival` 指定時のみ |
146
+
147
+ ---
148
+
149
+ *本書は docs/report-schema.md(正本・機械検証付き)の平易版です。数値の厳密な定義・単位・符号化は正本を参照してください。参考値の更新は分布の新版(v2026.11 → vNext)とともに行われます。*
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "grift-cli"
3
- version = "0.5.2"
3
+ version = "0.5.4"
4
4
  description = "Deterministic TEP evidence metrics from a single git repository. No LLM."
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -56,6 +56,10 @@ def main() -> int:
56
56
  ":!docs/PREPUB*",
57
57
  ":!docs/kpi*",
58
58
  ":!docs/pilot*",
59
+ ":!docs/SMOKE*",
60
+ ":!docs/REREVIEW*",
61
+ ":!docs/RELEASE-REHEARSAL*",
62
+ ":!docs/INTERNAL-PILOT*",
59
63
  ],
60
64
  cwd=ROOT,
61
65
  capture_output=True,
@@ -16,10 +16,11 @@ from tep_core.verify import CANNOT_VERIFY, MISMATCH, VERIFIED, verify_report
16
16
  from tep_core.version import __version__
17
17
 
18
18
  _EPILOG = """examples:
19
- grift analyze . --scope repo
20
- grift analyze ./repo --format md --out ./out
19
+ grift analyze . --scope repo --out ./out
20
+ grift report # analyze current HEAD ./out/report.{json,md} (always re-analyzes)
21
+ grift report path/report.json # re-render md from an existing report (no re-analysis)
21
22
  grift verify ./out/report.json --repo ./repo
22
- grift contribute ./out/report.json --out contribution.json
23
+ grift contribute # build opt-in payload from ./out/report.json (never sends)
23
24
  詳細: README"""
24
25
 
25
26
 
@@ -141,11 +142,17 @@ def build_parser() -> argparse.ArgumentParser:
141
142
 
142
143
  render = sub.add_parser(
143
144
  "report",
144
- help="Re-render report.md from an existing report.json (no re-analysis)",
145
+ help="Analyze the current repo and write ./out/report.{json,md}; or re-render md from an existing report.json",
145
146
  epilog=_EPILOG,
146
147
  formatter_class=argparse.RawDescriptionHelpFormatter,
147
148
  )
148
- render.add_argument("report_json", type=Path, help="Path to report.json")
149
+ render.add_argument(
150
+ "report_json",
151
+ type=Path,
152
+ nargs="?",
153
+ default=None,
154
+ help="Path to an existing report.json to re-render (default: analyze the current repo into ./out)",
155
+ )
149
156
  render.add_argument(
150
157
  "--out",
151
158
  type=Path,
@@ -159,7 +166,13 @@ def build_parser() -> argparse.ArgumentParser:
159
166
  epilog=_EPILOG,
160
167
  formatter_class=argparse.RawDescriptionHelpFormatter,
161
168
  )
162
- contribute.add_argument("report_json", type=Path, help="Path to a repo-scope report.json")
169
+ contribute.add_argument(
170
+ "report_json",
171
+ type=Path,
172
+ nargs="?",
173
+ default=None,
174
+ help="Path to a repo-scope report.json (default: search ./out, .grift-out, ./)",
175
+ )
163
176
  contribute.add_argument(
164
177
  "--out",
165
178
  type=Path,
@@ -243,11 +256,35 @@ def _run_verify(args: argparse.Namespace) -> int:
243
256
  return 2
244
257
 
245
258
 
259
+ _DEFAULT_REPORT_SEARCH = (
260
+ Path("out") / "report.json",
261
+ Path(".grift-out") / "report.json",
262
+ Path("report.json"),
263
+ )
264
+
265
+
266
+ def _resolve_report_path(explicit: Path | None) -> tuple[Path | None, str]:
267
+ """Return (path, guidance). Search ./out, .grift-out, ./ when no explicit path."""
268
+ if explicit is not None:
269
+ return explicit, ""
270
+ for candidate in _DEFAULT_REPORT_SEARCH:
271
+ if candidate.is_file():
272
+ return candidate, ""
273
+ return None, ""
274
+
275
+
246
276
  def _run_report(args: argparse.Namespace) -> int:
277
+ if args.report_json is None:
278
+ # UX (代表 2026-08-23): bare `grift report` ALWAYS re-analyzes the
279
+ # current HEAD and rewrites ./out/report.{json,md}. It never falls
280
+ # back to a stale ./out/report.json — "report で測ったつもりが古い
281
+ # SHA のまま" は自己証明の罠になるため、既存ファイルは無条件に
282
+ # 上書きする。再分析なしの再レンダリングは引数指定時のみ。
283
+ return _analyze_to_out(scope="repo")
247
284
  try:
248
285
  payload = json.loads(args.report_json.read_text(encoding="utf-8"))
249
286
  except (OSError, json.JSONDecodeError) as exc:
250
- sys.stderr.write(f"report unreadable: {exc}\n")
287
+ sys.stderr.write(f"report unreadable ({args.report_json}): {exc}\n")
251
288
  return 2
252
289
  try:
253
290
  markdown = render_markdown(payload)
@@ -263,13 +300,43 @@ def _run_report(args: argparse.Namespace) -> int:
263
300
  return 0
264
301
 
265
302
 
303
+ def _analyze_to_out(*, scope: str = "repo") -> int:
304
+ """Run the analysis in the current directory and write ./out/report.{json,md}.
305
+
306
+ UX (代表 2026-08-23): bare `grift report` should just produce the reports —
307
+ create ./out if missing. Reuses the analyze pipeline on '.'.
308
+ """
309
+ repo = Path(".")
310
+ if not (repo / ".git").exists():
311
+ sys.stderr.write("grift: not a git repository (run inside the repo you want to analyze)\n")
312
+ return 2
313
+ identity = discover_identity(repo, None)
314
+ report = analyze_repository(repo, identity, Lineage(), scope=scope)
315
+ markdown = render_markdown(report)
316
+ encoded = json.dumps(report, indent=2, ensure_ascii=False) + "\n"
317
+ out_dir = Path("out")
318
+ out_dir.mkdir(parents=True, exist_ok=True)
319
+ (out_dir / "report.json").write_text(encoded, encoding="utf-8")
320
+ (out_dir / "report.md").write_text(markdown, encoding="utf-8")
321
+ sys.stdout.write(markdown)
322
+ sys.stderr.write(f"\nwrote {out_dir / 'report.json'} and {out_dir / 'report.md'}\n")
323
+ return 0
324
+
325
+
266
326
  def _run_contribute(args: argparse.Namespace) -> int:
267
327
  from tep_core.contribute import CONFIRMATION_TEXT, build_contribution, render_confirmation
268
328
 
329
+ report_path, _guidance = _resolve_report_path(args.report_json)
330
+ if report_path is None:
331
+ sys.stderr.write(
332
+ "contribute: no report.json found (searched ./out/report.json, .grift-out/report.json, ./report.json). "
333
+ "Run `grift report` first — it will analyze the repo and write ./out/report.json.\n"
334
+ )
335
+ return 2
269
336
  try:
270
- report = json.loads(args.report_json.read_text(encoding="utf-8"))
337
+ report = json.loads(report_path.read_text(encoding="utf-8"))
271
338
  except (OSError, json.JSONDecodeError) as exc:
272
- sys.stderr.write(f"report unreadable: {exc}\n")
339
+ sys.stderr.write(f"report unreadable ({report_path}): {exc}\n")
273
340
  return 2
274
341
  try:
275
342
  payload = build_contribution(report)
@@ -1,6 +1,6 @@
1
- __version__ = "0.5.2"
1
+ __version__ = "0.5.4"
2
2
  REPORT_SCHEMA_VERSION = "report-v1"
3
- DEFINITION_VERSION = "tep-v0.5.2-2026-08-23"
3
+ DEFINITION_VERSION = "tep-v0.5.4-2026-08-23"
4
4
  ORIGIN_DEFINITION_VERSION = "origin-v1.1-2026-08-22"
5
5
  ACTIVITY_DEFINITION_VERSION = "activity-v1-2026-08-16"
6
6
  CORE_ACTIVITY_DEFINITION_VERSION = "core-activity-v1-2026-08-16"
@@ -0,0 +1,134 @@
1
+ """Bare `grift report` / `grift contribute`: default-path search + guidance.
2
+
3
+ 代表 UX feedback (2026-08-23): invoking the subcommands without a path used
4
+ to hard-error on argparse / file-not-found without telling the user what to
5
+ do first. Now they search ./out, .grift-out, ./ and, when nothing is found,
6
+ exit 2 with actionable guidance.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ from pathlib import Path
13
+
14
+ from tep_cli.__main__ import main
15
+
16
+ from git_fixture import commit, init_repo
17
+
18
+
19
+ def _make_report_json(dir_path: Path) -> Path:
20
+ from tep_core.analyze import analyze_repository
21
+ from tep_core.identity import empty_identity
22
+ from tep_core.lineage import Lineage
23
+
24
+ repo = init_repo(dir_path / "repo")
25
+ (repo / "pyproject.toml").write_text(
26
+ '[project]\nname = "demo"\ndependencies = ["pytest"]\n', encoding="utf-8"
27
+ )
28
+ for index in range(12):
29
+ commit(
30
+ repo,
31
+ email="a@example.com",
32
+ date="2026-01-05",
33
+ message=f"feat: {index}",
34
+ filename="app.py",
35
+ )
36
+ report = analyze_repository(repo, empty_identity(), Lineage(), scope="repo")
37
+ target = dir_path / "out" / "report.json"
38
+ target.parent.mkdir(parents=True, exist_ok=True)
39
+ target.write_text(json.dumps(report, ensure_ascii=False), encoding="utf-8")
40
+ return target
41
+
42
+
43
+ def test_report_bare_ignores_stale_out_and_reanalyzes(
44
+ tmp_path: Path, capsys: object, monkeypatch: object
45
+ ) -> None:
46
+ """Critical UX (提唱者 2026-08-23): bare `grift report` with an EXISTING
47
+ ./out/report.json must RE-ANALYZE the current HEAD, not re-render the
48
+ stale file."""
49
+ import json as _json
50
+ import subprocess as _sp
51
+
52
+ from git_fixture import commit as _commit, init_repo as _init
53
+
54
+ repo = _init(tmp_path / "repo")
55
+ (repo / "pyproject.toml").write_text(
56
+ '[project]\nname = "demo"\ndependencies = ["pytest"]\n', encoding="utf-8"
57
+ )
58
+ for index in range(5):
59
+ _commit(repo, email="a@example.com", date="2026-01-05", message=f"feat: {index}")
60
+ monkeypatch.chdir(repo)
61
+ assert main(["report"]) == 0
62
+ first_sha = _json.loads((repo / "out" / "report.json").read_text())["provenance"][
63
+ "analyzed_commit_sha"
64
+ ]
65
+ _commit(repo, email="a@example.com", date="2026-01-06", message="feat: more")
66
+ assert main(["report"]) == 0
67
+ second_sha = _json.loads((repo / "out" / "report.json").read_text())["provenance"][
68
+ "analyzed_commit_sha"
69
+ ]
70
+ assert first_sha != second_sha, "bare report must re-analyze, not re-render stale output"
71
+ head = _sp.run(
72
+ ["git", "rev-parse", "HEAD"], capture_output=True, text=True, cwd=repo
73
+ ).stdout.strip()
74
+ assert second_sha == head
75
+ assert "## Shared block" in capsys.readouterr().out
76
+
77
+
78
+ def test_report_with_explicit_path_still_re_renders(tmp_path: Path, capsys: object) -> None:
79
+ """Explicit path keeps the re-render (no re-analysis) contract."""
80
+ _make_report_json(tmp_path)
81
+ code = main(["report", str(tmp_path / "out" / "report.json")])
82
+ assert code == 0
83
+ assert "## Shared block" in capsys.readouterr().out
84
+
85
+
86
+ def test_report_bare_without_prior_output_analyzes_cwd(
87
+ tmp_path: Path, capsys: object, monkeypatch: object
88
+ ) -> None:
89
+ """UX: bare `grift report` in a git repo with no ./out → analyze now and
90
+ write ./out/report.{json,md}."""
91
+ from git_fixture import commit as _commit, init_repo as _init
92
+
93
+ repo = _init(tmp_path / "repo")
94
+ for index in range(3):
95
+ _commit(repo, email="a@example.com", date="2026-01-05", message=f"feat: {index}")
96
+ monkeypatch.chdir(repo)
97
+ code = main(["report"])
98
+ assert code == 0
99
+ out = capsys.readouterr()
100
+ assert "## Shared block" in out.out
101
+ assert (repo / "out" / "report.json").is_file()
102
+ assert (repo / "out" / "report.md").is_file()
103
+
104
+
105
+ def test_report_bare_outside_git_repo_refused(
106
+ tmp_path: Path, capsys: object, monkeypatch: object
107
+ ) -> None:
108
+ monkeypatch.chdir(tmp_path)
109
+ code = main(["report"])
110
+ assert code == 2
111
+ assert "not a git repository" in capsys.readouterr().err
112
+
113
+
114
+ def test_contribute_bare_without_report_gives_guidance(
115
+ tmp_path: Path, capsys: object, monkeypatch: object
116
+ ) -> None:
117
+ monkeypatch.chdir(tmp_path)
118
+ code = main(["contribute"])
119
+ assert code == 2
120
+ err = capsys.readouterr().err
121
+ assert "grift report" in err # guidance points to the one-step entry
122
+
123
+
124
+ def test_contribute_bare_uses_out_default(
125
+ tmp_path: Path, capsys: object, monkeypatch: object
126
+ ) -> None:
127
+ # repo-scope report in ./out → bare contribute reaches the F-C1 gate
128
+ _make_report_json(tmp_path)
129
+ monkeypatch.chdir(tmp_path)
130
+ code = main(["contribute", "--out", str(tmp_path / "c.json")]) # non-TTY, no --yes
131
+ assert code == 2 # F-C1 refusal path (not path-resolution error)
132
+ err = capsys.readouterr().err
133
+ assert "公開リポジトリに載ります" in err
134
+ assert "grift analyze . --out ./out" not in err # found the file; failed on consent instead
@@ -19,6 +19,10 @@ _AUDIT_PREFIXES = (
19
19
  "docs/PREPUB",
20
20
  "docs/kpi",
21
21
  "docs/pilot",
22
+ "docs/SMOKE",
23
+ "docs/REREVIEW",
24
+ "docs/RELEASE-REHEARSAL",
25
+ "docs/INTERNAL-PILOT",
22
26
  )
23
27
 
24
28
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes