approval-md 0.4.6 → 0.4.7

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 (57) hide show
  1. package/SPEC.md +11 -1
  2. package/dist/src/channels/contract.d.ts +15 -0
  3. package/dist/src/channels/contract.js +23 -1
  4. package/dist/src/channels/contract.js.map +1 -1
  5. package/dist/src/channels/telegram.d.ts +75 -2
  6. package/dist/src/channels/telegram.js +99 -6
  7. package/dist/src/channels/telegram.js.map +1 -1
  8. package/dist/src/cli/channel-telegram.d.ts +14 -0
  9. package/dist/src/cli/channel-telegram.js +44 -2
  10. package/dist/src/cli/channel-telegram.js.map +1 -1
  11. package/dist/src/cli/help.d.ts +2 -0
  12. package/dist/src/cli/help.js +36 -0
  13. package/dist/src/cli/help.js.map +1 -1
  14. package/dist/src/cli/hook.d.ts +29 -0
  15. package/dist/src/cli/hook.js +178 -15
  16. package/dist/src/cli/hook.js.map +1 -1
  17. package/dist/src/cli/instructions.js +9 -6
  18. package/dist/src/cli/instructions.js.map +1 -1
  19. package/dist/src/cli/main.js +9 -0
  20. package/dist/src/cli/main.js.map +1 -1
  21. package/dist/src/cli/study.d.ts +22 -0
  22. package/dist/src/cli/study.js +153 -0
  23. package/dist/src/cli/study.js.map +1 -0
  24. package/dist/src/cli/verb-registry.d.ts +14 -0
  25. package/dist/src/cli/verb-registry.js +37 -0
  26. package/dist/src/cli/verb-registry.js.map +1 -1
  27. package/dist/src/cli/wordmark.d.ts +2 -2
  28. package/dist/src/cli/wordmark.js +2 -2
  29. package/dist/src/core/gate.d.ts +38 -0
  30. package/dist/src/core/gate.js +26 -0
  31. package/dist/src/core/gate.js.map +1 -1
  32. package/dist/src/core/sender-identity.d.ts +13 -1
  33. package/dist/src/core/sender-identity.js +15 -0
  34. package/dist/src/core/sender-identity.js.map +1 -1
  35. package/dist/src/core/study-withdraw.d.ts +137 -0
  36. package/dist/src/core/study-withdraw.js +280 -0
  37. package/dist/src/core/study-withdraw.js.map +1 -0
  38. package/dist/src/daemon/git-evidence.d.ts +2 -2
  39. package/dist/src/daemon/git-evidence.js +1 -1
  40. package/dist/src/mcp/server.js +1 -1
  41. package/dist/src/mcp/server.js.map +1 -1
  42. package/docs/cli-reference.md +63 -0
  43. package/package.json +2 -2
  44. package/schema/event.schema.json +212 -0
  45. package/schema/fixtures/event/invalid/approval-granted-study.json +23 -0
  46. package/schema/fixtures/event/invalid/approval-rejected-looks-wrong-cli-channel.json +23 -0
  47. package/schema/fixtures/event/invalid/approval-rejected-looks-wrong-operator.json +24 -0
  48. package/schema/fixtures/event/invalid/approval-rejected-study-extra-key.json +24 -0
  49. package/schema/fixtures/event/invalid/approval-rejected-study-unknown-reason.json +23 -0
  50. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-email-operator.json +20 -0
  51. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-no-operator.json +19 -0
  52. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-no-ref.json +19 -0
  53. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-wrong-actor.json +20 -0
  54. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-wrong-channel.json +20 -0
  55. package/schema/fixtures/event/invalid/approval-revoked-study.json +20 -0
  56. package/schema/fixtures/event/valid/approval-rejected-looks-wrong.json +23 -0
  57. package/schema/fixtures/event/valid/approval-rejected-study-withdrawn.json +20 -0
@@ -2750,6 +2750,218 @@
2750
2750
  }
2751
2751
  }
2752
2752
  }
2753
+ },
2754
+ {
2755
+ "$comment": "SPEC.md §8 (amended, the study record): `payload.study` says why a study denied a request, and a study may only deny. So the field is admitted on `approval.rejected` alone and refused on every other event, `approval.granted`, `approval.revoked` and `approval.withdrawn` among them: a grant carrying it would read as a study approving something, which no study surface can do.",
2756
+ "if": {
2757
+ "type": "object",
2758
+ "properties": {
2759
+ "payload": {
2760
+ "type": "object",
2761
+ "properties": {
2762
+ "study": true
2763
+ },
2764
+ "required": [
2765
+ "study"
2766
+ ]
2767
+ }
2768
+ },
2769
+ "required": [
2770
+ "payload"
2771
+ ]
2772
+ },
2773
+ "then": {
2774
+ "type": "object",
2775
+ "properties": {
2776
+ "event": {
2777
+ "const": "approval.rejected"
2778
+ }
2779
+ },
2780
+ "required": [
2781
+ "event"
2782
+ ]
2783
+ }
2784
+ },
2785
+ {
2786
+ "$comment": "SPEC.md §8 (amended, the study record): the shape of `payload.study` on `approval.rejected`, and the record fields each reason binds. Closed, with two reasons. `study_withdrawn` is a hosted operator denying a still-pending request: it names the operator (`operator`, 8 hex of the SHA-256 of the lower-cased staff email, never the email) and the withdraw's replay reference (`ref`), and the record is written under `human:study-team` on channel `study`, so a reader can always tell it from a resident's answer. `looks_wrong` is the resident's own deny with a flag: it names no operator and no reference, and it arrives on `telegram`, the one channel that draws the button. No enforcement path reads this field; a reader that ignores it reads an ordinary rejection.",
2787
+ "if": {
2788
+ "type": "object",
2789
+ "properties": {
2790
+ "event": {
2791
+ "const": "approval.rejected"
2792
+ },
2793
+ "payload": {
2794
+ "type": "object",
2795
+ "properties": {
2796
+ "study": true
2797
+ },
2798
+ "required": [
2799
+ "study"
2800
+ ]
2801
+ }
2802
+ },
2803
+ "required": [
2804
+ "event",
2805
+ "payload"
2806
+ ]
2807
+ },
2808
+ "then": {
2809
+ "type": "object",
2810
+ "properties": {
2811
+ "payload": {
2812
+ "type": "object",
2813
+ "properties": {
2814
+ "study": {
2815
+ "type": "object",
2816
+ "additionalProperties": false,
2817
+ "required": [
2818
+ "reason"
2819
+ ],
2820
+ "properties": {
2821
+ "reason": {
2822
+ "type": "string",
2823
+ "enum": [
2824
+ "study_withdrawn",
2825
+ "looks_wrong"
2826
+ ],
2827
+ "description": "SPEC.md §8 (amended): why the study denied. `study_withdrawn`: a hosted operator withdrew the pending request before it could run. `looks_wrong`: the resident flagged the card as a suspected study card and skipped it."
2828
+ },
2829
+ "operator": {
2830
+ "type": "string",
2831
+ "pattern": "^[0-9a-f]{8}$",
2832
+ "description": "The first 8 hex of the SHA-256 of the withdrawing operator's lower-cased staff email. Derivable from the staff list and never the email itself. `study_withdrawn` only."
2833
+ },
2834
+ "ref": {
2835
+ "type": "string",
2836
+ "pattern": "^[A-Za-z0-9_-]{16,64}$",
2837
+ "description": "The withdraw's replay reference, minted once by the operator's tooling; the log is its idempotency authority. `study_withdrawn` only."
2838
+ }
2839
+ },
2840
+ "allOf": [
2841
+ {
2842
+ "if": {
2843
+ "type": "object",
2844
+ "properties": {
2845
+ "reason": {
2846
+ "const": "study_withdrawn"
2847
+ }
2848
+ },
2849
+ "required": [
2850
+ "reason"
2851
+ ]
2852
+ },
2853
+ "then": {
2854
+ "type": "object",
2855
+ "properties": {
2856
+ "operator": true,
2857
+ "ref": true
2858
+ },
2859
+ "required": [
2860
+ "operator",
2861
+ "ref"
2862
+ ]
2863
+ }
2864
+ },
2865
+ {
2866
+ "if": {
2867
+ "type": "object",
2868
+ "properties": {
2869
+ "reason": {
2870
+ "const": "looks_wrong"
2871
+ }
2872
+ },
2873
+ "required": [
2874
+ "reason"
2875
+ ]
2876
+ },
2877
+ "then": {
2878
+ "type": "object",
2879
+ "properties": {
2880
+ "operator": false,
2881
+ "ref": false
2882
+ }
2883
+ }
2884
+ }
2885
+ ]
2886
+ }
2887
+ }
2888
+ }
2889
+ },
2890
+ "allOf": [
2891
+ {
2892
+ "if": {
2893
+ "type": "object",
2894
+ "properties": {
2895
+ "payload": {
2896
+ "type": "object",
2897
+ "properties": {
2898
+ "study": {
2899
+ "type": "object",
2900
+ "properties": {
2901
+ "reason": {
2902
+ "const": "study_withdrawn"
2903
+ }
2904
+ },
2905
+ "required": [
2906
+ "reason"
2907
+ ]
2908
+ }
2909
+ }
2910
+ }
2911
+ }
2912
+ },
2913
+ "then": {
2914
+ "type": "object",
2915
+ "properties": {
2916
+ "actor": {
2917
+ "const": "human:study-team"
2918
+ },
2919
+ "channel": {
2920
+ "const": "study"
2921
+ }
2922
+ },
2923
+ "required": [
2924
+ "actor",
2925
+ "channel"
2926
+ ]
2927
+ }
2928
+ },
2929
+ {
2930
+ "if": {
2931
+ "type": "object",
2932
+ "properties": {
2933
+ "payload": {
2934
+ "type": "object",
2935
+ "properties": {
2936
+ "study": {
2937
+ "type": "object",
2938
+ "properties": {
2939
+ "reason": {
2940
+ "const": "looks_wrong"
2941
+ }
2942
+ },
2943
+ "required": [
2944
+ "reason"
2945
+ ]
2946
+ }
2947
+ }
2948
+ }
2949
+ }
2950
+ },
2951
+ "then": {
2952
+ "type": "object",
2953
+ "properties": {
2954
+ "channel": {
2955
+ "const": "telegram"
2956
+ }
2957
+ },
2958
+ "required": [
2959
+ "channel"
2960
+ ]
2961
+ }
2962
+ }
2963
+ ]
2964
+ }
2753
2965
  }
2754
2966
  ]
2755
2967
  }
@@ -0,0 +1,23 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.granted",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:carter",
8
+ "channel": "telegram",
9
+ "payload": {
10
+ "note": "approved via telegram",
11
+ "sender": {
12
+ "channel": "telegram",
13
+ "id": "42"
14
+ },
15
+ "sender_source": "policy",
16
+ "study": {
17
+ "reason": "looks_wrong"
18
+ }
19
+ },
20
+ "alg": "sha256/jcs",
21
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
22
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
23
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:carter",
8
+ "channel": "cli",
9
+ "payload": {
10
+ "note": "rejected via telegram (callback cb-1)",
11
+ "sender": {
12
+ "channel": "telegram",
13
+ "id": "42"
14
+ },
15
+ "sender_source": "policy",
16
+ "study": {
17
+ "reason": "looks_wrong"
18
+ }
19
+ },
20
+ "alg": "sha256/jcs",
21
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
22
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
23
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:carter",
8
+ "channel": "telegram",
9
+ "payload": {
10
+ "note": "rejected via telegram (callback cb-1)",
11
+ "sender": {
12
+ "channel": "telegram",
13
+ "id": "42"
14
+ },
15
+ "sender_source": "policy",
16
+ "study": {
17
+ "reason": "looks_wrong",
18
+ "operator": "3f9a01c2"
19
+ }
20
+ },
21
+ "alg": "sha256/jcs",
22
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
23
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:carter",
8
+ "channel": "telegram",
9
+ "payload": {
10
+ "note": "rejected via telegram (callback cb-1)",
11
+ "sender": {
12
+ "channel": "telegram",
13
+ "id": "42"
14
+ },
15
+ "sender_source": "policy",
16
+ "study": {
17
+ "reason": "looks_wrong",
18
+ "card": "study-card-7"
19
+ }
20
+ },
21
+ "alg": "sha256/jcs",
22
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
23
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
24
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:carter",
8
+ "channel": "telegram",
9
+ "payload": {
10
+ "note": "rejected via telegram (callback cb-1)",
11
+ "sender": {
12
+ "channel": "telegram",
13
+ "id": "42"
14
+ },
15
+ "sender_source": "policy",
16
+ "study": {
17
+ "reason": "suspicious"
18
+ }
19
+ },
20
+ "alg": "sha256/jcs",
21
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
22
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
23
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:study-team",
8
+ "channel": "study",
9
+ "payload": {
10
+ "note": "withdrawn by the study team",
11
+ "study": {
12
+ "reason": "study_withdrawn",
13
+ "operator": "ops@example.org",
14
+ "ref": "wd_7Hq2LmX9pR4sT1vB"
15
+ }
16
+ },
17
+ "alg": "sha256/jcs",
18
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
19
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
20
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:study-team",
8
+ "channel": "study",
9
+ "payload": {
10
+ "note": "withdrawn by the study team",
11
+ "study": {
12
+ "reason": "study_withdrawn",
13
+ "ref": "wd_7Hq2LmX9pR4sT1vB"
14
+ }
15
+ },
16
+ "alg": "sha256/jcs",
17
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
18
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
19
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:study-team",
8
+ "channel": "study",
9
+ "payload": {
10
+ "note": "withdrawn by the study team",
11
+ "study": {
12
+ "reason": "study_withdrawn",
13
+ "operator": "3f9a01c2"
14
+ }
15
+ },
16
+ "alg": "sha256/jcs",
17
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
18
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
19
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:carter",
8
+ "channel": "study",
9
+ "payload": {
10
+ "note": "withdrawn by the study team",
11
+ "study": {
12
+ "reason": "study_withdrawn",
13
+ "operator": "3f9a01c2",
14
+ "ref": "wd_7Hq2LmX9pR4sT1vB"
15
+ }
16
+ },
17
+ "alg": "sha256/jcs",
18
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
19
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:study-team",
8
+ "channel": "telegram",
9
+ "payload": {
10
+ "note": "withdrawn by the study team",
11
+ "study": {
12
+ "reason": "study_withdrawn",
13
+ "operator": "3f9a01c2",
14
+ "ref": "wd_7Hq2LmX9pR4sT1vB"
15
+ }
16
+ },
17
+ "alg": "sha256/jcs",
18
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
19
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.revoked",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:carter",
8
+ "channel": "cli",
9
+ "payload": {
10
+ "note": "withdrawn by the study team",
11
+ "study": {
12
+ "reason": "study_withdrawn",
13
+ "operator": "3f9a01c2",
14
+ "ref": "wd_7Hq2LmX9pR4sT1vB"
15
+ }
16
+ },
17
+ "alg": "sha256/jcs",
18
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
19
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
20
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:carter",
8
+ "channel": "telegram",
9
+ "payload": {
10
+ "note": "rejected via telegram (callback cb-1)",
11
+ "sender": {
12
+ "channel": "telegram",
13
+ "id": "42"
14
+ },
15
+ "sender_source": "policy",
16
+ "study": {
17
+ "reason": "looks_wrong"
18
+ }
19
+ },
20
+ "alg": "sha256/jcs",
21
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
22
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
23
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "seq": 9,
3
+ "ts": "2026-10-20T09:16:02Z",
4
+ "event": "approval.rejected",
5
+ "task": "task-study",
6
+ "action_key": "task-study:send:2026-10-20",
7
+ "actor": "human:study-team",
8
+ "channel": "study",
9
+ "payload": {
10
+ "note": "withdrawn by the study team",
11
+ "study": {
12
+ "reason": "study_withdrawn",
13
+ "operator": "3f9a01c2",
14
+ "ref": "wd_7Hq2LmX9pR4sT1vB"
15
+ }
16
+ },
17
+ "alg": "sha256/jcs",
18
+ "prev": "bcd21fd125d9c85cc4b4eb1ff519448989c72c0fd70305cbb06b821d04926686",
19
+ "hash": "5b1e0c7d2a94f3e8b6c1d0a7f5e2c9b84d3a6f1e0c7b2d9a8e5f4c3b2a1d0e9f"
20
+ }