grift-cli 0.5.5__tar.gz → 0.5.6__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 (125) hide show
  1. {grift_cli-0.5.5 → grift_cli-0.5.6}/CHANGELOG.md +21 -0
  2. {grift_cli-0.5.5 → grift_cli-0.5.6}/PKG-INFO +5 -5
  3. {grift_cli-0.5.5 → grift_cli-0.5.6}/README.en.md +5 -5
  4. {grift_cli-0.5.5 → grift_cli-0.5.6}/README.md +4 -4
  5. {grift_cli-0.5.5 → grift_cli-0.5.6}/action.yml +1 -1
  6. {grift_cli-0.5.5 → grift_cli-0.5.6}/pyproject.toml +1 -1
  7. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_cli/__main__.py +18 -11
  8. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/version.py +2 -2
  9. grift_cli-0.5.6/tests/test_verb_semantics.py +113 -0
  10. {grift_cli-0.5.5 → grift_cli-0.5.6}/.github/workflows/ci.yml +0 -0
  11. {grift_cli-0.5.5 → grift_cli-0.5.6}/.github/workflows/kpi-monthly.yml +0 -0
  12. {grift_cli-0.5.5 → grift_cli-0.5.6}/.github/workflows/publish.yml +0 -0
  13. {grift_cli-0.5.5 → grift_cli-0.5.6}/.gitignore +0 -0
  14. {grift_cli-0.5.5 → grift_cli-0.5.6}/LICENSE +0 -0
  15. {grift_cli-0.5.5 → grift_cli-0.5.6}/corpus/AD-PILOT-20260822.md +0 -0
  16. {grift_cli-0.5.5 → grift_cli-0.5.6}/corpus/DISCRIMINANT-v2026.09.md +0 -0
  17. {grift_cli-0.5.5 → grift_cli-0.5.6}/corpus/DISCRIMINANT-v2026.11.md +0 -0
  18. {grift_cli-0.5.5 → grift_cli-0.5.6}/corpus/ad-pilot.json +0 -0
  19. {grift_cli-0.5.5 → grift_cli-0.5.6}/corpus/manifest.toml +0 -0
  20. {grift_cli-0.5.5 → grift_cli-0.5.6}/corpus/pins-v2026.09.toml +0 -0
  21. {grift_cli-0.5.5 → grift_cli-0.5.6}/corpus/pins-v2026.11.toml +0 -0
  22. {grift_cli-0.5.5 → grift_cli-0.5.6}/corpus/v2026.09/distributions.json +0 -0
  23. {grift_cli-0.5.5 → grift_cli-0.5.6}/corpus/v2026.11/distributions.json +0 -0
  24. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/corpus-protocol.md +0 -0
  25. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/coverage-map.md +0 -0
  26. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/en/corpus-protocol.md +0 -0
  27. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/en/coverage-map.md +0 -0
  28. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/en/export-schema.md +0 -0
  29. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/en/identity-schema.md +0 -0
  30. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/en/metrics-guide.md +0 -0
  31. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/en/norms.md +0 -0
  32. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/en/persona-answers.md +0 -0
  33. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/en/report-schema.md +0 -0
  34. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/export-schema.md +0 -0
  35. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/identity-schema.md +0 -0
  36. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/metrics-guide.md +0 -0
  37. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/norms.md +0 -0
  38. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/persona-answers.md +0 -0
  39. {grift_cli-0.5.5 → grift_cli-0.5.6}/docs/report-schema.md +0 -0
  40. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/README.md +0 -0
  41. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/coverage.md +0 -0
  42. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G1-click.json +0 -0
  43. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G10-scvi-tools.json +0 -0
  44. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G11-kilo.json +0 -0
  45. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G12-httpx.json +0 -0
  46. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G13-voicevox.json +0 -0
  47. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G2-gitignore.json +0 -0
  48. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G3-spoon-knife.json +0 -0
  49. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G4-express.json +0 -0
  50. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G5-typer.json +0 -0
  51. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G6-claude-code.json +0 -0
  52. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G7-axios.json +0 -0
  53. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G8-co.json +0 -0
  54. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/expected/G9-pyscript.json +0 -0
  55. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/identity/G1-click.toml +0 -0
  56. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/identity/example.toml +0 -0
  57. {grift_cli-0.5.5 → grift_cli-0.5.6}/golden/pins.toml +0 -0
  58. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/admission_v2026_11.py +0 -0
  59. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/build_dist_v2026_11.py +0 -0
  60. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/build_distributions.py +0 -0
  61. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/check_forbidden_vocab.py +0 -0
  62. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/corpus_ad_pilot.py +0 -0
  63. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/corpus_run.py +0 -0
  64. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/discriminant_v2026.py +0 -0
  65. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/discriminant_v2026_11.py +0 -0
  66. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/gen_coverage_table.py +0 -0
  67. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/gen_golden_expected.py +0 -0
  68. {grift_cli-0.5.5 → grift_cli-0.5.6}/scripts/hygiene_grep.py +0 -0
  69. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_cli/__init__.py +0 -0
  70. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/__init__.py +0 -0
  71. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/activity.py +0 -0
  72. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/analyze.py +0 -0
  73. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/cochange.py +0 -0
  74. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/context_profile.py +0 -0
  75. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/contribute.py +0 -0
  76. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/core_period.py +0 -0
  77. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/data/v2026.09/distributions.json +0 -0
  78. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/data/v2026.11/distributions.json +0 -0
  79. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/export.py +0 -0
  80. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/gitutil.py +0 -0
  81. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/identity.py +0 -0
  82. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/lineage.py +0 -0
  83. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/observation.py +0 -0
  84. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/origin.py +0 -0
  85. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/paths.py +0 -0
  86. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/reference.py +0 -0
  87. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/report.py +0 -0
  88. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/rework.py +0 -0
  89. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/schema.py +0 -0
  90. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/scope.py +0 -0
  91. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/survival.py +0 -0
  92. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/tests_observed.py +0 -0
  93. {grift_cli-0.5.5 → grift_cli-0.5.6}/src/tep_core/verify.py +0 -0
  94. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/conftest.py +0 -0
  95. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/coverage_matrix.py +0 -0
  96. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/git_fixture.py +0 -0
  97. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_activity.py +0 -0
  98. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_cli.py +0 -0
  99. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_cochange.py +0 -0
  100. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_context_profile.py +0 -0
  101. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_contribute.py +0 -0
  102. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_coverage.py +0 -0
  103. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_default_report_path.py +0 -0
  104. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_discriminant_claims.py +0 -0
  105. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_export.py +0 -0
  106. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_export_recipe.py +0 -0
  107. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_forbidden_vocab.py +0 -0
  108. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_gitutil.py +0 -0
  109. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_golden.py +0 -0
  110. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_h5_swarm.py +0 -0
  111. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_hygiene.py +0 -0
  112. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_identity_validation.py +0 -0
  113. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_intake_disclosure.py +0 -0
  114. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_lifecycle_v2.py +0 -0
  115. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_not_observed.py +0 -0
  116. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_origin.py +0 -0
  117. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_persona_answers.py +0 -0
  118. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_release_gate.py +0 -0
  119. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_report.py +0 -0
  120. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_rework.py +0 -0
  121. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_schema.py +0 -0
  122. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_scope.py +0 -0
  123. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_shared_block.py +0 -0
  124. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_survival.py +0 -0
  125. {grift_cli-0.5.5 → grift_cli-0.5.6}/tests/test_verify.py +0 -0
@@ -4,6 +4,27 @@
4
4
 
5
5
  タグ・PyPI・リポ public 化は本ファイルの記載対象外(人間ゲート)。
6
6
 
7
+ ## [0.5.6] — 2026-08-23
8
+
9
+ 公開後検収で発見の仕様記述↔実挙動の矛盾 3 件(D-1/D-2/D-3)を解消するホットフィックス。
10
+
11
+ ### Fixed
12
+
13
+ - **動詞の意味を確定: `analyze` = 表示(stdout)・`report` = 記録(`.grift/`)**。README 日英の 2 語動詞表の analyze 行(「`.grift/` を生成」と誤記)を訂正
14
+ - **D-3b**: 裸 `grift analyze` の既定スコープを **repo** に統一(パス明示の従来モードは既定 tenant のまま不変)
15
+ - **D-3a**: `grift report --scope {repo,tenant}`(既定 repo)を追加
16
+ - D-2 文書: report の help「default: ./out」誤記を `.grift/` に訂正・docstring/examples を動詞意味確定版に更新
17
+ - **errata(0.5.5 の記載について)**: 0.5.5 の README/EVIDENCE に「裸 `grift analyze` が `.grift/` を自動生成する」との記述があったが、実挙動は stdout 出力のみ(`.grift/` は `grift report` が作る)。本版で文書をバイナリに合わせ、動詞意味を上記のとおり確定した
18
+ - CHANGELOG の受け口 URL 表記を tep-contributions に統一
19
+
20
+ ### Added(報告規律・恒久)
21
+
22
+ - **「wheel 実測」節の各 claim には実行コマンドと生ログを添付する**(0.5.5 で裸 analyze の claim が公開 wheel で再現しなかった件の再発防止)
23
+
24
+ ### テスト(反証)
25
+
26
+ - 裸 analyze が `.grift/` を作らず stdout へ出すピン / 裸 analyze の scope=repo ピン / パス明示の既定 tenant 維持ピン / 裸 report が `.grift/` を書き scope=repo のピン / `report --scope tenant` のピン / help 文言の golden(`./out` 残存なし)
27
+
7
28
  ## [0.5.5] — 2026-08-23
8
29
 
9
30
  UX 統一(2語動詞 + `.grift/` 出力)+ contribute 受け口の実運用化 + 全ドキュメント日英対応。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: grift-cli
3
- Version: 0.5.5
3
+ Version: 0.5.6
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
@@ -41,16 +41,16 @@ Named after the Grift product line.
41
41
  ```bash
42
42
  pipx install grift-cli
43
43
  cd your-repo
44
- grift analyze # 分析 → .grift/report.{json,md} に書き出し
44
+ grift analyze # 分析結果を stdout に表示(repo スコープ)
45
45
  grift verify # .grift/report.json を現在のリポジトリで再計算・突合
46
46
  ```
47
47
 
48
- 基本操作はすべて **`grift <動詞>` の2語**で完結します(対象 = カレントディレクトリ、出力 = `.grift/`):
48
+ 基本操作はすべて **`grift <動詞>` の2語**で完結します。動詞の意味は「**analyze = 表示(stdout)**・**report = 記録(`.grift/`)**」です:
49
49
 
50
50
  | 2語コマンド | 動作 |
51
51
  |---|---|
52
- | `grift analyze` | カレントリポジトリを分析し `.grift/report.{json,md}` を生成 |
53
- | `grift report` | `grift analyze` と同結果(常に現在の HEAD を再分析) |
52
+ | `grift analyze` | カレントリポジトリを**stdout に表示**(repo スコープ・`.grift/` は作らない) |
53
+ | `grift report` | 分析して `.grift/report.{json,md}` に**記録**(常に現在の HEAD を再分析・既定 repo スコープ・`--scope tenant` 可) |
54
54
  | `grift verify` | `.grift/report.json` を同条件で再計算し改ざんを検出(VERIFIED / MISMATCH / CANNOT_VERIFY) |
55
55
  | `grift contribute` | `.grift/report.json` から opt-in 提出 payload を組む(**何も送信しない**) |
56
56
 
@@ -18,17 +18,17 @@ Commit counts, line counts, and “activity” inflate easily once agents and ge
18
18
  ```bash
19
19
  pipx install grift-cli
20
20
  cd your-repo
21
- grift analyze # analyze → writes .grift/report.{json,md}
21
+ grift analyze # analyze and print to stdout (repo scope)
22
22
  grift verify # recompute .grift/report.json against the current repo
23
23
  ```
24
24
 
25
- Every basic operation is just **`grift <verb>`** (target = current directory,
26
- output = `.grift/`):
25
+ Every basic operation is just **`grift <verb>`**. Verb semantics:
26
+ **analyze = display (stdout)** · **report = record (`.grift/`)**:
27
27
 
28
28
  | Verb | Action |
29
29
  |---|---|
30
- | `grift analyze` | Analyze the current repository into `.grift/report.{json,md}` |
31
- | `grift report` | Same as bare analyze (always re-analyzes the current HEAD) |
30
+ | `grift analyze` | Analyze the current repository and **print to stdout** (repo scope; does not create `.grift/`) |
31
+ | `grift report` | Analyze and **record into `.grift/report.{json,md}`** (always re-analyzes the current HEAD; repo scope by default, `--scope tenant` available) |
32
32
  | `grift verify` | Recompute `.grift/report.json` under recorded provenance (VERIFIED / MISMATCH / CANNOT_VERIFY) |
33
33
  | `grift contribute` | Build an opt-in submission payload from `.grift/report.json` (**never sends**) |
34
34
 
@@ -18,16 +18,16 @@ Named after the Grift product line.
18
18
  ```bash
19
19
  pipx install grift-cli
20
20
  cd your-repo
21
- grift analyze # 分析 → .grift/report.{json,md} に書き出し
21
+ grift analyze # 分析結果を stdout に表示(repo スコープ)
22
22
  grift verify # .grift/report.json を現在のリポジトリで再計算・突合
23
23
  ```
24
24
 
25
- 基本操作はすべて **`grift <動詞>` の2語**で完結します(対象 = カレントディレクトリ、出力 = `.grift/`):
25
+ 基本操作はすべて **`grift <動詞>` の2語**で完結します。動詞の意味は「**analyze = 表示(stdout)**・**report = 記録(`.grift/`)**」です:
26
26
 
27
27
  | 2語コマンド | 動作 |
28
28
  |---|---|
29
- | `grift analyze` | カレントリポジトリを分析し `.grift/report.{json,md}` を生成 |
30
- | `grift report` | `grift analyze` と同結果(常に現在の HEAD を再分析) |
29
+ | `grift analyze` | カレントリポジトリを**stdout に表示**(repo スコープ・`.grift/` は作らない) |
30
+ | `grift report` | 分析して `.grift/report.{json,md}` に**記録**(常に現在の HEAD を再分析・既定 repo スコープ・`--scope tenant` 可) |
31
31
  | `grift verify` | `.grift/report.json` を同条件で再計算し改ざんを検出(VERIFIED / MISMATCH / CANNOT_VERIFY) |
32
32
  | `grift contribute` | `.grift/report.json` から opt-in 提出 payload を組む(**何も送信しない**) |
33
33
 
@@ -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.5"
19
+ default: "0.5.6"
20
20
  comment:
21
21
  description: "Opt-in: post the shared block as a PR comment (default off)"
22
22
  required: false
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "grift-cli"
3
- version = "0.5.5"
3
+ version = "0.5.6"
4
4
  description = "Deterministic TEP evidence metrics from a single git repository. No LLM."
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -1,4 +1,4 @@
1
- """grift CLI (method = TEP)."""
1
+ """grift CLI (method = TEP). Verb semantics: analyze=display (stdout), report=record (.grift/)."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -16,11 +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 # analyze the current repo → .grift/report.{json,md}
20
- grift report # same as bare analyze: always re-analyzes HEAD
19
+ grift analyze # analyze the current repo, print to stdout (repo scope)
20
+ grift report # analyze and RECORD into .grift/report.{json,md} (always re-analyzes)
21
+ grift report --scope tenant # record a tenant-scope report (needs .tep/identity.toml)
21
22
  grift verify # verify .grift/report.json against the current repo
22
23
  grift contribute # build an opt-in payload from .grift/report.json (never sends)
23
- grift analyze PATH --scope tenant --identity .tep/identity.toml # custom
24
24
  詳細: README"""
25
25
 
26
26
 
@@ -105,8 +105,8 @@ def build_parser() -> argparse.ArgumentParser:
105
105
  analyze.add_argument(
106
106
  "--scope",
107
107
  choices=("tenant", "repo"),
108
- default="tenant",
109
- help="tenant: identity-matched evidence. repo: all human commits (reference distribution).",
108
+ default=None,
109
+ help="tenant: identity-matched evidence. repo: all human commits (reference distribution). Bare `grift analyze` defaults to repo; explicit-path invocations default to tenant.",
110
110
  )
111
111
  analyze.add_argument(
112
112
  "--reference-version",
@@ -154,7 +154,7 @@ def build_parser() -> argparse.ArgumentParser:
154
154
 
155
155
  render = sub.add_parser(
156
156
  "report",
157
- help="Analyze the current repo and write ./out/report.{json,md}; or re-render md from an existing report.json",
157
+ help="Analyze the current repo and RECORD into .grift/report.{json,md}; or re-render md from an existing report.json",
158
158
  epilog=_EPILOG,
159
159
  formatter_class=argparse.RawDescriptionHelpFormatter,
160
160
  )
@@ -163,7 +163,13 @@ def build_parser() -> argparse.ArgumentParser:
163
163
  type=Path,
164
164
  nargs="?",
165
165
  default=None,
166
- help="Path to an existing report.json to re-render (default: analyze the current repo into ./out)",
166
+ help="Path to an existing report.json to re-render (default: analyze the current repo into .grift/)",
167
+ )
168
+ render.add_argument(
169
+ "--scope",
170
+ choices=("tenant", "repo"),
171
+ default="repo",
172
+ help="Analysis scope for the bare form (default: repo)",
167
173
  )
168
174
  render.add_argument(
169
175
  "--out",
@@ -183,7 +189,7 @@ def build_parser() -> argparse.ArgumentParser:
183
189
  type=Path,
184
190
  nargs="?",
185
191
  default=None,
186
- help="Path to a repo-scope report.json (default: .grift/, then legacy locations)",
192
+ help="Path to a repo-scope report.json (default: .grift/report.json)",
187
193
  )
188
194
  contribute.add_argument(
189
195
  "--out",
@@ -201,6 +207,7 @@ def build_parser() -> argparse.ArgumentParser:
201
207
 
202
208
  def _run_analyze(args: argparse.Namespace) -> int:
203
209
  repo: Path = args.repo if args.repo is not None else Path(".")
210
+ scope = str(args.scope) if args.scope is not None else ("repo" if args.repo is None else "tenant")
204
211
  if not (repo / ".git").exists() and not repo.joinpath("HEAD").exists():
205
212
  sys.stderr.write(f"not a git repository: {repo}\n")
206
213
  return 2
@@ -219,7 +226,7 @@ def _run_analyze(args: argparse.Namespace) -> int:
219
226
  include_files=bool(args.vendor_scan),
220
227
  include_local_path=bool(args.include_local_path),
221
228
  survival=bool(args.survival),
222
- scope=str(args.scope),
229
+ scope=scope,
223
230
  reference_version=str(args.reference_version) if args.reference_version else None,
224
231
  )
225
232
  markdown = render_markdown(report)
@@ -298,7 +305,7 @@ def _run_report(args: argparse.Namespace) -> int:
298
305
  # back to a stale report.json — "report で測ったつもりが古い
299
306
  # SHA のまま" は自己証明の罠になるため、既存ファイルは無条件に
300
307
  # 上書きする。再分析なしの再レンダリングは引数指定時のみ。
301
- return _analyze_to_grift_dir(scope="repo")
308
+ return _analyze_to_grift_dir(scope=str(args.scope))
302
309
  try:
303
310
  payload = json.loads(args.report_json.read_text(encoding="utf-8"))
304
311
  except (OSError, json.JSONDecodeError) as exc:
@@ -1,6 +1,6 @@
1
- __version__ = "0.5.5"
1
+ __version__ = "0.5.6"
2
2
  REPORT_SCHEMA_VERSION = "report-v1"
3
- DEFINITION_VERSION = "tep-v0.5.5-2026-08-23"
3
+ DEFINITION_VERSION = "tep-v0.5.6-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,113 @@
1
+ """v0.5.6 verb semantics (D-1/D-2/D-3 fixes): analyze=display, report=record.
2
+
3
+ Falsifiable pins:
4
+ - bare `grift analyze` prints to stdout and does NOT create .grift/ (D-1)
5
+ - bare `grift analyze` uses repo scope (D-3b)
6
+ - bare `grift report` writes .grift/report.{json,md} with scope=repo (D-2)
7
+ - `grift report --scope tenant` records a tenant-scope report (D-3a)
8
+ - help text pins (no stale ./out mention)
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ from pathlib import Path
15
+
16
+ from tep_cli.__main__ import build_parser, main
17
+
18
+ from git_fixture import commit, init_repo
19
+
20
+
21
+ def _repo_with_tests(tmp_path: Path, name: str = "repo") -> Path:
22
+ repo = init_repo(tmp_path / name)
23
+ (repo / "pyproject.toml").write_text(
24
+ '[project]\nname = "demo"\ndependencies = ["pytest"]\n', encoding="utf-8"
25
+ )
26
+ (repo / "tests").mkdir()
27
+ (repo / "tests" / "t.py").write_text("def test_o():\n pass\n", encoding="utf-8")
28
+ (repo / ".tep").mkdir()
29
+ (repo / ".tep" / "identity.toml").write_text(
30
+ 'schema_version = "identity-v1"\n\n[[actors]]\ncanonical_id = "alice"\n'
31
+ 'emails = ["a@example.com"]\nattribution_state = "verified"\n',
32
+ encoding="utf-8",
33
+ )
34
+ for index in range(24):
35
+ commit(
36
+ repo,
37
+ email="a@example.com",
38
+ date="2026-01-05",
39
+ message=f"feat: {index}",
40
+ filename="app.py",
41
+ )
42
+ if index % 2 == 0:
43
+ commit(
44
+ repo,
45
+ email="a@example.com",
46
+ date="2026-01-05",
47
+ message=f"test: {index}",
48
+ filename="tests/t.py",
49
+ )
50
+ return repo
51
+
52
+
53
+ def test_d1_bare_analyze_stdout_only_no_grift_dir(tmp_path, capsys, monkeypatch):
54
+ """D-1: bare analyze = display. Prints to stdout, never creates .grift/."""
55
+ repo = _repo_with_tests(tmp_path)
56
+ monkeypatch.chdir(repo)
57
+ code = main(["analyze"])
58
+ assert code == 0
59
+ out = capsys.readouterr()
60
+ assert "# TEP analysis" in out.out # markdown on stdout
61
+ assert not (repo / ".grift").exists(), "analyze must NOT create .grift/ (that is report's job)"
62
+
63
+
64
+ def test_d3b_bare_analyze_defaults_to_repo_scope(tmp_path, capsys, monkeypatch):
65
+ """D-3b: bare analyze default scope = repo (deciles may appear)."""
66
+ repo = _repo_with_tests(tmp_path)
67
+ monkeypatch.chdir(repo)
68
+ code = main(["analyze", "--format", "json"])
69
+ assert code == 0
70
+ payload = json.loads(capsys.readouterr().out)
71
+ assert payload["provenance"]["analysis_scope"] == "repo"
72
+
73
+
74
+ def test_d3b_explicit_path_still_defaults_tenant(tmp_path, capsys):
75
+ """Back-compat: explicit-path invocation keeps the tenant default."""
76
+ repo = _repo_with_tests(tmp_path, "explicit")
77
+ code = main(["analyze", str(repo), "--format", "json"])
78
+ assert code == 0
79
+ payload = json.loads(capsys.readouterr().out)
80
+ assert payload["provenance"]["analysis_scope"] == "tenant"
81
+
82
+
83
+ def test_d2_bare_report_records_grift_dir_repo_scope(tmp_path, capsys, monkeypatch):
84
+ """D-2: bare report = record. Writes .grift/report.{json,md} with repo scope."""
85
+ repo = _repo_with_tests(tmp_path)
86
+ monkeypatch.chdir(repo)
87
+ code = main(["report"])
88
+ assert code == 0
89
+ payload = json.loads((repo / ".grift" / "report.json").read_text(encoding="utf-8"))
90
+ assert payload["provenance"]["analysis_scope"] == "repo"
91
+ assert (repo / ".grift" / "report.md").is_file()
92
+
93
+
94
+ def test_d3a_report_scope_tenant(tmp_path, capsys, monkeypatch):
95
+ """D-3a: report --scope tenant records a tenant-scope report."""
96
+ repo = _repo_with_tests(tmp_path)
97
+ monkeypatch.chdir(repo)
98
+ code = main(["report", "--scope", "tenant"])
99
+ assert code == 0
100
+ payload = json.loads((repo / ".grift" / "report.json").read_text(encoding="utf-8"))
101
+ assert payload["provenance"]["analysis_scope"] == "tenant"
102
+ assert payload["identity"]["actor_count"] == 1
103
+
104
+
105
+ def test_help_text_has_no_stale_out_dir(tmp_path):
106
+ """D-2 doc pin: help mentions .grift/, never the stale './out' default."""
107
+ parser = build_parser()
108
+ report_help = parser._subparsers._group_actions[0].choices["report"].format_help()
109
+ analyze_help = parser._subparsers._group_actions[0].choices["analyze"].format_help()
110
+ assert ".grift/" in report_help
111
+ collapsed = " ".join(report_help.split())
112
+ assert "default: analyze the current repo into .grift/" in collapsed
113
+ assert "./out" not in collapsed and "./out" not in " ".join(analyze_help.split())
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
File without changes