gentle-pi 2.1.2 → 2.3.0

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 (228) hide show
  1. package/README.md +108 -79
  2. package/assets/agents/gentle-ai-worker.md +7 -3
  3. package/assets/agents/jd-fix-agent.md +1 -1
  4. package/assets/agents/jd-judge-a.md +3 -1
  5. package/assets/agents/jd-judge-b.md +3 -1
  6. package/assets/agents/review-readability.md +4 -3
  7. package/assets/agents/review-reliability.md +4 -3
  8. package/assets/agents/review-resilience.md +4 -3
  9. package/assets/agents/review-risk.md +4 -3
  10. package/assets/agents/sdd-apply.md +11 -4
  11. package/assets/agents/sdd-archive.md +6 -1
  12. package/assets/agents/sdd-design.md +6 -1
  13. package/assets/agents/sdd-explore.md +6 -2
  14. package/assets/agents/sdd-init.md +10 -2
  15. package/assets/agents/sdd-onboard.md +6 -1
  16. package/assets/agents/sdd-proposal.md +8 -1
  17. package/assets/agents/sdd-research.md +54 -0
  18. package/assets/agents/sdd-spec.md +6 -1
  19. package/assets/agents/sdd-status.md +10 -5
  20. package/assets/agents/sdd-sync.md +6 -1
  21. package/assets/agents/sdd-tasks.md +8 -4
  22. package/assets/agents/sdd-verify.md +27 -2
  23. package/assets/chains/4r-review.chain.md +2 -0
  24. package/assets/chains/sdd-full.chain.md +2 -2
  25. package/assets/chains/sdd-plan.chain.md +1 -1
  26. package/assets/chains/sdd-verify.chain.md +2 -2
  27. package/assets/orchestrator-delegation.md +145 -160
  28. package/assets/orchestrator-memory.md +2 -0
  29. package/assets/orchestrator.md +25 -48
  30. package/assets/sdd-orchestrator-workflow.md +163 -25
  31. package/assets/support/sdd-status-contract.md +24 -6
  32. package/contracts/review-integration/v1/fixtures/consent.fixture.json +3 -3
  33. package/contracts/review-integration/v1/fixtures/start-v2.fixture.json +19 -28
  34. package/contracts/review-integration/v1/fixtures/start.fixture.json +1 -10
  35. package/contracts/review-integration/v1/fixtures/status-v2.fixture.json +12 -21
  36. package/contracts/review-integration/v1/schemas/correction-plan-request.schema.json +49 -0
  37. package/contracts/review-integration/v1/schemas/operation.schema.json +76 -0
  38. package/contracts/review-integration/v1/schemas/repair.schema.json +39 -0
  39. package/contracts/review-integration/v1/schemas/status-v2.schema.json +4 -2
  40. package/contracts/review-integration/v1/schemas/status.schema.json +4 -2
  41. package/contracts/review-integration/v1/schemas/transition-execution.schema.json +42 -0
  42. package/contracts/review-integration/v2/fixtures/consent.fixture.json +1 -1
  43. package/contracts/review-integration/v2/fixtures/start.fixture.json +1 -10
  44. package/contracts/review-integration/v2/fixtures/status.fixture.json +1 -10
  45. package/contracts/review-integration/v2/schemas/failure.schema.json +5 -1
  46. package/contracts/review-integration/v2/schemas/last-event-closure.schema.json +66 -0
  47. package/contracts/review-integration/v2/schemas/opencode-provider-role.schema.json +14 -0
  48. package/contracts/review-integration/v2/schemas/operation.schema.json +6 -1
  49. package/contracts/review-integration/v2/schemas/repair.schema.json +4 -2
  50. package/contracts/review-integration/v2/schemas/start.schema.json +5 -2
  51. package/contracts/review-integration/v2/schemas/status.schema.json +4 -2
  52. package/contracts/review-provider-contract-mirror/provider-contract.lock.json +30 -0
  53. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/README.md +12 -0
  54. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/manifest.json +65 -0
  55. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/lens.schema.json +16 -0
  56. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/refuter.schema.json +1 -0
  57. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/targeted-validator.schema.json +1 -0
  58. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/lens.json +1 -0
  59. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/refuter.json +1 -0
  60. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/targeted-validator.json +1 -0
  61. package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-capabilities.baseline.json +15 -0
  62. package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-roles.baseline.json +42 -0
  63. package/docs/native-authority-architecture.md +14 -16
  64. package/docs/review-integration.md +27 -353
  65. package/extensions/ask-user-choice.ts +151 -0
  66. package/extensions/gentle-ai.ts +2710 -3211
  67. package/extensions/quiet-tools.ts +515 -32
  68. package/extensions/sdd-init.ts +21 -12
  69. package/extensions/skill-registry.ts +10 -2
  70. package/extensions/startup-banner.ts +10 -4
  71. package/lib/gentle-ai-binary.ts +173 -2
  72. package/lib/gentle-ai-renderer.ts +70 -0
  73. package/lib/model-routing-authority.ts +133 -0
  74. package/lib/native-review-cli.ts +676 -781
  75. package/lib/opaque-pi-reviewer-adapter.ts +284 -0
  76. package/lib/provider-contract-bundle.ts +704 -0
  77. package/lib/review-candidate-view.ts +836 -118
  78. package/lib/review-compact-contract.ts +59 -248
  79. package/lib/review-host-relay.ts +578 -0
  80. package/lib/review-integration-v2.ts +1254 -221
  81. package/lib/review-last-event-controller.ts +35 -0
  82. package/lib/review-relay-contract.ts +16 -0
  83. package/lib/sdd-preflight.ts +230 -67
  84. package/lib/sdd-status.ts +66 -111
  85. package/lib/terminal-theme.ts +1 -1
  86. package/package.json +83 -79
  87. package/runtime/gentle-ai-binary.mjs +174 -3
  88. package/runtime/native-review-cli.mjs +610 -715
  89. package/runtime/review-integration-v2.mjs +1223 -190
  90. package/runtime/review-relay-contract.mjs +17 -0
  91. package/scripts/{build-git-commit-transaction-runner.mjs → build-runtime-modules.mjs} +5 -5
  92. package/scripts/check-provider-contract.mjs +138 -0
  93. package/scripts/gentle-ai-installer.mjs +85 -22
  94. package/scripts/maintainer/provider-relay-matrix.mjs +403 -0
  95. package/scripts/mirror-provider-contract.mjs +143 -0
  96. package/scripts/test-packed-runner.mjs +20 -9
  97. package/scripts/verify-package-files.mjs +119 -43
  98. package/skills/_shared/review-ledger-contract.md +11 -19
  99. package/skills/chained-pr/SKILL.md +3 -0
  100. package/skills/cognitive-doc-design/SKILL.md +1 -1
  101. package/skills/comment-writer/SKILL.md +1 -1
  102. package/skills/gentle-ai/SKILL.md +7 -74
  103. package/skills/issue-creation/SKILL.md +94 -168
  104. package/skills/judgment-day/SKILL.md +9 -5
  105. package/skills/judgment-day/references/prompts-and-formats.md +2 -0
  106. package/skills/rdd-defect-workflow/SKILL.md +54 -0
  107. package/skills/release/SKILL.md +3 -3
  108. package/skills/skill-registry/SKILL.md +1 -1
  109. package/skills/work-unit-commits/SKILL.md +3 -1
  110. package/tests/artifact-language.test.ts +24 -11
  111. package/tests/ask-user-choice.test.ts +264 -0
  112. package/tests/background-subagents.test.ts +771 -0
  113. package/tests/codegraph-tools.test.ts +3 -3
  114. package/tests/crosslane/cross-lane.mjs +16 -0
  115. package/tests/delegated-key-learnings-contract.test.ts +240 -0
  116. package/tests/devbinary/native-review-parity.devtest.ts +167 -250
  117. package/tests/devbinary/pi-host-relay.devtest.ts +867 -0
  118. package/tests/fixtures/devbinary/capabilities-v2.1.derived.json +331 -0
  119. package/tests/fixtures/devbinary/capabilities-v2.2.captured.json +340 -0
  120. package/tests/fixtures/devbinary/consent-v3.captured.json +37 -0
  121. package/tests/fixtures/devbinary/failure-v2-capture-evidence.captured.json +16 -0
  122. package/tests/fixtures/devbinary/last-event-capture-correction-plan.captured.json +10 -0
  123. package/tests/fixtures/devbinary/last-event-capture-refuter-approved.captured.json +20 -0
  124. package/tests/fixtures/devbinary/last-event-capture-refuter-correction-required.captured.json +26 -0
  125. package/tests/fixtures/devbinary/last-event-capture-result-approved.captured.json +8 -0
  126. package/tests/fixtures/devbinary/last-event-capture-result-correction-required.captured.json +26 -0
  127. package/tests/fixtures/devbinary/last-event-capture-validation-approved.captured.json +8 -0
  128. package/tests/fixtures/devbinary/last-event-closure.provenance.md +13 -0
  129. package/tests/fixtures/devbinary/result-artifact-v2-path.captured.json +12 -0
  130. package/tests/fixtures/devbinary/result-artifact-v2.captured.json +12 -0
  131. package/tests/fixtures/devbinary/review-acknowledged-v1.captured.json +9 -0
  132. package/tests/fixtures/devbinary/review-acknowledged.provenance.md +31 -0
  133. package/tests/fixtures/devbinary/start-v3-consent-declined.captured.json +19 -0
  134. package/tests/fixtures/devbinary/start-v3-consent-granted.captured.json +109 -0
  135. package/tests/fixtures/devbinary/start-v3-zero-lens-closed.captured.json +21 -0
  136. package/tests/fixtures/devbinary/status-v5-capture-result-submission.captured.json +184 -0
  137. package/tests/fixtures/devbinary/status-v5-repository-context.captured.json +138 -0
  138. package/tests/fixtures/devbinary/status-v5.captured.json +88 -0
  139. package/tests/fixtures/native-review-cli/v2.5.0-rc.1/PROVENANCE.txt +15 -0
  140. package/tests/fixtures/provider-contract-bundle/v1.1.0/README.md +12 -0
  141. package/tests/fixtures/provider-contract-bundle/v1.1.0/manifest.json +65 -0
  142. package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/lens.schema.json +16 -0
  143. package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/refuter.schema.json +1 -0
  144. package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/targeted-validator.schema.json +1 -0
  145. package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/lens.json +1 -0
  146. package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/refuter.json +1 -0
  147. package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/targeted-validator.json +1 -0
  148. package/tests/gentle-ai-binary.test.ts +83 -13
  149. package/tests/gentle-ai-dev-binary-surfacing.test.ts +195 -0
  150. package/tests/gentle-ai-dev-binary.test.ts +159 -0
  151. package/tests/gentle-ai-installer.test.ts +138 -48
  152. package/tests/gentle-ai.test.ts +820 -131
  153. package/tests/gentle-theme.test.ts +133 -0
  154. package/tests/maintainer/provider-relay.maintest.ts +601 -0
  155. package/tests/model-routing-authority.test.ts +257 -0
  156. package/tests/native-review-capability-contract.test.ts +75 -2
  157. package/tests/native-review-cli.test.ts +588 -908
  158. package/tests/native-review-consent.test.ts +283 -38
  159. package/tests/native-review-parity-runtime.test.ts +108 -350
  160. package/tests/native-review-parity.test.ts +684 -732
  161. package/tests/native-sdd-attempt-authority.test.ts +235 -0
  162. package/tests/opaque-pi-reviewer-adapter.test.ts +266 -0
  163. package/tests/orchestrator-budget.test.ts +158 -89
  164. package/tests/orchestrator-rdd-ownership.test.ts +103 -0
  165. package/tests/package-manifest.test.ts +128 -100
  166. package/tests/provider-contract-bundle.test.ts +385 -0
  167. package/tests/provider-contract-mirror.test.ts +206 -0
  168. package/tests/provider-defect-handoff.test.ts +252 -0
  169. package/tests/quiet-tool-rendering.test.ts +1055 -28
  170. package/tests/review-actor-tool-deny.test.ts +12 -13
  171. package/tests/review-authority-recovery-docs.test.ts +1 -2
  172. package/tests/review-candidate-view.test.ts +898 -12
  173. package/tests/review-compact-contract.test.ts +29 -122
  174. package/tests/review-controller-lock-status.test.ts +2 -2
  175. package/tests/review-controller-native-recovery.test.ts +366 -857
  176. package/tests/review-controller-native-routing.test.ts +1253 -4199
  177. package/tests/review-controller-retired-ops.test.ts +1 -1
  178. package/tests/review-controller-workspace-root.test.ts +236 -70
  179. package/tests/review-controller.test.ts +26 -816
  180. package/tests/review-corrected-finalize-binding.test.ts +134 -0
  181. package/tests/review-dispatch-hydration-gap.test.ts +145 -0
  182. package/tests/review-gate.test.ts +0 -45
  183. package/tests/review-host-relay-restart-parity.test.ts +360 -0
  184. package/tests/review-host-relay-routing.test.ts +352 -0
  185. package/tests/review-host-relay.test.ts +754 -0
  186. package/tests/review-integration-v2-forward.test.ts +654 -0
  187. package/tests/review-integration-v2.test.ts +317 -129
  188. package/tests/review-last-event-closure.test.ts +408 -0
  189. package/tests/review-ledger-contract.test.ts +106 -60
  190. package/tests/review-recovered-lineage-routing.test.ts +199 -0
  191. package/tests/review-relay-transport-agent.test.ts +322 -0
  192. package/tests/review-snapshot.test.ts +3 -2
  193. package/tests/runtime-harness.mjs +573 -180
  194. package/tests/sdd-agent-tools.test.ts +53 -32
  195. package/tests/sdd-preflight.test.ts +81 -15
  196. package/tests/sdd-status.test.ts +109 -110
  197. package/tests/skill-collision-prefixes.test.ts +6 -8
  198. package/tests/skill-registry.test.ts +50 -1
  199. package/tests/verify-package-files.test.ts +62 -0
  200. package/tests/writer-edit-surface-scope.test.ts +230 -0
  201. package/themes/Gentleman-Cute.json +94 -0
  202. package/themes/Gentleman-Sexy.json +92 -0
  203. package/assets/agents/review-refuter.md +0 -40
  204. package/assets/agents/review-validator.md +0 -23
  205. package/lib/git-commit-transaction.ts +0 -801
  206. package/lib/native-review-remediation.ts +0 -49
  207. package/lib/review-compact.ts +0 -947
  208. package/lib/review-refuter-adapter.ts +0 -129
  209. package/lib/review-runtime-contract.ts +0 -68
  210. package/prompts/gcl.md +0 -54
  211. package/prompts/gis.md +0 -25
  212. package/prompts/gpr.md +0 -41
  213. package/prompts/gwr.md +0 -31
  214. package/runtime/git-commit-transaction.mjs +0 -802
  215. package/scripts/run-git-commit-transaction.mjs +0 -35
  216. package/tests/fixtures/native-review-cli/v2.1.2/bind-sdd.json +0 -25
  217. package/tests/fixtures/native-review-cli/v2.1.2/finalize.json +0 -8
  218. package/tests/fixtures/native-review-cli/v2.1.2/sdd-status-engram.json +0 -139
  219. package/tests/fixtures/native-review-cli/v2.1.2/sdd-status.json +0 -200
  220. package/tests/fixtures/native-review-cli/v2.1.2/start.json +0 -12
  221. package/tests/fixtures/native-review-cli/v2.1.2/validate-allow.json +0 -24
  222. package/tests/fixtures/native-review-cli/v2.1.2/validate-deny-empty-context.json +0 -20
  223. package/tests/fixtures/native-review-cli/v2.1.2/validate-deny.json +0 -28
  224. package/tests/fixtures/native-review-cli/v2.1.3/sdd-status-engram.json +0 -139
  225. package/tests/fixtures/native-review-cli/v2.1.3/sdd-status.json +0 -200
  226. package/tests/git-commit-transaction.test.ts +0 -302
  227. package/tests/review-compact.test.ts +0 -243
  228. package/tests/review-refuter-adapter.test.ts +0 -89
@@ -1,200 +0,0 @@
1
- {
2
- "schemaName": "gentle-ai.sdd-status",
3
- "schemaVersion": 1,
4
- "changeName": "native-review-authority-parity",
5
- "artifactStore": "openspec",
6
- "planningHome": {
7
- "mode": "repo-local",
8
- "path": "/tmp/gentle-ai-v212-contract-9XGIB4/openspec"
9
- },
10
- "changeRoot": "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity",
11
- "artifactPaths": {
12
- "proposal": [
13
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/proposal.md"
14
- ],
15
- "specs": [
16
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/specs/review/spec.md"
17
- ],
18
- "design": [
19
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/design.md"
20
- ],
21
- "tasks": [
22
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/tasks.md"
23
- ],
24
- "applyProgress": [],
25
- "verifyReport": [
26
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/verify-report.md"
27
- ],
28
- "reviewPolicy": [],
29
- "reviewLedger": [],
30
- "reviewReceipt": [],
31
- "reviewBundle": [],
32
- "reviewContext": [],
33
- "reviewState": [
34
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/reviews/transaction.json"
35
- ]
36
- },
37
- "contextFiles": {
38
- "proposal": [
39
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/proposal.md"
40
- ],
41
- "specs": [
42
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/specs/review/spec.md"
43
- ],
44
- "design": [
45
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/design.md"
46
- ],
47
- "tasks": [
48
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/tasks.md"
49
- ],
50
- "applyProgress": [],
51
- "verifyReport": [
52
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/verify-report.md"
53
- ],
54
- "reviewPolicy": [],
55
- "reviewLedger": [],
56
- "reviewReceipt": [],
57
- "reviewBundle": [],
58
- "reviewContext": [],
59
- "reviewState": [
60
- "/tmp/gentle-ai-v212-contract-9XGIB4/openspec/changes/native-review-authority-parity/reviews/transaction.json"
61
- ]
62
- },
63
- "artifacts": {
64
- "applyProgress": "missing",
65
- "design": "done",
66
- "proposal": "done",
67
- "reviewBundle": "missing",
68
- "reviewContext": "missing",
69
- "reviewLedger": "missing",
70
- "reviewReceipt": "missing",
71
- "reviewState": "done",
72
- "specs": "done",
73
- "tasks": "done",
74
- "verifyReport": "done"
75
- },
76
- "taskProgress": {
77
- "total": 1,
78
- "completed": 1,
79
- "pending": 0,
80
- "allComplete": true
81
- },
82
- "dependencies": {
83
- "proposal": "all_done",
84
- "specs": "all_done",
85
- "design": "all_done",
86
- "tasks": "all_done",
87
- "apply": "all_done",
88
- "verify": "all_done",
89
- "archive": "ready"
90
- },
91
- "applyState": "all_done",
92
- "actionContext": {
93
- "mode": "repo-local",
94
- "workspaceRoot": "/tmp/gentle-ai-v212-contract-9XGIB4",
95
- "allowedEditRoots": [
96
- "/tmp/gentle-ai-v212-contract-9XGIB4"
97
- ]
98
- },
99
- "relationships": {
100
- "dependsOn": [],
101
- "supersedes": [],
102
- "amends": [],
103
- "conflictsWith": [],
104
- "sameDomainActiveChanges": []
105
- },
106
- "remediationState": {
107
- "required": false,
108
- "complete": false,
109
- "failedEvidenceRevision": "",
110
- "lineageId": "",
111
- "generation": 0,
112
- "fixBatch": 0,
113
- "reason": ""
114
- },
115
- "reviewGate": {
116
- "result": "allow",
117
- "reason": "explicit bound compact authority exactly matches the current repository"
118
- },
119
- "reviewTransaction": {
120
- "schema": "gentle-ai.review-transaction/v1",
121
- "lineage_id": "issue-1099-corrected-candidate-review-20260710",
122
- "mode": "ordinary_4r",
123
- "generation": 1,
124
- "state": "reviewing",
125
- "snapshot": {
126
- "kind": "current-changes",
127
- "base_tree": "ba040f17cdcd2a5960361971eaff79684c48847a",
128
- "candidate_tree": "5bc2f4bdd086d064bc3ada255f16c631facc6839",
129
- "paths_digest": "sha256:4a52771035fe6ef25f0155b11c052a661879b1cd1af4bbe5cd337118a9dd29dd",
130
- "intended_untracked": [
131
- "assets/agents/gentle-ai-explore.md",
132
- "assets/agents/gentle-ai-verify.md"
133
- ],
134
- "intended_untracked_proof": "sha256:ea65650186e3d14ed53ae4912a2a30127863fe9aec0e4c14050e4fa154b0bfa3",
135
- "paths": [
136
- "assets/agents/gentle-ai-explore.md",
137
- "assets/agents/gentle-ai-verify.md",
138
- "assets/orchestrator-delegation.md",
139
- "assets/orchestrator.md",
140
- "tests/package-manifest.test.ts",
141
- "tests/sdd-agent-tools.test.ts"
142
- ],
143
- "identity": "sha256:d88627d276b790d1ed58abc44118d1d95a9620d9755d5e61c8fbda0f97cea53c"
144
- },
145
- "base_tree": "ba040f17cdcd2a5960361971eaff79684c48847a",
146
- "paths_digest": "sha256:4a52771035fe6ef25f0155b11c052a661879b1cd1af4bbe5cd337118a9dd29dd",
147
- "initial_review_tree": "5bc2f4bdd086d064bc3ada255f16c631facc6839",
148
- "final_candidate_tree": "5bc2f4bdd086d064bc3ada255f16c631facc6839",
149
- "fix_delta_hash": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
150
- "policy_hash": "sha256:9197a98b8c5ceac34da4e8d213908d68023f215e891ecb85dfe9fb0b355f9482",
151
- "ledger_hash": "",
152
- "ledger_findings_hash": "",
153
- "evidence_hash": "",
154
- "judge_proofs": [],
155
- "counters": {
156
- "full_reviews": 1,
157
- "refuter_batches": 0,
158
- "fix_batches": 0,
159
- "scoped_fix_validations": 0,
160
- "final_verifications": 0,
161
- "fix_rounds": 0,
162
- "scoped_rejudgments": 0,
163
- "judge_executions": 0
164
- },
165
- "findings": [],
166
- "classifications": {},
167
- "outcomes": {},
168
- "fix_finding_ids": [],
169
- "pending_refuter_ids": [],
170
- "fix_caused_findings": [],
171
- "follow_ups": []
172
- },
173
- "phaseInstructions": {
174
- "apply": [
175
- "Change: native-review-authority-parity",
176
- "State: all_done",
177
- "Read proposal, specs, design, and tasks before editing.",
178
- "Implement only unchecked tasks and update tasks.md checkboxes as work completes."
179
- ],
180
- "verify": [
181
- "Change: native-review-authority-parity",
182
- "State: all_done",
183
- "Verify implementation against proposal, specs, design, and task completion.",
184
- "Run final verification only after every task is complete; apply-progress never makes final verification ready."
185
- ],
186
- "remediate": [
187
- "Change: native-review-authority-parity",
188
- "Remediation is allowed only when the persisted review transaction has remaining mode-specific budget.",
189
- "Bind focused tests, runtime harness evidence, and rollback evidence to the exact failed evidence revision.",
190
- "A bare remediation envelope or stale failed revision never completes remediation."
191
- ],
192
- "archive": [
193
- "Change: native-review-authority-parity",
194
- "State: ready",
195
- "Archive only when verify-report.md exists and every task checkbox is complete."
196
- ]
197
- },
198
- "nextRecommended": "archive",
199
- "blockedReasons": []
200
- }
@@ -1,302 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { execFileSync } from "node:child_process";
3
- import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
- import test from "node:test";
7
- import { setTimeout as delay } from "node:timers/promises";
8
- import {
9
- COMMIT_TRANSACTION_STATE,
10
- assertNoUnresolvedCommitTransaction,
11
- inspectCommitTransaction,
12
- prepareCommitTransactionInvocation,
13
- reconcileCommitTransaction,
14
- runGitCommitTransaction,
15
- verifyCommitTransactionResult,
16
- } from "../lib/git-commit-transaction.ts";
17
- import type { NativeReviewCli, NativeValidateResult } from "../lib/native-review-cli.ts";
18
-
19
- function git(cwd: string, ...arguments_: string[]): string {
20
- return execFileSync("git", arguments_, { cwd, encoding: "utf8" }).trim();
21
- }
22
-
23
- function repository(t: test.TestContext): string {
24
- const cwd = mkdtempSync(join(tmpdir(), "gentle-pi-commit-transaction-"));
25
- t.after(() => rmSync(cwd, { recursive: true, force: true }));
26
- git(cwd, "init", "-b", "main");
27
- git(cwd, "config", "user.name", "Commit Transaction Test");
28
- git(cwd, "config", "user.email", "commit-transaction@example.invalid");
29
- writeFileSync(join(cwd, "tracked.txt"), "base\n");
30
- git(cwd, "add", "tracked.txt");
31
- git(cwd, "commit", "-m", "base");
32
- return cwd;
33
- }
34
-
35
- function installHook(cwd: string, name: string, body: string): void {
36
- const hooks = git(cwd, "rev-parse", "--path-format=absolute", "--git-path", "hooks");
37
- mkdirSync(hooks, { recursive: true });
38
- const path = join(hooks, name);
39
- writeFileSync(path, `#!/bin/sh\nset -eu\n${body}\n`);
40
- chmodSync(path, 0o700);
41
- }
42
-
43
- function stage(cwd: string, content = "candidate\n"): string {
44
- writeFileSync(join(cwd, "tracked.txt"), content);
45
- git(cwd, "add", "tracked.txt");
46
- return git(cwd, "write-tree");
47
- }
48
-
49
- function invocation(cwd: string, lineageId: string, arguments_: readonly string[] = ["-m", "candidate"]) {
50
- const intendedTree = git(cwd, "write-tree");
51
- const command = `git commit ${arguments_.map((value) => JSON.stringify(value)).join(" ")}`;
52
- return prepareCommitTransactionInvocation({
53
- command,
54
- cwd,
55
- arguments: arguments_,
56
- authorization: {
57
- lineageId,
58
- storeRevision: "sha256:" + "a".repeat(64),
59
- fingerprint: "sha256:" + "b".repeat(64),
60
- intendedTree,
61
- },
62
- });
63
- }
64
-
65
- function native(cwd: string, lineageId: string, result: NativeValidateResult["result"] = "allow"): NativeReviewCli {
66
- return {
67
- async validate(request) {
68
- const tree = git(cwd, "write-tree");
69
- return {
70
- allowed: result === "allow",
71
- result,
72
- action: result === "allow" ? "continue" : result === "scope-changed" ? "create-new-lineage" : "explicit-maintainer-action",
73
- reason: result === "allow" ? "receipt allows exact tree" : "post-hook tree differs from receipt",
74
- gateContext: {
75
- lineageId,
76
- storeRevision: "sha256:" + "c".repeat(64),
77
- raw: { gate: request.gate, lineage_id: lineageId, candidate_tree: tree },
78
- },
79
- };
80
- },
81
- } as NativeReviewCli;
82
- }
83
-
84
- test("a non-mutating pre-commit hook runs once and HEAD proves the native-authorized tree", async (t) => {
85
- const cwd = repository(t);
86
- stage(cwd);
87
- const count = join(cwd, ".git", "hook-count");
88
- installHook(cwd, "pre-commit", `printf '1\\n' >> ${JSON.stringify(count)}`);
89
- const before = git(cwd, "rev-parse", "HEAD");
90
- const result = await runGitCommitTransaction(invocation(cwd, "non-mutating"), { nativeReviewCli: native(cwd, "non-mutating") });
91
- assert.notEqual(result.head, before);
92
- assert.equal(result.tree, git(cwd, "rev-parse", "HEAD^{tree}"));
93
- assert.equal(readFileSync(count, "utf8"), "1\n");
94
- assert.deepEqual(inspectCommitTransaction(cwd), { status: "clean" });
95
- });
96
-
97
- test("a mutating hook creates no commit until the post-hook tree is reviewed, then exact retry skips the hook", async (t) => {
98
- const cwd = repository(t);
99
- stage(cwd, "unformatted\n");
100
- const count = join(cwd, ".git", "hook-count");
101
- installHook(cwd, "pre-commit", `printf 'formatted\\n' > tracked.txt\ngit add tracked.txt\nprintf '1\\n' >> ${JSON.stringify(count)}`);
102
- const before = git(cwd, "rev-parse", "HEAD");
103
- await assert.rejects(
104
- runGitCommitTransaction(invocation(cwd, "before-format"), { nativeReviewCli: native(cwd, "before-format", "scope-changed") }),
105
- /not the authorized tree/,
106
- );
107
- assert.equal(git(cwd, "rev-parse", "HEAD"), before);
108
- assert.equal(inspectCommitTransaction(cwd).record?.state, COMMIT_TRANSACTION_STATE.AWAITING_REVIEW);
109
- assert.throws(() => assertNoUnresolvedCommitTransaction(cwd), /publication is blocked/);
110
- const result = await runGitCommitTransaction(invocation(cwd, "after-format"), { nativeReviewCli: native(cwd, "after-format") });
111
- assert.equal(result.tree, git(cwd, "rev-parse", "HEAD^{tree}"));
112
- assert.equal(readFileSync(count, "utf8"), "1\n");
113
- });
114
-
115
- test("a failing pre-commit hook creates no commit and leaves explicit recovery state", async (t) => {
116
- const cwd = repository(t);
117
- stage(cwd);
118
- installHook(cwd, "pre-commit", "exit 23");
119
- const before = git(cwd, "rev-parse", "HEAD");
120
- await assert.rejects(runGitCommitTransaction(invocation(cwd, "hook-failure"), { nativeReviewCli: native(cwd, "hook-failure") }), /hook failed/);
121
- assert.equal(git(cwd, "rev-parse", "HEAD"), before);
122
- assert.equal(inspectCommitTransaction(cwd).record?.state, COMMIT_TRANSACTION_STATE.HOOK_FAILED);
123
- assert.match(inspectCommitTransaction(cwd).record?.error ?? "", /exit 23/);
124
- });
125
-
126
- test("a native scope-changed denial on the unmutated authorized tree awaits explicit review", async (t) => {
127
- const cwd = repository(t);
128
- const authorizedTree = stage(cwd);
129
- const before = git(cwd, "rev-parse", "HEAD");
130
- let validations = 0;
131
- const denying = native(cwd, "scope-changed-denial", "scope-changed");
132
- const counting = {
133
- async validate(request) {
134
- validations += 1;
135
- return denying.validate(request);
136
- },
137
- } as NativeReviewCli;
138
- await assert.rejects(
139
- runGitCommitTransaction(invocation(cwd, "scope-changed-denial"), { nativeReviewCli: counting }),
140
- /native pre-commit validation denied the post-hook tree: scope-changed/,
141
- );
142
- assert.equal(validations, 1, "the unmutated tree must pass the mutation guard and reach native validation");
143
- assert.equal(git(cwd, "rev-parse", "HEAD"), before);
144
- assert.equal(git(cwd, "write-tree"), authorizedTree, "the denied index stays exactly as authorized");
145
- const inspection = inspectCommitTransaction(cwd);
146
- assert.equal(inspection.record?.state, COMMIT_TRANSACTION_STATE.AWAITING_REVIEW);
147
- assert.equal(inspection.record?.error, "post-hook tree differs from receipt");
148
- assert.equal(typeof inspection.record?.native_result, "object", "the native denial must be recorded on the transaction");
149
- });
150
-
151
- test("a non-scope-changed native denial fails validation and requires explicit recovery", async (t) => {
152
- const cwd = repository(t);
153
- stage(cwd);
154
- const before = git(cwd, "rev-parse", "HEAD");
155
- await assert.rejects(
156
- runGitCommitTransaction(invocation(cwd, "invalidated-denial"), { nativeReviewCli: native(cwd, "invalidated-denial", "invalidated") }),
157
- /native pre-commit validation denied the post-hook tree: invalidated/,
158
- );
159
- assert.equal(git(cwd, "rev-parse", "HEAD"), before);
160
- assert.equal(inspectCommitTransaction(cwd).record?.state, COMMIT_TRANSACTION_STATE.VALIDATION_FAILED);
161
- await assert.rejects(
162
- runGitCommitTransaction(invocation(cwd, "invalidated-denial"), { nativeReviewCli: native(cwd, "invalidated-denial") }),
163
- /requires explicit recovery from state validation-failed/,
164
- );
165
- });
166
-
167
- test("a mutating pre-commit hook fails closed even when native validation would allow the post-hook tree", async (t) => {
168
- const cwd = repository(t);
169
- const authorizedTree = stage(cwd, "unformatted\n");
170
- const count = join(cwd, ".git", "hook-count");
171
- installHook(cwd, "pre-commit", `printf 'formatted\\n' > tracked.txt\ngit add tracked.txt\nprintf '1\\n' >> ${JSON.stringify(count)}`);
172
- const before = git(cwd, "rev-parse", "HEAD");
173
- // The fake native CLI allows whatever tree the index holds, so the only
174
- // defense against committing the hook-mutated tree is the transaction's
175
- // own binding to the invocation's authorized tree.
176
- await assert.rejects(
177
- runGitCommitTransaction(invocation(cwd, "permissive-native"), { nativeReviewCli: native(cwd, "permissive-native") }),
178
- /pre-commit hook mutated the staged candidate/,
179
- );
180
- assert.equal(git(cwd, "rev-parse", "HEAD"), before, "no commit object may be created from the mutated index");
181
- assert.equal(readFileSync(count, "utf8"), "1\n", "the mutating hook must have run exactly once");
182
- assert.notEqual(git(cwd, "write-tree"), authorizedTree, "the mutated index is preserved for inspection");
183
- const inspection = inspectCommitTransaction(cwd);
184
- assert.equal(inspection.record?.state, COMMIT_TRANSACTION_STATE.AWAITING_REVIEW);
185
- assert.match(inspection.record?.error ?? "", /normalize sources and re-run review explicitly, or make the hook convergent/);
186
- assert.throws(() => assertNoUnresolvedCommitTransaction(cwd), /publication is blocked/);
187
- });
188
-
189
- test("a convergent pre-commit hook runs exactly once and commits the exact authorized tree", async (t) => {
190
- const cwd = repository(t);
191
- const authorizedTree = stage(cwd, "formatted\n");
192
- const count = join(cwd, ".git", "hook-count");
193
- installHook(cwd, "pre-commit", `printf 'formatted\\n' > tracked.txt\ngit add tracked.txt\nprintf '1\\n' >> ${JSON.stringify(count)}`);
194
- const before = git(cwd, "rev-parse", "HEAD");
195
- const result = await runGitCommitTransaction(invocation(cwd, "convergent"), { nativeReviewCli: native(cwd, "convergent") });
196
- assert.notEqual(result.head, before);
197
- assert.equal(result.tree, authorizedTree);
198
- assert.equal(git(cwd, "rev-parse", "HEAD^{tree}"), authorizedTree);
199
- assert.equal(readFileSync(count, "utf8"), "1\n", "the convergent hook must have run exactly once");
200
- assert.deepEqual(inspectCommitTransaction(cwd), { status: "clean" });
201
- });
202
-
203
- test("a repository with no hooks commits the authorized tree unchanged", async (t) => {
204
- const cwd = repository(t);
205
- const authorizedTree = stage(cwd);
206
- const before = git(cwd, "rev-parse", "HEAD");
207
- const result = await runGitCommitTransaction(invocation(cwd, "no-hooks"), { nativeReviewCli: native(cwd, "no-hooks") });
208
- assert.notEqual(result.head, before);
209
- assert.equal(result.tree, authorizedTree);
210
- assert.equal(git(cwd, "rev-parse", "HEAD^{tree}"), authorizedTree);
211
- assert.deepEqual(inspectCommitTransaction(cwd), { status: "clean" });
212
- });
213
-
214
- test("the Git-created HEAD tree is proven equal to the authorized tree on success", async (t) => {
215
- const cwd = repository(t);
216
- const authorizedTree = stage(cwd);
217
- const result = await runGitCommitTransaction(invocation(cwd, "head-proof"), { nativeReviewCli: native(cwd, "head-proof") });
218
- assert.equal(result.tree, authorizedTree);
219
- assert.equal(git(cwd, "rev-parse", "HEAD^{tree}"), authorizedTree);
220
- const verified = verifyCommitTransactionResult(cwd, result.transactionId);
221
- assert.deepEqual(verified, { transactionId: result.transactionId, status: "committed", head: result.head, tree: authorizedTree });
222
- });
223
-
224
- test("message hooks cannot change the index after native authorization", async (t) => {
225
- const cwd = repository(t);
226
- stage(cwd);
227
- installHook(cwd, "prepare-commit-msg", "printf 'late mutation\\n' > tracked.txt\ngit add tracked.txt");
228
- const before = git(cwd, "rev-parse", "HEAD");
229
- await assert.rejects(runGitCommitTransaction(invocation(cwd, "late-mutation"), { nativeReviewCli: native(cwd, "late-mutation") }), /Git commit failed/);
230
- assert.equal(git(cwd, "rev-parse", "HEAD"), before);
231
- assert.equal(inspectCommitTransaction(cwd).record?.state, COMMIT_TRANSACTION_STATE.COMMIT_FAILED);
232
- });
233
-
234
- test("amend and post-commit crash reconciliation preserve the exact authorized tree", async (t) => {
235
- const cwd = repository(t);
236
- const authorizedTree = stage(cwd, "amended\n");
237
- const amended = await runGitCommitTransaction(invocation(cwd, "amend", ["--amend", "--no-edit"]), { nativeReviewCli: native(cwd, "amend") });
238
- assert.equal(amended.tree, authorizedTree);
239
- stage(cwd, "after-crash\n");
240
- await assert.rejects(
241
- runGitCommitTransaction(invocation(cwd, "crash"), { nativeReviewCli: native(cwd, "crash"), failpoint: "after-commit-before-proof" }),
242
- /test interruption/,
243
- );
244
- assert.equal(inspectCommitTransaction(cwd).record?.state, COMMIT_TRANSACTION_STATE.COMMIT_RUNNING);
245
- assert.deepEqual(reconcileCommitTransaction(cwd), { status: "clean" });
246
- assert.deepEqual(inspectCommitTransaction(cwd), { status: "clean" });
247
- });
248
-
249
- test("a post-commit hook cannot replace the exact commit created by Git", async (t) => {
250
- const cwd = repository(t);
251
- stage(cwd, "intermediate\n");
252
- git(cwd, "commit", "-m", "intermediate");
253
- stage(cwd);
254
- installHook(cwd, "post-commit", [
255
- "original=$(git rev-parse HEAD)",
256
- "tree=$(git rev-parse HEAD^{tree})",
257
- "alternate_parent=$(git rev-parse HEAD~2)",
258
- "replacement=$(printf 'replacement\\n' | git commit-tree \"$tree\" -p \"$alternate_parent\")",
259
- "git update-ref HEAD \"$replacement\" \"$original\"",
260
- ].join("\n"));
261
- await assert.rejects(
262
- runGitCommitTransaction(invocation(cwd, "post-commit-replacement"), { nativeReviewCli: native(cwd, "post-commit-replacement") }),
263
- /different commit|identity changed/,
264
- );
265
- assert.equal(inspectCommitTransaction(cwd).record?.state, COMMIT_TRANSACTION_STATE.INCIDENT);
266
- });
267
-
268
- test("cancellation cannot strand a commit after HEAD advances", async (t) => {
269
- const cwd = repository(t);
270
- stage(cwd);
271
- // Deterministic cancellation ordering (issue #178): a wall-clock
272
- // AbortSignal.timeout raced Git's own progress on loaded CI runners and
273
- // could abort before HEAD advanced. Git only runs the post-commit hook
274
- // after the commit object exists and HEAD has advanced, so the hook
275
- // reports that it started and then blocks until the test releases it.
276
- // Aborting between those two events guarantees the cancellation lands
277
- // while the commit process is still running and strictly after HEAD
278
- // advanced, on every runner.
279
- const started = join(cwd, ".git", "post-commit-started");
280
- const release = join(cwd, ".git", "post-commit-release");
281
- installHook(cwd, "post-commit", [
282
- `printf '1\\n' > ${JSON.stringify(started)}`,
283
- `while [ ! -e ${JSON.stringify(release)} ]; do sleep 0.02; done`,
284
- ].join("\n"));
285
- const before = git(cwd, "rev-parse", "HEAD");
286
- const abort = new AbortController();
287
- const pending = runGitCommitTransaction(invocation(cwd, "commit-cancellation"), {
288
- nativeReviewCli: native(cwd, "commit-cancellation"),
289
- signal: abort.signal,
290
- });
291
- let settled = false;
292
- void pending.then(() => { settled = true; }, () => { settled = true; });
293
- while (!existsSync(started) && !settled) await delay(10);
294
- assert.equal(settled, false, "commit transaction finished before the post-commit hook confirmed HEAD advanced");
295
- abort.abort();
296
- writeFileSync(release, "release\n");
297
- const result = await pending;
298
- assert.equal(abort.signal.aborted, true);
299
- assert.notEqual(result.head, before);
300
- assert.equal(result.status, "committed");
301
- assert.deepEqual(inspectCommitTransaction(cwd), { status: "clean" });
302
- });