binggupack 1.14.0__tar.gz
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.
- binggupack-1.14.0/LICENSE +21 -0
- binggupack-1.14.0/PKG-INFO +125 -0
- binggupack-1.14.0/README.md +107 -0
- binggupack-1.14.0/binggupack/__about__.py +7 -0
- binggupack-1.14.0/binggupack/__init__.py +9 -0
- binggupack-1.14.0/binggupack/capture/__init__.py +9 -0
- binggupack-1.14.0/binggupack/capture/buffer.py +117 -0
- binggupack-1.14.0/binggupack/capture/cli.py +93 -0
- binggupack-1.14.0/binggupack/capture/session.py +115 -0
- binggupack-1.14.0/binggupack/classifier/__init__.py +119 -0
- binggupack-1.14.0/binggupack/classifier/capture_classifier.py +199 -0
- binggupack-1.14.0/binggupack/cli/__init__.py +1 -0
- binggupack-1.14.0/binggupack/cli/interactive_save.py +127 -0
- binggupack-1.14.0/binggupack/mcp/__init__.py +1 -0
- binggupack-1.14.0/binggupack/pack/__init__.py +1 -0
- binggupack-1.14.0/binggupack/pack/smoke.py +113 -0
- binggupack-1.14.0/binggupack/policy/__init__.py +20 -0
- binggupack-1.14.0/binggupack/policy/match.py +288 -0
- binggupack-1.14.0/binggupack/review/__init__.py +16 -0
- binggupack-1.14.0/binggupack/review/reviewed_plan_preview.py +309 -0
- binggupack-1.14.0/binggupack/safety/__init__.py +18 -0
- binggupack-1.14.0/binggupack/safety/gate_log.py +173 -0
- binggupack-1.14.0/binggupack/safety/gate_text.py +34 -0
- binggupack-1.14.0/binggupack/safety/path_safety.py +180 -0
- binggupack-1.14.0/binggupack/schema/__init__.py +13 -0
- binggupack-1.14.0/binggupack/schema/verb_edge.py +167 -0
- binggupack-1.14.0/binggupack/workspace/__init__.py +25 -0
- binggupack-1.14.0/binggupack/workspace/platform.py +209 -0
- binggupack-1.14.0/binggupack.egg-info/PKG-INFO +125 -0
- binggupack-1.14.0/binggupack.egg-info/SOURCES.txt +33 -0
- binggupack-1.14.0/binggupack.egg-info/dependency_links.txt +1 -0
- binggupack-1.14.0/binggupack.egg-info/entry_points.txt +2 -0
- binggupack-1.14.0/binggupack.egg-info/top_level.txt +1 -0
- binggupack-1.14.0/pyproject.toml +38 -0
- binggupack-1.14.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BingguPack contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: binggupack
|
|
3
|
+
Version: 1.14.0
|
|
4
|
+
Summary: BingguPack — local-first, evidence-backed memory/context pack framework with an installable Claude Code MCP server (stdio JSON-RPC). Python stdlib only.
|
|
5
|
+
Author: BingguPack contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/darkjokee-arch/binggupack
|
|
8
|
+
Project-URL: Release, https://github.com/darkjokee-arch/binggupack/releases/tag/v1.14.0
|
|
9
|
+
Keywords: mcp,claude-code,local-first,ontology,context-packs,agi-memory
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# BingguPack
|
|
20
|
+
|
|
21
|
+
**내가 일할수록 나를 알아가는, 내 PC 안의 개인 지식 노트.**
|
|
22
|
+
|
|
23
|
+
> 최신: **v1.13.0 — 자기진화 거버넌스(Governance)** 🧭 · 내 학습과 기존 규칙이 충돌하면 양쪽을 보여주고 내가 선택 — 규칙 변경은 사람 손, 안전 규칙은 빙구팩이 못 바꿈.
|
|
24
|
+
> 이전: v1.12.0 화자 축 🗣️ (내 말/AI 요약 따로 쌓기 + 양방향 신뢰도)
|
|
25
|
+
> 🔒 로컬 우선 · 자동 저장 없음 · 내가 고른 것만 저장 · MIT License
|
|
26
|
+
> Release: <https://github.com/darkjokee-arch/binggupack/releases/tag/v1.13.0>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 빙구팩이 뭔가요?
|
|
31
|
+
|
|
32
|
+
AI와 대화하다 보면 정작 남기고 싶은 것 — **내 판단, 배운 점, 정한 방침** — 이 수십 개 대화창에 흩어져 사라집니다. 그렇다고 전부 자동 저장하면 잡음과 민감정보가 쌓이고 통제권을 잃죠.
|
|
33
|
+
|
|
34
|
+
빙구팩은 **넓게 줍고, 내가 고른 것만 저장하는** 개인 노트입니다.
|
|
35
|
+
|
|
36
|
+
- 원본은 전부 내 PC 안 파일 하나(`ledger.sqlite`)에 있습니다. 클라우드가 원본을 갖지 않습니다.
|
|
37
|
+
- **자동으로 저장되는 건 아무것도 없습니다.** 내가 직접 고른 것만 저장됩니다.
|
|
38
|
+
- 쓸수록 빙구팩은 "나"를 알아갑니다 — 내가 어떻게 판단하고, 뭘 선호하고, 어떤 실수를 했는지가 쌓여서, 다음에 비슷한 일이 오면 먼저 짚어줍니다.
|
|
39
|
+
|
|
40
|
+
## 무엇을 할 수 있나요
|
|
41
|
+
|
|
42
|
+
| 능력 | 한 줄 설명 |
|
|
43
|
+
|---|---|
|
|
44
|
+
| 🧹 **넓게 수집** | 어느 AI(Claude·ChatGPT·폰·웹)에서 일하든 남길 문장을 후보로 모음 |
|
|
45
|
+
| 👀 **미리보기** | 모은 후보를 먼저 보여줌 — 이 단계에선 저장 0 |
|
|
46
|
+
| ✍️ **내가 골라 저장** | 내가 직접 고른 것만 저장. AI는 저장 못 함 |
|
|
47
|
+
| 🗣️ **화자 축** | 내 말(직감·지적)과 AI 요약(수정·수용·반박)을 **따로** 쌓고, 수용/반박/수정으로 연결 |
|
|
48
|
+
| ⚖️ **양방향 신뢰도** | 내 직감과 AI 반박, **누가 더 잘 맞았나**를 기억. 한쪽 편 안 듦 — 나도 AI도 틀릴 수 있으니까 |
|
|
49
|
+
| 🔁 **자기수정** | 틀린 판단은 고치고, 예측은 결과로 검증해 다음 판단이 똑똑해짐 |
|
|
50
|
+
| 🧭 **충돌 조정(거버넌스)** | 내 학습과 기존 규칙이 부딪치면 양쪽 보여주고 내가 선택 — 규칙 변경은 사람 손(빙구팩은 제안만)·안전 규칙은 못 바꿈 |
|
|
51
|
+
| 🧠 **회상·반문** | 일 시작 전 관련 기억과 과거 실수 패턴을 먼저 떠올려줌 |
|
|
52
|
+
| 📦 **팩 만들기·검증** | 모은 지식을 다른 도구가 쓸 "꾸러미(팩)"로 묶고 구조를 검증 |
|
|
53
|
+
| 🔀 **워크플로우 추천** | "이 목표엔 이런 팩·데이터가 필요해요"를 자동 제안(추천만 — 실행은 사람) |
|
|
54
|
+
| ☁️ **안전하게 내보내기** | 외부 실행 엔진(OpenCrab)으로 보내기 전 안전점검·업로드 준비. 실제 전송은 내가 승인할 때만 |
|
|
55
|
+
| 🔌 **Claude Code 연결** | `clone` 한 번으로 MCP 패키지 설치(도구 8개) |
|
|
56
|
+
| 💻 **어디서나** | Windows · WSL · macOS · Linux |
|
|
57
|
+
|
|
58
|
+
## 빠른 시작
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/darkjokee-arch/binggupack.git
|
|
62
|
+
cd binggupack
|
|
63
|
+
python binggu.py init # 내 노트 만들기
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**내 말과 AI 요약을 따로 쌓고 연결하기 (화자 축)**
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# 내 직감 + AI 요약을 페어로 — relation: accepts(수용) / refutes(반박) / revises(수정)
|
|
70
|
+
python binggu.py pair "이 입찰은 보류한다" "데이터가 부족해 보수적 접근이 맞다" \
|
|
71
|
+
--relation refutes --confirm "PAIR ai_refutes owner:1 ai:1"
|
|
72
|
+
|
|
73
|
+
python binggu.py pair "다음엔 이 거래처 우선 검토" --confirm "PAIR owner:1" # 내 직감만
|
|
74
|
+
|
|
75
|
+
python binggu.py trust # 누가 더 잘 맞나 (양방향 신뢰도)
|
|
76
|
+
python binggu.py resolve <n> <id8> --outcome 성공 # 결과 기록 → 적중률 누적
|
|
77
|
+
python binggu.py route "..." # 뭘 할지 헷갈리면 안내해줌
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
> 더 자세히: [10분 튜토리얼](docs/BINGGUPACK_TUTORIAL.md) · [설치 가이드](INSTALL.md)
|
|
81
|
+
|
|
82
|
+
## 팩과 워크플로우 (외부 도구로 연결)
|
|
83
|
+
|
|
84
|
+
빙구팩은 모은 지식을 **혼자만 쓰는 게 아니라**, 다른 실행 도구(OpenCrab)가 받아 쓸 수 있게 **준비·검증·안전점검**까지 해줍니다.
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
목표 → 필요한 팩 추천 → 근거 모으기 → 팩 만들기 → 검증 → 발행 안전점검 → 내보내기 준비
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- 각 단계가 fail-closed로 막혀, **깨지거나 근거 없는 팩은 외부로 못 나갑니다.**
|
|
91
|
+
- 관계·워크플로우는 **추천만** 합니다 — 무엇을 만들고 내보낼지는 사람이 정합니다.
|
|
92
|
+
- 실제 외부 업로드·클라우드 전송은 **내가 명시 승인하기 전까지 멈춤(HOLD)**. 자동으로 나가는 건 없습니다.
|
|
93
|
+
|
|
94
|
+
> 흐름 상세: [팩 계약](docs/OPENBINGGU_PACK_CONTRACT.md) · [업로드 흐름](docs/OPENBINGGU_USER_DRIVEN_OPENCRAB_UPLOAD_FLOW.md) · [개인/팀 두 트랙](docs/OPENBINGGU_PRODUCT_DIRECTION_TWO_TRACK.md)
|
|
95
|
+
|
|
96
|
+
## 자기진화 거버넌스 (충돌 조정)
|
|
97
|
+
|
|
98
|
+
빙구팩 학습과 기존 규칙이 충돌할 때 — 빙구팩은 **양쪽을 보여주고 제안만**, 규칙 변경의 마지막 손은 **사람**입니다.
|
|
99
|
+
|
|
100
|
+
- **충돌하면 양쪽 펼쳐 보여줌** → 내가 선택 (자동 결정 0)
|
|
101
|
+
- **누가 더 잘 맞았나 기록** → 단 "참고 신호"일 뿐 결정 근거 아님
|
|
102
|
+
- **규칙 진화는 사람 손** → 빙구팩이 제안 → 내가 진짜(raw) 확인 → 승인 → 변경(되돌리기 가능). 빙구팩은 규칙 파일에 손 못 댐
|
|
103
|
+
- **안전 규칙은 불변** → DB삭제·시크릿 같은 안전 조항은 빙구팩이 무슨 학습을 해도 못 바꿈
|
|
104
|
+
- **세션 마무리 한마디** → "오늘 끝"(말투 자유) 하면 저장 목록 + 정리를 자동 표시(저장은 내가)
|
|
105
|
+
|
|
106
|
+
> 빙구팩은 똑똑한 비서처럼 조언하고 충돌 시 양쪽을 펼치지만, **도장은 항상 사람이** 찍습니다. 상세: [거버넌스 설계](docs/BINGGUPACK_GOVERNANCE_DESIGN.md).
|
|
107
|
+
|
|
108
|
+
## 안전 약속
|
|
109
|
+
|
|
110
|
+
빙구팩의 안전은 말이 아니라 **자동 테스트로 증명**됩니다.
|
|
111
|
+
|
|
112
|
+
- **자동 저장 없음** — 내가 직접 고른 것만 저장. AI/자동 경로는 차단.
|
|
113
|
+
- **민감정보 차단** — 비밀번호·개인정보는 후보 단계에서 자동 제외.
|
|
114
|
+
- **언제든 되돌리기** — 모든 변경 전 백업 + 되돌리기 가능.
|
|
115
|
+
- **원본은 내 PC** — 클라우드가 내 원본을 갖지 않음. 외부 업로드는 내가 승인하기 전까지 멈춤.
|
|
116
|
+
|
|
117
|
+
## 더 알아보기
|
|
118
|
+
|
|
119
|
+
- [자기진화 거버넌스 설계](docs/BINGGUPACK_GOVERNANCE_DESIGN.md) — 학습↔규칙 충돌 조정, self-modifying 회피 (v1.13.0)
|
|
120
|
+
- [화자 축 설계](docs/BINGGUPACK_SPEAKER_AXIS_DESIGN.md) — 내 말/AI 요약 따로 쌓기, 양방향 신뢰도
|
|
121
|
+
- [10분 튜토리얼](docs/BINGGUPACK_TUTORIAL.md) · [설치 가이드](INSTALL.md) · [변경 이력](CHANGELOG.md)
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
MIT License — Copyright (c) 2026 BingguPack contributors.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# BingguPack
|
|
2
|
+
|
|
3
|
+
**내가 일할수록 나를 알아가는, 내 PC 안의 개인 지식 노트.**
|
|
4
|
+
|
|
5
|
+
> 최신: **v1.13.0 — 자기진화 거버넌스(Governance)** 🧭 · 내 학습과 기존 규칙이 충돌하면 양쪽을 보여주고 내가 선택 — 규칙 변경은 사람 손, 안전 규칙은 빙구팩이 못 바꿈.
|
|
6
|
+
> 이전: v1.12.0 화자 축 🗣️ (내 말/AI 요약 따로 쌓기 + 양방향 신뢰도)
|
|
7
|
+
> 🔒 로컬 우선 · 자동 저장 없음 · 내가 고른 것만 저장 · MIT License
|
|
8
|
+
> Release: <https://github.com/darkjokee-arch/binggupack/releases/tag/v1.13.0>
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 빙구팩이 뭔가요?
|
|
13
|
+
|
|
14
|
+
AI와 대화하다 보면 정작 남기고 싶은 것 — **내 판단, 배운 점, 정한 방침** — 이 수십 개 대화창에 흩어져 사라집니다. 그렇다고 전부 자동 저장하면 잡음과 민감정보가 쌓이고 통제권을 잃죠.
|
|
15
|
+
|
|
16
|
+
빙구팩은 **넓게 줍고, 내가 고른 것만 저장하는** 개인 노트입니다.
|
|
17
|
+
|
|
18
|
+
- 원본은 전부 내 PC 안 파일 하나(`ledger.sqlite`)에 있습니다. 클라우드가 원본을 갖지 않습니다.
|
|
19
|
+
- **자동으로 저장되는 건 아무것도 없습니다.** 내가 직접 고른 것만 저장됩니다.
|
|
20
|
+
- 쓸수록 빙구팩은 "나"를 알아갑니다 — 내가 어떻게 판단하고, 뭘 선호하고, 어떤 실수를 했는지가 쌓여서, 다음에 비슷한 일이 오면 먼저 짚어줍니다.
|
|
21
|
+
|
|
22
|
+
## 무엇을 할 수 있나요
|
|
23
|
+
|
|
24
|
+
| 능력 | 한 줄 설명 |
|
|
25
|
+
|---|---|
|
|
26
|
+
| 🧹 **넓게 수집** | 어느 AI(Claude·ChatGPT·폰·웹)에서 일하든 남길 문장을 후보로 모음 |
|
|
27
|
+
| 👀 **미리보기** | 모은 후보를 먼저 보여줌 — 이 단계에선 저장 0 |
|
|
28
|
+
| ✍️ **내가 골라 저장** | 내가 직접 고른 것만 저장. AI는 저장 못 함 |
|
|
29
|
+
| 🗣️ **화자 축** | 내 말(직감·지적)과 AI 요약(수정·수용·반박)을 **따로** 쌓고, 수용/반박/수정으로 연결 |
|
|
30
|
+
| ⚖️ **양방향 신뢰도** | 내 직감과 AI 반박, **누가 더 잘 맞았나**를 기억. 한쪽 편 안 듦 — 나도 AI도 틀릴 수 있으니까 |
|
|
31
|
+
| 🔁 **자기수정** | 틀린 판단은 고치고, 예측은 결과로 검증해 다음 판단이 똑똑해짐 |
|
|
32
|
+
| 🧭 **충돌 조정(거버넌스)** | 내 학습과 기존 규칙이 부딪치면 양쪽 보여주고 내가 선택 — 규칙 변경은 사람 손(빙구팩은 제안만)·안전 규칙은 못 바꿈 |
|
|
33
|
+
| 🧠 **회상·반문** | 일 시작 전 관련 기억과 과거 실수 패턴을 먼저 떠올려줌 |
|
|
34
|
+
| 📦 **팩 만들기·검증** | 모은 지식을 다른 도구가 쓸 "꾸러미(팩)"로 묶고 구조를 검증 |
|
|
35
|
+
| 🔀 **워크플로우 추천** | "이 목표엔 이런 팩·데이터가 필요해요"를 자동 제안(추천만 — 실행은 사람) |
|
|
36
|
+
| ☁️ **안전하게 내보내기** | 외부 실행 엔진(OpenCrab)으로 보내기 전 안전점검·업로드 준비. 실제 전송은 내가 승인할 때만 |
|
|
37
|
+
| 🔌 **Claude Code 연결** | `clone` 한 번으로 MCP 패키지 설치(도구 8개) |
|
|
38
|
+
| 💻 **어디서나** | Windows · WSL · macOS · Linux |
|
|
39
|
+
|
|
40
|
+
## 빠른 시작
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/darkjokee-arch/binggupack.git
|
|
44
|
+
cd binggupack
|
|
45
|
+
python binggu.py init # 내 노트 만들기
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**내 말과 AI 요약을 따로 쌓고 연결하기 (화자 축)**
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# 내 직감 + AI 요약을 페어로 — relation: accepts(수용) / refutes(반박) / revises(수정)
|
|
52
|
+
python binggu.py pair "이 입찰은 보류한다" "데이터가 부족해 보수적 접근이 맞다" \
|
|
53
|
+
--relation refutes --confirm "PAIR ai_refutes owner:1 ai:1"
|
|
54
|
+
|
|
55
|
+
python binggu.py pair "다음엔 이 거래처 우선 검토" --confirm "PAIR owner:1" # 내 직감만
|
|
56
|
+
|
|
57
|
+
python binggu.py trust # 누가 더 잘 맞나 (양방향 신뢰도)
|
|
58
|
+
python binggu.py resolve <n> <id8> --outcome 성공 # 결과 기록 → 적중률 누적
|
|
59
|
+
python binggu.py route "..." # 뭘 할지 헷갈리면 안내해줌
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
> 더 자세히: [10분 튜토리얼](docs/BINGGUPACK_TUTORIAL.md) · [설치 가이드](INSTALL.md)
|
|
63
|
+
|
|
64
|
+
## 팩과 워크플로우 (외부 도구로 연결)
|
|
65
|
+
|
|
66
|
+
빙구팩은 모은 지식을 **혼자만 쓰는 게 아니라**, 다른 실행 도구(OpenCrab)가 받아 쓸 수 있게 **준비·검증·안전점검**까지 해줍니다.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
목표 → 필요한 팩 추천 → 근거 모으기 → 팩 만들기 → 검증 → 발행 안전점검 → 내보내기 준비
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- 각 단계가 fail-closed로 막혀, **깨지거나 근거 없는 팩은 외부로 못 나갑니다.**
|
|
73
|
+
- 관계·워크플로우는 **추천만** 합니다 — 무엇을 만들고 내보낼지는 사람이 정합니다.
|
|
74
|
+
- 실제 외부 업로드·클라우드 전송은 **내가 명시 승인하기 전까지 멈춤(HOLD)**. 자동으로 나가는 건 없습니다.
|
|
75
|
+
|
|
76
|
+
> 흐름 상세: [팩 계약](docs/OPENBINGGU_PACK_CONTRACT.md) · [업로드 흐름](docs/OPENBINGGU_USER_DRIVEN_OPENCRAB_UPLOAD_FLOW.md) · [개인/팀 두 트랙](docs/OPENBINGGU_PRODUCT_DIRECTION_TWO_TRACK.md)
|
|
77
|
+
|
|
78
|
+
## 자기진화 거버넌스 (충돌 조정)
|
|
79
|
+
|
|
80
|
+
빙구팩 학습과 기존 규칙이 충돌할 때 — 빙구팩은 **양쪽을 보여주고 제안만**, 규칙 변경의 마지막 손은 **사람**입니다.
|
|
81
|
+
|
|
82
|
+
- **충돌하면 양쪽 펼쳐 보여줌** → 내가 선택 (자동 결정 0)
|
|
83
|
+
- **누가 더 잘 맞았나 기록** → 단 "참고 신호"일 뿐 결정 근거 아님
|
|
84
|
+
- **규칙 진화는 사람 손** → 빙구팩이 제안 → 내가 진짜(raw) 확인 → 승인 → 변경(되돌리기 가능). 빙구팩은 규칙 파일에 손 못 댐
|
|
85
|
+
- **안전 규칙은 불변** → DB삭제·시크릿 같은 안전 조항은 빙구팩이 무슨 학습을 해도 못 바꿈
|
|
86
|
+
- **세션 마무리 한마디** → "오늘 끝"(말투 자유) 하면 저장 목록 + 정리를 자동 표시(저장은 내가)
|
|
87
|
+
|
|
88
|
+
> 빙구팩은 똑똑한 비서처럼 조언하고 충돌 시 양쪽을 펼치지만, **도장은 항상 사람이** 찍습니다. 상세: [거버넌스 설계](docs/BINGGUPACK_GOVERNANCE_DESIGN.md).
|
|
89
|
+
|
|
90
|
+
## 안전 약속
|
|
91
|
+
|
|
92
|
+
빙구팩의 안전은 말이 아니라 **자동 테스트로 증명**됩니다.
|
|
93
|
+
|
|
94
|
+
- **자동 저장 없음** — 내가 직접 고른 것만 저장. AI/자동 경로는 차단.
|
|
95
|
+
- **민감정보 차단** — 비밀번호·개인정보는 후보 단계에서 자동 제외.
|
|
96
|
+
- **언제든 되돌리기** — 모든 변경 전 백업 + 되돌리기 가능.
|
|
97
|
+
- **원본은 내 PC** — 클라우드가 내 원본을 갖지 않음. 외부 업로드는 내가 승인하기 전까지 멈춤.
|
|
98
|
+
|
|
99
|
+
## 더 알아보기
|
|
100
|
+
|
|
101
|
+
- [자기진화 거버넌스 설계](docs/BINGGUPACK_GOVERNANCE_DESIGN.md) — 학습↔규칙 충돌 조정, self-modifying 회피 (v1.13.0)
|
|
102
|
+
- [화자 축 설계](docs/BINGGUPACK_SPEAKER_AXIS_DESIGN.md) — 내 말/AI 요약 따로 쌓기, 양방향 신뢰도
|
|
103
|
+
- [10분 튜토리얼](docs/BINGGUPACK_TUTORIAL.md) · [설치 가이드](INSTALL.md) · [변경 이력](CHANGELOG.md)
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT License — Copyright (c) 2026 BingguPack contributors.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Single source of version truth for BingguPack.
|
|
2
|
+
|
|
3
|
+
v1.13.0(자기진화 거버넌스) 위 v1.14.0 — MCP save_candidate 크래시 수정(snap_dir)
|
|
4
|
+
+ speaker CLI(save --speaker) + post-S4 release readiness docs. main 반영 완료.
|
|
5
|
+
(GitHub release tag 는 owner 결정 — version 파일은 main 코드 상태를 반영한다.)
|
|
6
|
+
"""
|
|
7
|
+
__version__ = "1.14.0"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""BingguPack — local-first, evidence-backed memory/context pack framework.
|
|
2
|
+
|
|
3
|
+
v1.11.0 groundwork: 내부 구현을 패키지 모듈로 단계적 이관.
|
|
4
|
+
public entrypoint(scripts/smoke_test.py, scripts/install_claude_mcp.py, binggu.py)는
|
|
5
|
+
backward-compatible 하게 유지된다.
|
|
6
|
+
"""
|
|
7
|
+
from binggupack.__about__ import __version__
|
|
8
|
+
|
|
9
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""capture — 빙구팩 캡처 파이프라인 모듈. v1.11.0 strangler phase5 이관 시작.
|
|
2
|
+
|
|
3
|
+
현재: buffer(메모리 내 candidate 누적 + preview 렌더, 영속화 0) + session(buffer 를 감싸는
|
|
4
|
+
on_user_prompt/on_session_end entrypoint). scripts/binggu_capture_buffer.py·binggu_capture_session.py
|
|
5
|
+
는 backward-compatible thin wrapper 로 유지된다. classify 정본은 binggupack.classifier.
|
|
6
|
+
"""
|
|
7
|
+
from .buffer import CaptureBuffer # noqa: F401
|
|
8
|
+
from .session import CaptureSession # noqa: F401
|
|
9
|
+
from .cli import run_batch # noqa: F401
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""빙구팩 캡처 버퍼 + batch preview 렌더 — 메모리 내만 (v1.11.0 strangler phase5 이관).
|
|
2
|
+
|
|
3
|
+
scripts/binggu_capture_buffer.py 에서 이 모듈로 이관했다. scripts 파일은 backward-compatible
|
|
4
|
+
thin wrapper(sys.path bootstrap + 전체 심볼 re-export + __main__ _selftest)로 유지되며
|
|
5
|
+
공개 심볼(CaptureBuffer)/동작은 byte-identical 하다(기능 변경 0).
|
|
6
|
+
|
|
7
|
+
classify import 는 binggupack 정본(binggupack.classifier.classify) 경유로 고정한다
|
|
8
|
+
(scripts 역참조 안 함 — strangler 단방향 원칙). classify 함수 자체는 phase4 에서 이관된
|
|
9
|
+
binggupack.classifier.capture_classifier.classify 와 동일 객체다.
|
|
10
|
+
|
|
11
|
+
설계: BINGGUPACK_USER_ONTOLOGY_EVENT_SCHEMA_DESIGN.md §4
|
|
12
|
+
- 메모리 buffer만 / ledger write 0 / 파일 저장 0 / OpenCrab 0
|
|
13
|
+
- active 저장 0 / owner approval 처리 0 / candidate→confirmed 전이 0
|
|
14
|
+
- preview_trigger 또는 세션말 시 후보 리스트 렌더(버퍼 삭제 안 함)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from binggupack.classifier import classify
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CaptureBuffer:
|
|
21
|
+
"""메모리 내 candidate 누적 버퍼. 영속화 일절 없음."""
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
self._candidates = [] # 메모리 리스트만
|
|
25
|
+
|
|
26
|
+
def feed(self, utterance, prev_turn=None):
|
|
27
|
+
"""발화 1건 처리. captured_candidate면 누적, preview_trigger면 렌더 반환.
|
|
28
|
+
반환: {"action": "captured"|"preview"|"ignored", "verdict": <classify dict>, "preview": <list|None>}"""
|
|
29
|
+
v = classify(utterance, prev_turn)
|
|
30
|
+
if v["state"] == "preview_trigger":
|
|
31
|
+
return {"action": "preview", "verdict": v, "preview": self.render_preview()}
|
|
32
|
+
if v["state"] == "captured_candidate":
|
|
33
|
+
self._candidates.append({
|
|
34
|
+
"text": utterance.strip(),
|
|
35
|
+
"pinned": v["pinned"],
|
|
36
|
+
"confidence": v["confidence"],
|
|
37
|
+
"signals": list(v["signals"]),
|
|
38
|
+
"state": "captured_candidate", # active/confirmed 절대 아님
|
|
39
|
+
})
|
|
40
|
+
return {"action": "captured", "verdict": v, "preview": None}
|
|
41
|
+
return {"action": "ignored", "verdict": v, "preview": None}
|
|
42
|
+
|
|
43
|
+
def _ordered(self):
|
|
44
|
+
# pinned 상단 → 그 외. 안정 정렬(입력 순 보존)
|
|
45
|
+
return sorted(self._candidates, key=lambda c: 0 if c["pinned"] else 1)
|
|
46
|
+
|
|
47
|
+
def render_preview(self):
|
|
48
|
+
"""현재 buffer를 preview 리스트로 렌더. 버퍼 삭제 안 함. write 0."""
|
|
49
|
+
lines = []
|
|
50
|
+
for i, c in enumerate(self._ordered(), 1):
|
|
51
|
+
tags = []
|
|
52
|
+
if c["pinned"]:
|
|
53
|
+
tags.append("PINNED")
|
|
54
|
+
if c["confidence"] == "weak":
|
|
55
|
+
tags.append("약함")
|
|
56
|
+
tag = f" [{' · '.join(tags)}]" if tags else ""
|
|
57
|
+
lines.append({
|
|
58
|
+
"idx": i,
|
|
59
|
+
"text": c["text"],
|
|
60
|
+
"pinned": c["pinned"],
|
|
61
|
+
"confidence": c["confidence"],
|
|
62
|
+
"label": f"{i}. {c['text']}{tag}",
|
|
63
|
+
"state": c["state"], # 항상 captured_candidate
|
|
64
|
+
})
|
|
65
|
+
return {"count": len(lines), "items": lines, "note": "owner 승인 전 candidate (active 아님)"}
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def size(self):
|
|
69
|
+
return len(self._candidates)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# ---------------- 셀프테스트 (메모리만, write 0) ----------------
|
|
73
|
+
def _selftest():
|
|
74
|
+
buf = CaptureBuffer()
|
|
75
|
+
ok = True
|
|
76
|
+
|
|
77
|
+
def check(cond, msg):
|
|
78
|
+
nonlocal ok
|
|
79
|
+
mark = "PASS" if cond else "FAIL"
|
|
80
|
+
if not cond:
|
|
81
|
+
ok = False
|
|
82
|
+
print(f" [{mark}] {msg}")
|
|
83
|
+
return cond
|
|
84
|
+
|
|
85
|
+
# 3개 후보: pinned(명시), 일반(결정), weak(추측+판단)
|
|
86
|
+
r1 = buf.feed("이거 저장해")
|
|
87
|
+
check(r1["action"] == "captured" and r1["verdict"]["pinned"], "T1 명시저장 → captured + pinned")
|
|
88
|
+
r2 = buf.feed("B안으로 결정")
|
|
89
|
+
check(r2["action"] == "captured" and r2["verdict"]["confidence"] == "normal", "T2 결정 → captured normal")
|
|
90
|
+
r3 = buf.feed("아마 이게 더 맞을 거야, 캐시 때문에")
|
|
91
|
+
check(r3["action"] == "captured" and r3["verdict"]["confidence"] == "weak", "T3 추측+판단 → weak 누적")
|
|
92
|
+
|
|
93
|
+
# ignored는 누적 안 됨
|
|
94
|
+
buf.feed("ㅋㅋ 웃기네")
|
|
95
|
+
check(buf.size == 3, "T4 ignored는 버퍼에 안 들어감 (size=3)")
|
|
96
|
+
|
|
97
|
+
# preview 트리거
|
|
98
|
+
r5 = buf.feed("빙구팩 저장해")
|
|
99
|
+
pv = r5["preview"]
|
|
100
|
+
check(r5["action"] == "preview", "T5 '빙구팩 저장해' → preview 액션")
|
|
101
|
+
check(pv["count"] == 3, "T6 preview 리스트 3개")
|
|
102
|
+
check(pv["items"][0]["pinned"], "T7 pinned가 맨 위")
|
|
103
|
+
check(any(it["confidence"] == "weak" for it in pv["items"]), "T8 weak 표시 존재")
|
|
104
|
+
check("약함" in [t for it in pv["items"] for t in [it["label"]] if "약함" in it["label"]] or
|
|
105
|
+
any("약함" in it["label"] for it in pv["items"]), "T9 weak 라벨 '약함' 표기")
|
|
106
|
+
|
|
107
|
+
# 렌더 후에도 버퍼 유지 + candidate 그대로
|
|
108
|
+
check(buf.size == 3, "T10 render 후 버퍼 유지 (size=3)")
|
|
109
|
+
check(all(it["state"] == "captured_candidate" for it in pv["items"]), "T11 candidate→active/confirmed 전이 0")
|
|
110
|
+
|
|
111
|
+
print("\n preview 렌더 결과:")
|
|
112
|
+
for it in pv["items"]:
|
|
113
|
+
print(" ", it["label"])
|
|
114
|
+
|
|
115
|
+
gate = "GO" if ok else "NO-GO"
|
|
116
|
+
print(f"\nGATE={gate}")
|
|
117
|
+
return ok
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""빙구팩 캡처 수동 호출 경로 (hook 없이) — stateless 배치 (v1.11.0 phase7 이관).
|
|
2
|
+
|
|
3
|
+
scripts/binggu_capture_cli.py 에서 이 모듈로 이관했다. scripts 파일은 backward-compatible
|
|
4
|
+
thin wrapper(sys.path bootstrap + 전체 심볼 re-export + __main__ run_cli)로 유지되며
|
|
5
|
+
공개 심볼/동작/출력은 byte-identical 하다(기능 변경 0).
|
|
6
|
+
|
|
7
|
+
CaptureSession import 는 binggupack 정본(binggupack.capture.session) 경유로 고정한다
|
|
8
|
+
(scripts 역참조 0 — strangler 단방향). 서브모듈 직접 import 로 capture/__init__ 순환 회피.
|
|
9
|
+
|
|
10
|
+
설계: BINGGUPACK_CAPTURE_MCP_EXPOSURE_DESIGN.md §3
|
|
11
|
+
- 발화 리스트를 받아 CaptureSession에 순차 feed → preview 반환.
|
|
12
|
+
- 메모리만 / ledger write 0 / 파일 저장 0 / OpenCrab 0 / active·approval 0.
|
|
13
|
+
- MCP capture_preview(utterances[]) 무상태 노출의 로컬 검증판(동일 엔진).
|
|
14
|
+
|
|
15
|
+
사용(수동):
|
|
16
|
+
echo 발화들 | python scripts/binggu_capture_cli.py # stdin 줄단위
|
|
17
|
+
python scripts/binggu_capture_cli.py --feed "B안으로 결정" --feed "이거 저장해"
|
|
18
|
+
→ preview JSON(stdout). "빙구팩 저장해" 만나면 그 시점 preview, 아니면 끝에서 preview.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
from binggupack.capture.session import CaptureSession
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def run_batch(utterances):
|
|
26
|
+
"""발화 리스트를 무상태로 처리 → preview dict 반환. write 0."""
|
|
27
|
+
s = CaptureSession()
|
|
28
|
+
triggered = None
|
|
29
|
+
for u in utterances:
|
|
30
|
+
r = s.on_user_prompt(u)
|
|
31
|
+
if r["action"] == "preview": # "빙구팩 저장해" 시점
|
|
32
|
+
triggered = r["preview"]
|
|
33
|
+
# 명시 트리거 없었으면 세션말 preview
|
|
34
|
+
preview = triggered if triggered is not None else s.on_session_end()["preview"]
|
|
35
|
+
return {
|
|
36
|
+
"trigger": "explicit" if triggered is not None else "session_end",
|
|
37
|
+
"buffer_size": s.size,
|
|
38
|
+
"preview": preview,
|
|
39
|
+
"note": "candidate (owner 승인 전, active 아님). write 0.",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _read_args(argv):
|
|
44
|
+
utts = []
|
|
45
|
+
i = 0
|
|
46
|
+
while i < len(argv):
|
|
47
|
+
if argv[i] == "--feed" and i + 1 < len(argv):
|
|
48
|
+
utts.append(argv[i + 1])
|
|
49
|
+
i += 2
|
|
50
|
+
else:
|
|
51
|
+
i += 1
|
|
52
|
+
return utts
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# ---------------- 셀프테스트 (write 0) ----------------
|
|
56
|
+
def _selftest():
|
|
57
|
+
ok = True
|
|
58
|
+
|
|
59
|
+
def check(c, m):
|
|
60
|
+
nonlocal ok
|
|
61
|
+
ok = ok and c
|
|
62
|
+
print(f" [{'PASS' if c else 'FAIL'}] {m}")
|
|
63
|
+
|
|
64
|
+
# 명시 트리거 경로
|
|
65
|
+
out = run_batch(["B안으로 결정", "이거 저장해", "빙구팩 저장해", "이건 무시될 농담 ㅋㅋ"])
|
|
66
|
+
check(out["trigger"] == "explicit", "T1 '빙구팩 저장해' → explicit 트리거")
|
|
67
|
+
check(out["preview"]["count"] == 2, "T2 트리거 시점 후보 2개(결정+pinned)")
|
|
68
|
+
check(out["preview"]["items"][0]["pinned"], "T3 pinned 맨 위")
|
|
69
|
+
check(all(it["state"] == "captured_candidate" for it in out["preview"]["items"]), "T4 active 전이 0")
|
|
70
|
+
|
|
71
|
+
# 세션말 경로(명시 트리거 없음)
|
|
72
|
+
out2 = run_batch(["로컬로 가자", "아마 B가 더 나을 거야, 비용 때문에", "ㅋㅋ"])
|
|
73
|
+
check(out2["trigger"] == "session_end", "T5 트리거 없으면 session_end")
|
|
74
|
+
check(out2["preview"]["count"] == 2, "T6 세션말 후보 2개(결정+weak)")
|
|
75
|
+
check(any(it["confidence"] == "weak" for it in out2["preview"]["items"]), "T7 weak 표시")
|
|
76
|
+
check(out2["buffer_size"] == 2, "T8 ignored(ㅋㅋ) 누적 안 됨")
|
|
77
|
+
|
|
78
|
+
print(f"\nGATE={'GO' if ok else 'NO-GO'}")
|
|
79
|
+
return ok
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def run_cli(argv=None):
|
|
83
|
+
"""CLI entrypoint. --selftest / --feed / stdin 줄단위. 동작·출력·exit code byte-identical."""
|
|
84
|
+
import sys
|
|
85
|
+
argv = sys.argv[1:] if argv is None else argv
|
|
86
|
+
if "--selftest" in argv:
|
|
87
|
+
return 0 if _selftest() else 1
|
|
88
|
+
# 수동 호출: --feed 인자 우선, 없으면 stdin 줄단위
|
|
89
|
+
utts = _read_args(argv)
|
|
90
|
+
if not utts and not sys.stdin.isatty():
|
|
91
|
+
utts = [ln.strip() for ln in sys.stdin if ln.strip()]
|
|
92
|
+
print(json.dumps(run_batch(utts), ensure_ascii=False, indent=2))
|
|
93
|
+
return 0
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""빙구팩 캡처 세션 entrypoint — buffer/classifier를 감싸는 트리거 함수 (v1.11.0 phase6 이관).
|
|
2
|
+
|
|
3
|
+
scripts/binggu_capture_session.py 에서 이 모듈로 이관했다. scripts 파일은 backward-compatible
|
|
4
|
+
thin wrapper(sys.path bootstrap + 전체 심볼 re-export + __main__ _selftest)로 유지되며
|
|
5
|
+
공개 심볼(CaptureSession)/동작은 byte-identical 하다(기능 변경 0).
|
|
6
|
+
|
|
7
|
+
CaptureBuffer import 는 binggupack 정본(binggupack.capture.buffer) 경유로 고정한다
|
|
8
|
+
(scripts 역참조 0 — strangler 단방향). 서브모듈 직접 import 로 capture/__init__ 순환 회피.
|
|
9
|
+
|
|
10
|
+
설계: BINGGUPACK_USER_ONTOLOGY_EVENT_SCHEMA_DESIGN.md §4
|
|
11
|
+
- 메모리만 / ledger write 0 / 파일 저장 0 / OpenCrab 0 / active·confirmed·approval 0
|
|
12
|
+
- 신규 hook 등록 0 (수동 호출 가능한 entrypoint 함수까지만).
|
|
13
|
+
|
|
14
|
+
연결 지점(확인만, 등록은 미래 별도 GO):
|
|
15
|
+
- UserPromptSubmit hook → on_user_prompt(utterance) (발화 캡처 + "빙구팩 저장해" preview)
|
|
16
|
+
- Stop hook → on_session_end() (세션말 preview)
|
|
17
|
+
- "빙구팩 저장해" 감지 = 기존 detect-bingoo-trigger.sh 패턴 재사용 가능
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from binggupack.capture.buffer import CaptureBuffer
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CaptureSession:
|
|
24
|
+
"""세션 1개 동안의 캡처 흐름. 전부 메모리. 영속화 0."""
|
|
25
|
+
|
|
26
|
+
def __init__(self):
|
|
27
|
+
self.buf = CaptureBuffer()
|
|
28
|
+
|
|
29
|
+
def on_user_prompt(self, utterance, prev_turn=None):
|
|
30
|
+
"""발화 1건 진입점(UserPromptSubmit 연결 후보).
|
|
31
|
+
captured면 누적, '빙구팩 저장해'면 preview 반환."""
|
|
32
|
+
return self.buf.feed(utterance, prev_turn)
|
|
33
|
+
|
|
34
|
+
def on_session_end(self):
|
|
35
|
+
"""세션말 진입점(Stop hook 연결 후보). preview 반환, buffer 삭제 안 함."""
|
|
36
|
+
return {"action": "preview", "trigger": "session_end", "preview": self.buf.render_preview()}
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def size(self):
|
|
40
|
+
return self.buf.size
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ---------------- 셀프테스트 (메모리만, write 0) ----------------
|
|
44
|
+
def _selftest():
|
|
45
|
+
ok = True
|
|
46
|
+
|
|
47
|
+
def check(cond, msg):
|
|
48
|
+
nonlocal ok
|
|
49
|
+
if not cond:
|
|
50
|
+
ok = False
|
|
51
|
+
print(f" [{'PASS' if cond else 'FAIL'}] {msg}")
|
|
52
|
+
|
|
53
|
+
s = CaptureSession()
|
|
54
|
+
|
|
55
|
+
# 1) 일반 후보 2개 누적
|
|
56
|
+
s.on_user_prompt("로컬 정본으로 가자")
|
|
57
|
+
s.on_user_prompt("이건 절대 운영DB 건들지 마")
|
|
58
|
+
check(s.size == 2, "T1 일반 후보 2개 누적")
|
|
59
|
+
|
|
60
|
+
# 2) 명시저장 pinned 누적
|
|
61
|
+
s.on_user_prompt("이거 저장해")
|
|
62
|
+
check(s.size == 3, "T2 '이거 저장해' pinned 누적 (size=3)")
|
|
63
|
+
|
|
64
|
+
# 3) "빙구팩 저장해" preview 반환
|
|
65
|
+
r = s.on_user_prompt("빙구팩 저장해")
|
|
66
|
+
check(r["action"] == "preview" and r["preview"]["count"] == 3, "T3 '빙구팩 저장해' → preview 3개")
|
|
67
|
+
check(r["preview"]["items"][0]["pinned"], "T3b pinned 맨 위")
|
|
68
|
+
|
|
69
|
+
# 4) 세션말 함수 호출 preview 반환
|
|
70
|
+
e = s.on_session_end()
|
|
71
|
+
check(e["action"] == "preview" and e["trigger"] == "session_end" and e["preview"]["count"] == 3,
|
|
72
|
+
"T4 세션말 함수 → preview 3개")
|
|
73
|
+
|
|
74
|
+
# 5) preview 후 buffer 유지
|
|
75
|
+
check(s.size == 3, "T5 preview 후 buffer 유지 (size=3)")
|
|
76
|
+
|
|
77
|
+
# 6) active/confirmed 전이 0
|
|
78
|
+
check(all(it["state"] == "captured_candidate" for it in e["preview"]["items"]),
|
|
79
|
+
"T6 active/confirmed 전이 0")
|
|
80
|
+
|
|
81
|
+
# 7) 실발화 20개 경계 보정 (캡처 10 + 제외 10 기대)
|
|
82
|
+
print("\n [실발화 20개 흘림]")
|
|
83
|
+
capture_expect = [
|
|
84
|
+
"로컬 정본으로 가자", "이건 절대 운영DB 건들지 마", "마감 임박건 우선 처리해",
|
|
85
|
+
"난 짧은 보고를 선호해", "나중에 이거 유료로 팔 거야", "그게 아니라 캐시부터 확인해야지",
|
|
86
|
+
"이거 저장해", "테스트는 항상 먼저 돌려", "아마 B가 더 나을 거야, 비용 때문에",
|
|
87
|
+
"위험해, 마감 직전에 터질 수 있어",
|
|
88
|
+
]
|
|
89
|
+
ignore_expect = [
|
|
90
|
+
"ㅋㅋ 그래", "지금 진행상황 보여줘", "와 잘됐다", "테스트 한번 돌려봐", "음 그렇구나",
|
|
91
|
+
"고마워 수고했어", "아마 그럴걸", "이거 뭐였지?", "좀 피곤하네", "로그 확인 좀",
|
|
92
|
+
]
|
|
93
|
+
s2 = CaptureSession()
|
|
94
|
+
cap_hit = ign_hit = 0
|
|
95
|
+
mis = []
|
|
96
|
+
for u in capture_expect:
|
|
97
|
+
st = s2.on_user_prompt(u)["verdict"]["state"]
|
|
98
|
+
if st == "captured_candidate":
|
|
99
|
+
cap_hit += 1
|
|
100
|
+
else:
|
|
101
|
+
mis.append(("누락", u, st))
|
|
102
|
+
base = s2.size
|
|
103
|
+
for u in ignore_expect:
|
|
104
|
+
st = s2.on_user_prompt(u)["verdict"]["state"]
|
|
105
|
+
if st == "ignored":
|
|
106
|
+
ign_hit += 1
|
|
107
|
+
else:
|
|
108
|
+
mis.append(("오탐", u, st))
|
|
109
|
+
print(f" 캡처 적중 {cap_hit}/10, 제외 적중 {ign_hit}/10, 버퍼 누적={s2.size}")
|
|
110
|
+
for kind, u, st in mis:
|
|
111
|
+
print(f" - [{kind}] {u!r} → {st}")
|
|
112
|
+
check(cap_hit >= 9 and ign_hit >= 9, "T7 실발화 경계: 캡처/제외 각 9/10 이상")
|
|
113
|
+
|
|
114
|
+
print(f"\nGATE={'GO' if ok else 'NO-GO'}")
|
|
115
|
+
return ok
|