polydeukes 0.9.0 → 0.10.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.
@@ -0,0 +1,277 @@
1
+ # Declaration language reference
2
+
3
+ **English** · [한국어](./index.ko.md)
4
+
5
+ Use this reference to write the `declare` block of a discipline. The tables list every supported
6
+ source, extraction step, combinator, relation, and mechanism. For installation and worked examples,
7
+ see [Write disciplines](../../how-to/write-disciplines.md). Project settings and enforcement are in
8
+ the [configuration reference](../configuration/index.md).
9
+
10
+ <a id="declaration-shape"></a>
11
+ ## Declaration structure
12
+
13
+ A judged entry has `id`, `declare`, and optional `why` and `enforce` fields. Put it in the
14
+ [list that can observe its sources](../configuration/index.md#three-lists).
15
+
16
+ | Field inside `declare` | Required | Meaning |
17
+ |---|---|---|
18
+ | `mechanism` | Yes | A name from the mechanism table below; it restricts the allowed axes and relations. |
19
+ | `scope` | No | Select observations using regular expressions over one source. |
20
+ | `sources` | No | Bind names to files or session evidence. |
21
+ | `supply` | No | Choose what happens when a source is absent. Unspecified sources use `error`. |
22
+ | `extract` | Yes | A map from extraction names to non-empty lists of steps. |
23
+ | `relate` | Yes | A non-empty list of comparisons and their diagnostic messages. |
24
+ | `witness` | No | Additional comparisons that can allow a violation through. |
25
+
26
+ The outer `id` names the discipline; do not repeat it as `discipline` inside `declare`.
27
+ Names and keys are case-sensitive. Unknown declaration keys are rejected.
28
+
29
+ This complete entry reports `.db` paths outside `data/`:
30
+
31
+ ```yaml
32
+ disciplines:
33
+ - id: 'database-location'
34
+ why: 'Keep database files under data/.'
35
+ enforce: advise
36
+ declare:
37
+ mechanism: 'naming'
38
+ scope: { source: 'target.path', include: ['\.db$'] }
39
+ extract:
40
+ outside:
41
+ - { op: 'source', of: 'target.path' }
42
+ - { op: 'matches', re: '^(?!data/)' }
43
+ relate:
44
+ - id: 'location'
45
+ relation: { op: 'empty', of: 'outside' }
46
+ message: '{value} must be under data/'
47
+ ```
48
+
49
+ <a id="scope"></a>
50
+ ## Scope
51
+
52
+ | Key | Meaning |
53
+ |---|---|
54
+ | `source` | Required. One of `target.path`, `pre`, `post`, `command`, or a named `file` source. |
55
+ | `include` | Regex strings. At least one must match. Omitted or empty accepts any string. |
56
+ | `exclude` | Regex strings. Any match excludes the observation. Omitted or empty excludes nothing. |
57
+ | `excludeIgnoreCase` | Optional boolean, default `false`. Applies only to `exclude`. |
58
+
59
+ Without `scope`, every observation is eligible. With `scope`, an absent source does not match.
60
+ These patterns are regular expressions, not path globs. `include` is always case-sensitive.
61
+
62
+ <a id="fixed-sources"></a>
63
+ ## Fixed sources
64
+
65
+ Each observation supplies the sources it can prove. File changes are evaluated separately, so
66
+ `target.path`, `pre`, and `post` refer to the current change.
67
+
68
+ | Source | Value | Availability |
69
+ |---|---|---|
70
+ | `target.path` | Repository-relative path string | An observation with a file target. |
71
+ | `pre` | File text before the change | Modifications; deletions when the prior text is available. Absent on creation. |
72
+ | `post` | Proposed file text after the change | Creations and modifications. Absent on deletion. |
73
+ | `state` | Before/after pair `{ pre, post }` | Modifications. Its pipeline runs separately on each side. |
74
+ | `changes` | Array of paths in the observed change set | Read in `changeSetDisciplines`; use `items` to extract individual paths. |
75
+ | `command` | Shell command text | Session shell calls, including calls with no file target. Literal stdin data is excluded from this source. |
76
+ | `actor` | Object with optional `agentType` | A session host that proves the actor: `{}` for its main session. Absent when the host supplies no actor. |
77
+
78
+ An absent source differs from a present empty string or empty array. Use `supply` to handle absence.
79
+ `state` does not store workflow progress between calls. Only `unchanged` accepts its paired result.
80
+ The [command-source example](../configuration/index.md#disciplines) explains shell stdin handling.
81
+
82
+ <a id="source-kinds"></a>
83
+ ## Additional source kinds
84
+
85
+ `sources` maps a new name to exactly one binding. A new name cannot replace a fixed source name.
86
+
87
+ | Kind | Binding example | Supplied value |
88
+ |---|---|---|
89
+ | `file` | `en: { file: 'locales/en.json' }` | File text. Paths are repository-relative, with no leading `/` or `..` segment. |
90
+ | `sidecar` | `spawns: { sidecar: true }` | JSON text containing the host's spawn records. Parse with `json` before `agentType` or `items`. |
91
+ | `transcript` | `session: { transcript: true }` | Session snapshot with `observedAtMs`, `toolCalls`, and `userMessages`. |
92
+
93
+ Read a binding with `{ op: 'source', of: 'en' }`. A changed file uses its proposed `post` text;
94
+ other named files use the surface's observation of the project. `sidecar` and `transcript` require
95
+ `sessionDisciplines` and a host that supplies those channels. Their marker is the literal `true`.
96
+
97
+ <a id="supply-policies"></a>
98
+ ## Supply policies
99
+
100
+ | Policy | On an absent source |
101
+ |---|---|
102
+ | `error` | Default. The observation cannot be judged and is blocked, including at `advise`. |
103
+ | `pass` | Record `skipped` with reason `supply-pass`; do not judge this declaration. |
104
+ | `empty` | Continue with an empty item list. Valid for single sources, never `state`. |
105
+
106
+ Every `supply` key must name a fixed or bound source. For before/after comparisons that should skip
107
+ creations and deletions, use `supply: { state: 'pass' }`. For added-only content checks, use
108
+ `supply: { pre: 'empty', post: 'empty' }`. Invalid JSON is a supply error even with `pass` or
109
+ `empty`; those policies apply to absent sources.
110
+
111
+ <a id="items-and-pipelines"></a>
112
+ ## Items and pipelines
113
+
114
+ An extraction produces ordered items shaped as `{ key, value }`. The key identifies an item for
115
+ combinators and key comparisons. The value is the data compared by value relations. Re-keying an
116
+ item does not change its value.
117
+
118
+ Each pipeline begins with `source` or a combinator. Combinators reference other extraction names
119
+ and can appear only first. References must exist and cannot form cycles. A combinator cannot
120
+ combine a paired extraction from `state`. Further steps transform the result in sequence.
121
+
122
+ <a id="extract-steps"></a>
123
+ ## Extraction steps
124
+
125
+ The table lists all 17 unary steps. Example arguments show their exact keys; an unknown argument
126
+ causes a compilation error. Unless stated otherwise, steps preserve item order.
127
+
128
+ | Step | Arguments | Result |
129
+ |---|---|---|
130
+ | `source` | `of: 'post'` (required) | Start from the named source as one item with key `'0'`. `state` starts a paired extraction. |
131
+ | `json` | None | Parse each string value as JSON, keeping its key. Invalid JSON fails supply. |
132
+ | `select` | `path: 'args.command'` (required) | Follow a dot path through objects. Drop missing paths. An array result becomes items keyed by position; a scalar keeps its key. |
133
+ | `items` | None | Expand each array by one level into items keyed by zero-based position. Drop non-arrays. |
134
+ | `keyBy` | `field: 'id'` (required) | Set the key to the string form of an object's field. Drop non-objects and absent, null, or object-valued fields. Keep the original value. |
135
+ | `keyByPattern` | `re: '^(.+)\.ts$'` (required), `i: true` (optional, default `false`) | Set the key to capture group 1 of the first regex match. Drop non-matches and unbound captures. Keep the original value. |
136
+ | `field` | `name: 'version'` (required) | Keep the key and replace the value with that object property. An absent property yields `undefined`; a non-object is dropped. |
137
+ | `filter` | `when: [{ field: 'succeeded', eq: true }]` (required) | Keep items satisfying every predicate. `when: []` keeps all items. See the predicate table below. |
138
+ | `flattenKeys` | None | List nested leaf paths, such as `home.title`, as both keys and values. Translation text is discarded. |
139
+ | `sort` | None | Stable ascending sort by value: numeric if all values are numbers, otherwise by string comparison. |
140
+ | `lines` | None | Split stringified values on newline, trim each line, and drop empty lines. Keys are original one-based line numbers. |
141
+ | `matches` | `re: '^test:'` (required), `i: true` (optional, default `false`) | Keep items whose stringified value matches the regex; preserve keys and values. |
142
+ | `toolUses` | `names: ['Bash']`, `subagentType: 'reviewer'` (both optional) | Extract calls from a session snapshot, keyed by observation ordinal. Supplied filters must both match. Does not require success automatically. |
143
+ | `userTexts` | `re: '^approved$'` (required), `i: true` (optional, default `false`) | Extract matching user messages, keyed by ordinal. Each value also receives the snapshot's `observedAtMs`. |
144
+ | `agentType` | `is: 'reviewer'` (required) | Keep matching parsed sidecar records, keyed by position. Accepts a record array or a single object. |
145
+ | `first` | None | Keep the first item and its key. An empty input stays empty. Does not sort. |
146
+ | `ageMs` | None | Add `ageMs = observedAtMs - timestampMs` to object values. Drop missing/non-numeric timestamps and future observations. |
147
+
148
+ `items` and array-valued `select` number each array separately. If several arrays are expanded,
149
+ their keys can collide; use `keyBy` when a later comparison needs an object's identifier.
150
+ `flattenKeys` descends through plain objects. An array is a leaf at its property's path, so array
151
+ indices are not enumerated. Empty objects produce no paths, including when nested.
152
+
153
+ Regex steps use JavaScript regular expressions. `i` is the supported flag; there is no `g` or `m`
154
+ argument. A regex over whole file text anchors `^` at the start of that text. Put `lines` first to
155
+ match each trimmed line. `keyByPattern` requires a capturing group, and uses only its first match.
156
+
157
+ <a id="filter-predicates"></a>
158
+ ## Filter predicates
159
+
160
+ Each predicate contains `field` and exactly one operator. `field` names a direct object property,
161
+ not a dot path. Non-object values fail a predicate. All predicates in `when` must pass.
162
+
163
+ | Operator | Example | Condition |
164
+ |---|---|---|
165
+ | `eq` | `{ field: 'succeeded', eq: true }` | Structural equality with the constant. |
166
+ | `ne` | `{ field: 'status', ne: 'draft' }` | Structural inequality with the constant. |
167
+ | `size` | `{ field: 'errors', size: 0 }` | The field is an array with exactly this many elements. |
168
+ | `notIn` | `{ field: 'status', notIn: ['draft', 'failed'] }` | The field value is unequal to every constant in the array. |
169
+ | `lte` | `{ field: 'ageMs', lte: 600000 }` | The field is a number less than or equal to the numeric bound. |
170
+ | `gte` | `{ field: 'count', gte: 1 }` | The field is a number greater than or equal to the numeric bound. |
171
+
172
+ `size`, `lte`, and `gte` take numbers; `notIn` takes an array. An absent property is `undefined`,
173
+ so it can satisfy `ne` or `notIn`. Neither operator establishes that the property exists.
174
+
175
+ <a id="combinators"></a>
176
+ ## Combinators
177
+
178
+ The operands are two distinct extraction names. `onlyIn` and `intersect` compare **keys**;
179
+ `union` concatenates the lists. All three preserve item values and do not sort or deduplicate.
180
+
181
+ | Combinator | Syntax | Result |
182
+ |---|---|---|
183
+ | `union` | `{ op: 'union', of: ['a', 'b'] }` | All items from `a`, followed by all items from `b`, including duplicate keys. |
184
+ | `onlyIn` | `{ op: 'onlyIn', of: 'a', notIn: 'b' }` | Items from `a` whose keys do not occur in `b`. |
185
+ | `intersect` | `{ op: 'intersect', of: ['a', 'b'] }` | Items from `a` whose keys occur in `b`, with values from `a`. |
186
+
187
+ <a id="relations"></a>
188
+ ## Relations
189
+
190
+ All seven relations return the items that violate the condition. No returned items means the
191
+ condition holds. `a` and `b` below name extractions, not source files.
192
+
193
+ | Relation | Syntax | Condition |
194
+ |---|---|---|
195
+ | `empty` | `{ op: 'empty', of: 'a' }` | `a` has no items. Every item is reported on failure. |
196
+ | `nonEmpty` | `{ op: 'nonEmpty', of: 'a' }` | `a` has at least one item. Failure reports the extraction name with value `null`. |
197
+ | `equal` | `{ op: 'equal', of: ['a', 'b'] }` | The sets of values are equal in both directions. Reports left-only items, then right-only items. |
198
+ | `subset` | `{ op: 'subset', of: 'a', in: 'b' }` | Every value in `a` occurs in `b`. Reports unmatched items from `a`. |
199
+ | `implies` | `{ op: 'implies', of: 'a', requires: 'b' }` | Every key in `a` occurs in `b`. Reports items from `a` with missing required keys. |
200
+ | `ordered` | `{ op: 'ordered', of: 'a', strict: false }` | Values are ascending. `strict` defaults to `false`; `true` also rejects equal neighbours. Reports the later item in each failing pair. |
201
+ | `unchanged` | `{ op: 'unchanged', of: 'a' }` | For a paired extraction from `state`, values at shared keys agree before and after. Added and removed keys do not violate this relation. |
202
+
203
+ `equal` and `subset` compare values structurally, ignoring item keys, collection order, and
204
+ duplicate counts. Arrays *inside* values remain ordered. `implies` compares keys and ignores
205
+ values. For example, `{ key: 'en', value: 'home' }` and `{ key: 'ko', value: 'home' }` satisfy
206
+ `equal`, but the first does not imply the second because their keys differ.
207
+
208
+ `ordered` compares numerically when every value is a number; otherwise it compares string forms.
209
+ It does not sort. Empty and single-item inputs satisfy it. Sorting immediately before `ordered`
210
+ cannot establish that the original input was ordered.
211
+
212
+ Only `unchanged` accepts a pair; all other relations take single extractions. `equal`, `subset`,
213
+ and `implies` require two distinct extraction names.
214
+
215
+ <a id="messages-and-witness"></a>
216
+ ## Messages and declaration witnesses
217
+
218
+ Each `relate` entry requires a unique `id`, a `relation`, and exactly one message form:
219
+
220
+ | Field | Use |
221
+ |---|---|
222
+ | `message` | One diagnostic template for any relation. |
223
+ | `messageBySide` | `{ left: '…', right: '…' }`, allowed only for `equal`. |
224
+
225
+ Templates substitute `{key}` and `{value}` from the first violating item. `{before}` is its
226
+ previous value for `unchanged`, or an empty string when absent. Multiple violations add a count
227
+ suffix. Object values use their JavaScript string form; extract the field you want to display.
228
+
229
+ The optional declaration `witness` has its own optional `extract` and required `relate`. It can
230
+ reference the body's extractions; the body cannot reference witness extractions, and witness
231
+ extraction names cannot shadow body names. If the body fails and every witness comparison holds,
232
+ the declaration is witnessed. A witness supply failure does not release the violation.
233
+ The top-level [human witness setting](../configuration/index.md#witness) is configured separately.
234
+
235
+ <a id="mechanisms"></a>
236
+ ## Mechanisms
237
+
238
+ Every declaration names one mechanism. The compiler derives axes from `source` steps: fixed
239
+ sources except `actor` give `change`; `actor` gives `actor`; file and sidecar bindings give
240
+ `world`; transcript bindings give `history`. A scope alone does not add an axis. Body relation
241
+ names and derived axes must fit the selected mechanism. Witness extraction sources also contribute
242
+ axes. The mechanism does not supply a predicate; write the extraction and comparison yourself.
243
+
244
+ | Mechanism | Allowed axes | Allowed body relations | Purpose or required structure |
245
+ |---|---|---|---|
246
+ | `pairing` | `world` | `equal`, `subset` | Compare corresponding data from supplied files. |
247
+ | `companion` | `change`, `world` | `implies` | Require matching keys in another extraction. |
248
+ | `monotonic-order` | `change`, `world` | `ordered` | Check a sequence's order. |
249
+ | `fingerprint-sync` | `world` | `equal` | Compare extracted fingerprint values. |
250
+ | `producer-owned` | `actor` | `empty`, `nonEmpty` | Check the observed actor. |
251
+ | `self-absolution-ban` | `change` | `unchanged`, `empty` | Check changes to the file's own contents. |
252
+ | `actor-scope` | `actor` | `empty`, `nonEmpty` | Restrict work by the observed actor. |
253
+ | `precedent` | `history`, `world` | `nonEmpty` | Require prior evidence. |
254
+ | `phase-order` | `history` | `ordered` | Compare extracted observation ordinals. |
255
+ | `turn-locality` | `history` | `nonEmpty` | Require evidence within a declared time window. |
256
+ | `stated-ground` | `history` | `nonEmpty` | Require a matching user statement. |
257
+ | `controlled-vocabulary` | `change`, `world` | `subset` | Compare extracted values with an allowed set. |
258
+ | `naming` | `change` | `empty`, `nonEmpty` | `scope.source` must be `target.path`. |
259
+ | `added-only` | `change` | `empty` | Usually compares the `post`/`pre` difference. |
260
+ | `one-way-marker` | `change` | `subset` | Require selected values to remain present. |
261
+ | `delegated-scope` | — | — | Reserved; rejected at load time. |
262
+ | `scoped-valve` | `change`, `actor`, `world`, `history` | All seven | A declaration `witness` block is required. |
263
+ | `forbidden-command` | `change` | `empty` | `scope.source` must be `command`. |
264
+
265
+ The 18 names include one reserved name, so 17 can be used. Mechanism constraints and
266
+ [discipline-list placement](../configuration/index.md#placement-rule) are separate checks.
267
+
268
+ <a id="validation"></a>
269
+ ## Validate a declaration
270
+
271
+ Run `pnpm exec pdks explain` and confirm the entry is a `declare` registration on the intended
272
+ surface. A `skip` registration with `config-fault` means compilation failed; read its location
273
+ and reason. Unknown keys or invalid source/list combinations can instead fail configuration loading.
274
+
275
+ Then exercise a violating input and a valid input through the matching surface. See
276
+ [the worked locale example](../../how-to/write-disciplines.md#locale-key-pairing).
277
+ An exit code of 0 alone is insufficient: `advised` and `skipped` can both exit 0.
@@ -69,7 +69,7 @@ Claude Code의 입력을 공통 형식으로 번역합니다. 에이전트와
69
69
  <a id="consumer-contract"></a>
70
70
  ## 사용자와의 접점
71
71
 
72
- 프로젝트 루트에서 줄이면 Claude Code 세션 표면이 섭니다.
72
+ 프로젝트 루트에서 다음 명령을 실행하면 Claude Code 세션 표면을 설치하고 연결합니다.
73
73
 
74
74
  ```sh
75
75
  npm install --save-dev polydeukes @polydeukes/core @polydeukes/adapter-claude-code
@@ -72,7 +72,7 @@ there is no separate precedent evaluator in this package. The grammar is in
72
72
  <a id="consumer-contract"></a>
73
73
  ## Where the consumer touches it
74
74
 
75
- Two lines install the Claude Code session surface, run from the project root:
75
+ Run these commands from the project root to install and connect the Claude Code session surface:
76
76
 
77
77
  ```sh
78
78
  npm install --save-dev polydeukes @polydeukes/core @polydeukes/adapter-claude-code
@@ -30,8 +30,8 @@ Codex의 입력을 공통 형식으로 번역합니다. 에이전트와 도구
30
30
  `pdks covenant check --enforce block`을 스폰하고 그 자식 프로세스의 종료 코드를 그대로
31
31
  돌려줍니다. 판정은 그 자식 프로세스가 하며, 이 패키지에는 판정 코드가 없습니다.
32
32
 
33
- **이 패키지는 텔레메트리 행을 쓰지 않습니다.** 스폰 전에 실패하면 사실을 한 줄로 만들어
34
- `pdks`의 stdin으로 보내므로, 그 호출이 남기는 행은 언제나 한 곳에서만 기록됩니다.
33
+ **텔레메트리는 `pdks`가 기록합니다.** 어댑터는 스폰 전에 발생한 실패도 `pdks`의 stdin으로
34
+ 보내 같은 경로로 기록합니다.
35
35
 
36
36
  <a id="apply-patch"></a>
37
37
  ## 형제 어댑터는 인자를 읽는데 이 어댑터가 텍스트를 해석하는 이유
@@ -29,8 +29,8 @@ removes only that session's file. `PreToolUse` builds the IR with the `tools` ro
29
29
  `pdks covenant check --enforce block` in `repoRoot` and returns the child's exit code. The
30
30
  judging happens in that child process; this package carries no judgment logic.
31
31
 
32
- **This package writes no telemetry rows.** A failure before the spawn is sent to `pdks` on
33
- stdin, so the row that call earns is written by the one writer.
32
+ **`pdks` writes the telemetry.** The adapter sends failures that occur before spawning to
33
+ `pdks` on stdin, so those failures are recorded through the same path.
34
34
 
35
35
  <a id="apply-patch"></a>
36
36
  ## Why this adapter parses text where its siblings read arguments
@@ -141,8 +141,8 @@ function declarationChannels(body: Omit<AlgebraDeclaration, 'discipline'>): Decl
141
141
  다섯 가운데 하나도 이름 짓지 않는 본체는 변경된 파일과 저장소 파일만 읽고 그 둘은 두 표면이
142
142
  모두 공급하므로 `disciplines`에 속합니다. 그 밖은 항목과 통로와 가야 할 목록을 대는
143
143
  `ConfigValidationError`이며, 메시지는
144
- [설정 참조](../configuration/index.ko.md#placement-rule)에 있습니다. 우산 패키지는 같은 질문을
145
- 따로 답하지 않고 함수를 불러 씁니다.
144
+ [설정 참조](../configuration/index.ko.md#placement-rule)에 있습니다. 우산 패키지는 함수를
145
+ 사용해 적용할 규율 목록을 정합니다.
146
146
 
147
147
  <a id="consumer-contract"></a>
148
148
  ## 사용자와의 접점
@@ -147,8 +147,8 @@ compare the same list.
147
147
  A body naming none of the five reads the changed file and repository files alone, which both
148
148
  surfaces supply, so it belongs in `disciplines`. Anything else is a `ConfigValidationError`
149
149
  naming the entry, its channels, and the list it belongs in; the messages are in [the
150
- configuration reference](../configuration/index.md#placement-rule). The umbrella imports this
151
- function rather than answering the same question a second time.
150
+ configuration reference](../configuration/index.md#placement-rule). The umbrella uses this
151
+ function to select the applicable discipline list.
152
152
 
153
153
  <a id="consumer-contract"></a>
154
154
  ## Where the consumer touches it
@@ -2,8 +2,8 @@
2
2
 
3
3
  [English](sdk-ts.md) · **한국어**
4
4
 
5
- > **TypeScript에서 판정기로 가는 동사 하나**입니다. 약속(covenant) 입력 IR을
6
- > `pdks covenant check`에 건네고 판정 결과를 값으로 돌려받습니다.
5
+ > **TypeScript에서 판정기를 호출합니다.** `checkCovenant`에 약속(covenant) 입력 IR을
6
+ > 전달하면 `pdks covenant check`의 판정 결과를 값으로 반환합니다.
7
7
  >
8
8
  > 베타입니다. `polydeukes` · `@polydeukes/core`와 함께 설치하며, 둘 다 이 패키지의
9
9
  > `peerDependencies`입니다.
@@ -11,18 +11,16 @@
11
11
  <a id="ownership"></a>
12
12
  ## 담당하는 기능
13
13
 
14
- 스폰과, 종료 상태를 값으로 옮기는 일이 전부입니다. 판정받는 프로젝트의 `polydeukes` 설치를
15
- 찾고, 실행 파일에 입력을 표준 입력으로 넣어 돌린 뒤, 자식이 답한 것을 돌려줍니다. 판정
16
- 코드는 여기에 없습니다. 분기는 우산 패키지를 찾았는지와 자식이 어떤 상태로 끝났는지뿐입니다.
14
+ 판정받는 프로젝트에서 `polydeukes`를 찾고, 실행 파일의 표준 입력으로 IR을 전달한
15
+ 자식 프로세스의 종료 코드를 판정 결과로 변환합니다. 실제 판정은 자식 프로세스가 수행합니다.
17
16
 
18
17
  | 단위 | 하는 일 |
19
18
  |---|---|
20
19
  | `checkCovenant` | 판정받는 프로젝트에서 `pdks covenant check`를 스폰하고 판정 결과를 돌려줍니다 |
21
- | 우산 해소 | `repoRoot`의 설치 그래프에서 `polydeukes`를 찾아 `pdks` 실행 파일을 읽습니다 |
20
+ | 우산 패키지 찾기 | `repoRoot`의 설치 그래프에서 `polydeukes`를 찾아 `pdks` 실행 파일을 읽습니다 |
22
21
  | 판정 결과 변환 | 종료 코드 `0`은 `upheld`, `2`는 `blocked`, 그 밖은 모두 `unjudged`입니다 |
23
22
 
24
- 패키지는 텔레메트리 행을 쓰지 않습니다. 실행이 남기는 행은 모두 판정이 일어난 자식
25
- 프로세스가 쓰므로, 호출 하나에 행 하나는 그대로입니다.
23
+ 텔레메트리는 자식 프로세스가 판정 중에 기록합니다. SDK는 중복 행을 추가하지 않습니다.
26
24
 
27
25
  <a id="install"></a>
28
26
  ## 설치
@@ -31,11 +29,12 @@
31
29
  pnpm add @polydeukes/sdk-ts polydeukes @polydeukes/core
32
30
  ```
33
31
 
34
- 실행 파일도 설치 단계도 없습니다. 우산 패키지가 SDK가 스폰할 판정기를 공급하고, 코어가
32
+ 별도의 초기화 명령은 필요하지 않습니다. 우산 패키지가 SDK가 스폰할 판정기를 공급하고, 코어가
35
33
  호출자가 채우는 `CovenantInput` 타입을 공급합니다.
36
34
 
35
+ <a id="동사"></a>
37
36
  <a id="verb"></a>
38
- ## 동사
37
+ ## `checkCovenant`
39
38
 
40
39
  이 패키지는 ESM 전용입니다(`"type": "module"`, `import` 조건만 있고 `require`는 없음).
41
40
  호출하는 파일이 `.mjs`이거나 그 `package.json`이 `"type": "module"`을 선언해야 합니다.
@@ -90,7 +89,7 @@ type CheckCovenantSpawnSpec = { command: string; args: string[]; cwd: string; st
90
89
  | `repoRoot` | 판정받는 프로젝트입니다. 설정 발견, 세계 축, 자식의 cwd, 우산 패키지를 찾는 설치 그래프가 모두 여기 걸립니다 |
91
90
  | `input` | 호출자 자신의 IR이며 자식의 표준 입력으로 원문 그대로 갑니다 |
92
91
  | `enforce` | 실행 전체에 대한 관측자의 기본 자세입니다. **적지 않으면 `block`입니다** |
93
- | `spawn` | 주입하는 스폰 이음매입니다. 없으면 프로세스의 node 실행 파일로 자식을 띄웁니다 |
92
+ | `spawn` | 자식 프로세스 실행 함수를 지정합니다. 생략하면 현재 프로세스의 Node.js 실행 파일을 사용합니다 |
94
93
 
95
94
  **`enforce`의 기본값은 `block`입니다.** 이것은 표면의 강제 수준이지 항목의 것이 아닙니다.
96
95
  보호 경로와 `enforce: block`을 단 항목이 호출을 멈추고, 나머지 위반은 종료 코드 0에
@@ -118,17 +117,15 @@ type CheckCovenantVerdict =
118
117
  | `blocked` | `2` | 호출이 판정을 받았고 무언가 막았습니다. `reason`은 자식의 stderr 원문입니다. 진행하지 않습니다 |
119
118
  | `unjudged` | 그 밖의 상태이거나 우산 패키지가 없음 | 판정이 일어나지 않았습니다. `reason`이 어느 쪽인지 말합니다. 이것을 통과로 읽으면 판정기가 설치되지 않은 프로젝트에서 모든 호출이 지나갑니다 |
120
119
 
121
- **밸브가 없고 자리를 사유가 대신합니다.** 무인 실시간 호출자에게는 TTY도 사람의 턴도
122
- 없으므로, 패키지는 증인 인자를 받지 않고 세션을 지어내지도 않습니다. 대신
123
- `blocked.reason`과 `upheld.advisories`가 데이터로 돌아오고, 그 텍스트를 이슈나 로그에 적을지
124
- 모델에게 돌려줄지는 소비자가 정합니다. 설정 저자와 소비자를 위한 기본 자세 규칙은
125
- [규율 작성하기](../../how-to/write-disciplines.ko.md#posture)에 있습니다.
120
+ SDK는 `blocked.reason`과 `upheld.advisories`를 데이터로 반환합니다. 소비자는 내용을
121
+ 모델에게 전달하거나 이슈 또는 로그에 기록할 수 있습니다. SDK는 별도의 증인 인자를 받지 않습니다.
122
+ 무인 루프에서 결과를 처리하는 방법은
123
+ [규율 작성하기](../../how-to/write-disciplines.ko.md#posture)를 참고하세요.
126
124
 
127
125
  <a id="failure"></a>
128
126
  ## 실패 예제
129
127
 
130
- 프로젝트에 `polydeukes`가 설치돼 있지 않으면 스폰할 것이 없고, 동사는 `upheld`로 답하는 대신
131
- 그 사실을 말합니다.
128
+ 프로젝트에 `polydeukes`가 설치돼 있지 않으면 `checkCovenant`는 `unjudged`를 반환합니다.
132
129
 
133
130
  ```ts
134
131
  const verdict = await checkCovenant({ repoRoot: '/tmp/project-without-polydeukes', input });
@@ -2,8 +2,8 @@
2
2
 
3
3
  **English** · [한국어](sdk-ts.ko.md)
4
4
 
5
- > **One verb from TypeScript to the judge** — hand a covenant input IR to
6
- > `pdks covenant check` and read the verdict back as a value.
5
+ > **Call the judge from TypeScript.** Pass a covenant input IR to `checkCovenant`
6
+ > and receive the verdict from `pdks covenant check` as a value.
7
7
  >
8
8
  > Beta. Install it next to `polydeukes` and `@polydeukes/core`, which it names as
9
9
  > `peerDependencies`.
@@ -11,10 +11,9 @@
11
11
  <a id="ownership"></a>
12
12
  ## What this package owns
13
13
 
14
- The spawn and the translation of an exit status into a value, and nothing else. It locates the
15
- `polydeukes` install of the project being judged, runs its bin with the input on stdin, and
16
- returns what the child answered. No judgment logic lives here: the branches are whether the
17
- umbrella resolved and what status the child left with.
14
+ The package finds `polydeukes` in the project being judged, runs its bin with the input on
15
+ stdin, and converts the child process's exit status into a verdict. The child process performs
16
+ the judgment.
18
17
 
19
18
  | Unit | What it does |
20
19
  |---|---|
@@ -22,8 +21,7 @@ umbrella resolved and what status the child left with.
22
21
  | Umbrella resolution | Finds `polydeukes` in the install graph of `repoRoot` and reads its `pdks` bin |
23
22
  | Verdict translation | Exit `0` is `upheld`, exit `2` is `blocked`, everything else is `unjudged` |
24
23
 
25
- This package writes no telemetry row. Every row a run leaves is written by the child process,
26
- where the judgment happened, so one call still leaves one row.
24
+ The child process writes telemetry during judgment. The SDK does not add duplicate rows.
27
25
 
28
26
  <a id="install"></a>
29
27
  ## Install
@@ -32,11 +30,12 @@ where the judgment happened, so one call still leaves one row.
32
30
  pnpm add @polydeukes/sdk-ts polydeukes @polydeukes/core
33
31
  ```
34
32
 
35
- There is no bin and no install step. The umbrella supplies the judge the SDK spawns, and the
33
+ No separate initialization command is needed. The umbrella supplies the judge the SDK spawns, and the
36
34
  core supplies the `CovenantInput` type the caller fills in.
37
35
 
36
+ <a id="the-verb"></a>
38
37
  <a id="verb"></a>
39
- ## The verb
38
+ ## `checkCovenant`
40
39
 
41
40
  The package is ESM only (`"type": "module"`, an `import` condition and no `require`): the calling
42
41
  file is a `.mjs`, or its `package.json` declares `"type": "module"`.
@@ -120,17 +119,15 @@ type CheckCovenantVerdict =
120
119
  | `blocked` | `2` | The call was judged and something blocked it. `reason` is the child's stderr verbatim. Do not proceed |
121
120
  | `unjudged` | anything else, or no umbrella | No judgment happened. `reason` says which. Reading it as an uphold would let an uninstalled judge pass every call |
122
121
 
123
- **There is no valve, and the reason is the substitute.** An unattended real-time caller has no
124
- TTY and no human turn, so this package takes no witness argument and invents no session.
125
- `blocked.reason` and `upheld.advisories` come back as data instead, and the consumer decides
126
- where the text goes — an issue, a log, or back to the model. The posture rules for a config
127
- author and for a consumer are in [write disciplines](../../how-to/write-disciplines.md#posture).
122
+ The SDK returns `blocked.reason` and `upheld.advisories` as data. The consumer decides where
123
+ to send them: to the model, an issue, or a log. The SDK accepts no separate witness argument.
124
+ See [write disciplines](../../how-to/write-disciplines.md#posture) for handling these results
125
+ in an unattended loop.
128
126
 
129
127
  <a id="failure"></a>
130
128
  ## A failure example
131
129
 
132
- When the project has no `polydeukes` installed there is nothing to spawn, and the verb says so
133
- rather than answering `upheld`:
130
+ If the project has no `polydeukes` installed, `checkCovenant` returns `unjudged`:
134
131
 
135
132
  ```ts
136
133
  const verdict = await checkCovenant({ repoRoot: '/tmp/project-without-polydeukes', input });
@@ -153,13 +153,17 @@ covenant check failed closed: invalid config in polydeukes.config.yaml: … —
153
153
  <a id="skipped-rows-on-the-change-set-surface"></a>
154
154
  ## 변경 집합 표면의 미판정 기록
155
155
 
156
- 대화 기록을 읽는 선언에 `supply: { session: 'pass' }`가 있으면 세션이 없는 변경 집합 표면에서는
157
- `supply-pass`를 기록합니다. 약속은 세션 표면에서 확인하세요. 미판정은 과거 작업을
158
- 검증했다는 뜻이 아닙니다. 다른 채널을 공급할 없으면 `no-observation`이 나올 수도
159
- 있습니다. 모든 자료 부재를 같은 실패로 취급하지 말고 등록 내용과 사유를 확인하세요.
156
+ 변경 집합 표면은 `disciplines`와 `changeSetDisciplines`를 컴파일합니다.
157
+ 대화 기록이나 명령줄을 읽는 항목은 `sessionDisciplines`에 속하며 diff 판정에는 포함되지
158
+ 않습니다. 이런 항목을 공용 목록이나 변경 집합 목록에 넣으면 설정 오류가 발생합니다.
160
159
 
161
- 스테이징한 변경에는 명령줄이 없으므로 명령 범위 선언은 적용되지 않으며 판정도 기록하지
162
- 않습니다. 설정, 범위 일치, 자료 공급, 최종 비교는 각각 다른 단계입니다.
160
+ 변경 집합 항목이 읽는 소스가 없고 `supply` 정책이 `pass`이면 `supply-pass`를 기록합니다.
161
+ 선언을 컴파일할 없으면 `config-fault`를 기록합니다. `pdks explain`과 로그의 사유를
162
+ 확인하세요. 미판정은 규율을 지켰다는 증거가 아닙니다.
163
+
164
+ 세션 표면에서는 호스트가 대화 기록을 공급하지 않을 때
165
+ `supply: { session: 'pass' }`로 건너뛸 수 있습니다.
166
+ [규율 목록 셋](./reference/configuration/index.ko.md#three-lists)을 참고하세요.
163
167
 
164
168
  <a id="local-state"></a>
165
169
  ## 다른 컴퓨터로 프로젝트를 옮길 때
@@ -152,13 +152,17 @@ from the hook command rather than editing the config — the row is still writte
152
152
  <a id="skipped-rows-on-the-change-set-surface"></a>
153
153
  ## `skipped` rows on the change-set surface
154
154
 
155
- A transcript-reading declaration with `supply: { session: 'pass' }` records `supply-pass` when the
156
- change-set surface has no session. Use the session surface for that promise; a skip does not verify
157
- history. Other unavailable channels can produce `no-observation`. Inspect the registration and
158
- reason instead of treating every missing source as the same failure.
155
+ The change-set surface compiles `disciplines` and `changeSetDisciplines`.
156
+ Transcript- and command-reading entries belong in `sessionDisciplines` and are not compiled
157
+ for a diff. Putting either kind in a shared or change-set list causes a configuration error.
159
158
 
160
- A command-scoped declaration does not match a staged diff's absent command line and records no
161
- judgment there. Configuration, scope matching, supply, and the final comparison are separate steps.
159
+ A change-set entry can record `supply-pass` when a source it reads is absent and its `supply`
160
+ policy is `pass`, or `config-fault` when the declaration cannot compile. Check `pdks explain`
161
+ and the log's reason field. A skip does not establish that the discipline was upheld.
162
+
163
+ On the session surface, a transcript-reading entry can use `supply: { session: 'pass' }`
164
+ when a host supplies no session history. See the [three discipline
165
+ lists](./reference/configuration/index.md#three-lists).
162
166
 
163
167
  <a id="local-state"></a>
164
168
  ## Moving a project between machines
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydeukes",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "A development discipline framework for building alongside an AI coding partner — deterministic covenants, a verifiable work ledger, local memory, and adversarial verification. Beta.",
5
5
  "author": "huskyhoochu <dfg1499@gmail.com>",
6
6
  "keywords": [
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "yaml": "2.9.0",
47
- "@polydeukes/core": "^0.9.0"
47
+ "@polydeukes/core": "^0.10.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^24.0.0",