approval-md 0.4.5 → 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 (83) hide show
  1. package/SPEC.md +24 -3
  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 +180 -33
  6. package/dist/src/channels/telegram.js +256 -87
  7. package/dist/src/channels/telegram.js.map +1 -1
  8. package/dist/src/cli/channel-telegram.d.ts +114 -35
  9. package/dist/src/cli/channel-telegram.js +259 -120
  10. package/dist/src/cli/channel-telegram.js.map +1 -1
  11. package/dist/src/cli/doctor.js +33 -13
  12. package/dist/src/cli/doctor.js.map +1 -1
  13. package/dist/src/cli/help.d.ts +2 -0
  14. package/dist/src/cli/help.js +39 -3
  15. package/dist/src/cli/help.js.map +1 -1
  16. package/dist/src/cli/hook-exec-code.d.ts +150 -0
  17. package/dist/src/cli/hook-exec-code.js +357 -0
  18. package/dist/src/cli/hook-exec-code.js.map +1 -0
  19. package/dist/src/cli/hook.d.ts +154 -1
  20. package/dist/src/cli/hook.js +2344 -111
  21. package/dist/src/cli/hook.js.map +1 -1
  22. package/dist/src/cli/instructions.js +9 -6
  23. package/dist/src/cli/instructions.js.map +1 -1
  24. package/dist/src/cli/main.js +9 -0
  25. package/dist/src/cli/main.js.map +1 -1
  26. package/dist/src/cli/study.d.ts +22 -0
  27. package/dist/src/cli/study.js +153 -0
  28. package/dist/src/cli/study.js.map +1 -0
  29. package/dist/src/cli/verb-registry.d.ts +14 -0
  30. package/dist/src/cli/verb-registry.js +37 -0
  31. package/dist/src/cli/verb-registry.js.map +1 -1
  32. package/dist/src/cli/wordmark.d.ts +2 -2
  33. package/dist/src/cli/wordmark.js +2 -2
  34. package/dist/src/core/command-class.d.ts +349 -1
  35. package/dist/src/core/command-class.js +2347 -87
  36. package/dist/src/core/command-class.js.map +1 -1
  37. package/dist/src/core/exec-code.d.ts +77 -0
  38. package/dist/src/core/exec-code.js +60 -0
  39. package/dist/src/core/exec-code.js.map +1 -0
  40. package/dist/src/core/gate.d.ts +38 -0
  41. package/dist/src/core/gate.js +26 -0
  42. package/dist/src/core/gate.js.map +1 -1
  43. package/dist/src/core/git-push-argv.d.ts +111 -0
  44. package/dist/src/core/git-push-argv.js +375 -0
  45. package/dist/src/core/git-push-argv.js.map +1 -0
  46. package/dist/src/core/harness-version.d.ts +71 -30
  47. package/dist/src/core/harness-version.js +101 -34
  48. package/dist/src/core/harness-version.js.map +1 -1
  49. package/dist/src/core/policy-load.d.ts +8 -0
  50. package/dist/src/core/policy-load.js.map +1 -1
  51. package/dist/src/core/sender-identity.d.ts +13 -1
  52. package/dist/src/core/sender-identity.js +15 -0
  53. package/dist/src/core/sender-identity.js.map +1 -1
  54. package/dist/src/core/study-withdraw.d.ts +137 -0
  55. package/dist/src/core/study-withdraw.js +280 -0
  56. package/dist/src/core/study-withdraw.js.map +1 -0
  57. package/dist/src/daemon/git-evidence.d.ts +2 -2
  58. package/dist/src/daemon/git-evidence.js +1 -1
  59. package/dist/src/mcp/server.js +1 -1
  60. package/dist/src/mcp/server.js.map +1 -1
  61. package/docs/README-extended.md +3 -2
  62. package/docs/cli-reference.md +83 -7
  63. package/package.json +2 -2
  64. package/schema/event.schema.json +213 -1
  65. package/schema/fixtures/event/invalid/approval-granted-study.json +23 -0
  66. package/schema/fixtures/event/invalid/approval-rejected-looks-wrong-cli-channel.json +23 -0
  67. package/schema/fixtures/event/invalid/approval-rejected-looks-wrong-operator.json +24 -0
  68. package/schema/fixtures/event/invalid/approval-rejected-study-extra-key.json +24 -0
  69. package/schema/fixtures/event/invalid/approval-rejected-study-unknown-reason.json +23 -0
  70. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-email-operator.json +20 -0
  71. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-no-operator.json +19 -0
  72. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-no-ref.json +19 -0
  73. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-wrong-actor.json +20 -0
  74. package/schema/fixtures/event/invalid/approval-rejected-study-withdrawn-wrong-channel.json +20 -0
  75. package/schema/fixtures/event/invalid/approval-revoked-study.json +20 -0
  76. package/schema/fixtures/event/valid/approval-rejected-looks-wrong.json +23 -0
  77. package/schema/fixtures/event/valid/approval-rejected-study-withdrawn.json +20 -0
  78. package/schema/fixtures/policy/invalid/exec-code-custody-boolean.json +11 -0
  79. package/schema/fixtures/policy/invalid/exec-code-execute-code-value.json +11 -0
  80. package/schema/fixtures/policy/invalid/exec-code-unknown-key.json +12 -0
  81. package/schema/fixtures/policy/valid/exec-code-both.json +17 -0
  82. package/schema/fixtures/policy/valid/exec-code-custody.json +11 -0
  83. package/schema/policy.schema.json +23 -0
@@ -136,7 +136,7 @@
136
136
  "minLength": 1,
137
137
  "maxLength": 64,
138
138
  "pattern": "^[\\u0020-\\u007e]+$",
139
- "description": "SPEC.md §10.1 (amended, APRV-227): the version of the harness binary that issued this record, taken from the hook event where the harness supplies one and otherwise from `<binary> --version`, normalized to its first line. SELF-REPORTED, and informational only: nothing in the runtime reads it back as an input to a verdict, a floor, a budget, a streak or a sampling draw, so it satisfies SPEC.md §11.1 invariant 4 by having no path along which it could reduce scrutiny — its single reader is a doctor row that can only ADD a red line. Its SHAPE is constrained here because the value is the output of a third-party process being written to an append-only log: one line, printable ASCII, capped at 64 characters, so a banner or a stack trace (and whatever a stack trace quotes) cannot arrive through it. §11.1 invariant 3 has no exception for provenance. OPTIONAL and additive."
139
+ "description": "SPEC.md §10.1 (amended, APRV-227): the version of the harness binary that issued this record, taken from the hook event where the harness supplies one and otherwise from `<binary> --version`, normalized to its first line. SELF-REPORTED, and informational only: nothing in the runtime reads it back as an input to a verdict, a floor, a budget, a streak or a sampling draw, so it satisfies SPEC.md §11.1 invariant 4 by having no path along which it could reduce scrutiny — its single reader is a doctor row that can only ADD a red line. Its SHAPE is constrained here because the value is the output of a third-party process being written to an append-only log: one line, printable ASCII, capped at 64 characters, so a banner or a stack trace (and whatever a stack trace quotes) cannot arrive through it. §11.1 invariant 3 has no exception for provenance. APRV-419 chose option (c) in its no-schema form: a writer records the first line's printable-ASCII PREFIX, trimmed, cutting everything from the first character outside U+0020..U+007E, where it used to refuse the whole line. That is why a Hermes record carries `Hermes Agent v0.21.3 (2026.9.14)` from the line `Hermes Agent v0.21.3 (2026.9.14) \u00b7 upstream 913d4098`. Every value the prefix rule produces already satisfied this pattern and cap, so neither moved and no record changes validity. OPTIONAL and additive."
140
140
  },
141
141
  "est_cost_usd": {
142
142
  "$ref": "#/$defs/usd_amount",
@@ -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
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": "0.1",
3
+ "defaults": {
4
+ "autonomy": "manual"
5
+ },
6
+ "exec": {
7
+ "code": {
8
+ "custody": true
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": "0.1",
3
+ "defaults": {
4
+ "autonomy": "manual"
5
+ },
6
+ "exec": {
7
+ "code": {
8
+ "execute_code": "yes"
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": "0.1",
3
+ "defaults": {
4
+ "autonomy": "manual"
5
+ },
6
+ "exec": {
7
+ "code": {
8
+ "custody": "attested",
9
+ "inline": "on"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "version": "0.1",
3
+ "defaults": {
4
+ "autonomy": "manual"
5
+ },
6
+ "exec": {
7
+ "code": {
8
+ "custody": "attested",
9
+ "execute_code": "hooked"
10
+ }
11
+ },
12
+ "classes": {
13
+ "exec.code": {
14
+ "autonomy": "autonomous"
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": "0.1",
3
+ "defaults": {
4
+ "autonomy": "manual"
5
+ },
6
+ "exec": {
7
+ "code": {
8
+ "custody": "attested"
9
+ }
10
+ }
11
+ }
@@ -197,6 +197,29 @@
197
197
  }
198
198
  ]
199
199
  },
200
+ "exec": {
201
+ "type": "object",
202
+ "additionalProperties": false,
203
+ "description": "SPEC.md §5.2 and §7 (APRV-510, proposed; 0.4.5 candidate): what an operator has attested about opaque agent code. Absent, every policy behaves exactly as before the key existed: `python3 -c`, `node -e` and kin are refused `hook-opaque`, an interpreter running a file that is not a skill script is refused `hook-unclassified`, and a Hermes `execute_code` call is refused `hook-hermes-execute-code-unbound`. A core that predates this key refuses it at the schema, and the whole policy then fails closed (every class `manual`), so a tenant receives this block only after its pinned core knows it.",
204
+ "properties": {
205
+ "code": {
206
+ "type": "object",
207
+ "additionalProperties": false,
208
+ "description": "The `exec.code` class (APRV-510). Each key opens one thing and only the attested policy can set it; an absent key is the closed answer. With `custody: attested`, an inline program or a non-skill interpreter file run is classified `exec.code` and resolved by the policy like any class (an `exec.code` row, an `exec.*` family row, or `defaults.autonomy`), and a record of it binds the program's SHA-256 and length, never its text. The record says the program existed; it authorizes nothing the program then does through other tools, each of which is its own hook event.",
209
+ "properties": {
210
+ "custody": {
211
+ "enum": ["attested", "unattested"],
212
+ "description": "`attested`: the operator states that credential custody is in place for this agent (the code it runs cannot read credentials it should not hold), which is the precondition for opening the class at all, because opaque code run without it bypasses every gate that guards a credential. `unattested`, or the key absent: the class stays closed, and where the policy names `exec.code` anywhere the refusal is `hook-exec-code-custody-unattested`, naming this key."
213
+ },
214
+ "execute_code": {
215
+ "enum": ["hooked", "unhooked"],
216
+ "description": "Hermes `execute_code` only, and only beside `custody: attested`. `hooked`: the operator has observed that a tool call made from inside the `execute_code` kernel re-fires the hook (the lane L1 probe's answer), so the call is classified `exec.code` with the program's hash bound. `unhooked`, or the key absent: `execute_code` is refused `hook-hermes-execute-code-unbound` as before. Set it only on the probe's yes: if inner calls do not re-fire the hook, one `execute_code` call is an unbounded bypass of the whole adapter."
217
+ }
218
+ }
219
+ }
220
+ },
221
+ "examples": [{ "code": { "custody": "attested" } }, { "code": { "custody": "attested", "execute_code": "hooked" } }]
222
+ },
200
223
  "read_scope": {
201
224
  "type": "object",
202
225
  "additionalProperties": false,