approval-md 0.1.0 → 0.2.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 (266) hide show
  1. package/README.md +584 -553
  2. package/SPEC.md +42 -13
  3. package/dist/src/adapters/agentmail.d.ts +426 -0
  4. package/dist/src/adapters/agentmail.js +2 -2
  5. package/dist/src/adapters/conformance.d.ts +149 -0
  6. package/dist/src/adapters/contract.d.ts +628 -0
  7. package/dist/src/adapters/contract.js +110 -16
  8. package/dist/src/adapters/contract.js.map +1 -1
  9. package/dist/src/adapters/email.d.ts +324 -0
  10. package/dist/src/adapters/env-passphrase.d.ts +93 -0
  11. package/dist/src/adapters/public.d.ts +11 -0
  12. package/dist/src/adapters/public.js +11 -0
  13. package/dist/src/adapters/public.js.map +1 -0
  14. package/dist/src/adapters/registry.d.ts +59 -0
  15. package/dist/src/adapters/registry.js +2 -1
  16. package/dist/src/adapters/registry.js.map +1 -1
  17. package/dist/src/adapters/smtp.d.ts +213 -0
  18. package/dist/src/adapters/vault-provider.d.ts +114 -0
  19. package/dist/src/adapters/vault-provider.js +3 -3
  20. package/dist/src/adapters/zzz.d.ts +66 -0
  21. package/dist/src/adapters/zzz.js +299 -0
  22. package/dist/src/adapters/zzz.js.map +1 -0
  23. package/dist/src/channels/batch.d.ts +109 -0
  24. package/dist/src/channels/cli.d.ts +193 -0
  25. package/dist/src/channels/conformance.d.ts +92 -0
  26. package/dist/src/channels/contract.d.ts +623 -0
  27. package/dist/src/channels/payload-view.d.ts +35 -0
  28. package/dist/src/channels/render-queue.d.ts +149 -0
  29. package/dist/src/channels/tagging.d.ts +196 -0
  30. package/dist/src/channels/telegram.d.ts +1832 -0
  31. package/dist/src/channels/web.d.ts +341 -0
  32. package/dist/src/cli/adapter.d.ts +90 -0
  33. package/dist/src/cli/adapter.js +25 -15
  34. package/dist/src/cli/adapter.js.map +1 -1
  35. package/dist/src/cli/amend.d.ts +59 -0
  36. package/dist/src/cli/args.d.ts +43 -0
  37. package/dist/src/cli/attest.d.ts +41 -0
  38. package/dist/src/cli/audit-card.d.ts +62 -0
  39. package/dist/src/cli/audit.d.ts +59 -0
  40. package/dist/src/cli/channel-telegram.d.ts +806 -0
  41. package/dist/src/cli/channel-web.d.ts +131 -0
  42. package/dist/src/cli/channel.d.ts +71 -0
  43. package/dist/src/cli/checkpoint-tap.d.ts +169 -0
  44. package/dist/src/cli/codex.d.ts +2 -0
  45. package/dist/src/cli/codex.js +172 -0
  46. package/dist/src/cli/codex.js.map +1 -0
  47. package/dist/src/cli/coverage.d.ts +61 -0
  48. package/dist/src/cli/daemon.d.ts +120 -0
  49. package/dist/src/cli/doctor.d.ts +129 -0
  50. package/dist/src/cli/doctor.js +119 -5
  51. package/dist/src/cli/doctor.js.map +1 -1
  52. package/dist/src/cli/env.d.ts +65 -0
  53. package/dist/src/cli/execute.d.ts +202 -0
  54. package/dist/src/cli/exit-codes.d.ts +73 -0
  55. package/dist/src/cli/feedback.d.ts +60 -0
  56. package/dist/src/cli/gate-window.d.ts +40 -0
  57. package/dist/src/cli/gate.d.ts +68 -0
  58. package/dist/src/cli/git-scope.d.ts +190 -0
  59. package/dist/src/cli/gloss-attach.d.ts +85 -0
  60. package/dist/src/cli/gloss-codex-child.d.ts +9 -0
  61. package/dist/src/cli/gloss-codex.d.ts +24 -0
  62. package/dist/src/cli/gloss-options.d.ts +42 -0
  63. package/dist/src/cli/gloss.d.ts +265 -0
  64. package/dist/src/cli/help.d.ts +103 -0
  65. package/dist/src/cli/help.js +173 -51
  66. package/dist/src/cli/help.js.map +1 -1
  67. package/dist/src/cli/hook-codex.d.ts +78 -0
  68. package/dist/src/cli/hook-codex.js +167 -0
  69. package/dist/src/cli/hook-codex.js.map +1 -0
  70. package/dist/src/cli/hook.d.ts +331 -0
  71. package/dist/src/cli/hook.js +186 -80
  72. package/dist/src/cli/hook.js.map +1 -1
  73. package/dist/src/cli/import.d.ts +35 -0
  74. package/dist/src/cli/init.d.ts +84 -0
  75. package/dist/src/cli/init.js +2 -2
  76. package/dist/src/cli/init.js.map +1 -1
  77. package/dist/src/cli/instructions.d.ts +23 -0
  78. package/dist/src/cli/journal.d.ts +41 -0
  79. package/dist/src/cli/log-advance.d.ts +287 -0
  80. package/dist/src/cli/log-advance.js +102 -11
  81. package/dist/src/cli/log-advance.js.map +1 -1
  82. package/dist/src/cli/log-anchor.d.ts +176 -0
  83. package/dist/src/cli/log-checkpoint.d.ts +22 -0
  84. package/dist/src/cli/log-sync.d.ts +243 -0
  85. package/dist/src/cli/log-verbs.d.ts +16 -0
  86. package/dist/src/cli/log-verbs.js +7 -1
  87. package/dist/src/cli/log-verbs.js.map +1 -1
  88. package/dist/src/cli/long-help.d.ts +70 -0
  89. package/dist/src/cli/main.d.ts +77 -0
  90. package/dist/src/cli/main.js +155 -5
  91. package/dist/src/cli/main.js.map +1 -1
  92. package/dist/src/cli/mcp.d.ts +52 -0
  93. package/dist/src/cli/paths.d.ts +56 -0
  94. package/dist/src/cli/payload.d.ts +58 -0
  95. package/dist/src/cli/policy.d.ts +43 -0
  96. package/dist/src/cli/preflight.d.ts +363 -0
  97. package/dist/src/cli/preflight.js +294 -7
  98. package/dist/src/cli/preflight.js.map +1 -1
  99. package/dist/src/cli/progress.d.ts +78 -0
  100. package/dist/src/cli/prompt.d.ts +209 -0
  101. package/dist/src/cli/quickstart.d.ts +46 -0
  102. package/dist/src/cli/quickstart.js +297 -0
  103. package/dist/src/cli/quickstart.js.map +1 -0
  104. package/dist/src/cli/records.d.ts +34 -0
  105. package/dist/src/cli/render.d.ts +22 -0
  106. package/dist/src/cli/sandbox.d.ts +51 -0
  107. package/dist/src/cli/scaffold.d.ts +79 -0
  108. package/dist/src/cli/setup-adapter.d.ts +137 -0
  109. package/dist/src/cli/setup-adapter.js +38 -4
  110. package/dist/src/cli/setup-adapter.js.map +1 -1
  111. package/dist/src/cli/setup-channel.d.ts +117 -0
  112. package/dist/src/cli/setup-checkpoint.d.ts +57 -0
  113. package/dist/src/cli/setup-common.d.ts +275 -0
  114. package/dist/src/cli/setup-flow.d.ts +287 -0
  115. package/dist/src/cli/setup-service.d.ts +96 -0
  116. package/dist/src/cli/setup.d.ts +202 -0
  117. package/dist/src/cli/style.d.ts +320 -0
  118. package/dist/src/cli/token.d.ts +39 -0
  119. package/dist/src/cli/up.d.ts +155 -0
  120. package/dist/src/cli/up.js +4 -2
  121. package/dist/src/cli/up.js.map +1 -1
  122. package/dist/src/cli/usage.d.ts +37 -0
  123. package/dist/src/cli/values.d.ts +40 -0
  124. package/dist/src/cli/vault.d.ts +59 -0
  125. package/dist/src/cli/vault.js +2 -2
  126. package/dist/src/cli/vault.js.map +1 -1
  127. package/dist/src/cli/verb-registry.d.ts +76 -0
  128. package/dist/src/cli/verb-registry.js +176 -8
  129. package/dist/src/cli/verb-registry.js.map +1 -1
  130. package/dist/src/cli/wordmark.d.ts +31 -0
  131. package/dist/src/cli/wordmark.js +2 -2
  132. package/dist/src/codex/doctor.d.ts +13 -0
  133. package/dist/src/codex/doctor.js +41 -0
  134. package/dist/src/codex/doctor.js.map +1 -0
  135. package/dist/src/codex/manifest.d.ts +49 -0
  136. package/dist/src/codex/manifest.js +103 -0
  137. package/dist/src/codex/manifest.js.map +1 -0
  138. package/dist/src/codex/templates.d.ts +41 -0
  139. package/dist/src/codex/templates.js +319 -0
  140. package/dist/src/codex/templates.js.map +1 -0
  141. package/dist/src/codex/trust.d.ts +19 -0
  142. package/dist/src/codex/trust.js +183 -0
  143. package/dist/src/codex/trust.js.map +1 -0
  144. package/dist/src/codex/workspace-plan.d.ts +131 -0
  145. package/dist/src/codex/workspace-plan.js +561 -0
  146. package/dist/src/codex/workspace-plan.js.map +1 -0
  147. package/dist/src/core/actor.d.ts +2 -0
  148. package/dist/src/core/actor.js +5 -0
  149. package/dist/src/core/actor.js.map +1 -0
  150. package/dist/src/core/advance-cycle.d.ts +170 -0
  151. package/dist/src/core/agents-md.d.ts +276 -0
  152. package/dist/src/core/apply-patch.d.ts +49 -0
  153. package/dist/src/core/apply-patch.js +266 -0
  154. package/dist/src/core/apply-patch.js.map +1 -0
  155. package/dist/src/core/attest.d.ts +420 -0
  156. package/dist/src/core/attest.js +13 -1
  157. package/dist/src/core/attest.js.map +1 -1
  158. package/dist/src/core/audit.d.ts +492 -0
  159. package/dist/src/core/budgets.d.ts +238 -0
  160. package/dist/src/core/checkpoint.d.ts +500 -0
  161. package/dist/src/core/child-env.d.ts +88 -0
  162. package/dist/src/core/clock.d.ts +52 -0
  163. package/dist/src/core/command-class.d.ts +543 -0
  164. package/dist/src/core/command-class.js +43 -8
  165. package/dist/src/core/command-class.js.map +1 -1
  166. package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
  167. package/dist/src/core/coverage-sources/gh.d.ts +48 -0
  168. package/dist/src/core/coverage-sources/git.d.ts +101 -0
  169. package/dist/src/core/coverage.d.ts +217 -0
  170. package/dist/src/core/credential-spec.d.ts +72 -0
  171. package/dist/src/core/dark-session.d.ts +331 -0
  172. package/dist/src/core/decision-refusal.d.ts +185 -0
  173. package/dist/src/core/env-file.d.ts +450 -0
  174. package/dist/src/core/execute.d.ts +858 -0
  175. package/dist/src/core/execute.js +44 -6
  176. package/dist/src/core/execute.js.map +1 -1
  177. package/dist/src/core/frontmatter.d.ts +78 -0
  178. package/dist/src/core/gate-window.d.ts +312 -0
  179. package/dist/src/core/gate.d.ts +1364 -0
  180. package/dist/src/core/gate.js +68 -13
  181. package/dist/src/core/gate.js.map +1 -1
  182. package/dist/src/core/git-run.d.ts +73 -0
  183. package/dist/src/core/harness-version.d.ts +157 -0
  184. package/dist/src/core/harness-version.js +2 -1
  185. package/dist/src/core/harness-version.js.map +1 -1
  186. package/dist/src/core/harness-wait.d.ts +55 -0
  187. package/dist/src/core/head-retry.d.ts +107 -0
  188. package/dist/src/core/instance.d.ts +253 -0
  189. package/dist/src/core/intake-limits.d.ts +247 -0
  190. package/dist/src/core/jcs.d.ts +52 -0
  191. package/dist/src/core/journal.d.ts +144 -0
  192. package/dist/src/core/live-draw.d.ts +436 -0
  193. package/dist/src/core/log-reconcile.d.ts +89 -0
  194. package/dist/src/core/log-subscribe.d.ts +36 -0
  195. package/dist/src/core/log-subscribe.js +162 -0
  196. package/dist/src/core/log-subscribe.js.map +1 -0
  197. package/dist/src/core/log.d.ts +278 -0
  198. package/dist/src/core/loop.d.ts +274 -0
  199. package/dist/src/core/loop.js +11 -0
  200. package/dist/src/core/loop.js.map +1 -1
  201. package/dist/src/core/md-fence.d.ts +41 -0
  202. package/dist/src/core/money.d.ts +147 -0
  203. package/dist/src/core/payload-census.d.ts +74 -0
  204. package/dist/src/core/payload-store.d.ts +175 -0
  205. package/dist/src/core/payload.d.ts +71 -0
  206. package/dist/src/core/policy-diff.d.ts +292 -0
  207. package/dist/src/core/policy-diff.js +27 -4
  208. package/dist/src/core/policy-diff.js.map +1 -1
  209. package/dist/src/core/policy-expectations.d.ts +199 -0
  210. package/dist/src/core/policy-explain.d.ts +150 -0
  211. package/dist/src/core/policy-explain.js +31 -3
  212. package/dist/src/core/policy-explain.js.map +1 -1
  213. package/dist/src/core/policy-load.d.ts +527 -0
  214. package/dist/src/core/policy-load.js +15 -3
  215. package/dist/src/core/policy-load.js.map +1 -1
  216. package/dist/src/core/policy-match.d.ts +281 -0
  217. package/dist/src/core/policy-match.js +20 -9
  218. package/dist/src/core/policy-match.js.map +1 -1
  219. package/dist/src/core/policy-proposal.d.ts +265 -0
  220. package/dist/src/core/prompt-layout.d.ts +221 -0
  221. package/dist/src/core/protected-path-guard.d.ts +453 -0
  222. package/dist/src/core/protected-path-guard.js +514 -35
  223. package/dist/src/core/protected-path-guard.js.map +1 -1
  224. package/dist/src/core/registration.d.ts +25 -0
  225. package/dist/src/core/reindex.d.ts +99 -0
  226. package/dist/src/core/sampler.d.ts +313 -0
  227. package/dist/src/core/sandbox.d.ts +290 -0
  228. package/dist/src/core/seal.d.ts +165 -0
  229. package/dist/src/core/state.d.ts +505 -0
  230. package/dist/src/core/task-file.d.ts +185 -0
  231. package/dist/src/core/telegram-config.d.ts +93 -0
  232. package/dist/src/core/token.d.ts +409 -0
  233. package/dist/src/core/token.js +21 -38
  234. package/dist/src/core/token.js.map +1 -1
  235. package/dist/src/core/validate.d.ts +138 -0
  236. package/dist/src/core/values.d.ts +137 -0
  237. package/dist/src/core/vault.d.ts +291 -0
  238. package/dist/src/core/verified-snapshot.d.ts +204 -0
  239. package/dist/src/core/verify.d.ts +336 -0
  240. package/dist/src/core/version.d.ts +8 -0
  241. package/dist/src/core/wysiwys.d.ts +370 -0
  242. package/dist/src/daemon/advance-child.d.ts +39 -0
  243. package/dist/src/daemon/advance.d.ts +466 -0
  244. package/dist/src/daemon/audit.d.ts +87 -0
  245. package/dist/src/daemon/daemon.d.ts +1180 -0
  246. package/dist/src/daemon/dark-session.d.ts +64 -0
  247. package/dist/src/daemon/draw-child.d.ts +36 -0
  248. package/dist/src/daemon/draw.d.ts +154 -0
  249. package/dist/src/daemon/git-evidence.d.ts +173 -0
  250. package/dist/src/daemon/git-evidence.js +1 -1
  251. package/dist/src/daemon/projection.d.ts +180 -0
  252. package/dist/src/daemon/prune.d.ts +207 -0
  253. package/dist/src/mcp/http.d.ts +113 -0
  254. package/dist/src/mcp/server.d.ts +265 -0
  255. package/dist/src/mcp/server.js +9 -1
  256. package/dist/src/mcp/server.js.map +1 -1
  257. package/docs/adapter-api.md +106 -0
  258. package/docs/cli-reference.md +389 -36
  259. package/docs/codex-enforced-session.md +30 -0
  260. package/package.json +12 -2
  261. package/schema/codex-instance.schema.json +82 -0
  262. package/schema/event.schema.json +2 -1
  263. package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
  264. package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
  265. package/schema/policy.schema.json +21 -1
  266. package/templates/codex/README.md +9 -0
@@ -0,0 +1,185 @@
1
+ /**
2
+ * The task-file writer: round-trip rewriting that preserves everything it does
3
+ * not own (SPEC.md §6, APRV-61).
4
+ *
5
+ * SPEC.md §6 is a MUST: "Implementations MUST preserve unknown frontmatter keys
6
+ * when rewriting files." `core/frontmatter.ts` is the reader and is read-only in
7
+ * the strongest sense; this module is its counterpart, and the only place in the
8
+ * codebase that produces new task-file bytes.
9
+ *
10
+ * The bar is not "preserve the keys we can think of". Backlog.md 1.49.3 itself
11
+ * fails this MUST — the `envelope-edit-before` / `envelope-edit-after` fixtures
12
+ * record it dropping our whole `approval:` key on an unrelated `task edit` — and
13
+ * we extend that convention rather than fork it, so the writer that has to be
14
+ * trustworthy is ours.
15
+ *
16
+ * ## Why lines, not a YAML document
17
+ *
18
+ * The obvious implementation reserialises the frontmatter through the `yaml`
19
+ * library's Document API. It preserves more than a naive `parse`/`stringify`
20
+ * round trip, but it does not preserve *bytes*: quoting style, intra-line
21
+ * spacing, comment placement, and blank-line runs are all reconstructed from the
22
+ * library's own defaults. Every one of those is a spurious diff in a user's git
23
+ * history, and a diff nobody can explain is how a board tool's metadata gets
24
+ * quietly eaten.
25
+ *
26
+ * So this module treats the frontmatter as **lines with their terminators**, and
27
+ * the parsed YAML only as an oracle:
28
+ *
29
+ * - the hardened parser decides whether the block is *structurally* valid at
30
+ * all (and, being hardened, rejects duplicate keys, tags, and unbounded
31
+ * aliases before any of them can reach a rewrite);
32
+ * - a column-0 line scan finds the `approval:` key's line range;
33
+ * - only that range is rewritten, and for a state-only edit only the single
34
+ * `state:` line inside it;
35
+ * - every other line — every other key, its order, its quoting, its comments,
36
+ * the blank lines between them, both `---` delimiters, and the entire body
37
+ * after the closing delimiter — is re-emitted as the exact bytes that came
38
+ * in, terminator included.
39
+ *
40
+ * Byte-identity is therefore a property of the construction, not of a
41
+ * comparison performed afterwards: untouched lines are never parsed and never
42
+ * rebuilt. The corpus test (`tests/task-file.test.ts`) still asserts it against
43
+ * every real Backlog.md fixture, because a construction argument that is not
44
+ * checked is a construction argument that has already drifted.
45
+ *
46
+ * ## What "unknown key" means here
47
+ *
48
+ * Every frontmatter key except `approval:` is unknown to this writer, and that
49
+ * is deliberate. `id`, `title`, `status`, `milestone`, `ordinal`,
50
+ * `parent_task_id` and the rest belong to Backlog.md; a key some future board
51
+ * tool invents belongs to it. This module has no allow-list of keys it tolerates
52
+ * — it has one key it owns and rewrites, and it cannot express a change to
53
+ * anything else. There is no edit in {@link TaskFileEdit} that removes a key,
54
+ * reorders keys, or touches the body.
55
+ *
56
+ * ## This writer never touches the log
57
+ *
58
+ * A task file is a **projection** (SPEC.md §6.3): the daemon writes `state:`
59
+ * into the file *after* the event is appended, never the reverse. Nothing here
60
+ * opens `.approval/`, appends to `events.jsonl`, or computes a hash chain.
61
+ * {@link rewriteTaskFile} is a pure function of (bytes, edit) with no clock, no
62
+ * network, and no filesystem access at all; {@link writeTaskFileAtomic} writes
63
+ * exactly the one path it is handed.
64
+ *
65
+ * Determinism: same input bytes and same edit, same output bytes, always. Never
66
+ * throws — every failure is a structured result carrying one of the codes in
67
+ * {@link TaskFileErrorCode}.
68
+ */
69
+ import type { EnvelopeState } from "../daemon/projection.js";
70
+ /** The frontmatter key this module owns. Everything else is preserved verbatim. */
71
+ export declare const ENVELOPE_KEY = "approval";
72
+ /** The schema id (`schema/envelope.schema.json`) the result is validated against. */
73
+ export declare const ENVELOPE_SCHEMA_ID = "envelope";
74
+ /**
75
+ * Why a rewrite was refused. Closed union, pinned by `tests/task-file.test.ts`:
76
+ * a caller distinguishing "this file has no envelope yet" from "this file is
77
+ * corrupt" must be able to do so mechanically.
78
+ */
79
+ export type TaskFileErrorCode =
80
+ /** The file does not begin with a `---` line: no frontmatter to rewrite. */
81
+ "no-frontmatter"
82
+ /** An opening `---` with no closing `---` before end of file. */
83
+ | "unterminated"
84
+ /** The frontmatter is not parseable under the hardened YAML settings. */
85
+ | "yaml-error"
86
+ /** The frontmatter parsed, but not to a mapping. */
87
+ | "not-a-map"
88
+ /** A state edit was asked for and the file carries no `approval:` key. */
89
+ | "no-envelope"
90
+ /** An `approval:` key exists and its value is not a mapping. */
91
+ | "envelope-not-a-map"
92
+ /** The `approval:` block is not block-style mapping this writer can line-edit. */
93
+ | "unsupported-shape"
94
+ /** The envelope the edit would produce fails `envelope.schema.json`. */
95
+ | "invalid-envelope"
96
+ /** The envelope could not be serialised to YAML. */
97
+ | "serialize-failed"
98
+ /** Self-check: the rewritten bytes did not re-read as the intended document. */
99
+ | "round-trip-failed"
100
+ /** An unexpected throw, converted rather than propagated. */
101
+ | "internal-error"
102
+ /** {@link writeTaskFileAtomic} only: the bytes did not reach the disk. */
103
+ | "write-failed";
104
+ /** Outcome of {@link rewriteTaskFile}. */
105
+ export type RewriteResult = {
106
+ ok: true;
107
+ bytes: string;
108
+ changed: boolean;
109
+ } | {
110
+ ok: false;
111
+ code: TaskFileErrorCode;
112
+ message: string;
113
+ };
114
+ /**
115
+ * The changes this writer can express. Deliberately tiny, and deliberately
116
+ * additive: there is no edit that removes the envelope, removes any other key,
117
+ * reorders keys, or reaches the body.
118
+ *
119
+ * - `none` — rewrite nothing. The output is the input, byte for byte, once the
120
+ * frontmatter has been confirmed structurally sound. Used to prove the reader
121
+ * and the writer agree on a file before anything is changed.
122
+ * - `set-state` — replace the value on the envelope's direct `state:` line
123
+ * (SPEC.md §6.3). Exactly one line of the file changes.
124
+ * - `set-envelope` — write the whole `approval:` subtree, inserting the key when
125
+ * the file has none.
126
+ */
127
+ export type TaskFileEdit = {
128
+ kind: "none";
129
+ } | {
130
+ kind: "set-state";
131
+ state: EnvelopeState;
132
+ } | {
133
+ kind: "set-envelope";
134
+ envelope: Record<string, unknown>;
135
+ };
136
+ /** Options accepted by {@link rewriteTaskFile}. */
137
+ export interface RewriteOptions {
138
+ /** Schema directory, forwarded to {@link validate}. Injectable for tests. */
139
+ schemaDir?: string;
140
+ }
141
+ /**
142
+ * Rewrite a task file's `approval:` subtree, preserving everything else byte for
143
+ * byte.
144
+ *
145
+ * Returns the new bytes, plus `changed` so a caller can skip a pointless write.
146
+ * On any refusal the input is untouched and nothing has been written anywhere:
147
+ * this function does not touch the filesystem at all.
148
+ *
149
+ * Insertion position (`set-envelope` on a file with no `approval:` key): the key
150
+ * is appended as the **last** top-level key, immediately before the closing
151
+ * delimiter. The corpus is the reason this needed a decision rather than a
152
+ * default. Backlog.md does not append its own new keys — in `milestone-assign/`
153
+ * the CLI put `milestone:` between `labels:` and `dependencies:`, because it
154
+ * rewrites frontmatter from its own model in its own canonical order — and at
155
+ * 1.49.3 it does not preserve unknown keys at all, so no position we pick
156
+ * survives its next edit. Given that no position is safe from the CLI, the
157
+ * choice is made on the diff: last keeps a multi-line block out of the middle of
158
+ * the board's short scalar keys, so inserting it moves no existing line, and it
159
+ * matches where the hand-written `envelope-edit-before` fixture put the envelope.
160
+ */
161
+ export declare function rewriteTaskFile(text: string, edit: TaskFileEdit, options?: RewriteOptions): RewriteResult;
162
+ /** Outcome of {@link writeTaskFileAtomic}. */
163
+ export type WriteTaskFileResult = {
164
+ ok: true;
165
+ path: string;
166
+ bytes: number;
167
+ } | {
168
+ ok: false;
169
+ code: TaskFileErrorCode;
170
+ message: string;
171
+ };
172
+ /**
173
+ * Write task-file bytes atomically: temp file in the destination directory, then
174
+ * rename.
175
+ *
176
+ * The same idiom as `core/payload-store.ts` and `channels/render-queue.ts`, for
177
+ * the same reason: a reader — a board tool, an editor, the next agent — sees
178
+ * either the previous complete file or the new one, never a half-written task.
179
+ * The temp name is the only non-deterministic thing here and it never reaches
180
+ * the file's contents; it is removed on every failure path, so a failed write
181
+ * leaves no debris beside the task.
182
+ *
183
+ * Writes this one path and nothing else. It does not open the log.
184
+ */
185
+ export declare function writeTaskFileAtomic(path: string, bytes: string): WriteTaskFileResult;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Telegram configuration NAMES (SPEC.md §5.1 `channels.telegram.token_env` /
3
+ * `chat_id_env`, amended §5.2 by APRV-72).
4
+ *
5
+ * Constants and resolvers live in core, not in `channels/`, because more than
6
+ * one layer needs them: the Telegram CLI, doctor, and `approval env` (APRV-73)
7
+ * all ask "which variable holds the token?" and none of them may answer it
8
+ * differently. Placing them here also keeps `core/` free of any import from
9
+ * `channels/`, in the spirit of `tests/layering.test.ts`.
10
+ *
11
+ * NAMES only, in both directions: a policy that carried the token would be a
12
+ * bot credential in a file agents may read, which is exactly what §5.1's
13
+ * name-indirection exists to prevent. A policy that failed to load names
14
+ * nothing, so the default applies: a variable name is not a permission, and
15
+ * treating it as one would mean an unrelated policy typo locked the operator
16
+ * out of their own channel. This is `passphraseEnvFor`'s argument, verbatim,
17
+ * for the same reason: these are the same kind of key.
18
+ *
19
+ * Nothing here reads `process.env`. The CLI layer takes the name and looks
20
+ * the value up.
21
+ */
22
+ import type { CredentialSpec } from "./credential-spec.js";
23
+ import type { PolicyLoadResult } from "./policy-load.js";
24
+ /**
25
+ * The environment variable the bot token is read from when the policy declares
26
+ * no `channels.telegram.token_env`. A DEFAULT, not a fixed name: see
27
+ * {@link telegramTokenEnvFor}.
28
+ */
29
+ export declare const TELEGRAM_TOKEN_ENV = "APPROVAL_TG_TOKEN";
30
+ /**
31
+ * The environment variable the approver chat id is read from when the policy
32
+ * declares no `channels.telegram.chat_id_env`. Also a default.
33
+ */
34
+ export declare const TELEGRAM_CHAT_ENV = "APPROVAL_TG_CHAT";
35
+ /** The NAME of the variable this policy says the bot token lives in. */
36
+ export declare function telegramTokenEnvFor(load: PolicyLoadResult): string;
37
+ /** The NAME of the variable this policy says the approver chat id lives in. */
38
+ export declare function telegramChatEnvFor(load: PolicyLoadResult): string;
39
+ /**
40
+ * How a listener puts the pending set in front of the approver (APRV-216).
41
+ *
42
+ * `paced` sends one summary line and the oldest pending request, and the next
43
+ * one only once that request is decided, skipped, or passed over. `burst` is
44
+ * the pre-APRV-216 behaviour: every pending request this process has not sent
45
+ * yet, on every cycle, behind the APRV-196 re-delivery banner.
46
+ *
47
+ * Not a credential and not a name, so it sits beside the two that are for one
48
+ * reason: it is the third thing a caller asks the policy about the Telegram
49
+ * channel, and a second resolver module would be a second place for the
50
+ * fallback rule to drift.
51
+ */
52
+ export type TelegramDelivery = "paced" | "burst";
53
+ /**
54
+ * What an absent `channels.telegram.delivery` means.
55
+ *
56
+ * Paced, since APRV-216. The incident behind it is the one APRV-196 softened
57
+ * rather than closed: a restart with six pending policy edits put six prompts
58
+ * on a phone at once, and an approver reading a wall of near-identical
59
+ * questions is an approver who taps rather than reads. A default is a claim
60
+ * about which failure is worse, and the worse one here is inattentive approval
61
+ * rather than a slower queue: pacing withholds nothing, because every request
62
+ * stays pending in the log whether or not it has been shown, and `/queue`
63
+ * lists the whole set on demand.
64
+ */
65
+ export declare const TELEGRAM_DEFAULT_DELIVERY: TelegramDelivery;
66
+ /**
67
+ * The delivery mode this policy declares, or the default.
68
+ *
69
+ * Fail-soft in the same direction as the two name resolvers above: a policy
70
+ * that did not load declares nothing, and a delivery mode is not a permission,
71
+ * so an unrelated policy typo must not decide how requests are shown. The
72
+ * schema closes the enum, so a policy that LOADED can only carry one of the
73
+ * two; anything else reaching here (a hand-built load result, a key from a
74
+ * later version) falls back rather than being guessed at.
75
+ */
76
+ export declare function telegramDeliveryFor(load: PolicyLoadResult): TelegramDelivery;
77
+ /**
78
+ * The Telegram channel's credential manifest (APRV-79).
79
+ *
80
+ * The same shape an adapter declares (`core/credential-spec.ts`), for the same
81
+ * reason: `approval setup channel telegram` runs the shared conversation in
82
+ * `cli/setup-flow.ts`, and that conversation is DERIVED from a manifest. What
83
+ * differs from an adapter's is the destination and not the vocabulary — a
84
+ * channel's two values go to the OS keystore and `.approval/env`, because a
85
+ * channel holds no state and its token is what unlocks the machine, while an
86
+ * adapter's go to the vault (SPEC.md §4, §10.3, §10.4).
87
+ *
88
+ * The NAMES are the policy's, resolved through the two functions above, so the
89
+ * checklist an operator reads names the variables their own policy declares.
90
+ * A spec carries no value and no default on either entry: the token is the
91
+ * operator's and the chat id is discovered.
92
+ */
93
+ export declare function telegramCredentialSpecs(load: PolicyLoadResult): CredentialSpec[];