approval-md 0.2.0 → 0.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.
- package/README.md +63 -24
- package/SPEC.md +57 -11
- package/dist/src/channels/contract.d.ts +34 -1
- package/dist/src/channels/contract.js +200 -7
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/telegram.d.ts +123 -11
- package/dist/src/channels/telegram.js +218 -23
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/channels/web.d.ts +9 -0
- package/dist/src/channels/web.js +17 -0
- package/dist/src/channels/web.js.map +1 -1
- package/dist/src/cli/amend.js +214 -30
- package/dist/src/cli/amend.js.map +1 -1
- package/dist/src/cli/attest.d.ts +9 -0
- package/dist/src/cli/attest.js +134 -7
- package/dist/src/cli/attest.js.map +1 -1
- package/dist/src/cli/channel-telegram.d.ts +99 -26
- package/dist/src/cli/channel-telegram.js +311 -13
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/channel.d.ts +9 -0
- package/dist/src/cli/channel.js +9 -0
- package/dist/src/cli/channel.js.map +1 -1
- package/dist/src/cli/codex-bridge.d.ts +819 -0
- package/dist/src/cli/codex-bridge.js +1607 -0
- package/dist/src/cli/codex-bridge.js.map +1 -0
- package/dist/src/cli/codex.d.ts +1 -1
- package/dist/src/cli/codex.js +304 -7
- package/dist/src/cli/codex.js.map +1 -1
- package/dist/src/cli/daemon.js +4 -1
- package/dist/src/cli/daemon.js.map +1 -1
- package/dist/src/cli/doctor.js +467 -12
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/execute.js +25 -2
- package/dist/src/cli/execute.js.map +1 -1
- package/dist/src/cli/help.d.ts +6 -2
- package/dist/src/cli/help.js +165 -60
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook-codex.d.ts +49 -1
- package/dist/src/cli/hook-codex.js +60 -1
- package/dist/src/cli/hook-codex.js.map +1 -1
- package/dist/src/cli/hook.d.ts +459 -3
- package/dist/src/cli/hook.js +1062 -114
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.js +1 -1
- package/dist/src/cli/import.js.map +1 -1
- package/dist/src/cli/main.js +5 -3
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/policy-apply.d.ts +195 -0
- package/dist/src/cli/policy-apply.js +573 -0
- package/dist/src/cli/policy-apply.js.map +1 -0
- package/dist/src/cli/policy.js +14 -1
- package/dist/src/cli/policy.js.map +1 -1
- package/dist/src/cli/preflight.d.ts +151 -13
- package/dist/src/cli/preflight.js +398 -41
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/sandbox.js +17 -1
- package/dist/src/cli/sandbox.js.map +1 -1
- package/dist/src/cli/scaffold.d.ts +1 -1
- package/dist/src/cli/scaffold.js +1 -1
- package/dist/src/cli/setup-channel.d.ts +9 -0
- package/dist/src/cli/setup-channel.js +28 -1
- package/dist/src/cli/setup-channel.js.map +1 -1
- package/dist/src/cli/setup-common.d.ts +3 -1
- package/dist/src/cli/setup-common.js +3 -2
- package/dist/src/cli/setup-common.js.map +1 -1
- package/dist/src/cli/setup.d.ts +2 -0
- package/dist/src/cli/setup.js +94 -2
- package/dist/src/cli/setup.js.map +1 -1
- package/dist/src/cli/up.js +115 -51
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/values.js +3 -4
- package/dist/src/cli/values.js.map +1 -1
- package/dist/src/cli/verb-registry.js +174 -9
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +2 -2
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/codex/broker.d.ts +229 -0
- package/dist/src/codex/broker.js +548 -0
- package/dist/src/codex/broker.js.map +1 -0
- package/dist/src/codex/runner.d.ts +178 -0
- package/dist/src/codex/runner.js +231 -0
- package/dist/src/codex/runner.js.map +1 -0
- package/dist/src/codex/serve.d.ts +56 -0
- package/dist/src/codex/serve.js +98 -0
- package/dist/src/codex/serve.js.map +1 -0
- package/dist/src/codex/workspace-commit.d.ts +219 -0
- package/dist/src/codex/workspace-commit.js +549 -0
- package/dist/src/codex/workspace-commit.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +51 -0
- package/dist/src/core/advance-cycle.js +66 -2
- package/dist/src/core/advance-cycle.js.map +1 -1
- package/dist/src/core/agents-md.d.ts +20 -18
- package/dist/src/core/agents-md.js +33 -31
- package/dist/src/core/agents-md.js.map +1 -1
- package/dist/src/core/attest.d.ts +215 -0
- package/dist/src/core/attest.js +317 -7
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +18 -0
- package/dist/src/core/audit.js +13 -0
- package/dist/src/core/audit.js.map +1 -1
- package/dist/src/core/channel-owner.d.ts +213 -0
- package/dist/src/core/channel-owner.js +358 -0
- package/dist/src/core/channel-owner.js.map +1 -0
- package/dist/src/core/command-class.d.ts +154 -0
- package/dist/src/core/command-class.js +673 -20
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/commit-guard.d.ts +272 -0
- package/dist/src/core/commit-guard.js +424 -0
- package/dist/src/core/commit-guard.js.map +1 -0
- package/dist/src/core/daemon-actor.d.ts +45 -0
- package/dist/src/core/daemon-actor.js +54 -0
- package/dist/src/core/daemon-actor.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +109 -8
- package/dist/src/core/dark-session.js +266 -82
- package/dist/src/core/dark-session.js.map +1 -1
- package/dist/src/core/decision-refusal.d.ts +23 -2
- package/dist/src/core/decision-refusal.js +24 -2
- package/dist/src/core/decision-refusal.js.map +1 -1
- package/dist/src/core/env-file.d.ts +5 -0
- package/dist/src/core/env-file.js +60 -1
- package/dist/src/core/env-file.js.map +1 -1
- package/dist/src/core/execute.d.ts +15 -2
- package/dist/src/core/execute.js +15 -2
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/gate.d.ts +86 -1
- package/dist/src/core/gate.js +81 -1
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/gesture-refusal.d.ts +166 -0
- package/dist/src/core/gesture-refusal.js +188 -0
- package/dist/src/core/gesture-refusal.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +1 -1
- package/dist/src/core/harness-version.js +3 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/instance.d.ts +59 -2
- package/dist/src/core/instance.js +113 -0
- package/dist/src/core/instance.js.map +1 -1
- package/dist/src/core/log.d.ts +39 -1
- package/dist/src/core/log.js.map +1 -1
- package/dist/src/core/policy-explain.d.ts +10 -0
- package/dist/src/core/policy-explain.js +32 -0
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +41 -1
- package/dist/src/core/policy-load.js +21 -3
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +43 -0
- package/dist/src/core/policy-match.js +52 -0
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +52 -0
- package/dist/src/core/policy-proposal.js +102 -2
- package/dist/src/core/policy-proposal.js.map +1 -1
- package/dist/src/core/protected-path-guard.d.ts +117 -4
- package/dist/src/core/protected-path-guard.js +362 -48
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/question-preempted.d.ts +141 -0
- package/dist/src/core/question-preempted.js +152 -0
- package/dist/src/core/question-preempted.js.map +1 -0
- package/dist/src/core/read-scope.d.ts +172 -0
- package/dist/src/core/read-scope.js +252 -0
- package/dist/src/core/read-scope.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +81 -0
- package/dist/src/core/sandbox.js +190 -1
- package/dist/src/core/sandbox.js.map +1 -1
- package/dist/src/core/sender-identity.d.ts +476 -0
- package/dist/src/core/sender-identity.js +572 -0
- package/dist/src/core/sender-identity.js.map +1 -0
- package/dist/src/core/shlex.d.ts +102 -0
- package/dist/src/core/shlex.js +159 -0
- package/dist/src/core/shlex.js.map +1 -0
- package/dist/src/core/values.d.ts +18 -8
- package/dist/src/core/values.js +36 -1
- package/dist/src/core/values.js.map +1 -1
- package/dist/src/daemon/advance.d.ts +10 -0
- package/dist/src/daemon/advance.js +25 -4
- package/dist/src/daemon/advance.js.map +1 -1
- package/dist/src/daemon/daemon.js +9 -0
- package/dist/src/daemon/daemon.js.map +1 -1
- package/dist/src/daemon/git-evidence.d.ts +2 -2
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/mcp/server.js +8 -0
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/cli-reference.md +932 -32
- package/docs/codex-enforced-session.md +75 -2
- package/docs/codex-workspace-broker.md +118 -0
- package/package.json +3 -1
- package/schema/event.schema.json +538 -9
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-false.json +20 -0
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
- package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
- package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
- package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
- package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
- package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
- package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
- package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
- package/schema/fixtures/policy/valid/canonical.json +1 -1
- package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
- package/schema/fixtures/policy-md/valid/canonical.md +1 -1
- package/schema/fixtures/policy-md/valid/with-values.md +5 -7
- package/schema/fixtures/values/invalid/class-shaped.json +1 -1
- package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
- package/schema/fixtures/values/invalid/non-string-item.json +1 -1
- package/schema/fixtures/values/invalid/over-cap.json +1 -1
- package/schema/fixtures/values/invalid/unknown-key.json +1 -1
- package/schema/fixtures/values/invalid/version-float.json +1 -0
- package/schema/fixtures/values/invalid/version-integer.json +1 -0
- package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +2 -3
- package/schema/fixtures/values/valid/full.json +5 -7
- package/schema/fixtures/values/valid/minimal.json +1 -1
- package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
- package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
- package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
- package/schema/fixtures/values-md/invalid/version-1.md +69 -0
- package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
- package/schema/fixtures/values-md/valid/absent.md +1 -1
- package/schema/fixtures/values-md/valid/with-values.md +5 -7
- package/schema/policy.schema.json +54 -2
- package/schema/values.schema.json +7 -11
- package/schema/fixtures/values/invalid/version-string.json +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "version":
|
|
1
|
+
{ "version": "0.2" }
|
|
@@ -19,7 +19,7 @@ approvers:
|
|
|
19
19
|
classes:
|
|
20
20
|
read.*: { autonomy: autonomous }
|
|
21
21
|
files.write.workspace: { autonomy: autonomous }
|
|
22
|
-
calendar.write.own: { autonomy: supervised }
|
|
22
|
+
calendar.write.own: { autonomy: supervised-retro }
|
|
23
23
|
communicate.email.draft: { autonomy: autonomous }
|
|
24
24
|
communicate.email.external:
|
|
25
25
|
autonomy: manual
|
|
@@ -52,8 +52,10 @@ Everything after the block is prose again and is ignored by the parser.
|
|
|
52
52
|
## What I value
|
|
53
53
|
|
|
54
54
|
The block below parses as YAML and is refused by values.schema.json: `version`
|
|
55
|
-
is the string "1" rather than the
|
|
56
|
-
key the closed schema does not admit.
|
|
55
|
+
is the string "1" rather than the string "0.2", and `classes` is a policy-shaped
|
|
56
|
+
key the closed schema does not admit. The version here is a STRING on purpose,
|
|
57
|
+
so this fixture reaches the schema: a numeric version is refused earlier, by
|
|
58
|
+
name, as `version-unsupported` (APRV-336).
|
|
57
59
|
|
|
58
60
|
```yaml approval-values
|
|
59
61
|
version: "1"
|
|
@@ -19,7 +19,7 @@ approvers:
|
|
|
19
19
|
classes:
|
|
20
20
|
read.*: { autonomy: autonomous }
|
|
21
21
|
files.write.workspace: { autonomy: autonomous }
|
|
22
|
-
calendar.write.own: { autonomy: supervised }
|
|
22
|
+
calendar.write.own: { autonomy: supervised-retro }
|
|
23
23
|
communicate.email.draft: { autonomy: autonomous }
|
|
24
24
|
communicate.email.external:
|
|
25
25
|
autonomy: manual
|
|
@@ -55,7 +55,7 @@ Two values blocks: two answers to one question. The reader refuses rather than
|
|
|
55
55
|
picking one on document order.
|
|
56
56
|
|
|
57
57
|
```yaml approval-values
|
|
58
|
-
version:
|
|
58
|
+
version: "0.2"
|
|
59
59
|
like:
|
|
60
60
|
- success reported first, caveats after
|
|
61
61
|
```
|
|
@@ -63,7 +63,7 @@ like:
|
|
|
63
63
|
And, further down the file, a second one somebody pasted in later.
|
|
64
64
|
|
|
65
65
|
```yaml approval-values
|
|
66
|
-
version:
|
|
66
|
+
version: "0.2"
|
|
67
67
|
like:
|
|
68
68
|
- the opposite of the above
|
|
69
69
|
```
|
|
@@ -19,7 +19,7 @@ approvers:
|
|
|
19
19
|
classes:
|
|
20
20
|
read.*: { autonomy: autonomous }
|
|
21
21
|
files.write.workspace: { autonomy: autonomous }
|
|
22
|
-
calendar.write.own: { autonomy: supervised }
|
|
22
|
+
calendar.write.own: { autonomy: supervised-retro }
|
|
23
23
|
communicate.email.draft: { autonomy: autonomous }
|
|
24
24
|
communicate.email.external:
|
|
25
25
|
autonomy: manual
|
|
@@ -56,6 +56,6 @@ file; this reader will not, because the truncated tail of a block is
|
|
|
56
56
|
indistinguishable from a complete one.
|
|
57
57
|
|
|
58
58
|
```yaml approval-values
|
|
59
|
-
version:
|
|
59
|
+
version: "0.2"
|
|
60
60
|
like:
|
|
61
61
|
- success reported first, caveats after
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Approval Policy
|
|
2
|
+
|
|
3
|
+
Agents working in this project handle my life admin. Anything that leaves
|
|
4
|
+
the machine gets declared, and the classes below say what I sign off on.
|
|
5
|
+
|
|
6
|
+
```yaml approval-policy
|
|
7
|
+
version: "0.1"
|
|
8
|
+
|
|
9
|
+
defaults:
|
|
10
|
+
autonomy: manual # unknown/undeclared classes require sign-off
|
|
11
|
+
channel: telegram
|
|
12
|
+
approval_ttl: 24h # pending requests expire
|
|
13
|
+
on_expiry: reject
|
|
14
|
+
|
|
15
|
+
approvers:
|
|
16
|
+
alice:
|
|
17
|
+
channels: [telegram, cli]
|
|
18
|
+
|
|
19
|
+
classes:
|
|
20
|
+
read.*: { autonomy: autonomous }
|
|
21
|
+
files.write.workspace: { autonomy: autonomous }
|
|
22
|
+
calendar.write.own: { autonomy: supervised-retro }
|
|
23
|
+
communicate.email.draft: { autonomy: autonomous }
|
|
24
|
+
communicate.email.external:
|
|
25
|
+
autonomy: manual
|
|
26
|
+
approvers: [alice]
|
|
27
|
+
financial.spend:
|
|
28
|
+
autonomy: manual
|
|
29
|
+
approvers: [alice]
|
|
30
|
+
limits: { per_action_usd: 25, daily_usd: 100 }
|
|
31
|
+
public.post: { autonomy: manual }
|
|
32
|
+
data.delete: { autonomy: manual }
|
|
33
|
+
account.auth: { autonomy: manual }
|
|
34
|
+
|
|
35
|
+
budgets:
|
|
36
|
+
global: { daily_usd: 100, daily_actions: 200 }
|
|
37
|
+
|
|
38
|
+
audit:
|
|
39
|
+
supervised_sample_rate: 0.10 # fraction of supervised actions escalated
|
|
40
|
+
# for retrospective human review
|
|
41
|
+
|
|
42
|
+
channels:
|
|
43
|
+
telegram:
|
|
44
|
+
chat_id_env: APPROVAL_TG_CHAT
|
|
45
|
+
token_env: APPROVAL_TG_TOKEN
|
|
46
|
+
web:
|
|
47
|
+
port: 4680
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Everything after the block is prose again and is ignored by the parser.
|
|
51
|
+
|
|
52
|
+
## What I value
|
|
53
|
+
|
|
54
|
+
The block below is the FIRST revision of the values format: `version` is the
|
|
55
|
+
integer 1 and it carries a `wants:` list. APRV-336 folded `wants` into `like`
|
|
56
|
+
and moved the version to the quoted string "0.2", so this block is refused with
|
|
57
|
+
`version-unsupported` and a message naming the two edits.
|
|
58
|
+
|
|
59
|
+
```yaml approval-values
|
|
60
|
+
version: 1
|
|
61
|
+
like:
|
|
62
|
+
- success reported first, caveats after
|
|
63
|
+
|
|
64
|
+
wants:
|
|
65
|
+
- say when you are stuck rather than guessing a fourth time
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Prose again after the block. The policy above loads exactly as it does in every
|
|
69
|
+
other fixture here: a values failure is never a policy failure.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Approval Policy
|
|
2
|
+
|
|
3
|
+
Agents working in this project handle my life admin. Anything that leaves
|
|
4
|
+
the machine gets declared, and the classes below say what I sign off on.
|
|
5
|
+
|
|
6
|
+
```yaml approval-policy
|
|
7
|
+
version: "0.1"
|
|
8
|
+
|
|
9
|
+
defaults:
|
|
10
|
+
autonomy: manual # unknown/undeclared classes require sign-off
|
|
11
|
+
channel: telegram
|
|
12
|
+
approval_ttl: 24h # pending requests expire
|
|
13
|
+
on_expiry: reject
|
|
14
|
+
|
|
15
|
+
approvers:
|
|
16
|
+
alice:
|
|
17
|
+
channels: [telegram, cli]
|
|
18
|
+
|
|
19
|
+
classes:
|
|
20
|
+
read.*: { autonomy: autonomous }
|
|
21
|
+
files.write.workspace: { autonomy: autonomous }
|
|
22
|
+
calendar.write.own: { autonomy: supervised-retro }
|
|
23
|
+
communicate.email.draft: { autonomy: autonomous }
|
|
24
|
+
communicate.email.external:
|
|
25
|
+
autonomy: manual
|
|
26
|
+
approvers: [alice]
|
|
27
|
+
financial.spend:
|
|
28
|
+
autonomy: manual
|
|
29
|
+
approvers: [alice]
|
|
30
|
+
limits: { per_action_usd: 25, daily_usd: 100 }
|
|
31
|
+
public.post: { autonomy: manual }
|
|
32
|
+
data.delete: { autonomy: manual }
|
|
33
|
+
account.auth: { autonomy: manual }
|
|
34
|
+
|
|
35
|
+
budgets:
|
|
36
|
+
global: { daily_usd: 100, daily_actions: 200 }
|
|
37
|
+
|
|
38
|
+
audit:
|
|
39
|
+
supervised_sample_rate: 0.10 # fraction of supervised actions escalated
|
|
40
|
+
# for retrospective human review
|
|
41
|
+
|
|
42
|
+
channels:
|
|
43
|
+
telegram:
|
|
44
|
+
chat_id_env: APPROVAL_TG_CHAT
|
|
45
|
+
token_env: APPROVAL_TG_TOKEN
|
|
46
|
+
web:
|
|
47
|
+
port: 4680
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Everything after the block is prose again and is ignored by the parser.
|
|
51
|
+
|
|
52
|
+
## What I value
|
|
53
|
+
|
|
54
|
+
The block below is the current revision written without quotes. YAML reads the
|
|
55
|
+
dotted identifier as the float 0.2, which is why the version is quoted at all,
|
|
56
|
+
so the reader refuses it with `version-unsupported` and the quotes as the fix.
|
|
57
|
+
|
|
58
|
+
```yaml approval-values
|
|
59
|
+
version: 0.2
|
|
60
|
+
like:
|
|
61
|
+
- success reported first, caveats after
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Prose again after the block, ignored by both readers.
|
|
@@ -19,7 +19,7 @@ approvers:
|
|
|
19
19
|
classes:
|
|
20
20
|
read.*: { autonomy: autonomous }
|
|
21
21
|
files.write.workspace: { autonomy: autonomous }
|
|
22
|
-
calendar.write.own: { autonomy: supervised }
|
|
22
|
+
calendar.write.own: { autonomy: supervised-retro }
|
|
23
23
|
communicate.email.draft: { autonomy: autonomous }
|
|
24
24
|
communicate.email.external:
|
|
25
25
|
autonomy: manual
|
|
@@ -55,7 +55,7 @@ The block below is not YAML: the second entry is indented into a mapping that
|
|
|
55
55
|
was never opened.
|
|
56
56
|
|
|
57
57
|
```yaml approval-values
|
|
58
|
-
version:
|
|
58
|
+
version: "0.2"
|
|
59
59
|
like:
|
|
60
60
|
- success reported first
|
|
61
61
|
key: value
|
|
@@ -19,7 +19,7 @@ approvers:
|
|
|
19
19
|
classes:
|
|
20
20
|
read.*: { autonomy: autonomous }
|
|
21
21
|
files.write.workspace: { autonomy: autonomous }
|
|
22
|
-
calendar.write.own: { autonomy: supervised }
|
|
22
|
+
calendar.write.own: { autonomy: supervised-retro }
|
|
23
23
|
communicate.email.draft: { autonomy: autonomous }
|
|
24
24
|
communicate.email.external:
|
|
25
25
|
autonomy: manual
|
|
@@ -19,7 +19,7 @@ approvers:
|
|
|
19
19
|
classes:
|
|
20
20
|
read.*: { autonomy: autonomous }
|
|
21
21
|
files.write.workspace: { autonomy: autonomous }
|
|
22
|
-
calendar.write.own: { autonomy: supervised }
|
|
22
|
+
calendar.write.own: { autonomy: supervised-retro }
|
|
23
23
|
communicate.email.draft: { autonomy: autonomous }
|
|
24
24
|
communicate.email.external:
|
|
25
25
|
autonomy: manual
|
|
@@ -55,7 +55,7 @@ The block below is guidance, never policy. Nothing in it grants anything, and
|
|
|
55
55
|
no part of the runtime reads it to decide.
|
|
56
56
|
|
|
57
57
|
```yaml approval-values
|
|
58
|
-
version:
|
|
58
|
+
version: "0.2"
|
|
59
59
|
|
|
60
60
|
love:
|
|
61
61
|
- seeing the real change, not a description of it
|
|
@@ -64,16 +64,14 @@ love:
|
|
|
64
64
|
like:
|
|
65
65
|
- success reported first, caveats after
|
|
66
66
|
- small reviewable commits
|
|
67
|
+
- honest opinions on the work, including when you think a task is wrong
|
|
68
|
+
- a journal entry of about five points per milestone
|
|
67
69
|
|
|
68
70
|
dislike:
|
|
69
71
|
- prose where a command would do
|
|
70
72
|
- being asked to approve something I cannot see
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
- honest opinions on the work, including when you think a task is wrong
|
|
74
|
-
- a journal entry of about five points per milestone
|
|
75
|
-
|
|
76
|
-
responds: "Usually within the hour on the phone; a bare 'ok' means yes and is not curtness."
|
|
74
|
+
communication: "Usually within the hour on the phone; a bare 'ok' means yes and is not curtness."
|
|
77
75
|
```
|
|
78
76
|
|
|
79
77
|
Prose again after the block, ignored by both readers.
|
|
@@ -85,6 +85,21 @@
|
|
|
85
85
|
]
|
|
86
86
|
]
|
|
87
87
|
},
|
|
88
|
+
"read_scope": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"additionalProperties": false,
|
|
91
|
+
"description": "SPEC.md §5.2 (amended, APRV-347): the directories an agent's reads may stay inside. Reads outside every root classify `read.file.out_of_scope`, both for shell readers (`cat`, `head`, `tail`, `sed -n`, `grep`, `rg`, `ls`, `find`, and kin) and for a harness read tool that names a path (Claude Code's `Read`, `Glob` and `Grep`). ADDITIVE ONLY, exactly as `protected_paths` is: the built-in roots — the GATE ROOT (the directory holding this policy file), the session scratchpad, and the system temp root — are in scope whatever this block says or omits, because a runtime that could be denied its own policy, log and workspace could not run at all, and a policy able to shrink its own read scope would be a policy an agent could narrow until nothing was gated. Omitting the block entirely means the built-in roots alone, which is the common case and the whole of the muse jail: a session whose `APPROVAL.md` sits in `~/dev/muse` has `~/dev/muse` as its gate root and every sibling under `~/dev` is out of scope with no grammar here at all. An unparseable value is refused at load like every other key in this closed schema, and a refused policy means every class is `manual` (SPEC.md §5.2, fail closed).",
|
|
92
|
+
"properties": {
|
|
93
|
+
"roots": {
|
|
94
|
+
"type": "array",
|
|
95
|
+
"uniqueItems": true,
|
|
96
|
+
"description": "Extra directories whose reads stay ordinary `read.shell`. An absolute path is taken as written; a relative path is resolved against the gate root, so a policy stays portable between a checkout and a clone of it. Each entry is compared by PATH SEGMENT after the runtime resolves it, so a symlink cannot smuggle a read out of a root and `/dev/muse-other` does not match a root of `/dev/muse`. No globs and no negation: a pattern language half-implemented here would be a scope the author believed was in force and the runtime did not apply.",
|
|
97
|
+
"items": { "$ref": "#/$defs/readRoot" },
|
|
98
|
+
"examples": [["/Users/carter/dev/shared-fixtures"], ["../vendor"]]
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"examples": [{ "roots": ["/opt/reference-corpus"] }]
|
|
102
|
+
},
|
|
88
103
|
"approvers": {
|
|
89
104
|
"type": "object",
|
|
90
105
|
"additionalProperties": false,
|
|
@@ -103,6 +118,31 @@
|
|
|
103
118
|
"uniqueItems": true,
|
|
104
119
|
"description": "SPEC.md §5.1/§10.3: channel names this approver can decide on, e.g. [telegram, cli]. At least one, since an approver reachable nowhere can never grant.",
|
|
105
120
|
"items": { "type": "string", "minLength": 1 }
|
|
121
|
+
},
|
|
122
|
+
"senders": {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"minProperties": 1,
|
|
125
|
+
"additionalProperties": false,
|
|
126
|
+
"description": "SPEC.md §5.2/§10.3 (amended, APRV-324): the transport account ids this person decides from, per channel. ADDITIVE and OPTIONAL: a policy that declares none behaves exactly as every build before this key existed, recording each decision against the actor the listener process was launched with. Declaring the first mapping for a channel is what turns enforcement on for that channel — from then on a sender that channel authenticates and this block does not name is REFUSED (`sender-unmapped`) rather than attributed to the listener's own identity, because a fallback there would let anyone who can reach the configured chat approve as the operator. Written person to sender rather than sender to person, so each human's identity is in one place and a duplicate id is visible on the page; the runtime inverts it at lookup and REFUSES a policy in which two approvers claim one id (`sender-ambiguous`, a load failure, so every class resolves `manual`). The value is the transport's own attribution and never a name the sender chose: for `telegram` it is the numeric `callback_query.from.id`, as a string, and never `from.username`, which is mutable and reusable and would transfer an identity with a handle. This block is an operator ASSERTION that an account belongs to a person, which is what the `policy.core` attestation ceremony makes accountable; it is not, and cannot be, proof of personhood.",
|
|
127
|
+
"propertyNames": { "$ref": "#/$defs/senderChannel" },
|
|
128
|
+
"properties": {
|
|
129
|
+
"telegram": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"minLength": 1,
|
|
132
|
+
"pattern": "^(?:[0-9]+|hmac-sha256:[0-9a-f]{64})$",
|
|
133
|
+
"description": "The Telegram account this person decides from, in one of two forms (SPEC.md §5.2, APRV-324 and APRV-370).\n\nRAW: the account id the Bot API reports as `callback_query.from.id`, as a decimal string. Digits only, because a `@handle` here would be a mapping key the transport never sends and `from.username` is mutable and reusable.\n\nKEYED: `hmac-sha256:<64 lowercase hex>`, the HMAC-SHA-256 of that same decimal string under an operator-held key named by `APPROVAL_SENDER_KEY`, minted by `approval setup sender-key`. It exists because a policy and a log that are PUBLISHED disclose the account otherwise, once in the file and then on every decision. A plain unkeyed digest would not fix that: a Telegram id is a short decimal number and the whole space is enumerable on a laptop, so the digest has to be keyed to be a digest of anything.\n\nThe form is per entry: a policy may carry both while a migration is in progress, and they cannot collide, since a decimal id can never look like the prefixed form. A channel mapped in the keyed form with no key in the listener's environment REFUSES every decision on that channel (`sender-key-unavailable`) rather than falling back to a raw comparison, because without the key the runtime can compute no digest and therefore cannot run the ambiguity check the mapping rests on. The key is not an authenticator: nothing about the gate's safety rests on its secrecy, and losing it costs the ability to resolve accounts, never the ability to refuse.",
|
|
134
|
+
"examples": [
|
|
135
|
+
"12345678",
|
|
136
|
+
"hmac-sha256:3f2a9c11b4d7e6085a1c2f9d8e7b6a5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a"
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"examples": [
|
|
141
|
+
{ "telegram": "12345678" },
|
|
142
|
+
{
|
|
143
|
+
"telegram": "hmac-sha256:3f2a9c11b4d7e6085a1c2f9d8e7b6a5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a"
|
|
144
|
+
}
|
|
145
|
+
]
|
|
106
146
|
}
|
|
107
147
|
}
|
|
108
148
|
}
|
|
@@ -328,7 +368,7 @@
|
|
|
328
368
|
"autonomy": {
|
|
329
369
|
"type": "string",
|
|
330
370
|
"enum": ["human-only", "manual", "supervised-live", "supervised", "supervised-retro", "autonomous"],
|
|
331
|
-
"description": "SPEC.md §5.2 (amended, APRV-127; widened, APRV-185): autonomy level, strictest first — `human-only` (reserved to human hands: a person performs the action outside agent execution, so no agent may request it, be granted it, or run it, and every gate verb refuses it with the code `class-human-only`) > `manual` (human sign-off before every action) > `supervised-live` (a policy-declared fraction of actions blocks on the human gate exactly as `manual` does; the rest proceed) > `supervised-retro` (executes immediately, sampled for retrospective review) > `autonomous` (executes freely). `supervised` is the pre-APRV-127 spelling and is
|
|
371
|
+
"description": "SPEC.md §5.2 (amended, APRV-127; widened, APRV-185): autonomy level, strictest first — `human-only` (reserved to human hands: a person performs the action outside agent execution, so no agent may request it, be granted it, or run it, and every gate verb refuses it with the code `class-human-only`) > `manual` (human sign-off before every action) > `supervised-live` (a policy-declared fraction of actions blocks on the human gate exactly as `manual` does; the rest proceed) > `supervised-retro` (executes immediately, sampled for retrospective review) > `autonomous` (executes freely). `supervised` is the pre-APRV-127 spelling and is a DEPRECATED ALIAS OF `supervised-retro` (APRV-335), so every policy written before the split keeps the meaning its author gave it; the runtime records a load-time note naming the alias rather than silently reinterpreting it, and reports it in its health surfaces. Write `supervised-retro`: the bare spelling is admitted here for compatibility and a future version of this schema removes it. The enum is closed: an unrecognised level cannot be ordered against the others, so it must fail validation rather than be guessed at.",
|
|
332
372
|
"examples": ["human-only", "manual", "supervised-live", "supervised-retro"]
|
|
333
373
|
},
|
|
334
374
|
"liveRate": {
|
|
@@ -348,7 +388,7 @@
|
|
|
348
388
|
"defaultAutonomy": {
|
|
349
389
|
"type": "string",
|
|
350
390
|
"enum": ["human-only", "manual", "supervised", "supervised-retro", "autonomous"],
|
|
351
|
-
"description": "SPEC.md §5.2 (amended, APRV-127; widened, APRV-185): the autonomy levels a policy may name as its DEFAULT. Identical to `autonomy` less `supervised-live`, which carries a required `live_rate` that `defaults` has nowhere to declare. `human-only` IS admitted, and the asymmetry with `supervised-live` is the reason: that level is excluded for a key `defaults` cannot hold, and `human-only` carries no key at all, so an author naming it here declares maximal strictness over everything the policy did not name — a statement a policy is entitled to make. It is NOT the fail-closed target. A policy that fails to load still resolves every class to `manual`, because a broken policy must stay recoverable through its own gate, and a file whose every class became `human-only` would put the repair for a typo behind a level that admits no gated repair."
|
|
391
|
+
"description": "SPEC.md §5.2 (amended, APRV-127; widened, APRV-185): the autonomy levels a policy may name as its DEFAULT. Identical to `autonomy` less `supervised-live`, which carries a required `live_rate` that `defaults` has nowhere to declare. The bare `supervised` is admitted here on the same terms it is admitted there, as the DEPRECATED ALIAS of `supervised-retro` (APRV-335): a policy that names it still defaults to retrospective supervision, the runtime notes the spelling at load and in its health surfaces, and a future version of this schema removes it. `human-only` IS admitted, and the asymmetry with `supervised-live` is the reason: that level is excluded for a key `defaults` cannot hold, and `human-only` carries no key at all, so an author naming it here declares maximal strictness over everything the policy did not name — a statement a policy is entitled to make. It is NOT the fail-closed target. A policy that fails to load still resolves every class to `manual`, because a broken policy must stay recoverable through its own gate, and a file whose every class became `human-only` would put the repair for a typo behind a level that admits no gated repair."
|
|
352
392
|
},
|
|
353
393
|
"duration": {
|
|
354
394
|
"type": "string",
|
|
@@ -360,6 +400,12 @@
|
|
|
360
400
|
"pattern": "^[a-z0-9][a-z0-9_-]*$",
|
|
361
401
|
"description": "SPEC.md §5.1: lowercase identifier used for approver ids — starts alphanumeric, then alphanumerics, `_` or `-`. Case-insensitive collisions are excluded by construction."
|
|
362
402
|
},
|
|
403
|
+
"senderChannel": {
|
|
404
|
+
"type": "string",
|
|
405
|
+
"enum": ["telegram"],
|
|
406
|
+
"description": "SPEC.md §10.3 (amended, APRV-324): a channel whose transport attributes a gesture to an account id, and therefore the only kind of channel an `approvers.<id>.senders` entry may name. `telegram` qualifies because the Bot API reports `callback_query.from.id`, which the sender cannot choose. `web` and `cli` are deliberately absent: an unauthenticated form post names nobody and a local process names whoever runs it, so a sender mapping for either would be a binding the runtime cannot check and an identity key the channel cannot support.",
|
|
407
|
+
"examples": ["telegram"]
|
|
408
|
+
},
|
|
363
409
|
"classPattern": {
|
|
364
410
|
"type": "string",
|
|
365
411
|
"pattern": "^(?:[a-z0-9][a-z0-9_-]*|\\*)(?:\\.(?:[a-z0-9][a-z0-9_-]*|\\*))*$",
|
|
@@ -496,6 +542,12 @@
|
|
|
496
542
|
"minLength": 1,
|
|
497
543
|
"pattern": "^(?!/)(?!.*(?:^|/)\\.\\.?(?:/|$))(?!.*//)[^*?\\[\\]{}\\\\\\s]+$",
|
|
498
544
|
"description": "A repo-relative path: exact file (`SPEC.md`) or directory prefix with a trailing `/` (`design/`). Leading `/`, `.`/`..` segments, whitespace and the glob characters `* ? [ ] { }` are rejected — each would be a path the matcher cannot honour literally, and silently ignoring one would leave the author believing a file is gated when it is not. Named as a `$def` since APRV-266 so the bare-string entry and the `path` of a routed entry are held to one grammar and cannot drift apart."
|
|
545
|
+
},
|
|
546
|
+
"readRoot": {
|
|
547
|
+
"type": "string",
|
|
548
|
+
"minLength": 1,
|
|
549
|
+
"pattern": "^[^*?\\[\\]{}\\\\\\s$~]+$",
|
|
550
|
+
"description": "A directory whose reads stay in scope (APRV-347). Absolute, or relative to the gate root. The glob characters `* ? [ ] { }`, whitespace, `$` and a leading `~` are rejected for the same reason `protectedPath` rejects them: this runtime compares path segments and expands nothing, so a value it would have to expand is one an author believes is in force and the runtime does not apply. Unlike `protectedPath`, an absolute path and `..` segments are both ALLOWED, because a read root may legitimately sit beside the gate root (`../vendor`) or anywhere on the machine — a root widens the scope and is resolved on disk before it is compared, where `..` has a meaning."
|
|
499
551
|
}
|
|
500
552
|
}
|
|
501
553
|
}
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
"$id": "https://approval.md/schema/values.schema.json",
|
|
4
4
|
"$comment": "CC0 1.0. See schema/LICENSE.",
|
|
5
5
|
"title": "APPROVAL.md values block",
|
|
6
|
-
"description": "SPEC.md §5.3: the parsed YAML of the OPTIONAL second fenced block inside APPROVAL.md, marked ```yaml approval-values```. It is the human-to-agent direction of a file that until now carried control in one direction only: the policy block says what an agent may do, and this block says what the operator values and how they read what comes back. It is human-authored GUIDANCE and it is never policy. No enforcement path reads it — not routing, not policy matching, not the sampler, not budgets, not token minting, not execution — which is SPEC.md §11.1 invariant 10 (\"guidance never reaches enforcement\"), pinned by tests/values-inert.test.ts. A values block that fails this schema never makes the policy block unloadable; the two are parsed and judged independently, and the failure of guidance can never widen or narrow a class. Because the block lives inside APPROVAL.md, editing it changes the file's bytes and therefore invalidates the whole-file attestation, so a values edit goes through the same attestation ceremony as a policy edit (SPEC.md §5.3). Absence is a declaration, never a default: a file with no values block means the operator has declared no values here, and an agent-facing surface must render it as absence rather than inventing a neutral middle.\n\nThe key set is closed (additionalProperties: false at every level) for the same reason the policy block is: an unrecognised key is something the author believed was in force and the runtime did not understand. Keys deliberately REJECTED, recorded here so the rejection is reviewable rather than forgotten: (1) anything class-shaped or override-shaped — a `classes:` map, an `autonomy:`, a `defaults:`, an `overrides:` — because that is policy, and admitting a policy-shaped key into the guidance block would make invariant 10 unprovable: a static guard cannot show that guidance never reaches enforcement once guidance is allowed to LOOK like enforcement; (2) `priority` and `weight`, because a number invites arithmetic and there is no arithmetic to do here — nothing ranks, sums or thresholds these entries, and a field that suggests it does would be read as a knob; (3) `hate`, because a fourth standing grade invites an escalation ladder, and the standing lists are deliberately three (love/like/dislike). Intensity is carried where it is actually observed, by the event vocabulary's `reaction` (disliked|indifferent|liked|loved, SPEC.md §5.2), not by an ever-longer list of nouns in a static file.",
|
|
6
|
+
"description": "SPEC.md §5.3: the parsed YAML of the OPTIONAL second fenced block inside APPROVAL.md, marked ```yaml approval-values```. It is the human-to-agent direction of a file that until now carried control in one direction only: the policy block says what an agent may do, and this block says what the operator values and how they read what comes back. It is human-authored GUIDANCE and it is never policy. No enforcement path reads it — not routing, not policy matching, not the sampler, not budgets, not token minting, not execution — which is SPEC.md §11.1 invariant 10 (\"guidance never reaches enforcement\"), pinned by tests/values-inert.test.ts. A values block that fails this schema never makes the policy block unloadable; the two are parsed and judged independently, and the failure of guidance can never widen or narrow a class. Because the block lives inside APPROVAL.md, editing it changes the file's bytes and therefore invalidates the whole-file attestation, so a values edit goes through the same attestation ceremony as a policy edit (SPEC.md §5.3). Absence is a declaration, never a default: a file with no values block means the operator has declared no values here, and an agent-facing surface must render it as absence rather than inventing a neutral middle.\n\nThe key set is closed (additionalProperties: false at every level) for the same reason the policy block is: an unrecognised key is something the author believed was in force and the runtime did not understand. Keys deliberately REJECTED, recorded here so the rejection is reviewable rather than forgotten: (1) anything class-shaped or override-shaped — a `classes:` map, an `autonomy:`, a `defaults:`, an `overrides:` — because that is policy, and admitting a policy-shaped key into the guidance block would make invariant 10 unprovable: a static guard cannot show that guidance never reaches enforcement once guidance is allowed to LOOK like enforcement; (2) `priority` and `weight`, because a number invites arithmetic and there is no arithmetic to do here — nothing ranks, sums or thresholds these entries, and a field that suggests it does would be read as a knob; (3) `hate`, because a fourth standing grade invites an escalation ladder, and the standing lists are deliberately three (love/like/dislike). Intensity is carried where it is actually observed, by the event vocabulary's `reaction` (disliked|indifferent|liked|loved, SPEC.md §5.2), not by an ever-longer list of nouns in a static file; (4) `wants`, which the first revision carried for what the operator asks of the agent as behaviour, folded into `like` by APRV-336. A request about behaviour and a preference about the output are graded by the same person in the same way, and one list is easier to keep true than two whose boundary has to be re-decided on every edit.",
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
|
9
9
|
"required": ["version"],
|
|
10
10
|
"properties": {
|
|
11
11
|
"version": {
|
|
12
|
-
"type": "
|
|
13
|
-
"const":
|
|
14
|
-
"description": "SPEC.md §5.3: values format version. The only REQUIRED key: a block with no version cannot be interpreted safely under a future format revision. It is
|
|
15
|
-
"examples": [
|
|
12
|
+
"type": "string",
|
|
13
|
+
"const": "0.2",
|
|
14
|
+
"description": "SPEC.md §5.3: values format version. The only REQUIRED key: a block with no version cannot be interpreted safely under a future format revision. It is a QUOTED STRING, spelled exactly as the policy block's version is (\"0.1\"), because the two blocks live in one file and are read by one person: two version styles in one document is a detail an author has to remember rather than one they can see, and a block that says `version: 1` beside a block that says `version: \"0.1\"` invites the reader to conclude the values block is four revisions ahead. The quotes are load-bearing. A dotted identifier written bare is a FLOAT to YAML, so `version: 0.2` parses as the number 0.2, which loses the distinction between \"0.20\" and \"0.2\" and is refused by this schema; the loader refuses it by name, with the quoting as the fix. (APRV-336 replaced the integer 1 of the first revision; a block still carrying it is refused by the values loader alone and never by the policy loader.)",
|
|
15
|
+
"examples": ["0.2"]
|
|
16
16
|
},
|
|
17
17
|
"love": {
|
|
18
18
|
"$ref": "#/$defs/valueList",
|
|
@@ -26,14 +26,10 @@
|
|
|
26
26
|
"$ref": "#/$defs/valueList",
|
|
27
27
|
"description": "SPEC.md §5.3: what the operator dislikes. The negative standing grade. It is NOT a prohibition: a prohibition belongs in the policy block, where it is enforced; an entry here is a preference an agent should weigh in how it works, and a runtime that refused an action because of it would be enforcing guidance."
|
|
28
28
|
},
|
|
29
|
-
"
|
|
30
|
-
"$ref": "#/$defs/valueList",
|
|
31
|
-
"description": "SPEC.md §5.3: what the operator wants FROM the agent, as behaviour rather than as taste. Where love/like/dislike describe the operator, this describes the working relationship they are asking for. Examples: \"honest opinions on the work, including when you think a task is wrong\"; \"a journal entry of about five points per milestone\". Still guidance: nothing here is checked, counted, or enforced, and an agent that does not do these things is not refused by anything."
|
|
32
|
-
},
|
|
33
|
-
"responds": {
|
|
29
|
+
"communication": {
|
|
34
30
|
"type": "string",
|
|
35
31
|
"maxLength": 500,
|
|
36
|
-
"description": "SPEC.md §5.3: how the operator reads and answers — the shape of the human end of the loop, so an agent can read silence, terseness or delay correctly. Deliberately a SINGLE SCALAR rather than a list: a list here would grow into a second rules document sitting beside the policy block, and the one thing this block must never become is a place where rules live outside the enforced file. One sentence or two, and no more room than that.",
|
|
32
|
+
"description": "SPEC.md §5.3: how the operator reads and answers — the shape of the human end of the loop, so an agent can read silence, terseness or delay correctly. Deliberately a SINGLE SCALAR rather than a list: a list here would grow into a second rules document sitting beside the policy block, and the one thing this block must never become is a place where rules live outside the enforced file. One sentence or two, and no more room than that. (Named `responds` in the first revision; renamed by APRV-336, which also freed the word from the `approval feedback` verb it collided with.)",
|
|
37
33
|
"examples": [
|
|
38
34
|
"Usually within the hour on the phone; a bare 'ok' means yes and is not curtness."
|
|
39
35
|
]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "version": "1" }
|