polydeukes 0.6.0 → 0.7.1

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 (117) hide show
  1. package/README.ko.md +30 -70
  2. package/README.md +31 -84
  3. package/dist/baseline.d.ts +37 -1
  4. package/dist/baseline.js +68 -1
  5. package/dist/bin.d.ts +3 -4
  6. package/dist/bin.js +79 -93
  7. package/dist/covenant/bash-line.d.ts +130 -0
  8. package/dist/covenant/bash-line.js +566 -0
  9. package/dist/covenant/declaration-engine.d.ts +108 -0
  10. package/dist/covenant/declaration-engine.js +415 -0
  11. package/dist/covenant/discipline.d.ts +103 -0
  12. package/dist/covenant/discipline.js +667 -0
  13. package/dist/covenant/dispatch.d.ts +153 -0
  14. package/dist/covenant/dispatch.js +237 -0
  15. package/dist/covenant/extract-steps.d.ts +122 -0
  16. package/dist/covenant/extract-steps.js +0 -0
  17. package/dist/covenant/mention.d.ts +117 -0
  18. package/dist/covenant/mention.js +209 -0
  19. package/dist/covenant/module.d.ts +25 -0
  20. package/dist/covenant/module.js +23 -0
  21. package/dist/covenant/mutation-rules.d.ts +40 -0
  22. package/dist/covenant/mutation-rules.js +146 -0
  23. package/dist/covenant/relations.d.ts +46 -0
  24. package/dist/covenant/relations.js +68 -0
  25. package/dist/covenant/run-covenant.d.ts +95 -0
  26. package/dist/covenant/run-covenant.js +164 -0
  27. package/dist/covenant/self-mod.d.ts +55 -0
  28. package/dist/covenant/self-mod.js +84 -0
  29. package/dist/covenant/shell-evidence.d.ts +38 -0
  30. package/dist/covenant/shell-evidence.js +247 -0
  31. package/dist/covenant/shell-mod.d.ts +84 -0
  32. package/dist/covenant/shell-mod.js +213 -0
  33. package/dist/covenant/supply.d.ts +54 -0
  34. package/dist/covenant/supply.js +50 -0
  35. package/dist/covenant/transcript-mod.d.ts +60 -0
  36. package/dist/covenant/transcript-mod.js +254 -0
  37. package/dist/covenant/ttl-witness.d.ts +47 -0
  38. package/dist/covenant/ttl-witness.js +80 -0
  39. package/dist/covenant-check.d.ts +72 -39
  40. package/dist/covenant-check.js +260 -121
  41. package/dist/diff-ir.d.ts +28 -0
  42. package/dist/diff-ir.js +310 -0
  43. package/dist/docs/README.ko.md +60 -0
  44. package/dist/docs/README.md +64 -0
  45. package/dist/docs/catalog.json +480 -0
  46. package/dist/docs/concepts/judgment.ko.md +113 -0
  47. package/dist/docs/concepts/judgment.md +113 -0
  48. package/dist/docs/how-to/configure-project.ko.md +99 -0
  49. package/dist/docs/how-to/configure-project.md +98 -0
  50. package/dist/docs/how-to/connect-surfaces.ko.md +120 -0
  51. package/dist/docs/how-to/connect-surfaces.md +121 -0
  52. package/dist/docs/how-to/write-disciplines.ko.md +179 -0
  53. package/dist/docs/how-to/write-disciplines.md +181 -0
  54. package/dist/docs/index.json +2280 -0
  55. package/dist/docs/reference/cli/covenant-check.ko.md +148 -0
  56. package/dist/docs/reference/cli/covenant-check.md +153 -0
  57. package/dist/docs/reference/cli/docs.ko.md +97 -0
  58. package/dist/docs/reference/cli/docs.md +95 -0
  59. package/dist/docs/reference/cli/explain.ko.md +84 -0
  60. package/dist/docs/reference/cli/explain.md +89 -0
  61. package/dist/docs/reference/cli/init.ko.md +119 -0
  62. package/dist/docs/reference/cli/init.md +119 -0
  63. package/dist/docs/reference/configuration/index.ko.md +553 -0
  64. package/dist/docs/reference/{configuration.md → configuration/index.md} +215 -90
  65. package/dist/docs/reference/packages/adapter-claude-code.ko.md +110 -0
  66. package/dist/docs/reference/{adapter-claude-code.md → packages/adapter-claude-code.md} +47 -19
  67. package/dist/docs/reference/packages/adapter-grok.ko.md +77 -0
  68. package/dist/docs/reference/packages/adapter-grok.md +79 -0
  69. package/dist/docs/reference/packages/core.ko.md +174 -0
  70. package/dist/docs/reference/{core.md → packages/core.md} +74 -12
  71. package/dist/docs/reference/packages/polydeukes.ko.md +195 -0
  72. package/dist/docs/reference/packages/polydeukes.md +204 -0
  73. package/dist/docs/reference/packages/sdk-ts.ko.md +161 -0
  74. package/dist/docs/reference/packages/sdk-ts.md +164 -0
  75. package/dist/docs/troubleshooting.ko.md +144 -0
  76. package/dist/docs/troubleshooting.md +103 -151
  77. package/dist/docs/tutorials/first-judgment.ko.md +82 -0
  78. package/dist/docs/tutorials/first-judgment.md +81 -0
  79. package/dist/docs-catalog.d.ts +25 -0
  80. package/dist/docs-catalog.js +450 -0
  81. package/dist/docs-library.d.ts +23 -0
  82. package/dist/docs-library.js +347 -0
  83. package/dist/docs-markdown.d.ts +32 -0
  84. package/dist/docs-markdown.js +150 -0
  85. package/dist/docs-query.d.ts +11 -40
  86. package/dist/docs-query.js +28 -122
  87. package/dist/docs-types.d.ts +105 -0
  88. package/dist/docs-types.js +2 -0
  89. package/dist/explain.d.ts +1 -1
  90. package/dist/explain.js +37 -22
  91. package/dist/load-config.d.ts +1 -1
  92. package/dist/load-config.js +1 -1
  93. package/dist/pre-state-reader.d.ts +1 -1
  94. package/dist/pre-state-reader.js +1 -1
  95. package/dist/scaffold-project.d.ts +2 -2
  96. package/dist/scaffold-project.js +9 -3
  97. package/dist/schema/polydeukes.schema.json +12 -0
  98. package/dist/worktree-reader.d.ts +19 -0
  99. package/dist/worktree-reader.js +30 -0
  100. package/package.json +3 -17
  101. package/dist/claude-code-hook.d.ts +0 -79
  102. package/dist/claude-code-hook.js +0 -372
  103. package/dist/claude-code.d.ts +0 -6
  104. package/dist/claude-code.js +0 -6
  105. package/dist/covenant-module.d.ts +0 -25
  106. package/dist/covenant-module.js +0 -42
  107. package/dist/docs/configuration.md +0 -103
  108. package/dist/docs/installation.md +0 -241
  109. package/dist/docs/reference/adapter-git.md +0 -100
  110. package/dist/docs/reference/covenant.md +0 -116
  111. package/dist/docs/reference/polydeukes.md +0 -315
  112. package/dist/index.d.ts +0 -22
  113. package/dist/index.js +0 -21
  114. package/dist/init-claude-code.d.ts +0 -52
  115. package/dist/init-claude-code.js +0 -469
  116. package/dist/init-grok.d.ts +0 -51
  117. package/dist/init-grok.js +0 -242
@@ -1,241 +0,0 @@
1
- # Installing Polydeukes
2
-
3
- **English** · [한국어](./installation.ko.md)
4
-
5
- > Alpha. This guide covers the install paths that ship today, and everything here is the
6
- > measured behaviour of the published packages.
7
-
8
- This is the getting-started layer: from an empty project to a first judged call.
9
-
10
- One devDependency, one command per surface. The umbrella package `polydeukes` is the only
11
- thing you install — it carries the core, the judge, and the adapters as its own
12
- dependencies, and `pdks` is its CLI (an alias of `polydeukes`).
13
-
14
- **Two surfaces ship, for two different situations — pick the one that matches how the
15
- project is developed.** A project built alongside an AI partner in Claude Code wires the
16
- **session surface**: a PreToolUse hook that judges every editing tool call and shell
17
- command as it is declared. A project you develop yourself wires the **commit surface**: a
18
- pre-commit hook that judges the staged diff, so the discipline you declared for yourself
19
- is applied at the moment work becomes history. They enforce the same config vocabulary,
20
- but they answer different situations — there is no general reason to wire both in one
21
- project.
22
-
23
- ## Prerequisites
24
-
25
- - **Node.js ≥ 24** — the engines floor of every published package.
26
- - **A package manager** — pnpm and npm both work; examples below use pnpm.
27
- - **Claude Code** — only for the session surface. The commit surface needs no AI tool at
28
- all: just git and a way to run a pre-commit hook.
29
-
30
- ## Install
31
-
32
- ```sh
33
- pnpm add -D polydeukes
34
- ```
35
-
36
- (or `npm install --save-dev polydeukes`.)
37
-
38
- This must be a real project dependency, not a one-off `npx` run — both surfaces load the
39
- judge from your project's own installed package.
40
-
41
- ## The session surface — developing with an AI partner
42
-
43
- From the project root:
44
-
45
- ```sh
46
- pnpm exec pdks init claude-code # Claude Code
47
- pnpm exec pdks init grok # Grok
48
- ```
49
-
50
- The command installs into the directory it is invoked from, and it proves the `polydeukes`
51
- package resolves there **before writing anything** — if it does not (say, the install step
52
- was skipped), it prints the install command and exits 2 with zero files written, never a
53
- half-wired tree.
54
-
55
- Nothing existing is overwritten. What exists is reported and kept — the hook, the config,
56
- and the discipline files are left alone, the settings file is merged, and `.gitignore` is
57
- only ever appended to — so re-running is always safe. One command-field exception: if
58
- `.grok/hooks/covenant-pretooluse.json` still names the grok delegator and a Claude
59
- delegator is on disk, the JSON `command` is rewritten to that Claude file so the host
60
- does not spawn two judges. Grok collapses two registrations only when `command` AND
61
- `matcher` are identical, so every grok entry naming that Claude file also takes the matcher
62
- of the `.claude/settings.json` entry that registers the same command — on a fresh write and
63
- on every re-run; `timeout` stays. A command you pointed elsewhere is left as it was.
64
-
65
- `pdks init claude-code` writes six artifacts:
66
-
67
- | Artifact | What it is |
68
- |---|---|
69
- | `.claude/hooks/covenant-pretooluse.mjs` | The hook — a thin delegator that loads the judge from the installed package. Upgrading the package upgrades the judge; this file never changes. |
70
- | `.claude/settings.json` | The PreToolUse registration for editing tools and shell calls. **Merged, never replaced** — your other hooks and permissions stay. |
71
- | `polydeukes.config.yaml` | The starter protection policy: a placeholder `languages` block, a minimum `protectedPaths` list, and the witness block. The comments in the file explain why each entry is there. |
72
- | `.claude/rules/polydeukes.md` | A scoped discipline file telling your AI partner that `pdks docs` exists and which topic answers what. It carries `paths` frontmatter, so it loads when a Polydeukes path is in play rather than sitting in every session's context. |
73
- | `.claude/skills/discipline-draft/SKILL.md` | The classification procedure. Describe a recurring problem to your AI partner and it lands as a config entry — judged at advise when a current family can express it, a `draft: true` entry otherwise — and the same file tells the agent to consult `advised` rows in the telemetry log at task boundaries. |
74
- | `.gitignore` | An appended ignore rule for `.polydeukes/`, with its comment line — telemetry is local observation data and never belongs in history. |
75
-
76
- `pdks init grok` shares the scaffold (config and the ignore line) and writes Grok's own
77
- registration. A Grok-only tree has four artifacts, and no `.claude/` directory:
78
-
79
- | Artifact | What it is |
80
- |---|---|
81
- | `.grok/hooks/covenant-pretooluse.mjs` | The hook — the same delegator text, only when no Claude delegator is already on disk. |
82
- | `.grok/hooks/covenant-pretooluse.json` | The PreToolUse matcher, `timeout` 60 (the host default is 5 seconds, and a timed-out hook fails open), and the command that names one delegator file. In a tree that also has `.claude/settings.json`, the matcher is copied from the settings entry with the same command — Grok reads that file too, and collapses the two registrations into one spawn only when `command` and `matcher` match exactly. That copy leans on Grok's tool-name aliases, so if you later remove `.claude/settings.json`, delete this JSON and run `pdks init grok` again to get the Grok-native matcher back. |
83
- | `polydeukes.config.yaml` | The same starter policy as above. |
84
- | `.gitignore` | The same appended ignore line. |
85
-
86
- If `.claude/hooks/covenant-pretooluse.mjs` already exists, the JSON command points at that
87
- file instead of planting a second one. A later `pdks init grok` or `pdks init claude-code`
88
- retargets an installer-generated grok-mjs command the same way.
89
-
90
- An already-open Grok session keeps the hook snapshot from start. Reload from the Hooks tab
91
- (`r`) or start a new session. The witness valve does not open on Grok — the session log is
92
- ACP `updates.jsonl`, not Claude's JSONL. A block is recovered from another terminal or the
93
- commit-surface TTY.
94
-
95
- ## First edit — `languages`
96
-
97
- The generated config ships a placeholder language profile, because the installer cannot
98
- know your stack:
99
-
100
- ```yaml
101
- languages:
102
- placeholder:
103
- productionGlob: 'src/**'
104
- testCmd: 'echo "set a verification command for {scope}"'
105
- ```
106
-
107
- Rename the key to your language, point `productionGlob` at your production sources, and put
108
- your real verification command in `testCmd`. (On the commit-surface path you write this
109
- block yourself as part of the config below.) The placeholder is valid as generated and no
110
- judgment path reads these values yet, so it cannot produce a wrong verdict while it waits —
111
- but `languages` is the schema's one required block, so *removing* it (or emptying it) makes
112
- the config invalid, and an invalid config blocks every call. Edit it, don't delete it.
113
-
114
- ## The commit surface — developing by yourself
115
-
116
- This path is for applying your own discipline to your own commits — no AI tool involved.
117
- It has no installer today; the wiring is two small manual steps.
118
-
119
- **First, the config.** Create `polydeukes.config.yaml` at the project root (there is no
120
- generator on this path — the file is yours from the first line):
121
-
122
- ```yaml
123
- languages:
124
- typescript:
125
- productionGlob: 'src/**'
126
- testCmd: 'pnpm test'
127
-
128
- # Judged at commit time: a staged change to these paths stops the commit
129
- # until you answer the witness prompt in person.
130
- protectedPaths:
131
- - 'db/migrations'
132
-
133
- witness:
134
- token: 'pdks witness'
135
- ttlMinutes: 10
136
- ```
137
-
138
- Add `.polydeukes/` to your `.gitignore` too — telemetry is local observation data.
139
-
140
- **Then, the hook.** One command judges what is currently staged and exits 2 on a broken
141
- covenant:
142
-
143
- ```sh
144
- pnpm exec pdks covenant check
145
- ```
146
-
147
- Register it as a pre-commit hook. With **lefthook**:
148
-
149
- ```yaml
150
- # lefthook.yml
151
- pre-commit:
152
- commands:
153
- covenant:
154
- priority: 1
155
- interactive: true # keep the witness prompt visible — see below
156
- run: ./node_modules/.bin/pdks covenant check
157
- ```
158
-
159
- With **husky**:
160
-
161
- ```sh
162
- # .husky/pre-commit
163
- ./node_modules/.bin/pdks covenant check
164
- ```
165
-
166
- With plain **`.git/hooks`** (make it executable):
167
-
168
- ```sh
169
- #!/bin/sh
170
- # .git/hooks/pre-commit
171
- ./node_modules/.bin/pdks covenant check
172
- ```
173
-
174
- Three things to know about this surface:
175
-
176
- - **The valve is a TTY prompt.** At the default `block` level, a commit that stages a
177
- protected change stops at a prompt only a human at a terminal can answer. Configure your
178
- hook runner so it does not swallow that prompt (lefthook needs `interactive: true`).
179
- - **Declarations judge here.** A staged diff carries file changes and nothing else, so
180
- protection lists and every `declare` entry over the change judge in full. A declaration
181
- scoped on `command` has no command line to read in a staged diff and observes nothing on
182
- this surface, and a declaration that reads the session (`precedent` and the other history
183
- mechanisms) is recorded as `skipped` — declare those where an AI partner's session exists
184
- to be judged.
185
- - **The commit surface has its own additive scope.** Paths that are fine to edit freely
186
- but whose promotion into history deserves a judged checkpoint go under the adapter
187
- namespace, judged on top of the shared list:
188
-
189
- ```yaml
190
- adapters:
191
- git:
192
- protectedPaths:
193
- - 'src/policy'
194
- ```
195
-
196
- ## The witness valve
197
-
198
- Both surfaces carry the same valve, spelled for their situation. It sits **after** the
199
- verdict — only a judgment that actually blocked can be witnessed open — and every allowance
200
- is recorded as `witnessed`, never silent.
201
-
202
- ```yaml
203
- witness:
204
- token: 'pdks witness'
205
- ttlMinutes: 10
206
- ```
207
-
208
- - **Session surface:** a human types the token so it stands alone on the first line of a
209
- conversation message; the window holds for `ttlMinutes`, then blocking resumes. An agent
210
- cannot open the valve for itself — only human-authored messages count.
211
- - **Commit surface:** the blocked commit shows a TTY prompt, and typing the full token
212
- there opens that one commit.
213
-
214
- Change the token and window as you like — the token is not a secret; the defence is
215
- provenance, not confidentiality. **Keep the block**: on the session surface the generated
216
- protection list covers `.claude/hooks` and `.grok/hooks`, so without a valve the first
217
- blocked call would freeze the project until a human edits the config from their own
218
- terminal.
219
-
220
- ## Prove the gate is live
221
-
222
- Prove it once on the surface you wired, then read the telemetry.
223
-
224
- - **Session surface:** ask your agent to append a line to
225
- `.claude/hooks/covenant-pretooluse.mjs` (a protected path). The call must come back
226
- blocked.
227
- - **Commit surface:** stage an edit to a path on your protection list and run
228
- `git commit`. It must stop at the witness prompt (answer it, or abort with Ctrl-C).
229
-
230
- ```sh
231
- cat .polydeukes/roi.log
232
- ```
233
-
234
- Every judgment appends exactly one record — `passed`, `blocked`, `witnessed`, `advised`, or
235
- `skipped` — so the block you just caused is the last line. A gate you have watched block
236
- once is a gate you know is wired.
237
-
238
- From here: [the configuration guide](./configuration.md) for the file and its wiring,
239
- [the configuration reference](./reference/configuration.md) for every field and for
240
- writing your own disciplines, and [troubleshooting](./troubleshooting.md) when something
241
- blocks and you don't know why.
@@ -1,100 +0,0 @@
1
- # `@polydeukes/adapter-git`
2
-
3
- **English** · [한국어](./adapter-git.ko.md)
4
-
5
- > **The commit surface's translator** — a staged diff becomes the covenant input IR, and
6
- > the `adapters.git` config namespace is defined here.
7
- >
8
- > Alpha. A transitive dependency of the umbrella: you do not install it and you do not
9
- > import it. The commit surface reaches it through
10
- > [`pdks covenant check`](./polydeukes.md#pdks-covenant-check).
11
-
12
- ## What this package owns
13
-
14
- The boundary where git's vocabulary is translated away. A staged diff becomes the same
15
- agent-neutral input IR the session surface produces — the same judgment for every hand,
16
- AI or human.
17
-
18
- | Unit | What it does |
19
- |---|---|
20
- | Staged-change collection | Reads the staging area into a list of changes with their content baselines |
21
- | Pure translation | Folds those changes into one `CovenantInput` |
22
- | Settings vocabulary | Validates this adapter's own config namespace |
23
-
24
- This is a pure library. It knows the staged-diff shape and nothing about installation, hook
25
- runners, or valves — wiring it into a pre-commit hook is a deployment act that lives in the
26
- umbrella. It names `@polydeukes/core` as a `peerDependency`: the vocabulary is shared with
27
- the judge, not installed a second time here.
28
-
29
- ## Collection and the `adapters.git` namespace
30
-
31
- **Three collectors, one shape.** `collectStagedChanges`, `collectWorktreeChanges`, and
32
- `collectRangeChanges({ repoRoot, range: '<base>..<head>' | '<base>...<head>' })` each return the same
33
- `StagedChange[]`, so the translator and everything after it is one path.
34
-
35
- | Collector | `pre` | `post` | Also |
36
- |---|---|---|---|
37
- | staged | HEAD blob | The **staged** blob — never the worktree, which may have diverged after `git add` | |
38
- | worktree | HEAD blob | The bytes on disk | Untracked, non-ignored files join as `added`; a file missing from disk is `deleted`, whether HEAD held it or only the index did; an unreadable path (a dangling symlink) yields null content and is judged on its path |
39
- | range | base blob | head blob | `...` resolves the base to `git merge-base`; a ref git cannot resolve, or two refs with no merge-base, throws |
40
-
41
- **Collection is deliberately narrow about what it trusts.**
42
-
43
- | Decision | Why |
44
- |---|---|
45
- | `--no-renames` forced on, in every collector | A rename is judged as a deletion plus an addition. A `git mv` of a protected file must not slip through as one opaque rename entry |
46
- | A binary blob or file yields null content | Rather than lossily decoded bytes |
47
- | The unborn first commit narrows to all-added | Rather than throwing — staged and worktree alike |
48
- | A type change (`T`) keeps its `pre` side | A symlink replaced by a file is a modification, so a delta judgment still sees what was removed |
49
- | Every listing ends with `--` | A branch that shares its name with a file is a ref, never an ambiguous argument |
50
-
51
- Translation produces one tool call per change, under the adapter-owned names `staged-write`
52
- and `staged-delete`. A deletion always carries its evidence. A write carries it unless the
53
- staged blob was binary — there is no text to compare, so the call arrives with no
54
- `fileChange` at all and is judged on its path alone, the same as any unproven call.
55
- **The session collections are honestly empty** — the commit surface has no session, and a
56
- key is never fabricated to look like one. There is no `actor` either: the hook cannot tell a
57
- human's `git commit` from an agent's, so it proves none.
58
-
59
- **The namespace is this adapter's own vocabulary.** The core validates only the container
60
- shape — one settings object per adapter — and passes the contents through verbatim, so the
61
- vocabulary, its validator, and its defaults all live here.
62
-
63
- | Key | Values | Default | Means |
64
- |---|---|---|---|
65
- | `adapters.git.enforce` | `block` \| `advise` | `block` | What a commit-surface verdict *does* |
66
- | `adapters.git.protectedPaths` | string[] | `[]` | The commit surface's **additive** protection scope, judged on top of the common list |
67
-
68
- An unknown key, an `enforce` outside the two values, or a `protectedPaths` that is not an
69
- array of strings each fail fast with the full field path. The writing reference is
70
- [the configuration reference's `adapters` section](./configuration.md#adapters).
71
-
72
- **The additive scope is additive for a reason.** The level belongs to the observer, and so
73
- does the scope: entries listed here are judged when work becomes history, and the session
74
- surface never reads them. That is what lets a repository leave judge *sources* editable in
75
- a session while still stopping the commit that promotes them.
76
-
77
- Under `enforce: advise` the valve is structurally absent: a verdict is recorded as
78
- `advised`, one advisory line lands on stderr, and the commit proceeds. Only the verdict is
79
- relaxed — a run that cannot judge still fails closed at exit `2` at either level.
80
-
81
- ## Where the consumer touches it
82
-
83
- - **The `adapters.git` block** in your config.
84
- - **The pre-commit hook** that runs `pdks covenant check`, wired by hand — the manual
85
- procedure for three hook managers is in
86
- [installation](../installation.md#the-commit-surface--developing-by-yourself).
87
-
88
- No import.
89
-
90
- ## Declared limits
91
-
92
- - **A declaration that reads the session cannot be judged here.** A `precedent` needs
93
- session history and a commit has none, so a matching entry records `skipped`. A permanent
94
- condition of this surface, not a fault in the entry.
95
- - **A commit never shows a gitignored file.** Anything outside version control — a built
96
- `dist`, a generated hook script — is invisible to this surface by nature. That is why the
97
- session surface carries those paths on the common list instead.
98
- - **The valve needs a human at a terminal.** No TTY means no prompt and no way through: a
99
- CI run and an agent-spawned `git commit` reach the same closed door. Nothing is ever
100
- persisted, so one answer never covers a later commit.
@@ -1,116 +0,0 @@
1
- # `@polydeukes/covenant`
2
-
3
- **English** · [한국어](./covenant.ko.md)
4
-
5
- > **The judge** — the dispatcher, the discipline library, the meta-covenants, and the TTL
6
- > witness valve.
7
- >
8
- > Alpha. A transitive dependency of the umbrella: you do not install it and you do not
9
- > import it. What you reach is its behaviour, through the `disciplines:` block in your config
10
- > and the rows it writes to `.polydeukes/roi.log`.
11
-
12
- ## What this package owns
13
-
14
- The judge. Everything that turns a declared promise into a verdict lives here.
15
-
16
- **This is the install unit for judgment.** An embedder that wants verdicts without the
17
- umbrella's surfaces installs this package and provides `@polydeukes/core` alongside it — core
18
- is a `peerDependency` here, so the vocabulary is shared rather than duplicated. The umbrella
19
- is what satisfies that peer for an ordinary consumer.
20
-
21
- | Unit | What it does |
22
- |---|---|
23
- | `runCovenant` wrapper | Runs a judge body, translates its non-blocking `1` into the blocking `2`, and logs every call. No covenant runs unmeasured |
24
- | Path-routing dispatcher | Registers covenants against protected paths and runs *every* matching one — no short-circuit, so the telemetry never under-counts |
25
- | Meta-covenants | Three registrations that protect the judging chain itself |
26
- | TTL witness | The time-boxed human valve, consulted only after a verdict blocked |
27
- | Delta layer | New-violation-only judgment over a file's before/after pair |
28
- | Discipline library | Config `disciplines:` entries become enforcement without a line of code |
29
-
30
- ## Discipline families and meta-covenants
31
-
32
- **A `disciplines:` entry is one declaration** — `judge = relate ∘ extract` over the
33
- observation as a world. What the declaration's sources bind decides what evidence the
34
- judgment needs, which is also what decides whether it can be judged on a given surface.
35
-
36
- | Sources | Judges | Evidence needed |
37
- |---|---|---|
38
- | the fixed names `target.path` · `pre` · `post` · `state` · `changes` | The change itself | A file change |
39
- | the fixed name `command` | The shell call's command line | A shell call — an Edit carries none |
40
- | `{ transcript: true }` | Session history — was a qualifying call actually executed *before* this one | A session |
41
- | `{ file: … }` · `{ sidecar: true }` | Another file, or the spawn-record channel | The surface's reader for it |
42
-
43
- The writing guide for these entries is [the configuration reference's `disciplines`
44
- section](./configuration.md#disciplines); the declaration grammar is the core's
45
- `algebra-declaration.schema.json`.
46
-
47
- **Three meta-covenants** protect the judging chain. They are covenants like any other; the
48
- vocabulary below applies to them unchanged.
49
-
50
- | Registration | Axis | Judges |
51
- |---|---|---|
52
- | self-mod | Tool | Mutations to protected paths through editing tools. Only the call's proven mutation target is compared — a protected path inside an edit's *content* is a mention and passes |
53
- | shell-mod | Shell | The same, through a command line. A command mentioning a protected path passes only if its leading word proves it read-only |
54
- | transcript-mod | Transcript | Writes to the live session transcript, judged by whole-path **equality** — never as a protected ancestor |
55
-
56
- **Six words** are the telemetry contract — five verdicts and one observation. A row in
57
- `.polydeukes/roi.log` carries exactly one of them, and the CLI, the docs, and the tests use
58
- the same word for the same event. How to read a row is in
59
- [troubleshooting](../troubleshooting.md#reading-a-verdict).
60
-
61
- | Verdict | Means |
62
- |---|---|
63
- | `passed` | The call was judged and upheld the covenant |
64
- | `blocked` | The call was judged and broke it |
65
- | `witnessed` | A **blocked** verdict a human opened in person. Never silent, never a clean call |
66
- | `advised` | A break recorded without stopping the call — the default for every discipline entry on both surfaces, and the commit surface's outcome under `adapters.git.enforce: advise` |
67
- | `skipped` | The call reached a registration that could not judge it. **Not a pass** — the recorded absence of a judgment |
68
- | `unattributed` | A protected entry's on-disk state moved and no judgment row explains it. **Not a verdict** — no call is blocked or passed by it; the session surface writes it after comparing state against a stored baseline |
69
-
70
- `unattributed` answers a question the other five cannot. They are all written by a judge
71
- about a call it was handed, so a write that arrives without a declared call — through an
72
- interpreter, a test runner's child process, a script that assembles the path from its own
73
- arguments — leaves no row at all. The comparison observes the result rather than the
74
- spelling, so it records that write after the fact. It never blocks: the write already
75
- happened, and the comparison fails open on both sides of the verdict.
76
-
77
- ## Where the consumer touches it
78
-
79
- - **The `disciplines:` block** in your config. One entry compiles into one registration,
80
- carrying its own telemetry label.
81
- - **`protectedPaths`**, which the path-routing dispatcher matches against.
82
- - **The `witness` block**, which arms the TTL valve.
83
- - **`.polydeukes/roi.log`**, where every judgment lands as one row.
84
-
85
- No import. The umbrella assembles this package for both surfaces.
86
-
87
- ## Declared limits
88
-
89
- - **The shell axis leaves `skipped` rows, and that row is the contract.** Predicting a
90
- shell command's target from its text is undecidable, so the invariant this axis holds is
91
- not "nothing gets through" — it is that **no call passes unrecorded**. A new spelling
92
- landing in `skipped` is the declared limit showing itself. A pass with no row at all, or
93
- one recorded `passed` without a judgment, is the defect class.
94
- - **A declaration that reads the session cannot be judged without one.** On the commit
95
- surface there is none, so a matching `precedent` (or any other transcript-reading)
96
- declaration records `skipped` with the reason `supply-pass`. That is a permanent
97
- condition of that surface.
98
- - **A declaration scoped on `command` is absent from the commit surface, and absent without
99
- a row.** A staged diff carries no command line, so no world such a declaration observes is
100
- admitted there. This leaves nothing in `.polydeukes/roi.log`, so the log cannot separate a
101
- command discipline that never triggered from one whose surface never observed a command.
102
- - **A declaration the compiler cannot resolve compiles to a skip registration** — routing
103
- intact, no body: a step outside the registry, an argument outside a step's keys, a pattern
104
- that does not compile, a mechanism whose shape the syntax does not fit. Assembly therefore
105
- never throws: one unresolvable entry cannot take down its siblings, the meta-covenants, and
106
- the valve, which would leave no way to fix the config that caused it. A source the world
107
- lacks at judgment time is a different case — the declaration's own `supply` policy
108
- disposes of it, and with no policy the body answers unjudgeable (exit 2), never upheld.
109
- - **Complete containment is a non-goal.** There are no blocklists here — enumerating bypass
110
- spellings is always one step behind, so the logic is inverted: a mention of a protected
111
- path blocks unless proven safe. Residual vectors such as indirect path computation are
112
- telemetry targets, not block targets. The two friction valves — the read-only allowlist
113
- and the TTL witness — both leave a measurable trace.
114
- - **The valve stands after the verdict.** Only a judgment that actually blocked can be
115
- witnessed open, a mid-sentence mention of the token does not arm it, and an AI can never
116
- open the valve for itself.