Skip to content

Commit f57172d

Browse files
web-flowclaude
andcommitted
feat: TASKS の終端に cancelled を足す(v1.14.0)
TASKS の終端は done 一つで、依頼が取り下げられても要件が消滅しても、閉じる語は 「完了」しか無かった。notes に「これは達成ではない」と書き添えて凌げるが、 機械が読む status と人間が読む notes が食い違う台帳が残る。 この非対称は設計ではなく歴史だった。後から生えた表は非達成の終端を語で持っている (GOALS=abandoned / STEPS=skipped / SUBJECTS=deprecated)。SecretaryRole の雛型も STEPS について「不要になったら skipped(黙って消さない)」と書いている。同じ思想が TASKS 行だけ「完了で done」で止まっていたのは、TASKS が最古の表で、役割進化3表を 作ったときに育った語彙が遡って適用されなかったため。 語は表ごとにドメインの動詞を当てる。ステップは飛ばす、目標は諦める、そして TASKS は 他者起点の依頼ゆえ取り下げる(cancelled)。 下流はコード変更を要さなかった。archive_rotate.partition_for_archive は述語注入で "done" を持たず、orientation は ACTIVE_TASK_STATUSES という許可集合で active を 判定する(終端の語が増えても active 側は自動的に閉じる)。生の status == "done" 比較はコードベースに存在しない(WAL の done は別ドメイン)。 互換性: 書き込み側だけの拡張。canonical_record の fail-closed 検証は書き込み口に しか掛からないため、旧版のコードでも cancelled レコードは読める(書けないだけ)。 移行順はコード更新→書込で、既存データの書き換えは不要。 検証: pytest 861 passed / ruff All checks passed / ruff format 125 files already formatted / mypy strict Success (125 files) / /doc-check 0 issues Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent a5436ce commit f57172d

11 files changed

Lines changed: 75 additions & 12 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
"name": "TelegramSecretary",
118118
"source": "./TelegramSecretary",
119119
"description": "Telegram Bot API の long-polling を cloud routine 上で常駐させ、認可済みチャットからのメッセージに秘書エージェント(SecretaryRole)が即応する対話チャネル",
120-
"version": "1.13.0",
120+
"version": "1.14.0",
121121
"author": {
122122
"name": "Weave @ TelegramSecretary"
123123
},

TelegramSecretary/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "TelegramSecretary",
3-
"version": "1.13.0",
3+
"version": "1.14.0",
44
"description": "Telegram Bot API の long-polling を cloud routine 上で常駐させ、認可済みチャットからのメッセージに秘書エージェント(SecretaryRole)が即応する対話チャネル",
55
"author": {
66
"name": "Weave @ TelegramSecretary"

TelegramSecretary/docs/CHANGELOG.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,47 @@
22

33
すべての主要な変更をこのファイルに記録する。形式は [Keep a Changelog](https://keepachangelog.com/ja/1.1.0/)、バージョニングは [Semantic Versioning](https://semver.org/lang/ja/) に準拠する。
44

5+
## [1.14.0] - 2026-08-28 — TASKS の終端に `cancelled` を足す:取り止めを「やり遂げた」と記録しない
6+
7+
TASKS の終端は `done` 一つだった。依頼が取り下げられても要件が消滅しても、閉じる語は
8+
「完了」しか無い。notes に「これは達成ではない」と書き添えて凌ぐことはできるが、
9+
**status を機械が読み、notes を人間が読む**以上、この二つが食い違う台帳を残すことになる。
10+
11+
### 機序
12+
13+
**この非対称は設計ではなく歴史だった。** 後から生えた表は、非達成の終端を語で持っている——
14+
GOALS は `abandoned`、STEPS は `skipped`、SUBJECTS は `deprecated`。SecretaryRole の雛型も
15+
STEPS について「不要になったら skipped(**黙って消さない**)」と書いている。同じ思想が
16+
TASKS 行だけ「完了で done」で止まっていたのは、TASKS が最古の表で、役割進化3表を作った
17+
ときに育った語彙が遡って適用されなかったからである。
18+
19+
**語は表ごとにドメインの動詞を当てる。** ステップは飛ばす(`skipped`)、目標は諦める
20+
`abandoned`)、そして TASKS は**他者起点の依頼**なので取り下げる(`cancelled`)。
21+
全表を一語に揃えるより、各表がその意味論の語を持つ方が、書く側が迷わない。
22+
23+
**下流はコード変更を要さなかった。** `archive_rotate.partition_for_archive` は述語注入で
24+
`"done"` を持たず、orientation は `ACTIVE_TASK_STATUSES`(open / in_progress / blocked)という
25+
**許可集合**で active を判定する——終端の語が増えても active の側は自動的に閉じる。
26+
生の `status == "done"` 比較はコードベースに存在しない(WAL の `done` は別ドメイン)。
27+
28+
### Added
29+
30+
- `Task.status``cancelled`(完了条件を満たさずに閉じた終端)。`done` と同じく `closed_at`
31+
が付き、Archive と orientation の扱いも同じ
32+
33+
### Changed
34+
35+
- `TASKS.template.json``status` / `closed_at` / `_archive_policy` を二終端に更新
36+
- `SecretaryRole.template.md` の TASKS 行に「黙って done にしない」を明記(STEPS 行と同じ形)
37+
- `DESIGN.md` §3.5 の Archive 方針、`ROUTINE_PROMPT.md` の「notes が載らない範囲」を
38+
`done` から終端(`done` / `cancelled`)へ
39+
40+
### 互換性
41+
42+
書き込み側だけの拡張。`canonical_record` の fail-closed 検証は**書き込み口にしか掛からない**
43+
(read 経路は前方互換)ため、旧版のコードでも `cancelled` レコードは**読める**——書けないだけ。
44+
移行順は「コードを更新してから `cancelled` を書く」。既存データの書き換えは不要。
45+
546
## [1.13.0] - 2026-08-25 — strict を本番だけに掛ける:1294 の 7% が本体だった
647

748
前版で型検査を配線し 18 件を 0 にしたが、`strict = true` は入れていなかった。入れると

TelegramSecretary/docs/DESIGN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Infrastructure → Interface(Adapter) → UseCase → Domain
9393

9494
| 管理表 | 方式(既定方針) | 理由 |
9595
|---|---|---|
96-
| **TASKS** | 日付 Archive(done が N 日経過) | 完了タスクは「過去ログ」が自然。時系列で流れる |
96+
| **TASKS** | 日付 Archive(done / cancelled が N 日経過) | 閉じたタスクは「過去ログ」が自然。時系列で流れる |
9797
| **INDIVIDUALS** | 日付 Archive(blocked + 長期非接触) | 離脱者は稀に過去ログ化 |
9898
| **KNOWLEDGE** | **カテゴリ分割**(Archive せず) | 知識は**蓄積が本質**(判例DBは古いから捨てない)。肥大化は category 単位のシャード分割で解く |
9999
| **ABILITIES** | **カテゴリ分割**(Archive せず、KNOWLEDGE と同型) | 能力カタログも蓄積が本質(捨てない)。分割の単位・キーは必要時にエージェントが定義(§3.2 の JSON 柔軟性) |

TelegramSecretary/docs/ROUTINE_PROMPT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ source /tmp/telegram-secretary.env.sh && \
101101
ダイジェストが答えるのは「今どうなっているか」であり、表は相互参照する——「tasks をどう扱うか」の方針(自由時間の運用規範・grant 条件・行使してよい能力)は knowledge / abilities 側にあり、伴走の文脈は profile / goals / steps 側にある:
102102

103103
- **individuals(誰と)** — 相手の tone / honorific / taboo、疎遠な相手の鮮度(全文。上記呼び出しでは `identity.context_notes` だけが 400 バイトで丸まる——全文は `individuals get --key`
104-
- **tasks(何を頼まれ)**`id | status | priority | due_date | title` の一行要約(上記呼び出しでは新しい順 9 件、見出しの `latest 9 of M` が母数を開示。**active が落ちない最大の絞り**として実測で選んだ値ゆえ、落ちるのは古い done の要約行)+ active(open / in_progress / blocked)の notes 末尾(上記呼び出しでは 500 バイト、既定 4000)。**done の notes は載らない**。長い notes は handoff 分離前の legacy 堆積ゆえ末尾だけを見て、全文が要るときは `tasks get --key`
104+
- **tasks(何を頼まれ)**`id | status | priority | due_date | title` の一行要約(上記呼び出しでは新しい順 9 件、見出しの `latest 9 of M` が母数を開示。**active が落ちない最大の絞り**として実測で選んだ値ゆえ、落ちるのは古い終端(done / cancelled)の要約行)+ active(open / in_progress / blocked)の notes 末尾(上記呼び出しでは 500 バイト、既定 4000)。**終端(done / cancelled)の notes は載らない**。長い notes は handoff 分離前の legacy 堆積ゆえ末尾だけを見て、全文が要るときは `tasks get --key`
105105
- **knowledge(どう判断するか)**`id | subjects | topic` の索引のみ(`content` は載らない)。判断方針・運用規範(**自由時間の使い方・actionability ゲート・grant 条件**)の在り処を索引で掴む。上記呼び出しでは新しい順 30 件(見出しの `latest 30 of M` が母数を開示)——それ以前の在り処は `--knowledge-category`(認識の型)か `--knowledge-subject`(主題)で軸を指定して引く。主題列が `-` の行は主題未付与(付ける価値があると判断したら `knowledge add``import` で足す)
106106
- **subjects(どの軸で引けるか)** — 主題の語彙表の `id | label | aliases | status | note` 索引(**全件**。件数は絞らない——ここは「どの主題で引くか」を選ぶ一覧なので母数を減らすと選べない語が出る。丸まるのは `note` 列だけ、timestamps は載らない)。**`--knowledge-subject` に渡せるのはここの active な id だけ**で、knowledge へ主題を付けるときもこの表の語彙から選ぶ(範囲外は候補列挙付きで exit 2)。足りない語があれば `subjects add` で足す(コード変更は要らない)
107107
- **abilities(何ができるか)** — 行使できる能力カタログ(`trigger` / `skill_path` / `guidance`)。上記呼び出しでは `guidance` が 700 バイトで丸まる——発動判断に要る `trigger` / `skill_path` は丸めない。手順の全文は `abilities get --key`

TelegramSecretary/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "telegramsecretary"
7-
version = "1.13.0"
7+
version = "1.14.0"
88
description = "Telegram Bot API の long-polling を cloud routine 上で常駐させ、認可済みチャットからのメッセージに秘書エージェント(SecretaryRole)が即応する対話チャネル"
99
license = {text = "MIT"}
1010
requires-python = ">=3.10"

TelegramSecretary/scripts/domain/registry.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
{"family", "friend", "client", "vendor", "employee", "peer", "introducer", "other"}
1919
)
2020
_BIASES = frozenset({"low", "normal", "high"})
21-
_TASK_STATUSES = frozenset({"open", "in_progress", "blocked", "done"})
21+
# 終端は2つ——`done`(完了条件を満たした)と `cancelled`(満たさずに閉じた)。
22+
# 取り止めを `done` に潰すと台帳は後から「やり遂げた」としか読めない。GOALS の
23+
# `abandoned` / STEPS の `skipped` と同じ思想で、依頼のドメインの語を当てる。
24+
_TASK_STATUSES = frozenset({"open", "in_progress", "blocked", "done", "cancelled"})
2225
_PRIORITIES = frozenset({"low", "normal", "high"})
2326
_PROFILE_METHODS = frozenset(
2427
{"precognitive_viewer", "json_fortune", "mbti", "interview", "observation", "other"}

TelegramSecretary/scripts/tests/domain/test_registry.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,24 @@ def test_task_rejects_invalid_status():
131131
)
132132

133133

134+
def test_task_accepts_cancelled_status():
135+
# 取り止めは達成ではない——done に潰すと台帳が「やり遂げた」と読める。
136+
# GOALS の abandoned / STEPS の skipped と同じ、非達成の終端を語で持つ
137+
t = Task.from_dict(
138+
{
139+
"id": "t",
140+
"title": "x",
141+
"status": "cancelled",
142+
"priority": "normal",
143+
"requester": "principal",
144+
"created_at": "t",
145+
"updated_at": "t",
146+
"closed_at": "t",
147+
}
148+
)
149+
assert t.status == "cancelled"
150+
151+
134152
def test_task_rejects_invalid_priority():
135153
with pytest.raises(ValueError):
136154
Task(

TelegramSecretary/scripts/usecases/orientation.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
TRUNCATION_MARK = "…"
3535
_MARK_BYTES = len(TRUNCATION_MARK.encode("utf-8"))
3636

37-
# 一行要約に載せる tasks の status(done は要約行のみで notes を載せない)
37+
# 一行要約に載せる tasks の status(終端=done / cancelled は要約行のみで notes を載せない)。
38+
# 許可集合ゆえ、終端の語が増えても active の側は自動的に閉じる
3839
ACTIVE_TASK_STATUSES = frozenset({"open", "in_progress", "blocked"})
3940

4041
# 全文で載る小表の「支配的長文フィールド」への経路(orientation_report_20260810 実測の支配項)。
@@ -407,7 +408,7 @@ def _tasks_section(
407408
lines += ["", f"## tasks.notes (active only, last {notes_tail} bytes)"]
408409
for task in ordered:
409410
if task.get("status") not in ACTIVE_TASK_STATUSES:
410-
continue # done の notes は載せない(過去の申し送りは digest の対象外)
411+
continue # 終端の notes は載せない(過去の申し送りは digest の対象外)
411412
notes = str(task.get("notes", ""))
412413
if not notes:
413414
continue

TelegramSecretary/templates/SecretaryRole.template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
## 管理表の更新方針(CRUD は判断、I/O はコード)
5858

5959
- **INDIVIDUALS**: 新規接触者を登録(status=pending)、関係性が判明したら identity を更新
60-
- **TASKS**: 依頼を受けたら起票、進捗で status 更新、完了で done
60+
- **TASKS**: 依頼を受けたら起票、進捗で status 更新、完了は done、取り下げ・要件消滅は cancelled(黙って done にしない——done しか無い台帳は、取り止めを「やり遂げた」としか記録できない)
6161
- **KNOWLEDGE**: 再利用価値のある判断・対応を残す(一過性のやり取りは残さない)。`category`(認識の型・許可集合 10 種)と `subjects[]`(主題・SUBJECTS 表の active な id)の二軸を付ける(後で引くときの入口になる)
6262
- **SUBJECTS**: 主題の語彙表(`subjects` の照合先)。**増やすより育てる**——まず既存の id で足りないかを確かめ、足りなければ note 付きで add する。追加の目安は「その主題に該当する knowledge が 10 件以上見込めるか」(**10 件は仮置き**——運用実測で校正する)。使わなくなった語は remove せず `status=deprecated`(過去レコードの主題が読めなくなるため)
6363
- **ABILITIES**: 行使できる能力(スキル)のカタログ。依頼を受けたら応答前に `abilities list` で該当能力を確認し、`trigger` が合えば `skill_path` の SKILL.md を読んで行使する。新たに実在を確認した能力のみ登録(未検証の能力は宣言しない)

0 commit comments

Comments
 (0)