therookie 0.4.19 → 0.4.20
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/package.json
CHANGED
package/src/commands/save.js
CHANGED
|
@@ -91,17 +91,58 @@ export async function runSave(flags) {
|
|
|
91
91
|
return
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
if (flags.quiet) return
|
|
95
94
|
const s = res.summary ?? {}
|
|
95
|
+
|
|
96
|
+
// --quiet 는 **출력**만 줄인다. 종료 코드까지 삼키면 자동화가 검토 게이트에 걸린 저장을
|
|
97
|
+
// 성공으로 읽는다 — 두목이 가르친 규칙이 조용히 사라지는 경로다.
|
|
98
|
+
if (flags.quiet) {
|
|
99
|
+
if (res.all_committed === false) process.exit(1)
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// audit 2026-08-24 §4-2 Phase 3 — 검토 게이트에 걸린 자기규칙은 목록을 그대로 펼친다.
|
|
104
|
+
// 요약해 버리면 판단 재료가 사라져 게이트가 그냥 "저장 실패"로 읽힌다.
|
|
105
|
+
for (const r of s.self_rules?.rejected ?? []) {
|
|
106
|
+
if (r.reason_code !== 'peer_review_required') continue
|
|
107
|
+
console.error(
|
|
108
|
+
`\n⚠ 자기규칙 미저장 (self_rules[${r.index}]) — 같은 category 활성 규칙 ${r.peers_total ?? 0}건을 먼저 보세요` +
|
|
109
|
+
(r.peers_truncated ? ` (앞 ${r.peers?.length ?? 0}건만 표시)` : '')
|
|
110
|
+
)
|
|
111
|
+
if (typeof r.peers_same_as_index === 'number') {
|
|
112
|
+
// 같은 category 목록은 첫 건에만 실린다 — 어디를 보라고 가리켜 준다.
|
|
113
|
+
console.error(` (목록은 self_rules[${r.peers_same_as_index}] 것과 같습니다)`)
|
|
114
|
+
}
|
|
115
|
+
for (const p of r.peers ?? []) {
|
|
116
|
+
console.error(` ${p.id} [강화 ${p.reinforcement_count}] ${p.head}`)
|
|
117
|
+
}
|
|
118
|
+
console.error(
|
|
119
|
+
'→ 같은 취지가 있으면 reinforces:"<id>"(강화) 또는 supersedes:"<id>"(정정), 없으면 peers_reviewed:true 로 재전송하세요.'
|
|
120
|
+
)
|
|
121
|
+
}
|
|
96
122
|
const parts = []
|
|
97
123
|
if (s.working_cards?.ids?.length) parts.push(`카드 ${s.working_cards.ids.length}`)
|
|
98
124
|
if (s.evidence?.applied) parts.push(`증거 ${s.evidence.applied}`)
|
|
99
|
-
if (s.self_rules?.applied)
|
|
125
|
+
if (s.self_rules?.applied) {
|
|
126
|
+
const reinforced = s.self_rules.reinforced ?? 0
|
|
127
|
+
parts.push(
|
|
128
|
+
reinforced > 0
|
|
129
|
+
? `자기규칙 ${s.self_rules.applied}(강화 ${reinforced})`
|
|
130
|
+
: `자기규칙 ${s.self_rules.applied}`
|
|
131
|
+
)
|
|
132
|
+
}
|
|
100
133
|
if (s.verifications?.applied) parts.push(`검증 ${s.verifications.applied}`)
|
|
101
134
|
const label = parts.length > 0 ? parts.join(' · ') : '0건'
|
|
102
135
|
|
|
103
136
|
if (res.all_committed === false) {
|
|
104
|
-
|
|
137
|
+
// 검토 게이트만 걸린 저장은 "미저장 0건" 이 아니라 다음 행동이 남은 상태다.
|
|
138
|
+
const pendingReview = (s.self_rules?.rejected ?? []).some(
|
|
139
|
+
(r) => r.reason_code === 'peer_review_required'
|
|
140
|
+
)
|
|
141
|
+
if (pendingReview && parts.length === 0) {
|
|
142
|
+
console.error('⚠ 자기규칙 검토 대기 — 위 목록을 보고 재전송하세요.')
|
|
143
|
+
} else {
|
|
144
|
+
console.error(`⚠ 일부 미저장 — ${label}`)
|
|
145
|
+
}
|
|
105
146
|
process.exit(1)
|
|
106
147
|
}
|
|
107
148
|
console.log(`✓ 저장: ${label}`)
|
|
@@ -193,6 +193,17 @@ _handle_200() {
|
|
|
193
193
|
return 0
|
|
194
194
|
fi
|
|
195
195
|
|
|
196
|
+
# audit 2026-08-24 §10 — 자기 규칙 검토 게이트(peer_review_required)는 rejected[] 로만 온다.
|
|
197
|
+
# hook 경로에는 목록을 읽고 재전송할 에이전트가 없어 그 규칙은 저장되지 않는다. 재큐해도
|
|
198
|
+
# 같은 payload 라 또 거부되므로 큐에 넣지 않고 **로그로 드러낸다** — silent drop 만은 막는다.
|
|
199
|
+
local peer_gate_count
|
|
200
|
+
peer_gate_count=$(jq -r '[.rejected[]? | select(.reason_code == "peer_review_required")] | length // 0' < "$body_path" 2>>"$ROOKIE_DROP_LOG")
|
|
201
|
+
if [ -n "$peer_gate_count" ] && [ "$peer_gate_count" != "null" ] && [ "$peer_gate_count" -gt 0 ] 2>/dev/null; then
|
|
202
|
+
printf '%s self_rule_peer_review_required_unsaved count=%s session=%s\n' \
|
|
203
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$peer_gate_count" "$session_id" \
|
|
204
|
+
>> "$ROOKIE_DROP_LOG" 2>/dev/null || true
|
|
205
|
+
fi
|
|
206
|
+
|
|
196
207
|
if [ "$failed_count" -le 0 ] 2>/dev/null; then
|
|
197
208
|
return 0
|
|
198
209
|
fi
|
package/templates/skill/SKILL.md
CHANGED
|
@@ -131,7 +131,9 @@ curl -s -H "Authorization: Bearer $TOKEN" \
|
|
|
131
131
|
|
|
132
132
|
저장 전 **recall dedup** — 같은 취지 durable fact 존재 시 새 저장 금지. **재확인 근거가 있으면** `verifications` confirmed 1건(fact 당 하루 1회, 서버 동일 게이트 — working card·근거 없는 회상 제외)으로 반복 신호를 남긴다. 성공 시 `[기억] <요약> 저장` 1줄, 실패 시 retry 1회 + 알림.
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
**자기 규칙은 처음 보낼 때 저장되지 않는다** — 서버가 같은 category 기존 규칙 전량을 `peer_review_required` 로 돌려주고, 그 목록을 읽어 강화(`reinforces`)·정정(`supersedes`)·신규(`peers_reviewed:true`) 중 하나로 재전송해야 한다. 절차는 `references/save.md` §자기 규칙 검토 게이트.
|
|
135
|
+
|
|
136
|
+
**본문 작성 전 `references/save.md` 를 Read 한다(세션 첫 저장 전 1회 필수)** — 스키마·`all_committed` 게이트·귀속 확인·supersede 후보 처리·자기 규칙 검토 게이트·checkpoint/durable 분리 규칙이 거기 있다. 호출은 `rookie save --file <body.json>` (curl 직접 batch-mutate 금지, `rookie:mutate` fence 화면 출력 금지, 토큰 출력 금지).
|
|
135
137
|
|
|
136
138
|
## 세션 마무리 자동 저장 (추출 책임이 루키에게)
|
|
137
139
|
|
|
@@ -20,7 +20,7 @@ rookie save --file <body.json> # 또는: echo '<body>' | rookie save
|
|
|
20
20
|
```json
|
|
21
21
|
{
|
|
22
22
|
"working_cards": { "items": [ { "summary": "진입 카드 본문", "verification": { "method": "ssh|curl|git|grep|lsof|read|reasoning|assertion", "evidence": "직접 명령 결과 한 줄" }, "supersedes": ["부모 fact UUID"], "tags": ["custom"], "ttl_at": "ISO8601?", "artifact_paths": ["task 매칭용? ≤20 (저장 안 함)"], "project_id": "?", "task_id": "?" } ], "repo_url": "(권장) git remote — 없으면 project 귀속 안 됨", "repo_path": "(권장) repo_url 없을 때 hostname 매칭", "project_id": "(레거시) item 이 우선, 보통 생략", "conversation_id": "uuid?" },
|
|
23
|
-
"self_rules": [ { "category": "identity|voice|work_style|strength|preference|rule", "rule": "규칙 본문", "confidence": "high|medium|low", "supersedes": "기존 rule UUID?", "deprecation_reason": "user_override|conflict|noise|manual?", "ownership_scope": "personal|project?", "project_id": "scope=project 일 때 필수 uuid" } ],
|
|
23
|
+
"self_rules": [ { "category": "identity|voice|work_style|strength|preference|rule", "rule": "규칙 본문", "confidence": "high|medium|low", "peers_reviewed": "기존 규칙 목록을 보고 새 규칙이라 판단했을 때만 true (아래 검토 게이트)", "reinforces": "같은 취지 기존 규칙 UUID — 새로 저장하지 않고 강화만", "supersedes": "기존 rule UUID?", "deprecation_reason": "user_override|conflict|noise|manual?", "ownership_scope": "personal|project?", "project_id": "scope=project 일 때 필수 uuid" } ],
|
|
24
24
|
"evidence": [ { "capability": "능력 진술문", "approach": "접근법?", "status": "completed|in-progress?", "context": "맥락?", "artifact_paths": ["파일 절대경로 (project 폴백 매칭에도 쓰임)"], "project_id": "? (uuid=명시 귀속 / null=전역 / 생략=세션 귀속)" } ],
|
|
25
25
|
"supersede_links": [ { "parent_id": "폐기할 구 카드 UUID", "child_id": "대체하는 새 카드 UUID" } ],
|
|
26
26
|
"verifications": [ { "fact_id": "검증 대상 fact UUID", "method": "(위와 동일 enum)", "outcome": "confirmed|refuted|ambiguous", "evidence": "검증 명령 결과 한 줄" } ]
|
|
@@ -32,12 +32,22 @@ rookie save --file <body.json> # 또는: echo '<body>' | rookie save
|
|
|
32
32
|
**귀속 확인**(all_committed 와 무관): `repo_url`/`repo_path` 를 보냈는데 `working_cards.project_attributed=false` 또는 `evidence.unattributed>0` 이면(전역 의도 `project_id:null` 명시 제외) `⚠️ [기억] 프로젝트 미귀속 저장 — rookie link 상태 확인 필요` 1줄 경고.
|
|
33
33
|
|
|
34
34
|
top-level `all_committed` 가 **`true` 일 때만** `[기억] … 저장` 보고. `false` 면:
|
|
35
|
-
- **`rejected[]`**(정책 거부 —
|
|
35
|
+
- **`rejected[]`**(정책 거부 — 같은 payload 재전송 무의미): `meta_card_summary`·`residual_marker`(민감정보)·`self_rule_rejected` — 단일 fact 분리 또는 원인 제거 후 재저장, 민감정보 마커면 포기. **`reason_code:"peer_review_required"` 는 예외적으로 반드시 이어서 처리한다** — 아래 "자기 규칙 검토 게이트". `supersede_link_skipped` 는 id·scope 를 고쳐야 하는 것 — 같은 payload 재전송 금지.
|
|
36
36
|
- **`failed[]`** (일시 실패): `insert_failed`·`unexpected_error` 등 — retry 1회, 그래도 실패면 보고.
|
|
37
37
|
- 미저장이 남으면 `⚠️ [기억] N건 미저장 — <사유>` 한 줄(raw JSON·UUID 금지). `all_committed:true` 전엔 "저장 완료" 라 하지 않는다.
|
|
38
38
|
|
|
39
39
|
**supersede 후보 처리 (같은 턴, 필수)**: 저장 응답 `summary.working_cards.supersede_candidates[]` 에 후보가 있으면 **그 자리에서** 판단한다 — 후보(`summary_head`·`memory_at`)가 새 카드가 대체하는 같은 작업 흐름의 구 카드면 즉시 후속 `rookie save` 로 닫는다: `{"supersede_links":[{"parent_id":"<후보 id>","child_id":"<새 카드 id>"}]}`. 병행 중인 다른 트랙 카드면 무시. **후보를 보고도 무근거로 방치 금지** — 애매하면 애매한 이유가 있어야 한다. (구 결정·상태 카드가 안 닫혀 다음 세션이 폐기된 결정을 회상하는 사고의 1차 방지선.)
|
|
40
40
|
|
|
41
|
+
**자기 규칙 검토 게이트 (같은 턴, 필수)**: self_rule 은 **처음 보낼 때 저장되지 않는다.** 서버가 같은 category 활성 규칙 전량(`peers[]` — `id`·본문 앞부분·강화 횟수)을 실어 `reason_code:"peer_review_required"` 로 돌려준다. 그 목록을 **읽고** 셋 중 하나로 재전송한다:
|
|
42
|
+
|
|
43
|
+
- **같은 취지가 이미 있다** → `{"reinforces":"<그 규칙 id>"}` — 새 행을 만들지 않고 강화 횟수만 올린다. 문면이 달라도 지시가 같으면 강화다. **id 는 방금 받은 목록에서 고른다** — 목록 밖 id(비활성·다른 category)는 `reinforce_target_mismatch` 로 거부된다(엉뚱한 행만 올리고 본문이 사라지는 것을 막는다).
|
|
44
|
+
- **그 규칙을 정정·확장하는 것이다** → `{"peers_reviewed":true,"supersedes":"<구 규칙 id>","deprecation_reason":"user_override"}` — 구 규칙은 비활성으로 닫히고 행은 남는다. 대상도 목록에서 고른다(비활성·다른 category 면 `supersede_target_mismatch` 로 **저장 자체가 거부**된다 — 새 규칙만 들어가고 구 규칙이 살아남는 반쪽 상태를 막는다). 저장 뒤 `rejected[]` 에 `supersede_failed` 가 보이면 새 규칙은 들어갔고 구 규칙만 못 닫힌 것이다 — **재전송하지 말고** 구 규칙 상태를 확인한다.
|
|
45
|
+
- **정말 새 규칙이다** → `{"peers_reviewed":true}` 만 더해 재전송.
|
|
46
|
+
|
|
47
|
+
한 배치에 같은 category 가 여러 건이면 목록은 **첫 건에만** 실린다 — 나머지는 `peers_same_as_index` 가 가리키는 항목의 목록을 보면 된다.
|
|
48
|
+
|
|
49
|
+
목록이 길어도 **훑지 말고 같은 취지를 찾는다.** 이 게이트가 없던 동안 "원문자 금지" 한 지시가 다섯 번 저장됐고, 정정본과 피정정본이 나란히 살아남아 서로 다른 말을 했다. 애매하면 강화가 안전하다 — 새 행은 되돌리려면 두목 손이 필요하지만 강화는 손실이 없다.
|
|
50
|
+
|
|
41
51
|
## 트리거 조건 상세
|
|
42
52
|
|
|
43
53
|
- **working_cards**: 다음 세션 진입 카드 — 세션 마무리 + 아래 checkpoint, 매 턴·매 단계 금지. **body 에 `repo_url`/`repo_path` 필수** — 서버가 세션 project 로 자동 귀속, 미link 면 NULL(정상).
|
package/version.json
CHANGED