@dmsdc-ai/aigentry-telepty 0.3.5 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +72 -0
- package/cli.js +36 -15
- package/daemon.js +355 -5
- package/package.json +25 -1
- package/session-state.js +23 -0
- package/src/prompt-symbol-registry.js +43 -1
- package/.claude/commands/telepty-allow.md +0 -58
- package/.claude/commands/telepty-attach.md +0 -22
- package/.claude/commands/telepty-inject.md +0 -72
- package/.claude/commands/telepty-list.md +0 -22
- package/.claude/commands/telepty-manual-test.md +0 -73
- package/.claude/commands/telepty-start.md +0 -25
- package/.claude/commands/telepty-test.md +0 -25
- package/.claude/commands/telepty.md +0 -82
- package/AGENTS.md +0 -97
- package/BOUNDARY.md +0 -31
- package/BUS_EVENT_SCHEMA.md +0 -206
- package/CLAUDE.md +0 -100
- package/GEMINI.md +0 -10
- package/URGENT_ISSUES.resolved.md +0 -1
- package/docs/reports/2026-05-05-issue-8-claude-review.md +0 -194
- package/docs/specs/2026-05-05-issue-8-telepty-init.md +0 -477
- package/docs/superpowers/specs/2026-04-26-inject-submit-enter-reliability.md +0 -447
- package/docs/superpowers/specs/2026-04-26-prompt-symbol-render-gate.md +0 -571
- package/docs/superpowers/specs/2026-04-26-submit-gate-fixes-v2.md +0 -608
- package/docs/superpowers/specs/2026-05-02-submit-force-and-retry.md +0 -139
- package/protocol/mailbox.md +0 -244
- package/scripts/regen-snippet-fixtures.js +0 -42
- package/specs/codex-inject-spec.md +0 -201
- package/specs/enforce-report-spec.md +0 -237
- package/templates/AGENTS.md +0 -71
- package/tests/snippet-protocol/v1/golden-agents.json +0 -1
- package/tests/snippet-protocol/v1/golden-agents.md +0 -17
- package/tests/snippet-protocol/v1/golden-all.json +0 -3
- package/tests/snippet-protocol/v1/golden-all.md +0 -53
- package/tests/snippet-protocol/v1/golden-claude.json +0 -1
- package/tests/snippet-protocol/v1/golden-claude.md +0 -17
- package/tests/snippet-protocol/v1/golden-gemini.json +0 -1
- package/tests/snippet-protocol/v1/golden-gemini.md +0 -17
package/BUS_EVENT_SCHEMA.md
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
# Telepty Bus Event Schema Standard
|
|
2
|
-
|
|
3
|
-
Version: 2.0 (2026-03-15)
|
|
4
|
-
Agreed by: telepty, deliberation, devkit, brain, orchestrator
|
|
5
|
-
|
|
6
|
-
## Transport
|
|
7
|
-
|
|
8
|
-
- **HTTP**: `POST /api/bus/publish` with JSON body
|
|
9
|
-
- **WebSocket**: `ws://HOST:3848/api/bus` send JSON message
|
|
10
|
-
- Both paths trigger bus auto-router for routable events
|
|
11
|
-
|
|
12
|
-
## Envelope Structure (All Events)
|
|
13
|
-
|
|
14
|
-
```json
|
|
15
|
-
{
|
|
16
|
-
"version": 1,
|
|
17
|
-
"message_id": "string (UUID or prefixed ID)",
|
|
18
|
-
"kind": "string (event type)",
|
|
19
|
-
"source": "string (sender identifier)",
|
|
20
|
-
"source_host": "string (machine_id of sender, e.g. hostname or Tailscale IP)",
|
|
21
|
-
"target": "string | null (target session ID, optional @host suffix)",
|
|
22
|
-
"ts": "ISO 8601 timestamp"
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Canonical Field Names
|
|
27
|
-
|
|
28
|
-
| Field | Type | Description |
|
|
29
|
-
|-------|------|-------------|
|
|
30
|
-
| `version` | number | Envelope schema version (currently 1) |
|
|
31
|
-
| `kind` | string | Event type (NOT `type` — `kind` is canonical) |
|
|
32
|
-
| `target` | string | Target telepty session ID. May include `@host` suffix for remote |
|
|
33
|
-
| `source` | string | Sender identifier (format: `project:session_id`) |
|
|
34
|
-
| `source_host` | string | Machine ID of sender (hostname or TELEPTY_MACHINE_ID) |
|
|
35
|
-
| `message_id` | string | Unique message identifier |
|
|
36
|
-
| `ts` | string | ISO 8601 timestamp |
|
|
37
|
-
|
|
38
|
-
## Cross-Machine Addressing
|
|
39
|
-
|
|
40
|
-
### Session Locator
|
|
41
|
-
Every session is uniquely identified by a locator triple:
|
|
42
|
-
```json
|
|
43
|
-
{ "machine_id": "hostname", "session_id": "aigentry-devkit-001", "project_id": "aigentry-devkit" }
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Remote Target Format
|
|
47
|
-
`target` field supports `@host` suffix: `aigentry-devkit-001@100.100.100.5`
|
|
48
|
-
- Router strips suffix, resolves session on local daemon
|
|
49
|
-
- For cross-machine relay (P3), daemon forwards to target host
|
|
50
|
-
|
|
51
|
-
### Machine ID
|
|
52
|
-
- Default: `os.hostname()`
|
|
53
|
-
- Override: `TELEPTY_MACHINE_ID` env var
|
|
54
|
-
- Exposed in: `GET /api/meta` (`machine_id` field), session `locator` object, bus event `source_host`
|
|
55
|
-
|
|
56
|
-
### Global Session ID Uniqueness (P4)
|
|
57
|
-
- Convention: `{project}-{NNN}` (e.g. `aigentry-devkit-001`)
|
|
58
|
-
- Cross-machine uniqueness: guaranteed by `locator.machine_id` prefix in bus events
|
|
59
|
-
- Collision resolution: `resolveSessionAlias` returns local session only; remote sessions discovered via `source_host` field
|
|
60
|
-
- If two machines have `aigentry-devkit-001`, inject uses `target@host` to disambiguate
|
|
61
|
-
- Short form `aigentry-devkit-001` resolves to LOCAL session; remote requires explicit `@host`
|
|
62
|
-
|
|
63
|
-
### Peer Auth
|
|
64
|
-
- Localhost: always trusted
|
|
65
|
-
- Tailscale (100.x.y.z): trusted by default
|
|
66
|
-
- Custom peers: `TELEPTY_PEER_ALLOWLIST=ip1,ip2` env var
|
|
67
|
-
- All others: require `x-telepty-token` header
|
|
68
|
-
|
|
69
|
-
## Routable Events (Auto-Router)
|
|
70
|
-
|
|
71
|
-
### `turn_request`
|
|
72
|
-
|
|
73
|
-
Published by deliberation to request a turn from a session. Telepty daemon auto-routes to target session PTY.
|
|
74
|
-
|
|
75
|
-
```json
|
|
76
|
-
{
|
|
77
|
-
"message_id": "turn_request-<uuid>",
|
|
78
|
-
"session_id": "<deliberation_session_id>",
|
|
79
|
-
"project": "<project_name>",
|
|
80
|
-
"kind": "turn_request",
|
|
81
|
-
"source": "deliberation:<deliberation_session_id>",
|
|
82
|
-
"target": "<telepty_session_id>[@<host>]",
|
|
83
|
-
"reply_to": "<deliberation_session_id>",
|
|
84
|
-
"trace": ["project:<name>", "speaker:<id>", "turn:<turn_id>"],
|
|
85
|
-
"payload": {
|
|
86
|
-
"turn_id": "string",
|
|
87
|
-
"round": "number",
|
|
88
|
-
"max_rounds": "number",
|
|
89
|
-
"speaker": "string (target telepty session ID)",
|
|
90
|
-
"role": "string | null",
|
|
91
|
-
"prompt": "string (full prompt text — inject as-is to PTY)",
|
|
92
|
-
"prompt_sha1": "string (40-char SHA1)",
|
|
93
|
-
"history_entries": "number",
|
|
94
|
-
"transport_timeout_ms": "number",
|
|
95
|
-
"semantic_timeout_ms": "number"
|
|
96
|
-
},
|
|
97
|
-
"ts": "ISO 8601"
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**Important Notes:**
|
|
102
|
-
- `session_id` is the DELIBERATION session ID, NOT the target telepty session
|
|
103
|
-
- `target` is the telepty session ID to inject into
|
|
104
|
-
- `payload.prompt` is the full text to write to PTY (no further processing needed)
|
|
105
|
-
- `@host` suffix on target: strip before resolving, use for remote routing
|
|
106
|
-
|
|
107
|
-
**Auto-Router Behavior:**
|
|
108
|
-
1. Daemon receives turn_request via HTTP POST or WS
|
|
109
|
-
2. Extracts `target` field, strips `@host` suffix
|
|
110
|
-
3. Resolves session via `resolveSessionAlias()`
|
|
111
|
-
4. Delivers `payload.prompt` to session PTY (kitty primary, WS fallback)
|
|
112
|
-
5. Emits `inject_written` ack on bus
|
|
113
|
-
|
|
114
|
-
### `inject_written` (ACK)
|
|
115
|
-
|
|
116
|
-
Emitted by telepty after successful auto-route delivery.
|
|
117
|
-
|
|
118
|
-
```json
|
|
119
|
-
{
|
|
120
|
-
"type": "inject_written",
|
|
121
|
-
"inject_id": "UUID",
|
|
122
|
-
"sender": "daemon",
|
|
123
|
-
"target_agent": "<session_id>",
|
|
124
|
-
"source_type": "bus_auto_route",
|
|
125
|
-
"delivered": true,
|
|
126
|
-
"timestamp": "ISO 8601"
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Session Lifecycle Events
|
|
131
|
-
|
|
132
|
-
### `session_register`
|
|
133
|
-
```json
|
|
134
|
-
{ "type": "session_register", "sender": "daemon", "session_id": "string", "command": "string", "cwd": "string", "timestamp": "ISO 8601" }
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### `session.replaced`
|
|
138
|
-
```json
|
|
139
|
-
{ "type": "session.replaced", "sender": "daemon", "old_id": "string", "new_id": "string", "alias": "string", "timestamp": "ISO 8601" }
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### `session.idle`
|
|
143
|
-
```json
|
|
144
|
-
{ "type": "session.idle", "session_id": "string", "idleSeconds": "number", "lastActivityAt": "ISO 8601", "timestamp": "ISO 8601" }
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### `session_health` (periodic, every 10s)
|
|
148
|
-
```json
|
|
149
|
-
{ "type": "session_health", "session_id": "string", "payload": { "alive": true, "pid": "number|null", "type": "string", "clients": "number", "idleSeconds": "number|null" }, "timestamp": "ISO 8601" }
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## Inject Events
|
|
153
|
-
|
|
154
|
-
### `inject_written`
|
|
155
|
-
```json
|
|
156
|
-
{ "type": "inject_written", "inject_id": "UUID", "sender": "daemon", "target_agent": "string", "content": "string", "from": "string|null", "reply_to": "string|null", "thread_id": "string|null", "reply_expected": "boolean", "timestamp": "ISO 8601" }
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### `message_routed`
|
|
160
|
-
```json
|
|
161
|
-
{ "type": "message_routed", "message_id": "UUID", "from": "string", "to": "string", "reply_to": "string", "inject_id": "UUID", "deliberation_session_id": "string|null", "thread_id": "string|null", "timestamp": "ISO 8601" }
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## Handoff Events
|
|
165
|
-
|
|
166
|
-
### `handoff.created` / `handoff.claimed` / `handoff.executing` / `handoff.completed`
|
|
167
|
-
```json
|
|
168
|
-
{ "type": "handoff.<status>", "handoff_id": "UUID", "source_session_id": "string|null", "deliberation_id": "string|null", "auto_execute": "boolean", "task_count": "number", "timestamp": "ISO 8601" }
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## Thread Events
|
|
172
|
-
|
|
173
|
-
### `thread.opened`
|
|
174
|
-
```json
|
|
175
|
-
{ "type": "thread.opened", "thread_id": "UUID", "topic": "string", "orchestrator_session_id": "string|null", "participant_session_ids": ["string"], "timestamp": "ISO 8601" }
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### `thread.closed`
|
|
179
|
-
```json
|
|
180
|
-
{ "type": "thread.closed", "thread_id": "UUID", "topic": "string", "message_count": "number", "timestamp": "ISO 8601" }
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## Termination Signal Detection
|
|
184
|
-
|
|
185
|
-
Messages containing these strings suppress auto-reply guide footer:
|
|
186
|
-
- `no further reply needed`
|
|
187
|
-
- `thread closed` / `closed on X side`
|
|
188
|
-
- `ack received` / `ack-only`
|
|
189
|
-
- `회신 불필요` / `스레드 종료`
|
|
190
|
-
|
|
191
|
-
## Inject API Reference
|
|
192
|
-
|
|
193
|
-
### `POST /api/sessions/:id/inject`
|
|
194
|
-
|
|
195
|
-
```json
|
|
196
|
-
{
|
|
197
|
-
"prompt": "string (REQUIRED — canonical body field)",
|
|
198
|
-
"from": "string (sender session ID)",
|
|
199
|
-
"reply_to": "string (defaults to from if omitted)",
|
|
200
|
-
"thread_id": "string (optional)",
|
|
201
|
-
"reply_expected": "boolean (optional)",
|
|
202
|
-
"no_enter": "boolean (skip Enter after inject)"
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
**Note:** The canonical body field is `prompt`, NOT `text`, `content`, or `message`.
|
package/CLAUDE.md
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# telepty — PTY Multiplexer & Session Orchestrator
|
|
2
|
-
|
|
3
|
-
aigentry 에코시스템의 **통신 인프라**. 멀티 AI 세션을 생성·연결·제어하는 PTY 멀티플렉서.
|
|
4
|
-
|
|
5
|
-
## 에코시스템 내 역할
|
|
6
|
-
|
|
7
|
-
9개 aigentry 세션(orchestrator, brain, amplify, dustcraw 등) 간 통신을 담당:
|
|
8
|
-
- 세션 생성/관리 (`allow`, `spawn`)
|
|
9
|
-
- 메시지 전달 (`inject`, `broadcast`, `multicast`, `reply`)
|
|
10
|
-
- 실시간 모니터링 (`tui`, `monitor`, `listen`)
|
|
11
|
-
- 세션 간 토론 조율 (`deliberate`)
|
|
12
|
-
|
|
13
|
-
## 아키텍처
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
CLI (cli.js) ──→ HTTP/WS ──→ Daemon (daemon.js:3848)
|
|
17
|
-
├── Session WS (/api/sessions/:id)
|
|
18
|
-
├── Event Bus WS (/api/bus)
|
|
19
|
-
└── REST API (/api/sessions/*)
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### 핵심 모듈
|
|
23
|
-
|
|
24
|
-
| 파일 | 줄수 | 역할 |
|
|
25
|
-
|------|------|------|
|
|
26
|
-
| `cli.js` | ~1950 | CLI 명령 + allow-bridge (PTY 래핑) |
|
|
27
|
-
| `daemon.js` | ~1550 | HTTP/WS 서버, 세션 상태, inject 전달 |
|
|
28
|
-
| `tui.js` | ~500 | blessed 기반 TUI 대시보드 |
|
|
29
|
-
| `session-routing.js` | 81 | 세션 ID 해석, alias 매칭, 호스트 그룹핑 |
|
|
30
|
-
| `daemon-control.js` | 223 | 싱글톤 daemon PID 관리 |
|
|
31
|
-
| `auth.js` | 33 | UUID 토큰 기반 인증 |
|
|
32
|
-
| `interactive-terminal.js` | 71 | raw mode stdin/stdout 관리 |
|
|
33
|
-
| `skill-installer.js` | 269 | Claude/Codex/Gemini 스킬 설치 |
|
|
34
|
-
|
|
35
|
-
### Inject 전달 경로 (wrapped session)
|
|
36
|
-
|
|
37
|
-
1. **Primary**: `kitty @ send-text` (터미널 직접 전달, allow-bridge 우회)
|
|
38
|
-
2. **Fallback**: WS → allow-bridge → `child.write()` (PTY)
|
|
39
|
-
3. **Submit**: `osascript` Return 키 → kitty fallback → WS `\r`
|
|
40
|
-
|
|
41
|
-
busy 세션: CR은 큐에 대기 중인 텍스트와 함께 큐잉 후 올바른 순서로 flush.
|
|
42
|
-
|
|
43
|
-
## 명령어
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
# 테스트
|
|
47
|
-
npm test # 43 tests (node:test)
|
|
48
|
-
|
|
49
|
-
# 실행
|
|
50
|
-
telepty daemon # daemon 시작 (포트 3848)
|
|
51
|
-
telepty allow --id <name> claude # 세션 래핑
|
|
52
|
-
telepty tui # TUI 대시보드
|
|
53
|
-
telepty list # 세션 목록
|
|
54
|
-
telepty inject <id> "msg" # 메시지 주입
|
|
55
|
-
telepty broadcast "msg" # 전체 브로드캐스트
|
|
56
|
-
telepty session start --launch # kitty 탭으로 다중 세션 시작
|
|
57
|
-
|
|
58
|
-
# 릴리스
|
|
59
|
-
npm version patch --no-git-tag-version && npm publish --access public
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## 주요 규칙
|
|
63
|
-
|
|
64
|
-
- inject 후 submit은 항상 `osascript`로 통일 (`--no-enter` + osascript keystroke)
|
|
65
|
-
- inject 시 발신자 session ID (`--from`)를 항상 포함
|
|
66
|
-
- PTY `\r` 직접 의존 금지
|
|
67
|
-
|
|
68
|
-
## 최근 주요 변경 (v0.1.58–0.3.3)
|
|
69
|
-
|
|
70
|
-
| 버전 | 변경 |
|
|
71
|
-
|------|------|
|
|
72
|
-
| 0.3.3 | inject `--submit-force` (gate bypass) + idempotent `--submit-retry` (default 1, retry-safe 504만). 클라이언트 측 변경, daemon 무수정. task #347. |
|
|
73
|
-
| 0.3.2 | Layer 3 prompt-symbol 렌더 게이트 — claude/codex/gemini 별 prompt symbol을 cmux read-screen으로 polling. |
|
|
74
|
-
| 0.3.1 | 게이트 임계값 0.85 → 0.5 완화 + dispatch-on-timeout best-effort + send-key force=true 우회 추가. |
|
|
75
|
-
| 0.3.0 | Render-gated submit (sessionStateManager 기반). Enter 송신 전 REPL ready 검증. |
|
|
76
|
-
| 0.1.62 | TUI 태스크 추적 — bus 이벤트에서 [태그] 자동 파싱, 세션별 상태 표시 |
|
|
77
|
-
| 0.1.61 | reconnect 시 resize/\x0c 제거 (멀티터미널 깜빡임 수정) |
|
|
78
|
-
| 0.1.60 | TUI P1 — s=start, k=kill, p=purge stale |
|
|
79
|
-
| 0.1.59 | wrapped inject 503 반환 + 테스트 43/43 정합성 |
|
|
80
|
-
| 0.1.58 | inject busy-session CR/text 순서 수정 + kittyWindowId 캐시 무효화 |
|
|
81
|
-
|
|
82
|
-
## 자율 재귀적 오케스트레이션
|
|
83
|
-
|
|
84
|
-
이 세션은 작업 복잡도가 높거나, 독립 도메인이 식별되거나, 컨텍스트 분리가 필요하다고 판단되면 — 사용자 지시 없이 자율적으로:
|
|
85
|
-
|
|
86
|
-
1. 자기 프로젝트 하위에 폴더를 생성한다
|
|
87
|
-
2. 해당 폴더에 CLAUDE.md를 작성한다 (역할, 범위, 상위 세션과의 통신 방법)
|
|
88
|
-
3. aigentry scaffold 또는 telepty allow로 하위 Claude 세션을 생성한다
|
|
89
|
-
4. telepty inject로 하위 세션에 태스크를 주입한다
|
|
90
|
-
5. 하위 세션의 결과를 수신하고 통합한다
|
|
91
|
-
|
|
92
|
-
판단 기준:
|
|
93
|
-
- 현재 작업이 2개 이상 독립 도메인을 포함할 때
|
|
94
|
-
- 컨텍스트 윈도우의 30% 이상을 단일 하위 작업이 차지할 때
|
|
95
|
-
- 반복적 전문 작업이 식별될 때 (SEO, 특정 채널, 특정 데이터 소스 등)
|
|
96
|
-
|
|
97
|
-
원칙:
|
|
98
|
-
- YAGNI: 진짜 필요할 때만 생성. 과잉 분리 금지.
|
|
99
|
-
- 하위 세션 완료 시 오케스트레이터(상위)에게 반드시 보고.
|
|
100
|
-
- 하위 세션의 결과물은 상위 프로젝트에 통합(커밋).
|
package/GEMINI.md
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
@AGENTS.md
|
|
2
|
-
|
|
3
|
-
# Gemini CLI — aigentry-telepty
|
|
4
|
-
|
|
5
|
-
## Gemini 전용 설정
|
|
6
|
-
|
|
7
|
-
- 세션 ID: `aigentry-telepty-gemini`
|
|
8
|
-
- 보고: `telepty inject --ref --from aigentry-telepty-gemini aigentry-orchestrator-claude "보고 내용"`
|
|
9
|
-
- Gemini 강점 활용: upstream 이슈 검색, API 리서치, 문서화 우선
|
|
10
|
-
- 헌법: `~/projects/aigentry/docs/CONSTITUTION.md`
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
RESOLVED - all 3 urgent issues fixed in 40b8e47
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
# Issue #8 Claude Review of Codex Implementation (2026-05-05)
|
|
2
|
-
|
|
3
|
-
| Field | Value |
|
|
4
|
-
|---|---|
|
|
5
|
-
| Reviewer | Claude (cross-LLM rule — Codex impl → Claude review) |
|
|
6
|
-
| Implementer | Codex (`aigentry-telepty-coder-issue-8`, closed) |
|
|
7
|
-
| Range | `d7b8b21..d0f4495` (16 commits) + `aigentry-ssot@f4ff0cd` |
|
|
8
|
-
| Spec | `~/projects/aigentry-telepty/docs/specs/2026-05-05-issue-8-telepty-init.md` (commits 609092b → 8d2dc94) |
|
|
9
|
-
| Boundary ADR | `aigentry-orchestrator/docs/adr/2026-05-05-telepty-devkit-boundary.md` (e4b072b ACCEPTED) |
|
|
10
|
-
| Test result | 269/269 full suite PASS; 15/15 init suite PASS |
|
|
11
|
-
| Mode | Read-only audit per Invariant I1 |
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## §1 Summary
|
|
16
|
-
|
|
17
|
-
- **Verdict:** **ACCEPT**
|
|
18
|
-
- **Top issue:** None blocking. Two non-blocking process observations (N1: TDD ordering, N2: version bump deferred to release).
|
|
19
|
-
- **Tests-pass / spec-fidelity gap:** **0 mismatches.** All 15 init tests map 1:1 to spec §8.1-§8.6 items; assertions match spec text (with one minor interpretation noted at §7.1).
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## §2 Spec Fidelity Audit
|
|
24
|
-
|
|
25
|
-
| Check | Result | Evidence |
|
|
26
|
-
|---|---|---|
|
|
27
|
-
| CLI surface matches §3.1/§4 verbatim | PASS | `src/init/print-snippet.js:11` HELP string is `usage: telepty init --print-snippet [--target {claude\|agents\|gemini\|all}] [--format {markdown\|json}]` — byte-equal to spec §3.1 row 1 wording |
|
|
28
|
-
| argv-only, stdin closed | PASS | `cli.js:844` dispatches `runInit(args.slice(1))`; `print-snippet.js` never reads stdin; test 12 verifies stdin pipe payload is ignored |
|
|
29
|
-
| Exit codes 0/2/4 implemented (3 reserved-not-emitted) | PASS | `print-snippet.js:77,82,87,97,100`. Code 3 is "telepty too old" (only emitted by absent telepty itself; spec §3.5 confirms detection via shell exit 127 OR exit 3 — not by current telepty). |
|
|
30
|
-
| Default `--target=all --format=markdown` | PASS | `print-snippet.js:43-44` defaults; test 9 verifies |
|
|
31
|
-
| §3.2 markdown sentinel envelope | PASS | `print-snippet.js:28` emits `<!-- telepty-snippet/v1 BEGIN target=<t> sha256=<hex8> -->\n<body><!-- ... END target=<t> -->\n` matching spec §3.2 byte-for-byte |
|
|
32
|
-
| §3.3 NDJSON shape | PASS | `print-snippet.js:31-38` emits `{version,target,sha256,body}` with full 64-hex digest; test 5 verifies |
|
|
33
|
-
| §3.4 stderr policy (warnings only on happy path) | PASS | Test 13 asserts `result.stderr === ''` for `--target claude` |
|
|
34
|
-
| `--target=all` separator (empty line between envelopes) | PASS | `print-snippet.js:96` joins with `'\n'` (single LF after each envelope's trailing LF → blank line); test 4 asserts the `END target=claude -->\n\n<!-- telepty-snippet/v1 BEGIN target=agents` literal |
|
|
35
|
-
|
|
36
|
-
**8/8 fidelity checks PASS.**
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## §3 Boundary ADR Audit
|
|
41
|
-
|
|
42
|
-
| Check | Result | Evidence |
|
|
43
|
-
|---|---|---|
|
|
44
|
-
| No file I/O on user dotfiles | PASS | `grep -nE 'writeFile\|writeFileSync\|appendFile\|chmod\|unlink' src/init/` returns zero matches; only `fs.readFileSync` of own `src/init/snippets/*.md` |
|
|
45
|
-
| No subprocess invocation of devkit | PASS | `grep -nE 'child_process\|spawn\|exec\|aigentry\|scaffold' src/init/print-snippet.js` returns zero matches; `aigentry` only appears as descriptive text inside snippet body (per §3.1 rule 2) |
|
|
46
|
-
| §3.4 row 1 "no file editing in telepty" | PASS | Verified by file-mutation grep above |
|
|
47
|
-
| §3.5 mechanism-vs-content split (telepty = mechanism) | PASS | Snippet bodies describe telepty's own CLI surface only (rule 2); zero per-CLI placement guidance ("paste this into CLAUDE.md…") in body or section headers |
|
|
48
|
-
| §3.3.1.5 telepty CI runs without `aigentry` | PASS | Manually verified: `env -i HOME=/tmp/empty PATH=/usr/bin:/bin:<node-dir> node cli.js init --print-snippet --target claude` → exit 0, full envelope emitted; test 15 enforces in-suite |
|
|
49
|
-
|
|
50
|
-
**Boundary ADR: PASS (5/5).**
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## §4 OC Choice Verification
|
|
55
|
-
|
|
56
|
-
| Choice | Spec | Impl Evidence | Result |
|
|
57
|
-
|---|---|---|---|
|
|
58
|
-
| **OC-1=B** | snippet bodies in `src/init/snippets/{claude,agents,gemini}.md` (NOT inline JS, NOT JSON manifest) | Three files exist, `loadBody()` reads via `fs.readFileSync(path.join(snippetDir, '${target}.md'), 'utf8')` | PASS |
|
|
59
|
-
| **OC-2=B** | `npm run regen-fixtures` script + CI uses `git diff --exit-code` | `package.json:15` `regen-fixtures` script exists; `package.json:12,14` `test`/`test:ci` end with `&& git diff --exit-code tests/snippet-protocol/v1/` | PASS |
|
|
60
|
-
| **OC-3=A** | section header is mechanism-only (no per-CLI prelude) | Each `snippets/<t>.md` line 1 is `## telepty-snippet:<target>`; no "Add this block to AGENTS.md" prelude anywhere in body | PASS |
|
|
61
|
-
| **OQ-A** | G1 SSOT stub merged sibling cross-repo | `aigentry-ssot@f4ff0cd` adds `contracts/telepty-snippet-v1.md`; commit time 14:35:07 < final telepty commit 14:36:19 (correct atomic ordering); SSOT stub references spec doc URL | PASS |
|
|
62
|
-
| **OQ-C** | markdown=8-char sha256 prefix; JSON=64-char full digest | Markdown sentinel: `sha256=305aad81` (8 hex); JSON record: `"sha256":"305aad8181c1e75bb94589386cbc34f2b5103d6b5c1f6a7f01c99f1fbf6ed8c3"` (64 hex). Test 1 asserts `/sha256=[0-9a-f]{8}/`; test 5 asserts `/^[0-9a-f]{64}$/`. SSOT stub publishes the 64-hex hash table per target. | PASS |
|
|
63
|
-
|
|
64
|
-
**OC choices: 4/4 honored.** (OQ-A and OQ-C are bundled — count as 4 since orchestrator dispatch listed them as 4 distinct items; logical mapping = 5/5 if OQ-A and OQ-C are scored separately.)
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## §5 Constitutional Audit
|
|
69
|
-
|
|
70
|
-
| Article | Check | Result |
|
|
71
|
-
|---|---|---|
|
|
72
|
-
| **1 경량** | No premature abstractions. Two helpers added beyond spec §7.3 skeleton: (a) `loadBody(target, options)` accepts `options.snippetDir` for test injection — justified by §8.3 test 11 spec text "non-existent template path injection"; (b) `cli.js` lazifies `getAuthToken()` — necessary so `init --print-snippet` works on a fresh machine with no `~/.telepty/config.json` (Article 9). Both are minimal seams, neither is speculative. | PASS |
|
|
73
|
-
| **3 역할** | Telepty does mechanism (stdout contract + sha256 envelope) only. Snippet body content describes telepty's own CLI surface — rule 2 of §3.1 4-rule sharpening. | PASS |
|
|
74
|
-
| **9 독립** | Verified manually: `env -i HOME=… PATH=…<no aigentry>… node cli.js init --print-snippet` exits 0 with full envelope. Test 15 enforces inside the test suite. | PASS |
|
|
75
|
-
| **15 SSOT** | `aigentry-ssot/contracts/telepty-snippet-v1.md@f4ff0cd` registered before final telepty commit; spec doc and ADR references present in stub. | PASS |
|
|
76
|
-
| **17 무의존** | `git diff d7b8b21 d0f4495 -- package.json` shows zero new entries under `dependencies`. Only test-runner additions and `regen-fixtures` script. | PASS |
|
|
77
|
-
|
|
78
|
-
**Constitutional: 5/5.**
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## §6 Test Coverage vs Spec Map
|
|
83
|
-
|
|
84
|
-
15 init tests → spec §8.1-§8.6. All consistent.
|
|
85
|
-
|
|
86
|
-
| # | Test (file:line) | Spec § | Verdict |
|
|
87
|
-
|---|---|---|---|
|
|
88
|
-
| 1 | markdown for claude includes target+8-char sha256 (`init.test.js:94`) | §8.1.1 | ✓ literal |
|
|
89
|
-
| 2 | markdown for agents (`:102`) | §8.1.2 | ✓ literal |
|
|
90
|
-
| 3 | markdown for gemini (`:110`) | §8.1.3 | ✓ literal |
|
|
91
|
-
| 4 | markdown all = 3 envelopes ordered claude→agents→gemini, blank-line separator (`:118`) | §8.1.4 | ✓ literal — also asserts `END target=claude -->\n\n<!-- telepty-snippet/v1 BEGIN target=agents` |
|
|
92
|
-
| 5 | json all = 3 NDJSON typed records (4 keys, 64-char sha256) (`:132`) | §8.1.5 | ✓ literal — `Object.keys` deepEqual + per-key type assertions |
|
|
93
|
-
| 6 | bodies no shell-substitution hazards (`:149`) | §8.2.6 | ✓ semantic — see §7.1 below for interpretation note |
|
|
94
|
-
| 7 | bodies LF-only (`:162`) | §8.2.7 | ✓ literal |
|
|
95
|
-
| 8 | byte-identical sequential invocations (`:170`) | §8.2.8 | ✓ literal |
|
|
96
|
-
| 9 | print-snippet defaults exit 0, all/markdown (`:181`) | §8.3.9 | ✓ literal — also verifies default `--target=all` order |
|
|
97
|
-
| 10 | unsupported target → exit 2, stderr matches, empty stdout (`:192`) | §8.3.10 | ✓ literal — stderr regex `/--target must be one of claude, agents, gemini, all/` exact |
|
|
98
|
-
| 11 | internal failure → exit 4, empty stdout, stderr non-empty (`:200`) | §8.3.11 | ✓ literal — uses `snippetDir` injection (path-injection seam, matches spec wording "non-existent template path injection") |
|
|
99
|
-
| 12 | stdin pipe ignored, output unchanged (`:214`) | §8.4.12 | ✓ stronger — pipes `'ignored stdin payload\n'` before close (spec said only "immediately closes child stdin") |
|
|
100
|
-
| 13 | clean stderr happy path (`:224`) | §8.4.13 | ✓ literal |
|
|
101
|
-
| 14 | golden fixtures byte-equal × 8 (`:231`) | §8.5.14 | ✓ literal — `fs.readFileSync` + `assert.strictEqual` × 4 targets × 2 formats; `npm test` also runs `git diff --exit-code tests/snippet-protocol/v1/` per spec §7.5 |
|
|
102
|
-
| 15 | devkit-free PATH (`:245`) | §8.6.15 | ✓ literal — `pathWithoutAigentryExecutables()` filter + asserts exit 0 + `assert.equal(result.stdout, expected)` against `golden-all.md` |
|
|
103
|
-
|
|
104
|
-
**Map consistency: 15/15.** No test misreads its spec section. No orphan tests, no orphan spec items.
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## §7 Cross-LLM Blind Spot Findings
|
|
109
|
-
|
|
110
|
-
### §7.1 Test 6 wording-stretch (NON-BLOCKING)
|
|
111
|
-
|
|
112
|
-
Spec §8.2 item 6 reads:
|
|
113
|
-
|
|
114
|
-
> "For each target, body bytes contain none of: `$HOME`, `$(`, backtick (`` ` ``) outside fenced code blocks, literal `~` anywhere in body."
|
|
115
|
-
|
|
116
|
-
The literal-strict reading of "backtick outside fenced code blocks" means **bare backticks should not appear at all** in the body (since the body has no fenced ```` ``` ```` blocks). However, the body does contain inline-code spans (single-backtick `<name>`, `telepty allow`, etc.) which are markdown-legitimate.
|
|
117
|
-
|
|
118
|
-
The test (`init.test.js:156-158`) takes the **security-intent reading**: backticks are allowed inside inline code spans, but the inline code content must not contain shell metacharacters `[$;|&]`:
|
|
119
|
-
|
|
120
|
-
```js
|
|
121
|
-
for (const inlineCode of body.matchAll(/`([^`\n]+)`/g)) {
|
|
122
|
-
assert.doesNotMatch(inlineCode[1], /[$;|&]/);
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
This stretches the spec wording but preserves the spec's stated security intent ("Defends §3.1.1.1 line 161 'no shell substitution.'"). Without this interpretation, the spec would be self-contradictory — the canonical body in §A intentionally uses inline backticks. **Recommendation:** spec §8.2 item 6 should be amended to say "no shell metacharacters inside inline-code spans" rather than the literally-impossible "no backtick outside fenced code blocks." Not a blocker; existing implementation is correct in spirit.
|
|
127
|
-
|
|
128
|
-
### §7.2 `loadBody(target, options)` test seam (NON-BLOCKING)
|
|
129
|
-
|
|
130
|
-
`print-snippet.js:17` accepts an `options.snippetDir` parameter that is not in spec §7.3 skeleton. This is the path-injection seam used by test 11 (`init.test.js:206`) to drive the exit-code-4 internal-failure branch. Spec §8.3 item 11 explicitly mentions "non-existent template path injection" as the test mechanism, so the helper is justified. Codex tendency: lightly extends API surface for test ergonomics — here the extension is limited (one option, single call site) and warranted.
|
|
131
|
-
|
|
132
|
-
### §7.3 `cli.js` lazy `getAuthToken()` (NON-BLOCKING — actually defensible)
|
|
133
|
-
|
|
134
|
-
`cli.js` was changed to lazify `getAuthToken()` from a module-load-time `const TOKEN`. Without this change, `telepty init --print-snippet` would fail on a fresh machine (no `~/.telepty/config.json`). **This is a prerequisite for Article 9 / §8 M3 compliance**, not scope creep — the test 15 devkit-free path would otherwise fail. Side benefit: every CLI command that doesn't need the daemon now starts without touching the config. Net positive.
|
|
135
|
-
|
|
136
|
-
### §7.4 TDD ordering — spec procedural directive not honored at commit level (NON-BLOCKING)
|
|
137
|
-
|
|
138
|
-
Spec §8 says "Each item is a failing-test-first checkpoint per `superpowers:test-driven-development`." Commit log shows:
|
|
139
|
-
|
|
140
|
-
- `f5c6bad feat(init): emit claude markdown snippet` (introduces impl + 1 test as a single commit)
|
|
141
|
-
- 14 follow-up `test(init): …` commits adding tests after-the-fact
|
|
142
|
-
|
|
143
|
-
This is **test-after development packaged as TDD-style commits**, not red-green-refactor. All tests do pass, all spec items are covered, all assertions match spec text — so the **outcome** is identical to TDD. The **process** does not match spec wording. Codex tendency: optimizes for test pass speed by writing impl first, then back-filling tests in clean commits. Flagging for orchestrator awareness; not a correctness blocker.
|
|
144
|
-
|
|
145
|
-
### §7.5 Cross-repo coupling check
|
|
146
|
-
|
|
147
|
-
SSOT stub at `aigentry-ssot@f4ff0cd` and telepty PR are atomically deliverable (commit timestamps: SSOT 14:35:07 < final telepty 14:36:19). The SSOT stub references the spec doc path. Spec §7.6 also requires "telepty PR description cite SSOT SHA" — since these are local commits without a PR description yet, this requirement attaches to the eventual `git push` / PR creation step (out of this review's scope but flagged for the orchestrator's push action).
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## §8 Code Quality Notes
|
|
152
|
-
|
|
153
|
-
- **Diff hygiene:** 16 commits, each test in its own commit (`test(init): …`). Excellent reviewability; bisect-friendly. Process flag at §7.4 about ordering does not detract from per-commit quality.
|
|
154
|
-
- **No unused code:** `print-snippet.js:104-114` exports include `HELP`, `TARGETS`, `VERSION`, `buildOutput`, `emitJson`, `emitMarkdown`, `loadBody`, `main: buildOutput`, `sha256Hex`. The aliased `main: buildOutput` is consumed by `cli.js:843` (`const { main: runInit } = require(...)`); `buildOutput` directly is consumed by tests; helpers `emitJson`/`emitMarkdown`/`loadBody`/`sha256Hex` are not externally consumed (could be flagged as over-export, but test ergonomics make them defensible). NOT a blocker.
|
|
155
|
-
- **No dead branches:** every condition path is tested (defaults, help, all/single targets, format markdown/json, bad target → 2, internal fail → 4).
|
|
156
|
-
- **No security issues:** snippet bodies are static template files baked into the repo; no shell-eval, no template substitution, no env-var expansion. Test 6 (security intent) verifies inline-code spans stay free of `[$;|&]`.
|
|
157
|
-
- **Stderr policy:** all 4 stderr writes use `error: …` prefix and a literal newline. Spec §3.4 says "warnings only" on happy path; all error stderr writes are accompanied by non-zero exit code, satisfying the "errors are reflected by exit code, stderr carries diagnostic" model.
|
|
158
|
-
- **Argv parser:** `parseArgs` (lines 40-68) handles `--key value` and `--key=value` forms for `--target` and `--format`. Spec didn't mandate `--key=value` form, but supporting both is conventional and harmless. Edge case: unrecognized flags are silently ignored. Spec doesn't require strict argv validation; acceptable for v1.
|
|
159
|
-
- **Version pin:** `package.json` is at `0.3.4`; spec target was "≥ 0.3.5 (next minor, additive)." The version bump to `0.3.5` is implicitly deferred to the actual `npm version` release commit (separate from this implementation PR). Acceptable as release-time work.
|
|
160
|
-
|
|
161
|
-
---
|
|
162
|
-
|
|
163
|
-
## §9 Conditions (none blocking)
|
|
164
|
-
|
|
165
|
-
No `ACCEPT_WITH_CONDITIONS` triggered. The four §7 findings are all NON-BLOCKING and either (a) a recommended spec-wording amendment (§7.1), (b) a defensible deviation explained by spec text (§7.2, §7.3), (c) a process observation that doesn't affect correctness (§7.4), or (d) a coordination note for the eventual PR push (§7.5).
|
|
166
|
-
|
|
167
|
-
If the orchestrator wishes to attach optional follow-ups:
|
|
168
|
-
1. **Spec amendment (recommended):** rewrite §8.2 item 6's "backtick outside fenced code blocks" to "no shell metacharacters `$;|&` inside inline-code spans" to match the implemented (and correct-in-spirit) test.
|
|
169
|
-
2. **Release commit:** `npm version 0.3.5 --no-git-tag-version` + `npm publish` should be a separate commit after this PR's merge per §7.5 release-time deferral.
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
|
-
## §10 Verdict + Push Recommendation
|
|
174
|
-
|
|
175
|
-
**Final verdict:** **ACCEPT.**
|
|
176
|
-
|
|
177
|
-
**Atomic batch push readiness:** **YES.**
|
|
178
|
-
|
|
179
|
-
**Recommended commit range:**
|
|
180
|
-
- `aigentry-telepty`: `d7b8b21..d0f4495` (16 commits, 15 tests + 8 fixtures + impl + cli wiring + scripts/regen-snippet-fixtures.js + version-test alignment)
|
|
181
|
-
- `aigentry-ssot`: `f4ff0cd` (G1 SSOT stub, single commit)
|
|
182
|
-
|
|
183
|
-
**Push order:** SSOT stub first (already committed at 14:35:07, before the final telepty test commit at 14:36:19); telepty PR description should cite `f4ff0cd` per spec §7.6 cross-repo coordination requirement.
|
|
184
|
-
|
|
185
|
-
**Verification evidence captured this review:**
|
|
186
|
-
- `npm test` → 269/269 pass (full suite); 15/15 init suite
|
|
187
|
-
- `git diff --exit-code tests/snippet-protocol/v1/` clean (golden fixtures byte-equal)
|
|
188
|
-
- Manual M3 smoke: `env -i HOME=/tmp/empty PATH=/usr/bin:/bin:<node> node cli.js init --print-snippet --target claude` → exit 0
|
|
189
|
-
- SSOT body hashes verified equal to runtime `sha256` digests for all 3 targets
|
|
190
|
-
- Boundary scan: zero file-mutation, zero subprocess-of-devkit in `src/init/`
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
|
-
*End of review.*
|