polydeukes 0.6.1 → 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.
- package/README.ko.md +14 -28
- package/README.md +14 -28
- package/dist/baseline.d.ts +37 -1
- package/dist/baseline.js +68 -1
- package/dist/bin.d.ts +3 -4
- package/dist/bin.js +73 -90
- package/dist/covenant/bash-line.d.ts +130 -0
- package/dist/covenant/bash-line.js +566 -0
- package/dist/covenant/declaration-engine.d.ts +108 -0
- package/dist/covenant/declaration-engine.js +415 -0
- package/dist/covenant/discipline.d.ts +103 -0
- package/dist/covenant/discipline.js +667 -0
- package/dist/covenant/dispatch.d.ts +153 -0
- package/dist/covenant/dispatch.js +237 -0
- package/dist/covenant/extract-steps.d.ts +122 -0
- package/dist/covenant/extract-steps.js +0 -0
- package/dist/covenant/mention.d.ts +117 -0
- package/dist/covenant/mention.js +209 -0
- package/dist/covenant/module.d.ts +25 -0
- package/dist/covenant/module.js +23 -0
- package/dist/covenant/mutation-rules.d.ts +40 -0
- package/dist/covenant/mutation-rules.js +146 -0
- package/dist/covenant/relations.d.ts +46 -0
- package/dist/covenant/relations.js +68 -0
- package/dist/covenant/run-covenant.d.ts +95 -0
- package/dist/covenant/run-covenant.js +164 -0
- package/dist/covenant/self-mod.d.ts +55 -0
- package/dist/covenant/self-mod.js +84 -0
- package/dist/covenant/shell-evidence.d.ts +38 -0
- package/dist/covenant/shell-evidence.js +247 -0
- package/dist/covenant/shell-mod.d.ts +84 -0
- package/dist/covenant/shell-mod.js +213 -0
- package/dist/covenant/supply.d.ts +54 -0
- package/dist/covenant/supply.js +50 -0
- package/dist/covenant/transcript-mod.d.ts +60 -0
- package/dist/covenant/transcript-mod.js +254 -0
- package/dist/covenant/ttl-witness.d.ts +47 -0
- package/dist/covenant/ttl-witness.js +80 -0
- package/dist/covenant-check.d.ts +72 -39
- package/dist/covenant-check.js +260 -121
- package/dist/diff-ir.d.ts +28 -0
- package/dist/diff-ir.js +310 -0
- package/dist/docs/README.ko.md +8 -8
- package/dist/docs/README.md +8 -8
- package/dist/docs/catalog.json +44 -28
- package/dist/docs/concepts/judgment.ko.md +2 -2
- package/dist/docs/concepts/judgment.md +2 -2
- package/dist/docs/how-to/configure-project.ko.md +16 -16
- package/dist/docs/how-to/configure-project.md +15 -12
- package/dist/docs/how-to/connect-surfaces.ko.md +39 -34
- package/dist/docs/how-to/connect-surfaces.md +36 -33
- package/dist/docs/how-to/write-disciplines.ko.md +61 -6
- package/dist/docs/how-to/write-disciplines.md +62 -6
- package/dist/docs/index.json +607 -373
- package/dist/docs/reference/cli/covenant-check.ko.md +112 -65
- package/dist/docs/reference/cli/covenant-check.md +114 -59
- package/dist/docs/reference/cli/docs.ko.md +2 -2
- package/dist/docs/reference/cli/docs.md +2 -2
- package/dist/docs/reference/cli/explain.ko.md +14 -9
- package/dist/docs/reference/cli/explain.md +15 -10
- package/dist/docs/reference/cli/init.ko.md +52 -52
- package/dist/docs/reference/cli/init.md +43 -55
- package/dist/docs/reference/configuration/index.ko.md +171 -66
- package/dist/docs/reference/configuration/index.md +178 -71
- package/dist/docs/reference/packages/adapter-claude-code.ko.md +38 -11
- package/dist/docs/reference/packages/adapter-claude-code.md +38 -14
- package/dist/docs/reference/packages/adapter-grok.ko.md +77 -0
- package/dist/docs/reference/packages/adapter-grok.md +79 -0
- package/dist/docs/reference/packages/core.ko.md +49 -3
- package/dist/docs/reference/packages/core.md +54 -5
- package/dist/docs/reference/packages/polydeukes.ko.md +138 -77
- package/dist/docs/reference/packages/polydeukes.md +146 -81
- package/dist/docs/reference/packages/sdk-ts.ko.md +161 -0
- package/dist/docs/reference/packages/sdk-ts.md +164 -0
- package/dist/docs/troubleshooting.ko.md +17 -15
- package/dist/docs/troubleshooting.md +18 -14
- package/dist/docs/tutorials/first-judgment.ko.md +2 -2
- package/dist/docs/tutorials/first-judgment.md +2 -2
- package/dist/explain.d.ts +1 -1
- package/dist/explain.js +37 -22
- package/dist/load-config.d.ts +1 -1
- package/dist/load-config.js +1 -1
- package/dist/pre-state-reader.d.ts +1 -1
- package/dist/pre-state-reader.js +1 -1
- package/dist/scaffold-project.d.ts +2 -2
- package/dist/scaffold-project.js +9 -3
- package/dist/schema/polydeukes.schema.json +12 -0
- package/dist/worktree-reader.d.ts +19 -0
- package/dist/worktree-reader.js +30 -0
- package/package.json +3 -17
- package/dist/claude-code-hook.d.ts +0 -79
- package/dist/claude-code-hook.js +0 -372
- package/dist/claude-code.d.ts +0 -6
- package/dist/claude-code.js +0 -6
- package/dist/covenant-module.d.ts +0 -25
- package/dist/covenant-module.js +0 -42
- package/dist/docs/reference/packages/adapter-git.ko.md +0 -101
- package/dist/docs/reference/packages/adapter-git.md +0 -108
- package/dist/docs/reference/packages/covenant.ko.md +0 -115
- package/dist/docs/reference/packages/covenant.md +0 -123
- package/dist/index.d.ts +0 -22
- package/dist/index.js +0 -21
- package/dist/init-claude-code.d.ts +0 -52
- package/dist/init-claude-code.js +0 -586
- package/dist/init-grok.d.ts +0 -51
- package/dist/init-grok.js +0 -242
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
프로젝트 루트 바로 아래에 `polydeukes.config.yaml`, `polydeukes.config.yml`,
|
|
13
13
|
`polydeukes.config.json` 중 하나도 없으면 설정이 필요한 명령은 종료 코드 2를 반환합니다.
|
|
14
|
-
Git에서 원래 파일을 복원하세요. 새 프로젝트라면 `pdks init claude-code
|
|
15
|
-
`pdks init
|
|
14
|
+
Git에서 원래 파일을 복원하세요. 새 프로젝트라면 `pdks init`, `pdks-claude-code init`,
|
|
15
|
+
`pdks-grok init`로 만들 수 있습니다. 이후 `pdks explain`을 실행합니다. 설정이 없다고
|
|
16
16
|
기본 정책으로 대신 실행하지는 않습니다.
|
|
17
17
|
|
|
18
18
|
<a id="multiple-config"></a>
|
|
@@ -31,15 +31,15 @@ Git에서 원래 파일을 복원하세요. 새 프로젝트라면 `pdks init cl
|
|
|
31
31
|
설정은 실행할 코드가 아니라 데이터이기 때문입니다.
|
|
32
32
|
|
|
33
33
|
`protectedPath:`나 `adaptors:` 같은 오타는 거부합니다. 다만 어댑터 이름에는 확장 가능성을
|
|
34
|
-
남겨 두었습니다.
|
|
35
|
-
|
|
34
|
+
남겨 두었습니다. 아무도 구현하지 않은 네임스페이스도 설정으로 읽히지만 읽는 쪽이 없습니다.
|
|
35
|
+
수정한 뒤 `pdks explain`에서 실제 등록 내용을 확인하세요.
|
|
36
36
|
|
|
37
37
|
<a id="grok-witness"></a>
|
|
38
38
|
## Grok 증인
|
|
39
39
|
|
|
40
40
|
훅을 아직 읽지 않은 문제와 증인(witness) 밸브를 사용할 수 없는 문제는 구별해야 합니다.
|
|
41
41
|
|
|
42
|
-
- `pdks init
|
|
42
|
+
- `pdks-grok init` 실행 뒤 Hooks 탭을 다시 불러오거나 새 세션을 시작하세요. 실제 도구를
|
|
43
43
|
호출하고 텔레메트리를 확인합니다. 설치기가 성공했다고 해서 열린 세션에 훅이 적용됐다는
|
|
44
44
|
뜻은 아닙니다.
|
|
45
45
|
- Grok의 ACP 대화 기록에는 현재 세션 증인 밸브가 요구하는 Claude 형식의 인간 메시지가
|
|
@@ -105,19 +105,21 @@ covenant check failed closed: the covenant judges could not be loaded from …
|
|
|
105
105
|
<a id="blocked-commit"></a>
|
|
106
106
|
## 커밋이 차단됐을 때
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
변경 집합 표면은 사람에게 묻지 않습니다. 기본값에서는 모든 판정에 종료 코드 0을 내고 위반을
|
|
109
|
+
`advised`로 기록합니다. 종료 코드 2는 검사를 `--enforce block`으로 실행했을 때(보호 경로나
|
|
110
|
+
`enforce: block` 항목의 위반) 또는 판정 자체가 불가능할 때만 냅니다. 커밋을 멈출지는 훅
|
|
111
|
+
배선이 정합니다. 종료 코드를 따르는 훅은 커밋을 멈추고, 무시하는 훅은 멈추지 않습니다. Git은
|
|
112
|
+
커밋 실패를 다른 0이 아닌 코드로 알릴 수도 있습니다.
|
|
111
113
|
|
|
112
|
-
일반 규율 항목은
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
일반 규율 항목은 자신이 `enforce: block`을 적고 검사가 `--enforce block`으로 실행될 때만
|
|
115
|
+
작업을 멈춥니다. 설정에는 기본값 `advise`인 항목을 승격하는 것이 없습니다. 수준 변경은
|
|
116
|
+
정책을 바꾸는 결정이지 필수 복구 절차가 아닙니다. 판정된 위반을 통과시키려면 설정이 아니라
|
|
117
|
+
훅 명령에서 `--enforce block`을 빼세요. 행은 그대로 남습니다.
|
|
116
118
|
|
|
117
|
-
<a id="skipped-rows-on-the-
|
|
118
|
-
##
|
|
119
|
+
<a id="skipped-rows-on-the-change-set-surface"></a>
|
|
120
|
+
## 변경 집합 표면의 미판정 기록
|
|
119
121
|
|
|
120
|
-
대화 기록을 읽는 선언에 `supply: { session: 'pass' }`가 있으면 세션이 없는
|
|
122
|
+
대화 기록을 읽는 선언에 `supply: { session: 'pass' }`가 있으면 세션이 없는 변경 집합 표면에서는
|
|
121
123
|
`supply-pass`를 기록합니다. 이 약속은 세션 표면에서 확인하세요. 미판정은 과거 작업을
|
|
122
124
|
검증했다는 뜻이 아닙니다. 다른 채널을 공급할 수 없으면 `no-observation`이 나올 수도
|
|
123
125
|
있습니다. 모든 자료 부재를 같은 실패로 취급하지 말고 등록 내용과 사유를 확인하세요.
|
|
@@ -11,8 +11,9 @@ or the judging packages, provided its own installed documentation bundle is inta
|
|
|
11
11
|
|
|
12
12
|
Commands that need configuration exit 2 when none of `polydeukes.config.yaml`,
|
|
13
13
|
`polydeukes.config.yml`, or `polydeukes.config.json` exists directly at the project root.
|
|
14
|
-
Restore the intended file from Git, or use `pdks init
|
|
15
|
-
project. Then run `pdks explain`. No configuration means no silent
|
|
14
|
+
Restore the intended file from Git, or use `pdks init` / `pdks-claude-code init` /
|
|
15
|
+
`pdks-grok init` for a new project. Then run `pdks explain`. No configuration means no silent
|
|
16
|
+
default policy.
|
|
16
17
|
|
|
17
18
|
<a id="multiple-config"></a>
|
|
18
19
|
## More than one config file
|
|
@@ -28,15 +29,15 @@ field. Repair invalid YAML, custom tags, unknown fields, or an empty `languages`
|
|
|
28
29
|
Custom YAML tags are rejected even if the parser cannot execute them: configuration is data.
|
|
29
30
|
|
|
30
31
|
Typos such as `protectedPath:` or `adaptors:` are refused. Adapter namespace names are deliberately
|
|
31
|
-
open, however:
|
|
32
|
-
|
|
32
|
+
open, however: a namespace nobody implements loads without being read by anything. After repair,
|
|
33
|
+
run `pdks explain` and check the assembled registrations.
|
|
33
34
|
|
|
34
35
|
<a id="grok-witness"></a>
|
|
35
36
|
## Grok witness
|
|
36
37
|
|
|
37
38
|
A hook not yet loaded and an unavailable witness valve are different problems:
|
|
38
39
|
|
|
39
|
-
- After `pdks init
|
|
40
|
+
- After `pdks-grok init`, reload the Hooks tab or start a new session. Verify an actual call and
|
|
40
41
|
its telemetry; a successful installer run does not prove the open host loaded the hook.
|
|
41
42
|
- Grok's ACP history does not supply the Claude-format human message required by the current
|
|
42
43
|
session witness valve. Reloading does not add that capability. Perform a necessary repair
|
|
@@ -101,19 +102,22 @@ A witness cannot repair missing modules or other failures that prevent judgment
|
|
|
101
102
|
<a id="blocked-commit"></a>
|
|
102
103
|
## A blocked commit
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
The change-set surface does not prompt. By default it exits 0 on every verdict and records the
|
|
106
|
+
break as `advised`; it exits 2 only when the check runs with `--enforce block` (a protected
|
|
107
|
+
path or an entry set to `enforce: block` broke) or when it could not judge at all. Whether the
|
|
108
|
+
commit stops is your hook wiring — a hook that honours the exit code stops it, one that ignores
|
|
109
|
+
it does not. Git may report a different nonzero exit code for the failed commit.
|
|
107
110
|
|
|
108
|
-
A normal entry blocks only when
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
A normal entry blocks only when it declares `enforce: block` and the check runs with
|
|
112
|
+
`--enforce block`; nothing in the config promotes a default-`advise` entry. Changing a level is
|
|
113
|
+
a policy decision, not a required repair. To let a judged break through, drop `--enforce block`
|
|
114
|
+
from the hook command rather than editing the config — the row is still written.
|
|
111
115
|
|
|
112
|
-
<a id="skipped-rows-on-the-
|
|
113
|
-
## `skipped` rows on the
|
|
116
|
+
<a id="skipped-rows-on-the-change-set-surface"></a>
|
|
117
|
+
## `skipped` rows on the change-set surface
|
|
114
118
|
|
|
115
119
|
A transcript-reading declaration with `supply: { session: 'pass' }` records `supply-pass` when the
|
|
116
|
-
|
|
120
|
+
change-set surface has no session. Use the session surface for that promise; a skip does not verify
|
|
117
121
|
history. Other unavailable channels can produce `no-observation`. Inspect the registration and
|
|
118
122
|
reason instead of treating every missing source as the same failure.
|
|
119
123
|
|
|
@@ -17,8 +17,8 @@ mkdir pdks-example
|
|
|
17
17
|
cd pdks-example
|
|
18
18
|
git init
|
|
19
19
|
printf '{"name":"pdks-example","private":true}\n' > package.json
|
|
20
|
-
pnpm add -D polydeukes # 프로젝트 의존성. 일회성 npx 실행이 아님
|
|
21
|
-
pnpm exec pdks
|
|
20
|
+
pnpm add -D polydeukes @polydeukes/adapter-claude-code # 프로젝트 의존성. 일회성 npx 실행이 아님
|
|
21
|
+
pnpm exec pdks-claude-code init
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
설치기는 각 파일을 만들었으면 `created`, 이미 있어서 보존했으면 `skipped`로 보고합니다.
|
|
@@ -17,8 +17,8 @@ mkdir pdks-example
|
|
|
17
17
|
cd pdks-example
|
|
18
18
|
git init
|
|
19
19
|
printf '{"name":"pdks-example","private":true}\n' > package.json
|
|
20
|
-
pnpm add -D polydeukes #
|
|
21
|
-
pnpm exec pdks
|
|
20
|
+
pnpm add -D polydeukes @polydeukes/adapter-claude-code # project dependencies, not a one-off npx run
|
|
21
|
+
pnpm exec pdks-claude-code init
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
The installer reports `created` or `skipped` for each artifact. It creates a starter config,
|
package/dist/explain.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type ExplainSpec = {
|
|
|
17
17
|
* Read the config at `repoRoot`, assemble both surfaces, and render them.
|
|
18
18
|
*
|
|
19
19
|
* The session assembly is given a transcript path, so its `transcript-mod` registration
|
|
20
|
-
* exists here exactly as it does under a normal
|
|
20
|
+
* exists here exactly as it does under a normal call payload — the path is never read,
|
|
21
21
|
* because the injected transcript is the no-op one.
|
|
22
22
|
*/
|
|
23
23
|
export declare function explain(spec: ExplainSpec): Promise<{
|
package/dist/explain.js
CHANGED
|
@@ -9,15 +9,18 @@
|
|
|
9
9
|
* nothing and reads no disk. Every failure throws: an answer that cannot be given is never
|
|
10
10
|
* given halfway.
|
|
11
11
|
*/
|
|
12
|
-
import { join } from 'node:path';
|
|
13
|
-
import { resolveGitAdapterSettings } from '@polydeukes/adapter-git';
|
|
14
12
|
import { AXIS_NAMES, deriveShape, noopTranscript, RELATION_NAMES } from '@polydeukes/core';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { loadCovenantModule, resolveCovenantDist } from './covenant-module.js';
|
|
13
|
+
import { covenantModule } from './covenant/module.js';
|
|
14
|
+
import { assembleChangeSetRegistrations, assembleCheckRegistrations } from './covenant-check.js';
|
|
18
15
|
import { loadConfig } from './load-config.js';
|
|
19
16
|
/** The three meta-covenant labels: registrations that protect the judging chain itself. */
|
|
20
17
|
const META_LABELS = new Set(['self-mod', 'shell-mod', 'transcript-mod']);
|
|
18
|
+
/**
|
|
19
|
+
* What the session assembly here is given as its evidence path. The real one is loaded per
|
|
20
|
+
* call by an adapter, so this renderer names the IR key instead of a file: the assembly
|
|
21
|
+
* needs a present value to build the conditional registration, and nothing prints it.
|
|
22
|
+
*/
|
|
23
|
+
const SESSION_EVIDENCE_PATH_KEY = 'session.evidencePath';
|
|
21
24
|
/**
|
|
22
25
|
* The description of a declaration entry: its catalogue coordinate (the mechanism, the axes
|
|
23
26
|
* its sources derive, and the relations its entries decide), then what it routes on, how
|
|
@@ -58,7 +61,7 @@ function row(kind, label, width, description) {
|
|
|
58
61
|
/** The description of a meta-covenant registration — how much surface it covers. */
|
|
59
62
|
function metaDescription(registration, surface) {
|
|
60
63
|
if (registration.label === 'transcript-mod') {
|
|
61
|
-
return 'content predicate · conditional:
|
|
64
|
+
return 'content predicate · conditional: session.evidencePath';
|
|
62
65
|
}
|
|
63
66
|
return `paths ${registration.protectedPaths.length} (${surface})`;
|
|
64
67
|
}
|
|
@@ -105,47 +108,59 @@ function renderSurface(spec) {
|
|
|
105
108
|
* Read the config at `repoRoot`, assemble both surfaces, and render them.
|
|
106
109
|
*
|
|
107
110
|
* The session assembly is given a transcript path, so its `transcript-mod` registration
|
|
108
|
-
* exists here exactly as it does under a normal
|
|
111
|
+
* exists here exactly as it does under a normal call payload — the path is never read,
|
|
109
112
|
* because the injected transcript is the no-op one.
|
|
110
113
|
*/
|
|
111
114
|
export async function explain(spec) {
|
|
112
115
|
const { config, configPath } = loadConfig({ rootDir: spec.repoRoot });
|
|
113
|
-
//
|
|
114
|
-
// that would judge
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
const
|
|
116
|
+
// The judge module the two runners assemble against, so what this renders is the table
|
|
117
|
+
// that would judge.
|
|
118
|
+
const covenant = covenantModule;
|
|
119
|
+
const shared = config.disciplines ?? [];
|
|
120
|
+
const sessionOnly = config.sessionDisciplines ?? [];
|
|
121
|
+
const changeSetOnly = config.changeSetDisciplines ?? [];
|
|
118
122
|
const drafts = config.drafts ?? [];
|
|
119
|
-
const session =
|
|
123
|
+
const session = assembleCheckRegistrations({
|
|
120
124
|
config,
|
|
121
125
|
rootDir: spec.repoRoot,
|
|
122
126
|
covenant,
|
|
123
|
-
|
|
127
|
+
surface: 'session',
|
|
128
|
+
// The session key alone, and no roster: a roster is what an adapter loads onto each
|
|
129
|
+
// call, so a reader of the config would be shown one agent's names chosen by this
|
|
130
|
+
// renderer. The evidence path is the same kind of per-call value and is named by the
|
|
131
|
+
// key rather than by a filename: what a config reader is being shown is that the
|
|
132
|
+
// session surface carries a transcript-mod registration, and the renderer prints the
|
|
133
|
+
// key, never the path. The empty lists are the session a run supplies.
|
|
134
|
+
session: { evidencePath: SESSION_EVIDENCE_PATH_KEY, userMessages: [], toolCalls: [] },
|
|
124
135
|
transcript: noopTranscript,
|
|
125
136
|
});
|
|
126
|
-
const
|
|
137
|
+
const changeSet = assembleChangeSetRegistrations({
|
|
127
138
|
config,
|
|
128
139
|
rootDir: spec.repoRoot,
|
|
129
140
|
covenant,
|
|
141
|
+
surface: 'changeSet',
|
|
130
142
|
});
|
|
131
|
-
const gitSettings = resolveGitAdapterSettings({ namespace: config.adapters?.git });
|
|
132
143
|
const text = [
|
|
133
144
|
`pdks explain — ${configPath}`,
|
|
134
145
|
'',
|
|
135
146
|
renderSurface({
|
|
136
|
-
header: '
|
|
147
|
+
header: 'input: call IR (one call, stdin) · ' +
|
|
148
|
+
`disciplines ${shared.length} · sessionDisciplines ${sessionOnly.length} · ` +
|
|
149
|
+
'disciplines: advise unless enforce: block · meta: block',
|
|
137
150
|
registrations: session,
|
|
138
151
|
drafts,
|
|
139
|
-
disciplines,
|
|
152
|
+
disciplines: [...shared, ...sessionOnly],
|
|
140
153
|
selfModScope: 'common; includes the config file itself',
|
|
141
154
|
}),
|
|
142
155
|
'',
|
|
143
156
|
renderSurface({
|
|
144
|
-
header:
|
|
145
|
-
|
|
157
|
+
header: 'input: --diff (change set, stdin) · ' +
|
|
158
|
+
`disciplines ${shared.length} · changeSetDisciplines ${changeSetOnly.length} · ` +
|
|
159
|
+
'disciplines: advise unless enforce: block',
|
|
160
|
+
registrations: changeSet,
|
|
146
161
|
drafts,
|
|
147
|
-
disciplines,
|
|
148
|
-
selfModScope: 'common
|
|
162
|
+
disciplines: [...shared, ...changeSetOnly],
|
|
163
|
+
selfModScope: 'common; includes the config file itself',
|
|
149
164
|
}),
|
|
150
165
|
'',
|
|
151
166
|
].join('\n');
|
package/dist/load-config.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* This lives in its own module rather than in the package barrel because ESM re-exports are
|
|
6
6
|
* eager: importing `loadConfig` from the barrel would instantiate both composition roots,
|
|
7
|
-
* putting the session adapter on the
|
|
7
|
+
* putting the session adapter on the change-set surface's load path where it is never used. A
|
|
8
8
|
* workspace missing only that dist would then kill `pdks covenant check` before its
|
|
9
9
|
* fail-closed handler could record a row. Both composition roots import this module directly
|
|
10
10
|
* for the same reason.
|
package/dist/load-config.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* This lives in its own module rather than in the package barrel because ESM re-exports are
|
|
6
6
|
* eager: importing `loadConfig` from the barrel would instantiate both composition roots,
|
|
7
|
-
* putting the session adapter on the
|
|
7
|
+
* putting the session adapter on the change-set surface's load path where it is never used. A
|
|
8
8
|
* workspace missing only that dist would then kill `pdks covenant check` before its
|
|
9
9
|
* fail-closed handler could record a row. Both composition roots import this module directly
|
|
10
10
|
* for the same reason.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function sessionPreStateReader(location: string): string | null | undefined;
|
|
15
15
|
/**
|
|
16
|
-
* The
|
|
16
|
+
* The change-set surface's reader: it observes a staged diff, whose payloads already carry the
|
|
17
17
|
* pre their own observation saw, and it registers no shell axis — so no derivation ever asks
|
|
18
18
|
* for a pre-state here. Should one arrive, the working tree is not what this surface judges,
|
|
19
19
|
* and answering from it would compare the diff against the wrong baseline; `undefined` says
|
package/dist/pre-state-reader.js
CHANGED
|
@@ -21,7 +21,7 @@ export function sessionPreStateReader(location) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* The
|
|
24
|
+
* The change-set surface's reader: it observes a staged diff, whose payloads already carry the
|
|
25
25
|
* pre their own observation saw, and it registers no shell axis — so no derivation ever asks
|
|
26
26
|
* for a pre-state here. Should one arrive, the working tree is not what this surface judges,
|
|
27
27
|
* and answering from it would compare the diff against the wrong baseline; `undefined` says
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The half of an installation every distribution path shares: the data config the judges
|
|
5
5
|
* read, and the telemetry ignore line. What differs between paths is REGISTRATION — how the
|
|
6
|
-
* agent is told to spawn a judge at all — and that lives
|
|
7
|
-
*
|
|
6
|
+
* agent is told to spawn a judge at all — and that lives in each agent's own adapter
|
|
7
|
+
* installer. The split is what lets a second path reuse this function unchanged
|
|
8
8
|
* instead of scaffolding a config a second time, so nothing that registers anything belongs
|
|
9
9
|
* here.
|
|
10
10
|
*
|
package/dist/scaffold-project.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The half of an installation every distribution path shares: the data config the judges
|
|
5
5
|
* read, and the telemetry ignore line. What differs between paths is REGISTRATION — how the
|
|
6
|
-
* agent is told to spawn a judge at all — and that lives
|
|
7
|
-
*
|
|
6
|
+
* agent is told to spawn a judge at all — and that lives in each agent's own adapter
|
|
7
|
+
* installer. The split is what lets a second path reuse this function unchanged
|
|
8
8
|
* instead of scaffolding a config a second time, so nothing that registers anything belongs
|
|
9
9
|
* here.
|
|
10
10
|
*
|
|
@@ -32,7 +32,7 @@ const GITIGNORE_ENTRY = `# Polydeukes telemetry — local observation data, neve
|
|
|
32
32
|
* {@link schemaDirective} prepends the `yaml-language-server` line when the schema is where
|
|
33
33
|
* that line would name it.
|
|
34
34
|
*/
|
|
35
|
-
const GENERATED_CONFIG = `# Polydeukes protection policy — generated by \`pdks init
|
|
35
|
+
const GENERATED_CONFIG = `# Polydeukes protection policy — generated by \`pdks init\`.
|
|
36
36
|
#
|
|
37
37
|
# This file is data, never code. The judges read it, every verdict traces back to an entry
|
|
38
38
|
# below, and editing it is how you change what is judged.
|
|
@@ -79,6 +79,12 @@ witness:
|
|
|
79
79
|
# The disciplines you judge by, and the three rungs one climbs — shown as three entries so
|
|
80
80
|
# each rung is a line you can copy. Uncomment to start; ids must stay distinct.
|
|
81
81
|
#
|
|
82
|
+
# Three lists, chosen by what a declaration reads. \`disciplines:\` is judged on both
|
|
83
|
+
# surfaces (the changed file's own pre/post and file sources). \`sessionDisciplines:\` is
|
|
84
|
+
# judged only where a live call is observed before it runs (command line, transcript,
|
|
85
|
+
# actor). \`changeSetDisciplines:\` is judged only over a finished change set
|
|
86
|
+
# (\`--diff\`). The loader refuses an entry written in a list its channels do not fit.
|
|
87
|
+
#
|
|
82
88
|
# disciplines:
|
|
83
89
|
# # A draft: prose only, no predicate. Registered and read, never judged.
|
|
84
90
|
# - id: 'no-todo-in-shipped-code-draft'
|
|
@@ -33,6 +33,18 @@
|
|
|
33
33
|
"uniqueItems": true,
|
|
34
34
|
"items": { "$ref": "#/$defs/discipline" }
|
|
35
35
|
},
|
|
36
|
+
"sessionDisciplines": {
|
|
37
|
+
"description": "Disciplines only the session surface observes — every entry whose declaration binds a transcript, channel, command, or actor evidence channel. Fully identical entries are copy-paste duplicates (by-id uniqueness and the placement rule are enforced by the runtime validator).",
|
|
38
|
+
"type": "array",
|
|
39
|
+
"uniqueItems": true,
|
|
40
|
+
"items": { "$ref": "#/$defs/discipline" }
|
|
41
|
+
},
|
|
42
|
+
"changeSetDisciplines": {
|
|
43
|
+
"description": "Disciplines only the change-set surface observes — every entry whose declaration binds the changes source. Fully identical entries are copy-paste duplicates (by-id uniqueness and the placement rule are enforced by the runtime validator).",
|
|
44
|
+
"type": "array",
|
|
45
|
+
"uniqueItems": true,
|
|
46
|
+
"items": { "$ref": "#/$defs/discipline" }
|
|
47
|
+
},
|
|
36
48
|
"witness": { "$ref": "#/$defs/witness" }
|
|
37
49
|
},
|
|
38
50
|
"$defs": {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The change-set surface's supply body for named file sources — one repo-relative path read from
|
|
3
|
+
* the working tree.
|
|
4
|
+
*/
|
|
5
|
+
import type { SourceReader } from '@polydeukes/core';
|
|
6
|
+
/** {@link worktreeReader} input — the repository the paths are relative to. */
|
|
7
|
+
export type WorktreeReaderSpec = {
|
|
8
|
+
repoRoot: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A reader over the working tree under `repoRoot`, with the same absence table the session
|
|
12
|
+
* surface's disk reader pins in its own package.
|
|
13
|
+
*
|
|
14
|
+
* Symlinks are followed: on disk the observable fact IS the target's text, and the session
|
|
15
|
+
* surface reads the same way. A path the tree holds but cannot give as judgeable text —
|
|
16
|
+
* a directory, or bytes carrying a NUL — answers `undefined`, which the declaration's
|
|
17
|
+
* `supply` policy disposes of.
|
|
18
|
+
*/
|
|
19
|
+
export declare function worktreeReader(spec: WorktreeReaderSpec): SourceReader;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The change-set surface's supply body for named file sources — one repo-relative path read from
|
|
3
|
+
* the working tree.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
/**
|
|
8
|
+
* A reader over the working tree under `repoRoot`, with the same absence table the session
|
|
9
|
+
* surface's disk reader pins in its own package.
|
|
10
|
+
*
|
|
11
|
+
* Symlinks are followed: on disk the observable fact IS the target's text, and the session
|
|
12
|
+
* surface reads the same way. A path the tree holds but cannot give as judgeable text —
|
|
13
|
+
* a directory, or bytes carrying a NUL — answers `undefined`, which the declaration's
|
|
14
|
+
* `supply` policy disposes of.
|
|
15
|
+
*/
|
|
16
|
+
export function worktreeReader(spec) {
|
|
17
|
+
return (path) => {
|
|
18
|
+
let bytes;
|
|
19
|
+
try {
|
|
20
|
+
bytes = readFileSync(join(spec.repoRoot, path));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const { code } = error;
|
|
24
|
+
if (code === 'ENOENT' || code === 'EISDIR' || code === 'ENOTDIR')
|
|
25
|
+
return undefined;
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
return bytes.includes(0) ? undefined : bytes.toString('utf-8');
|
|
29
|
+
};
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polydeukes",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
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. Alpha.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"harness",
|
|
@@ -28,18 +28,7 @@
|
|
|
28
28
|
"pdks": "./dist/bin.js",
|
|
29
29
|
"polydeukes": "./dist/bin.js"
|
|
30
30
|
},
|
|
31
|
-
"main": "./dist/index.js",
|
|
32
|
-
"module": "./dist/index.js",
|
|
33
|
-
"types": "./dist/index.d.ts",
|
|
34
31
|
"exports": {
|
|
35
|
-
".": {
|
|
36
|
-
"types": "./dist/index.d.ts",
|
|
37
|
-
"import": "./dist/index.js"
|
|
38
|
-
},
|
|
39
|
-
"./claude-code": {
|
|
40
|
-
"types": "./dist/claude-code.d.ts",
|
|
41
|
-
"import": "./dist/claude-code.js"
|
|
42
|
-
},
|
|
43
32
|
"./schema.json": "./dist/schema/polydeukes.schema.json"
|
|
44
33
|
},
|
|
45
34
|
"files": [
|
|
@@ -54,15 +43,12 @@
|
|
|
54
43
|
},
|
|
55
44
|
"dependencies": {
|
|
56
45
|
"yaml": "2.9.0",
|
|
57
|
-
"@polydeukes/
|
|
58
|
-
"@polydeukes/core": "^0.6.1",
|
|
59
|
-
"@polydeukes/covenant": "^0.6.1",
|
|
60
|
-
"@polydeukes/adapter-git": "^0.6.1"
|
|
46
|
+
"@polydeukes/core": "^0.7.1"
|
|
61
47
|
},
|
|
62
48
|
"devDependencies": {
|
|
63
49
|
"@types/node": "^24.0.0",
|
|
64
50
|
"typescript": "7.0.2",
|
|
65
|
-
"vitest": "^
|
|
51
|
+
"vitest": "^5.0.0"
|
|
66
52
|
},
|
|
67
53
|
"scripts": {
|
|
68
54
|
"build": "tsc -p tsconfig.build.json && node scripts/copy-docs.mjs && node scripts/copy-schema.mjs",
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `runClaudeCodeHook` — the assembled session-surface judgment runner.
|
|
3
|
-
*
|
|
4
|
-
* The session counterpart of {@link runCovenantCheck}, and the one place where the Claude
|
|
5
|
-
* Code adapter (tool vocabulary, up-translation) and the covenant package (dispatcher +
|
|
6
|
-
* judge bodies) meet. Packages stay one-way — each depends only on core — so their
|
|
7
|
-
* composition lives here, in the umbrella, and the repository's PreToolUse hook shrinks to
|
|
8
|
-
* a delegator that calls this function. That is what makes the session surface installable:
|
|
9
|
-
* a consumer registers a hook that resolves this package instead of copying assembly.
|
|
10
|
-
*
|
|
11
|
-
* The protection-policy data (protectedPaths / disciplines / witness) is read from the root
|
|
12
|
-
* data config through {@link loadConfig}, which also attaches the config file to its own
|
|
13
|
-
* surface.
|
|
14
|
-
*
|
|
15
|
-
* The valve is the TTL witness, judged over the JSONL transcript provider. The judge body
|
|
16
|
-
* always spawns, and only an outcome that translated to blocked consults the witness —
|
|
17
|
-
* `witnessed` rows are would-block only. Its defence is provenance rather than secrecy: only
|
|
18
|
-
* a real human utterance carries the transcript marking `findUserMessages()` admits.
|
|
19
|
-
*
|
|
20
|
-
* fail-closed: ANY failure — an unbuilt judge body, an unreadable stdin, a missing or
|
|
21
|
-
* invalid config file — resolves to `{ exitCode: 2 }` with one `blocked` record under the
|
|
22
|
-
* `hook` label. Nothing throws: an uncaught rejection would exit the delegator non-blocking,
|
|
23
|
-
* the cheapest bypass vector there is. Recovery from an unbuilt clone is `pnpm build` (it
|
|
24
|
-
* mentions no protected path, so it is never blocked).
|
|
25
|
-
*/
|
|
26
|
-
import { type CanonicalTranscript } from '@polydeukes/core';
|
|
27
|
-
import { type CovenantRegistration } from '@polydeukes/covenant';
|
|
28
|
-
import { type CovenantModule } from './covenant-module.ts';
|
|
29
|
-
import { loadConfig } from './load-config.ts';
|
|
30
|
-
/** {@link runClaudeCodeHook} result — the exit code the hook process leaves with. */
|
|
31
|
-
export type ClaudeCodeHookOutcome = {
|
|
32
|
-
exitCode: 0 | 2;
|
|
33
|
-
};
|
|
34
|
-
/** `runClaudeCodeHook` input — the `CovenantCheckSpec` shape, session side. */
|
|
35
|
-
export type ClaudeCodeHookSpec = {
|
|
36
|
-
/** Repository root — config discovery and discipline glob scoping both anchor here. */
|
|
37
|
-
repoRoot: string;
|
|
38
|
-
/** One raw PreToolUse payload. ABSENT means read fd 0 (the hook's real stdin). */
|
|
39
|
-
rawPayload?: string;
|
|
40
|
-
/** Overrides the config's telemetry log path (tests and assembly injection). */
|
|
41
|
-
telemetryPath?: string;
|
|
42
|
-
/** Overrides the resolved covenant dist directory (tests and assembly injection). */
|
|
43
|
-
covenantDist?: string;
|
|
44
|
-
};
|
|
45
|
-
/** {@link assembleSessionRegistrations} input — what the session surface's assembly needs. */
|
|
46
|
-
export type SessionAssemblySpec = {
|
|
47
|
-
config: ReturnType<typeof loadConfig>['config'];
|
|
48
|
-
rootDir: string;
|
|
49
|
-
/**
|
|
50
|
-
* The covenant surface the registrations are built from — the module the caller loaded
|
|
51
|
-
* from the resolved dist, so what judges a call is what that dist carries, and what
|
|
52
|
-
* `explain` renders is what would judge it.
|
|
53
|
-
*/
|
|
54
|
-
covenant: CovenantModule;
|
|
55
|
-
/** The payload's transcript path. ABSENT leaves the transcript-mod registration out. */
|
|
56
|
-
transcriptPath?: string;
|
|
57
|
-
transcript?: CanonicalTranscript;
|
|
58
|
-
witness?: CovenantRegistration['witness'];
|
|
59
|
-
};
|
|
60
|
-
/**
|
|
61
|
-
* The session surface's registration set. One assembly, two consumers: the runner below
|
|
62
|
-
* dispatches it, `explain` renders it — so what a reader is shown is the table the judgment
|
|
63
|
-
* actually uses, never a second opinion about it.
|
|
64
|
-
*/
|
|
65
|
-
export declare function assembleSessionRegistrations(spec: SessionAssemblySpec): CovenantRegistration[];
|
|
66
|
-
/**
|
|
67
|
-
* The session-surface entry point: the post-hoc state comparison wrapped around the judgment.
|
|
68
|
-
*
|
|
69
|
-
* The comparison sits OUTSIDE {@link judgeHookCall}'s fail-closed try on both ends. Inside
|
|
70
|
-
* it, a comparison failure would become a blocked call — the opposite of a mechanism whose
|
|
71
|
-
* whole purpose is to record rather than stop — so each side carries its own catch and
|
|
72
|
-
* neither can reach the verdict. Observation is fail-open, the direction
|
|
73
|
-
* `appendRecordFailOpen` already established: the worst outcome is a missing datum.
|
|
74
|
-
*
|
|
75
|
-
* Order is the contract. The comparison runs first, so it reads the window the previous call
|
|
76
|
-
* left and its rows land ahead of this call's judgment; the re-establishment runs last, so
|
|
77
|
-
* this call's own judged writes are folded in rather than alarmed on next time.
|
|
78
|
-
*/
|
|
79
|
-
export declare function runClaudeCodeHook(spec: ClaudeCodeHookSpec): Promise<ClaudeCodeHookOutcome>;
|