agent-bios 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,182 @@
1
+ # UI text catalog: Japanese. Key-set must equal en.toml exactly (gate-enforced).
2
+
3
+ "mode.title" = "モード"
4
+ "mode.swe.label" = "Software Engineer"
5
+ "mode.swe.description" = "プロジェクト自身の AGENTS.md/CLAUDE.md に従うリポジトリ範囲の作業: 素の Vanilla セッション、または Custom で構成します。"
6
+ "mode.builder.label" = "Builder"
7
+ "mode.builder.description" = "固定プリセットで tier・レビュールート・権限を調整します。Custom を含みます。"
8
+ "mode.distill.label" = "Session distill"
9
+ "mode.distill.description" = "専用の session-distill ハブへ: ステータス、パッケージ、バージョン、セッション開始。"
10
+
11
+ "language.label" = "言語"
12
+ "language.description" = "TUI の言語を選択します (English / 한국어 / 日本語)。AI が消費するコーパスは英語のままで、ランチャーの画面だけが変わります。"
13
+ "language.title" = "言語"
14
+ "language.en.description" = "Show the launcher's screens in English."
15
+ "language.ko.description" = "런처 화면을 한국어로 표시합니다."
16
+ "language.ja.description" = "ランチャーの画面を日本語で表示します。"
17
+
18
+ "corpus.label" = "コーパスパッケージ"
19
+ "corpus.description" = "インストールするコーパスのドメインパッケージを選択します。適用するとインストーラーの onboard ループ(インストール + 起動カナリア)が実行されます。"
20
+ "corpus.title" = "コーパスパッケージ"
21
+ "corpus.core.line" = "core + infra は常にインストールされます。このチェックリストは任意のドメインパッケージのみを扱います。"
22
+ "corpus.toggle.description" = "このドメインパッケージを切り替えます。Apply まで何も変わりません。"
23
+ "corpus.apply.label" = "選択を適用{pending}"
24
+ "corpus.apply.description" = "チェックされた集合そのままで agent-bios onboard を実行します。デプロイ・カナリア・結果記録はインストーラーが行います。"
25
+ "corpus.apply.unchanged" = "チェックされた集合は適用済みの集合と同じです — 適用するものがありません"
26
+ "corpus.unavailable.line1" = "corpus status projection が存在しないか、ドメイン報告以前のバージョンです。"
27
+ "corpus.none.line" = "この投影には選択できるドメインパッケージがありません — core と infra は常にインストールされるため、ここで選ぶものはありません。"
28
+ "corpus.unavailable.line2" = "実行: agent-bios install (この画面が読む projection を書き直します)"
29
+ "corpus.back.hint" = "モードメニューに戻ります。"
30
+
31
+ "wizard.title" = "別のレビュアーを登録"
32
+ "wizard.id.label" = "Method id (kebab-case)"
33
+ "wizard.label.label" = "ラベル (method 一覧に表示)"
34
+ "wizard.description.label" = "説明 (何のレビューが得意か)"
35
+ "wizard.drives.title" = "このレビュアーは何が実行しますか?"
36
+ "wizard.drives.tool.label" = "インストール済みのツール"
37
+ "wizard.drives.tool.description" = "method が capability を駆動します: このマシンで解決できる command と operation/adapter/hosts offer。"
38
+ "wizard.drives.panel.label" = "新しい host サブプロセス (panel 型)"
39
+ "wizard.drives.panel.description" = "ツールなし: ホスト CLI 自体が分離パスを実行します。main シートに載る場合レビューは perspective_floor と評価され、そのとき観点だけがあなたとレビュアーを分けます。"
40
+ "wizard.cap.name.label" = "Capability 名"
41
+ "wizard.cap.command.label" = "Command (PATH で解決、host CLI なら ${backend})"
42
+ "wizard.cap.install.label" = "Install 行 (agent-bios install --with が実行; なければ '-')"
43
+ "wizard.cap.operation.label" = "Operation (method と offer を結ぶ join key)"
44
+ "wizard.cap.adapter.title" = "Adapter (core プロトコル codec)"
45
+ "wizard.cap.hosts.title" = "このツールが担える host seat は?"
46
+ "wizard.cap.hosts.both" = "両方 (codex と claude)"
47
+ "wizard.perspectives.label" = "Perspectives (カンマ区切り; 独自の文脈で読むレビュアーなら 1 つで十分)"
48
+ "wizard.perspectives.refused" = "観点を少なくとも 1 つ挙げてください — レビュアーに何を見るよう伝えるかです。"
49
+ "wizard.trials.label" = "Trials (分離パスの回数; 1 以上)"
50
+ "wizard.trials.refused" = "1 以上の整数が必要です。"
51
+ "wizard.instructions.label" = "Instructions (slot: {command} {model} {effort} {provider} {host} {service_tier} {perspectives} {trials} {severities})"
52
+ "wizard.emits.label" = "レビュアーが実際に報告する severity 値、そのまま (カンマ区切り)"
53
+ "wizard.map.title" = "canonical ladder へマッピング — {value}"
54
+ "wizard.confirm.title" = "reader がこのブロックを受理しました — 書き込みますか?"
55
+ "wizard.confirm.target" = "追記先: {target}"
56
+ "wizard.confirm.write.label" = "書き込む"
57
+ "wizard.confirm.write.description" = "アトミックに追記します。ファイルの既存内容には触れません。"
58
+ "wizard.confirm.edit.label" = "回答を再編集"
59
+ "wizard.refused.title" = "reader がこのブロックを拒否しました"
60
+ "wizard.refused.header" = "検証は reader のものです。そのメッセージをそのまま:"
61
+ "wizard.refused.edit.label" = "回答を編集 (既定値として保持)"
62
+ "wizard.refused.edit.description" = "質問をやり直します。すべての回答が事前入力されています。"
63
+ "wizard.refused.contract.label" = "ファイル契約を表示"
64
+ "wizard.raced.line" = "ウィザードを開いている間に methods ファイルが変更されました。何も書き込んでいません。登録をやり直してください。"
65
+ "wizard.postwrite.line" = "書き込み後の再読込で method が見つかりません。ファイルを直接確認してください。"
66
+ "wizard.postwrite.raced.line" = "書き込み後の再読み込みでメソッドが見つからず、その間にファイルが変更されました — 他の書き込みをロールバックせずそのまま残しました。手動で確認してください。"
67
+ "wizard.done.line" = "登録完了、このランチャーで即時利用可能: {method_id}"
68
+
69
+ # カスタム設定ハブ — 一回の起動で使うティア、レビュー、実行ポリシー。
70
+ "custom.title" = "カスタム設定"
71
+ "custom.main.label" = "メインティア: {tier}"
72
+ "custom.main.description" = "このセッションで使う主オーケストレーターを選択します。"
73
+ "custom.review.label" = "レビュー構成: {setup}"
74
+ "custom.review.description" = "レビューゲート発動時に要求するレビュー経路を選択します。"
75
+ "custom.policy.label" = "実行ポリシー: {policy}"
76
+ "custom.policy.description" = "ホストごとの権限またはサンドボックス動作を選択します。"
77
+ "custom.tier.description" = "{tier} ティアのモデルと推論強度を編集し、ここへ戻ります。"
78
+ "custom.save.label" = "この設定をグローバルに保存して開始"
79
+ "custom.save.description" = "この構成を名前付きプリセットとしてユーザー設定に保存し、他でも再利用できるようにしてから起動します。"
80
+ "custom.save.prompt" = "プリセット名を付けて保存"
81
+ "custom.start.label" = "この設定で開始"
82
+ "custom.start.description" = "上に表示された構成全体を確認し、起動へ進みます。"
83
+ "custom.exit.label" = "起動せずに終了"
84
+ "custom.exit.description" = "この起動を破棄してシェルに戻ります。"
85
+
86
+ "tier.title" = "メインティア"
87
+ "tier.frontier.description" = "難易度が高く影響の大きい作業と深いレビューを担う最高性能の専門家です。"
88
+ "tier.helm.description" = "計画・委任・統合・最終判断を担う主オーケストレーターです。"
89
+ "tier.workhorse.description" = "大量の実装と分析をコスト効率よくこなす実行役です。"
90
+ "tier.sweep.description" = "狭い範囲の検索・点検・機械的作業を高速かつ低コストで行います。"
91
+
92
+ "model.title" = "{tier} モデル"
93
+ "model.use.description" = "{model} をこのティアのモデルとして使用します。"
94
+ "model.other.label" = "その他 (モデル id を入力)"
95
+ "model.other.description" = "カタログになくてもバックエンドが受け付けるモデル id を入力します。"
96
+
97
+ "effort.title" = "{tier} 推論強度"
98
+ "effort.low.description" = "単純な作業で推論コストとレイテンシを最小化します。"
99
+ "effort.medium.description" = "多少の曖昧さがある通常の作業に中程度の推論を使います。"
100
+ "effort.high.description" = "複雑な実装と分析により多くの推論を費やします。"
101
+ "effort.xhigh.description" = "難しい調整と影響の大きい判断により深い推論を使います。"
102
+ "effort.max.description" = "フロンティア作業にホストの最も強力な標準推論モードを使います。"
103
+ "effort.ultra.description" = "例外的に深く分割可能なフロンティア作業に Codex Ultra を使います。"
104
+ "effort.unsupported.host" = "{host} では非対応"
105
+ "effort.unsupported.model" = "{model} では非対応"
106
+
107
+ "policy.codex.title" = "Codex 実行ポリシー"
108
+ "policy.codex.bypass.label" = "承認・サンドボックスを回避"
109
+ "policy.codex.bypass.description" = "Codex の承認やサンドボックス制限なしで実行します。"
110
+ "policy.codex.workspace-write.label" = "ワークスペース書き込みサンドボックス"
111
+ "policy.codex.workspace-write.description" = "Codex の workspace-write サンドボックスでワークスペースへの書き込みを許可します。"
112
+ "policy.codex.read-only.label" = "読み取り専用サンドボックス"
113
+ "policy.codex.read-only.description" = "Codex サンドボックスでファイル書き込みを禁じ、参照のみ許可します。"
114
+ "policy.codex.standard.label" = "標準"
115
+ "policy.codex.standard.description" = "ポリシーフラグを渡さず、Codex 本来の既定の承認・サンドボックス動作を使います。"
116
+
117
+ "policy.claude.title" = "Claude 権限モード (OS サンドボックスではありません)"
118
+ "policy.claude.bypassPermissions.label" = "権限確認を回避"
119
+ "policy.claude.bypassPermissions.description" = "Claude の権限確認をスキップします。OS のサンドボックス設定ではありません。"
120
+ "policy.claude.acceptEdits.label" = "編集を自動承認"
121
+ "policy.claude.acceptEdits.description" = "ファイル編集は自動承認し、その他の権限確認は維持します。"
122
+ "policy.claude.auto.label" = "自動権限"
123
+ "policy.claude.auto.description" = "Claude の自動権限モードを使います。"
124
+ "policy.claude.manual.label" = "手動権限"
125
+ "policy.claude.manual.description" = "Claude の手動権限フローに従い、操作前に確認します。"
126
+ "policy.claude.dontAsk.label" = "確認しない"
127
+ "policy.claude.dontAsk.description" = "Claude の dontAsk 権限モードを使います。"
128
+ "policy.claude.plan.label" = "プランモード"
129
+ "policy.claude.plan.description" = "直接実装せず、計画モードで Claude を開始します。"
130
+ "policy.claude.standard.label" = "標準"
131
+ "policy.claude.standard.description" = "ポリシーフラグを渡さず、Claude 本来の既定の権限確認を使います。"
132
+
133
+ # セッションディスティル領域 — コーパス内容のバージョン、パッケージ、ロールバック。
134
+ "distill.title" = "Session Distill"
135
+ "distill.start.label" = "Session distill を開始"
136
+ "distill.start.description" = "Session distill プリセットで起動します。ミッション行がセッションをワークフローガイドと SSOT へ導きます。"
137
+ "distill.start.unavailable" = "プリセット {preset} が設定されていません"
138
+ "distill.packages.label" = "コーパスパッケージ"
139
+ "distill.packages.description" = "どのコーパス内容が適用されているかを、インストール可能なパッケージ単位で表示します (v1: 単一 core コーパス)。"
140
+ "distill.versions.label" = "バージョンとロールバック"
141
+ "distill.versions.description" = "コーパス内容のバージョンです (システム配備とは別)。コーパスを前後のバージョンへ戻します。"
142
+ "distill.back.label" = "戻る"
143
+ "distill.back.hint" = "Session distill へ戻ります。"
144
+ "distill.back.launch" = "起動メニューへ戻ります。"
145
+ "distill.version.current" = "{version} (現在)"
146
+ "distill.version.description" = "closed {closed} · commit {commit} · {summary}"
147
+ "distill.version.applied.title" = "{version} が現在適用中のバージョンです"
148
+ "distill.version.applied.line" = "ロールバックまたはロールフォワードするには別のバージョンを選択してください。"
149
+ "distill.package.core.label" = "core コーパス @ {version}"
150
+ "distill.package.core.description" = "ドメインパッケージングが入るまで、すべてのコーパスドメインが一つのパッケージです (バックログ)。レイヤー: {layers}。ドメインごとの選択・バージョン・ロールバックは今後ここに表示されます。"
151
+ "distill.rollback.title" = "コーパスを {version} へロールバックしますか?"
152
+ "distill.rollback.cancel.label" = "キャンセル"
153
+ "distill.rollback.cancel.description" = "現在配備されているコーパス内容をそのまま維持します。"
154
+ "distill.rollback.confirm.label" = "{version} へロールバック"
155
+ "distill.rollback.confirm.description" = "そのコーパスバージョン時点の globals/guides/hooks を再配備します。システム配備 (ランチャー、ラッパー) は現行のままです。最新バージョンを選べば再びロールフォワードできます。"
156
+ "distill.rollback.done" = "ロールバック完了。"
157
+ "distill.rollback.failed" = "ロールバック失敗。"
158
+
159
+ # コーパス状態パネル — Session Distill 領域とチェックリストが共用します。
160
+ # ラベルは表示セル基準で整列するため、翻訳の長さが違っても崩れません。
161
+ "panel.unprojected" = "コーパス状態がまだ生成されていません — 実行: agent-bios install"
162
+ "panel.version.label" = "適用バージョン"
163
+ "panel.mechanisms.label" = "メカニズム"
164
+ "panel.ledger.label" = "台帳"
165
+ "panel.domains.label" = "ドメイン"
166
+ "panel.rolled-back" = "(ロールバック済み。最新は {latest})"
167
+ "panel.layers.none" = "なし"
168
+ "panel.ledger.value" = "配置 {placed} · インキュベート {incubating} · バージョン {versions}"
169
+ "panel.domains.unset" = "(未選択)"
170
+ "panel.last-apply" = "⚠ 最終適用 {outcome} @ {at} — 要求: {requested}"
171
+ "corpus.core-only" = "core+infra のみ"
172
+
173
+ # Numbered-prompt chrome. These were printed as English literals from the renderer
174
+ # while every line of content around them was translated, so the Korean and Japanese
175
+ # screens answered in one language and asked in another.
176
+ "prompt.select" = "選択 [{default}] ({actions}): "
177
+ "prompt.back" = "b 戻る"
178
+ "prompt.cancel" = "q キャンセル"
179
+ "prompt.unavailable" = "利用不可"
180
+ "prompt.unavailable.retry" = "利用不可: {reason}。利用可能な項目を選択してください。"
181
+ "prompt.no.previous" = "前のメニューがありません。q でキャンセルしてください。"
182
+ "prompt.corpus.header" = "-- コーパス状態 --"
@@ -0,0 +1,182 @@
1
+ # UI text catalog: Korean. Key-set must equal en.toml exactly (gate-enforced).
2
+
3
+ "mode.title" = "모드"
4
+ "mode.swe.label" = "Software Engineer"
5
+ "mode.swe.description" = "프로젝트 자체의 AGENTS.md/CLAUDE.md를 따르는 레포 범위 작업: 순정 Vanilla 세션 또는 Custom으로 직접 구성합니다."
6
+ "mode.builder.label" = "Builder"
7
+ "mode.builder.description" = "고정 프리셋들에서 tier·리뷰 라우트·권한을 조정합니다. Custom 포함."
8
+ "mode.distill.label" = "Session distill"
9
+ "mode.distill.description" = "전용 session-distill 허브로 이동: 상태, 패키지, 버전, 세션 시작."
10
+
11
+ "language.label" = "언어"
12
+ "language.description" = "TUI 언어를 선택합니다 (English / 한국어 / 日本語). AI가 소비하는 코퍼스는 영어 그대로이며, 런처 화면만 바뀝니다."
13
+ "language.title" = "언어"
14
+ "language.en.description" = "Show the launcher's screens in English."
15
+ "language.ko.description" = "런처 화면을 한국어로 표시합니다."
16
+ "language.ja.description" = "ランチャーの画面を日本語で表示します。"
17
+
18
+ "corpus.label" = "코퍼스 패키지"
19
+ "corpus.description" = "설치할 코퍼스 도메인 패키지를 선택합니다. 적용하면 설치기의 onboard 루프(설치 + 활성화 카나리)가 실행됩니다."
20
+ "corpus.title" = "코퍼스 패키지"
21
+ "corpus.core.line" = "core + infra는 항상 설치됩니다. 이 체크리스트는 선택 도메인 패키지만 다룹니다."
22
+ "corpus.toggle.description" = "이 도메인 패키지를 토글합니다. Apply 전까지 아무것도 바뀌지 않습니다."
23
+ "corpus.apply.label" = "선택 적용{pending}"
24
+ "corpus.apply.description" = "체크된 집합 그대로 agent-bios onboard를 실행합니다. 배포·카나리·결과 기록은 설치기가 수행합니다."
25
+ "corpus.apply.unchanged" = "체크된 집합이 적용된 집합과 같습니다 — 적용할 것이 없습니다"
26
+ "corpus.unavailable.line1" = "corpus status projection이 없거나 도메인 보고 이전 버전입니다."
27
+ "corpus.none.line" = "이 투영에는 선택 가능한 도메인 패키지가 없습니다 — core와 infra는 항상 설치되므로 여기서 고를 것이 없습니다."
28
+ "corpus.unavailable.line2" = "실행: agent-bios install (이 화면이 읽는 projection을 다시 씁니다)"
29
+ "corpus.back.hint" = "모드 메뉴로 돌아갑니다."
30
+
31
+ "wizard.title" = "다른 리뷰어 등록"
32
+ "wizard.id.label" = "Method id (kebab-case)"
33
+ "wizard.label.label" = "라벨 (method 목록에 표시)"
34
+ "wizard.description.label" = "설명 (무엇을 가장 잘 리뷰하는지)"
35
+ "wizard.drives.title" = "이 리뷰어는 무엇이 실행하나요?"
36
+ "wizard.drives.tool.label" = "설치된 도구"
37
+ "wizard.drives.tool.description" = "method가 capability를 구동합니다: 이 머신에서 해석 가능한 command와 operation/adapter/hosts offer."
38
+ "wizard.drives.panel.label" = "새 host 서브프로세스 (panel형)"
39
+ "wizard.drives.panel.description" = "도구 없음: 호스트 CLI 자체가 격리 패스를 수행합니다. main 좌석에 놓이면 리뷰 등급이 perspective_floor가 되며, 그때는 관점이 당신과 리뷰어를 가르는 유일한 요소입니다."
40
+ "wizard.cap.name.label" = "Capability 이름"
41
+ "wizard.cap.command.label" = "Command (PATH에서 해석, host CLI면 ${backend})"
42
+ "wizard.cap.install.label" = "Install 라인 (agent-bios install --with가 실행; 없으면 '-')"
43
+ "wizard.cap.operation.label" = "Operation (method와 offer를 잇는 join key)"
44
+ "wizard.cap.adapter.title" = "Adapter (core 프로토콜 codec)"
45
+ "wizard.cap.hosts.title" = "이 도구가 채울 수 있는 host seat는?"
46
+ "wizard.cap.hosts.both" = "둘 다 (codex와 claude)"
47
+ "wizard.perspectives.label" = "Perspectives (쉼표 구분; 자기 컨텍스트에서 읽는 리뷰어라면 1개로 충분)"
48
+ "wizard.perspectives.refused" = "관점을 최소 1개 적어 주세요 — 리뷰어에게 무엇을 보라고 지시할지입니다."
49
+ "wizard.trials.label" = "Trials (격리 패스 횟수; 1 이상)"
50
+ "wizard.trials.refused" = "1 이상의 정수가 필요합니다."
51
+ "wizard.instructions.label" = "Instructions (slot: {command} {model} {effort} {provider} {host} {service_tier} {perspectives} {trials} {severities})"
52
+ "wizard.emits.label" = "리뷰어가 실제로 보고하는 severity 값들, 그대로 (쉼표 구분)"
53
+ "wizard.map.title" = "canonical ladder로 매핑 — {value}"
54
+ "wizard.confirm.title" = "reader가 이 블록을 수락했습니다 — 기록할까요?"
55
+ "wizard.confirm.target" = "추가 대상: {target}"
56
+ "wizard.confirm.write.label" = "기록"
57
+ "wizard.confirm.write.description" = "원자적으로 append합니다. 파일의 기존 내용은 건드리지 않습니다."
58
+ "wizard.confirm.edit.label" = "답변 다시 편집"
59
+ "wizard.refused.title" = "reader가 이 블록을 거부했습니다"
60
+ "wizard.refused.header" = "검증은 reader의 것입니다. 그 메시지 그대로:"
61
+ "wizard.refused.edit.label" = "답변 편집 (기본값으로 유지됨)"
62
+ "wizard.refused.edit.description" = "질문을 다시 진행합니다. 모든 답이 미리 채워져 있습니다."
63
+ "wizard.refused.contract.label" = "파일 계약 보기"
64
+ "wizard.raced.line" = "마법사가 열려 있는 동안 methods 파일이 바뀌었습니다. 아무것도 기록하지 않았습니다. 등록을 다시 실행하세요."
65
+ "wizard.postwrite.line" = "기록 후 재확인에서 method가 보이지 않습니다. 파일을 직접 확인하세요."
66
+ "wizard.postwrite.raced.line" = "쓰기 후 재확인에서 메서드가 보이지 않았고, 그 사이 파일이 바뀌었습니다 — 다른 쪽의 편집을 되돌리지 않고 그대로 두었습니다. 직접 확인해 주세요."
67
+ "wizard.done.line" = "등록 완료, 이 런처에서 즉시 사용 가능: {method_id}"
68
+
69
+ # 사용자 설정 허브 — 한 번의 실행에 쓰이는 티어, 리뷰, 실행 정책.
70
+ "custom.title" = "사용자 설정"
71
+ "custom.main.label" = "메인 티어: {tier}"
72
+ "custom.main.description" = "이 세션에서 사용할 주 오케스트레이터를 선택합니다."
73
+ "custom.review.label" = "리뷰 구성: {setup}"
74
+ "custom.review.description" = "리뷰 게이트가 작동할 때 요청할 리뷰 경로를 선택합니다."
75
+ "custom.policy.label" = "실행 정책: {policy}"
76
+ "custom.policy.description" = "호스트별 권한 또는 샌드박스 동작을 선택합니다."
77
+ "custom.tier.description" = "{tier} 티어의 모델과 추론 강도를 편집하고 여기로 돌아옵니다."
78
+ "custom.save.label" = "이 설정을 전역으로 저장하고 시작"
79
+ "custom.save.description" = "이 구성을 사용자 설정에 이름 있는 프리셋으로 저장해 다른 곳에서도 재사용한 뒤 실행합니다."
80
+ "custom.save.prompt" = "프리셋 이름으로 저장"
81
+ "custom.start.label" = "이 설정으로 시작"
82
+ "custom.start.description" = "위에 표시된 전체 구성을 확인하고 실행을 계속합니다."
83
+ "custom.exit.label" = "실행하지 않고 종료"
84
+ "custom.exit.description" = "이 실행을 취소하고 셸로 돌아갑니다."
85
+
86
+ "tier.title" = "메인 티어"
87
+ "tier.frontier.description" = "어렵고 영향이 큰 작업과 심층 리뷰를 맡는 최고 성능 전문가입니다."
88
+ "tier.helm.description" = "계획, 위임, 통합, 최종 결정을 담당하는 주 오케스트레이터입니다."
89
+ "tier.workhorse.description" = "대량 구현과 분석을 비용 효율적으로 처리하는 실행자입니다."
90
+ "tier.sweep.description" = "좁은 범위의 검색·점검·기계적 작업을 빠르고 저렴하게 수행합니다."
91
+
92
+ "model.title" = "{tier} 모델"
93
+ "model.use.description" = "{model}을(를) 이 티어의 모델로 사용합니다."
94
+ "model.other.label" = "기타 (모델 id 직접 입력)"
95
+ "model.other.description" = "카탈로그에 없더라도 백엔드가 받아들이는 모델 id를 입력합니다."
96
+
97
+ "effort.title" = "{tier} 추론 강도"
98
+ "effort.low.description" = "단순한 작업에서 추론 비용과 지연을 최소화합니다."
99
+ "effort.medium.description" = "약간의 모호함이 있는 일상 작업에 적당한 추론을 사용합니다."
100
+ "effort.high.description" = "복잡한 구현과 분석에 더 많은 추론을 투입합니다."
101
+ "effort.xhigh.description" = "난도 높은 조율과 영향이 큰 결정에 더 깊은 추론을 사용합니다."
102
+ "effort.max.description" = "프런티어 작업에 호스트의 가장 강력한 표준 추론 모드를 사용합니다."
103
+ "effort.ultra.description" = "예외적으로 깊고 분할 가능한 프런티어 작업에 Codex Ultra를 사용합니다."
104
+ "effort.unsupported.host" = "{host}에서 지원하지 않음"
105
+ "effort.unsupported.model" = "{model}에서 지원하지 않음"
106
+
107
+ "policy.codex.title" = "Codex 실행 정책"
108
+ "policy.codex.bypass.label" = "승인·샌드박스 우회"
109
+ "policy.codex.bypass.description" = "Codex의 승인이나 샌드박스 제한 없이 실행합니다."
110
+ "policy.codex.workspace-write.label" = "워크스페이스 쓰기 샌드박스"
111
+ "policy.codex.workspace-write.description" = "Codex의 workspace-write 샌드박스에서 워크스페이스 쓰기를 허용합니다."
112
+ "policy.codex.read-only.label" = "읽기 전용 샌드박스"
113
+ "policy.codex.read-only.description" = "Codex 샌드박스로 파일 쓰기를 막고 조회만 허용합니다."
114
+ "policy.codex.standard.label" = "표준"
115
+ "policy.codex.standard.description" = "정책 플래그를 전달하지 않고 Codex의 기본 승인·샌드박스 동작을 사용합니다."
116
+
117
+ "policy.claude.title" = "Claude 권한 모드 (OS 샌드박스가 아님)"
118
+ "policy.claude.bypassPermissions.label" = "권한 확인 우회"
119
+ "policy.claude.bypassPermissions.description" = "Claude의 권한 확인을 건너뜁니다. OS 샌드박스 설정이 아닙니다."
120
+ "policy.claude.acceptEdits.label" = "편집 자동 수락"
121
+ "policy.claude.acceptEdits.description" = "파일 편집은 자동으로 수락하고 나머지 권한 확인은 유지합니다."
122
+ "policy.claude.auto.label" = "자동 권한"
123
+ "policy.claude.auto.description" = "Claude의 자동 권한 모드를 사용합니다."
124
+ "policy.claude.manual.label" = "수동 권한"
125
+ "policy.claude.manual.description" = "Claude의 수동 권한 흐름에 따라 작업 전에 확인합니다."
126
+ "policy.claude.dontAsk.label" = "묻지 않음"
127
+ "policy.claude.dontAsk.description" = "Claude의 dontAsk 권한 모드를 사용합니다."
128
+ "policy.claude.plan.label" = "플랜 모드"
129
+ "policy.claude.plan.description" = "직접 구현하지 않고 계획 모드로 Claude를 시작합니다."
130
+ "policy.claude.standard.label" = "표준"
131
+ "policy.claude.standard.description" = "정책 플래그를 전달하지 않고 Claude의 기본 권한 확인을 사용합니다."
132
+
133
+ # 세션 디스틸 영역 — 코퍼스 내용의 버전, 패키지, 롤백.
134
+ "distill.title" = "Session Distill"
135
+ "distill.start.label" = "Session distill 실행 시작"
136
+ "distill.start.description" = "Session distill 프리셋으로 실행합니다. 미션 라인이 세션을 워크플로 가이드와 SSOT로 안내합니다."
137
+ "distill.start.unavailable" = "프리셋 {preset}이(가) 설정되지 않았습니다"
138
+ "distill.packages.label" = "코퍼스 패키지"
139
+ "distill.packages.description" = "어떤 코퍼스 내용이 적용돼 있는지 설치 가능한 패키지 단위로 봅니다 (v1: 단일 core 코퍼스)."
140
+ "distill.versions.label" = "버전 및 롤백"
141
+ "distill.versions.description" = "코퍼스 내용의 버전입니다(시스템 배포와는 별개). 코퍼스를 이전 또는 이후 버전으로 되돌립니다."
142
+ "distill.back.label" = "뒤로"
143
+ "distill.back.hint" = "Session distill로 돌아갑니다."
144
+ "distill.back.launch" = "실행 메뉴로 돌아갑니다."
145
+ "distill.version.current" = "{version} (현재)"
146
+ "distill.version.description" = "closed {closed} · commit {commit} · {summary}"
147
+ "distill.version.applied.title" = "{version}이(가) 현재 적용된 버전입니다"
148
+ "distill.version.applied.line" = "롤백하거나 롤포워드하려면 다른 버전을 선택하세요."
149
+ "distill.package.core.label" = "core 코퍼스 @ {version}"
150
+ "distill.package.core.description" = "도메인 패키징이 도입되기 전까지 모든 코퍼스 도메인이 하나의 패키지입니다(백로그). 레이어: {layers}. 도메인별 선택·버전·롤백은 이후 여기에 표시됩니다."
151
+ "distill.rollback.title" = "코퍼스를 {version}(으)로 롤백할까요?"
152
+ "distill.rollback.cancel.label" = "취소"
153
+ "distill.rollback.cancel.description" = "현재 배포된 코퍼스 내용을 그대로 유지합니다."
154
+ "distill.rollback.confirm.label" = "{version}(으)로 롤백"
155
+ "distill.rollback.confirm.description" = "해당 코퍼스 버전 시점의 globals/guides/hooks를 다시 배포합니다. 시스템 배포(런처, 래퍼)는 현재 상태를 유지합니다. 최신 버전을 선택하면 다시 롤포워드됩니다."
156
+ "distill.rollback.done" = "롤백 완료."
157
+ "distill.rollback.failed" = "롤백 실패."
158
+
159
+ # 코퍼스 상태 패널 — Session Distill 영역과 체크리스트가 함께 씁니다.
160
+ # 라벨은 표시 셀 기준으로 정렬되므로 번역 길이가 달라도 어긋나지 않습니다.
161
+ "panel.unprojected" = "코퍼스 상태가 아직 생성되지 않았습니다 — 실행: agent-bios install"
162
+ "panel.version.label" = "적용 버전"
163
+ "panel.mechanisms.label" = "메커니즘"
164
+ "panel.ledger.label" = "원장"
165
+ "panel.domains.label" = "도메인"
166
+ "panel.rolled-back" = "(롤백됨. 최신은 {latest})"
167
+ "panel.layers.none" = "없음"
168
+ "panel.ledger.value" = "배치 {placed} · 인큐베이팅 {incubating} · 버전 {versions}"
169
+ "panel.domains.unset" = "(아직 선택 안 함)"
170
+ "panel.last-apply" = "⚠ 마지막 적용 {outcome} @ {at} — 요청: {requested}"
171
+ "corpus.core-only" = "core+infra만"
172
+
173
+ # Numbered-prompt chrome. These were printed as English literals from the renderer
174
+ # while every line of content around them was translated, so the Korean and Japanese
175
+ # screens answered in one language and asked in another.
176
+ "prompt.select" = "선택 [{default}] ({actions}): "
177
+ "prompt.back" = "b 뒤로"
178
+ "prompt.cancel" = "q 취소"
179
+ "prompt.unavailable" = "사용 불가"
180
+ "prompt.unavailable.retry" = "사용 불가: {reason}. 사용 가능한 항목을 선택하세요."
181
+ "prompt.no.previous" = "이전 메뉴가 없습니다. q로 취소하세요."
182
+ "prompt.corpus.header" = "-- 코퍼스 상태 --"
@@ -89,6 +89,19 @@ def validate_record(record, validator, domain_values):
89
89
  errors.append(
90
90
  f"domain: {dom!r} is not a registered domain key, tier name, "
91
91
  f"or 'unclassified' (compose/domains.json)")
92
+ # A lone surrogate is a valid `str` and a valid JSON Schema "string", so everything
93
+ # above passes it — and then the first `json.dumps(..., ensure_ascii=False)` refuses
94
+ # to encode it. That surfaced as a UnicodeEncodeError traceback out of the write in
95
+ # collect-learning rather than as a rejection, so the check belongs here, where every
96
+ # consumer of a record already asks whether it is valid. `ensure_ascii=False` is the
97
+ # form the writers use and the reason CJK stays readable in the artifacts; encoding
98
+ # is what actually fails, so the check performs it rather than scanning code points.
99
+ try:
100
+ json.dumps(record, ensure_ascii=False).encode("utf-8")
101
+ except UnicodeEncodeError as exc:
102
+ errors.append(
103
+ f"<root>: record is not encodable as UTF-8 (unpaired surrogate or "
104
+ f"similar): {exc}")
92
105
  return errors
93
106
 
94
107
 
@@ -166,6 +179,7 @@ def self_test():
166
179
  ("classification type out of A-G enum", swap("classification", {"type": "Z"}), "classification"),
167
180
  ("classification unknown sub-key", swap("classification", {"mechanism": "x"}), "classification"),
168
181
  ("proposed_domain with a concrete domain", {**base, "proposed_domain": "data-pipeline", "domain": "builder-base"}, "domain"),
182
+ ("lone surrogate in lesson", swap("lesson", "unpaired \ud800 surrogate"), "not encodable as UTF-8"),
169
183
  ]
170
184
 
171
185
  failures = []
@@ -180,6 +194,10 @@ def self_test():
180
194
  # positive control: proposed_domain rides only with an unclassified domain (base is unclassified).
181
195
  if validate_record({**base, "proposed_domain": "data-pipeline"}, validator, domain_values):
182
196
  failures.append("positive control (proposed_domain + unclassified) does not validate")
197
+ # positive control: the encodability check must not reject non-ASCII text, which is
198
+ # the only reason the writers pass ensure_ascii=False in the first place.
199
+ if validate_record({**base, "lesson": "한국어 교훈은 그대로 남는다"}, validator, domain_values):
200
+ failures.append("positive control (non-ASCII lesson) does not validate")
183
201
  for name, record, expect in mutations:
184
202
  errors = validate_record(record, validator, domain_values)
185
203
  if not errors:
@@ -193,7 +211,7 @@ def self_test():
193
211
  print(f" - {f}")
194
212
  return 1
195
213
  print(f"check-learning --self-test: OK ({len(mutations)} mutations caught, "
196
- f"4 positive controls)")
214
+ f"5 positive controls)")
197
215
  return 0
198
216
 
199
217
 
@@ -61,6 +61,34 @@ HOSTS = {
61
61
  CLAUDE_IMPORT_LINE = "@personal/learnings.md"
62
62
  CLAUDE_CENTRAL_IMPORT = "@central/bundle.md"
63
63
 
64
+
65
+ def import_line_index(body, directive):
66
+ """Index of the line where `directive` is an ACTIVE import, or None.
67
+
68
+ Raw containment is not this question. `@personal/learnings.md` inside a fenced
69
+ example, or named in a sentence, loads nothing — and reading it as an import made
70
+ two different files claim a file was wired when it was not: capture reported the
71
+ entry `present` and wrote nothing, and migrate-learnings read a fenced central
72
+ import as evidence the corpus was loaded, which is half of what authorizes deleting
73
+ a user's personal copy. An import is a line whose whole content is the directive.
74
+
75
+ Fences are tracked rather than stripped so the index is an index into `body`'s own
76
+ lines, which is what the insertion point needs. Both ``` and ~~~ open and close.
77
+ """
78
+ fenced = False
79
+ for index, line in enumerate(body.splitlines()):
80
+ stripped = line.strip()
81
+ if stripped.startswith("```") or stripped.startswith("~~~"):
82
+ fenced = not fenced
83
+ continue
84
+ if not fenced and stripped == directive:
85
+ return index
86
+ return None
87
+
88
+
89
+ def has_active_import(body, directive):
90
+ return import_line_index(body, directive) is not None
91
+
64
92
  # Codex AGENTS.md markers. The central pair is owned by compose/assemble.py
65
93
  # (kept in sync here); the personal-learnings pair is this tool's own region,
66
94
  # placed outside the central pair so re-assembly preserves it.
@@ -220,13 +248,16 @@ def ensure_claude_import(home, dry):
220
248
  entry.write_text(f"# CLAUDE.md\n\n{CLAUDE_IMPORT_LINE}\n", encoding="utf-8")
221
249
  return "created"
222
250
  body = entry.read_text(encoding="utf-8")
223
- if CLAUDE_IMPORT_LINE in body:
251
+ if has_active_import(body, CLAUDE_IMPORT_LINE):
224
252
  return "present"
225
253
  if dry:
226
254
  print(f" [dry] insert '{CLAUDE_IMPORT_LINE}' into {entry}")
227
255
  return "inserted"
228
256
  lines = body.splitlines(keepends=True)
229
- idx = next((i for i, ln in enumerate(lines) if CLAUDE_CENTRAL_IMPORT in ln), 0)
257
+ # Anchored to the ACTIVE central import for the same reason: matching a fenced one
258
+ # would insert this import inside that fence, where it loads nothing either.
259
+ central = import_line_index(body, CLAUDE_CENTRAL_IMPORT)
260
+ idx = central if central is not None else 0
230
261
  backup(entry)
231
262
  lines.insert(idx + 1, CLAUDE_IMPORT_LINE + "\n")
232
263
  entry.write_text("".join(lines), encoding="utf-8")
@@ -543,6 +574,30 @@ def _self_test():
543
574
  "\n" not in nl["lesson"] and "\r" not in nl["lesson"]
544
575
  and "line one line two line three" == nl["lesson"]))
545
576
 
577
+ # 10) An import is a LINE, not a substring. Reading a fenced example or a sentence as
578
+ # an active import made capture report the entry `present` and write nothing, so
579
+ # the learning it had just saved would never load. Each negative is paired with the
580
+ # positive that separates it: a guard that answered False to everything would
581
+ # satisfy the first three rows and fail the fourth.
582
+ real = f"# CLAUDE.md\n{CLAUDE_CENTRAL_IMPORT}\n{CLAUDE_IMPORT_LINE}\n"
583
+ for name, body, want in (
584
+ ("fenced ``` example is not an import", f"# e\n```\n{CLAUDE_IMPORT_LINE}\n```\n", False),
585
+ ("fenced ~~~ example is not an import", f"# e\n~~~md\n{CLAUDE_IMPORT_LINE}\n~~~\n", False),
586
+ ("a sentence naming the path is not an import",
587
+ f"# e\nWe import {CLAUDE_IMPORT_LINE} from here.\n", False),
588
+ ("a standalone directive line IS an import", real, True),
589
+ ("indentation and trailing space still import",
590
+ f"# e\n {CLAUDE_IMPORT_LINE} \n", True),
591
+ ):
592
+ checks.append((f"import detection: {name}",
593
+ has_active_import(body, CLAUDE_IMPORT_LINE) is want))
594
+ # The insertion point follows the same rule, or the new import lands inside the fence.
595
+ checks.append(("insertion anchors on an ACTIVE central import",
596
+ import_line_index(real, CLAUDE_CENTRAL_IMPORT) == 1))
597
+ checks.append(("a fenced central import anchors nothing",
598
+ import_line_index(f"# e\n```\n{CLAUDE_CENTRAL_IMPORT}\n```\n",
599
+ CLAUDE_CENTRAL_IMPORT) is None))
600
+
546
601
  failed = [name for name, ok in checks if not ok]
547
602
  if failed:
548
603
  for name in failed: