distill
既存のコードベースからAllium仕様を抽出します。ユーザーが既存のコードの振る舞いをスペックに落とし込みたい場合、実装からスペックをリバースエンジニアリングしたい場合、コードからスペックを生成したい場合、または実装をAllium形式の振る舞い仕様としてドキュメント化したい場合に使用します。
description の原文を見る
Extract an Allium specification from an existing codebase. Use when the user has existing code and wants to distil behaviour into a spec, reverse engineer a specification from implementation, generate a spec from code, turn implementation into a behavioural specification, or document what a codebase does in Allium terms.
SKILL.md 本文
蒸留ガイド
このガイドは、既存のコードベースから Allium 仕様を抽出することについて説明しています。核となる課題は前方ヒアリングと同じです。すなわち、適切な抽象化レベルを見つけることです。ヒアリングではあなたが実装案を除外します。蒸留では既存の実装詳細を除外します。どちらも、ドメインレベルで何が重要かについて同じ判断が必要です。
コードはあなたに「どのように」機能するかを示します。仕様は「何をするか」「なぜそれが重要か」をキャプチャします。スキルは「なぜステークホルダーがこれを気にするのか?」「これが異なっていてもシステムは同じであり得るか?」と問うことです。
蒸留作業のスコープ決定
コードに飛び込む前に、何を仕様化しようとしているのかを確立してください。コードのすべての行がスペックに値するわけではありません。
最初に問うべき質問
-
「このコードベースのどのサブセットを仕様化していますか?」 モノリポはしばしば複数の異なるシステムを含みます。1つのサービスまたはドメインに対する仕様のみが必要な場合があります。開始前に境界を明示的に明確にしてください。
-
「意図的に除外すべきコードはありますか?」
- レガシーコード: 後方互換性のために保持されているが、コアシステムの一部ではない機能
- 偶発的なコード: ドメインレベルではないサポートインフラストラクチャ(ログ、メトリクス、デプロイ)
- 非推奨パス: 削除予定のコード
- 実験的機能: フィーチャーフラグの背後にあり、まだ設計決定ではない
-
「この仕様を誰が所有していますか?」 異なるチームがモノリポの異なる部分を所有していることがあります。各チームの仕様は彼らのドメインに焦点を当てるべきです。
「再構築するか」テスト
遭遇するコードパスについて、次のように問うてください。「このシステムをゼロから再構築する場合、これは要件に含まれるでしょうか?」
- はい: 仕様に含める
- いいえ、レガシーです: 除外
- いいえ、インフラストラクチャです: 除外
- いいえ、これは回避策です: 除外(ただし、それが対処する基本的なニーズを記述してください)
スコープ決定の文書化
蒸留された仕様の先頭に、含まれているものと除外されているものを文書化してください:
-- allium: 3
-- interview-scheduling.allium
-- Scope: Interview scheduling flow only
-- Includes: Candidacy, Interview, InterviewSlot, Invitation, Feedback
-- Excludes:
-- - User authentication (use auth library spec)
-- - Analytics/reporting (separate spec)
-- - Legacy V1 API (deprecated, not specified)
-- - Greenhouse sync (use greenhouse library spec)
バージョンマーカー(-- allium: N)はすべての .allium ファイルの最初の行である必要があります。現在の言語バージョン番号を使用してください。
適切な抽象化レベルを見つける
蒸留とヒアリングは同じ基本的な課題を共有しています。すなわち、何を含めるかを選ぶことです。以下のテストは両方向に機能します。ステークホルダーがフィーチャーを説明しているのを聞いているのか、またはそれを実装するコードを読んでいるのかに関係なく。
「なぜ」テスト
コード内のすべての詳細について、次のように問うてください。「ステークホルダーはなぜこれを気にするのか?」
| コードの詳細 | なぜ? | 含める? |
|---|---|---|
| 招待は7日で有効期限切れ | 候補者体験に影響 | はい |
| トークンは32バイトのURL安全 | セキュリティ実装 | いいえ |
| セッションはRedisに保存 | パフォーマンスの選択 | いいえ |
| PostgreSQL JSONBを使用 | データベース実装 | いいえ |
| スロットのステータスが「提案」に変更 | 候補者が見るものに影響 | はい |
| 招待が受け入れられるとメール送信 | コミュニケーション要件 | はい |
ステークホルダーが気にする理由を説明できない場合、それはおそらく実装です。
「異なるものにすることはできるか」テスト
「これは異なるように実装できるか、しかしシステムは同じままか」と問うてください。
- はい: おそらく実装の詳細、抽象化して除外
- いいえ: おそらくドメインレベル、含める
| 詳細 | 異なるものにすることはできるか? | 含める? |
|---|---|---|
secrets.token_urlsafe(32) | はい、あらゆる安全なトークン生成 | いいえ |
| 7日間の招待有効期限 | いいえ、これは設計決定 | はい |
| PostgreSQLデータベース | はい、あらゆるデータベース | いいえ |
| 「保留中、確認済み、完了」状態 | いいえ、これはワークフロー | はい |
「テンプレート対インスタンス」テスト
これは「カテゴリ」なのか、それとも「特定のインスタンス」なのか?
| インスタンス(多くの場合実装) | テンプレート(多くの場合ドメインレベル) |
|---|---|
| Google OAuth | 認証プロバイダー |
| Slackウェブフック | 通知チャネル |
| SendGrid API | メール配信 |
timedelta(hours=3) | 確認期限 |
時々インスタンスはドメインの懸念です。下記の「具体的な詳細の問題」を参照してください。
蒸留の姿勢
コードは過度に仕様化されている
コードのすべての行は、ドメインレベルで重要ではない可能性のある決定を行います:
# コードはあなたに示すもの:
def send_invitation(candidate_id: int, slot_ids: List[int]) -> Invitation:
candidate = db.session.query(Candidate).get(candidate_id)
slots = db.session.query(InterviewSlot).filter(
InterviewSlot.id.in_(slot_ids),
InterviewSlot.status == 'confirmed'
).all()
invitation = Invitation(
candidate_id=candidate_id,
token=secrets.token_urlsafe(32),
expires_at=datetime.utcnow() + timedelta(days=7),
status='pending'
)
db.session.add(invitation)
for slot in slots:
slot.status = 'proposed'
invitation.slots.append(slot)
db.session.commit()
send_email(
to=candidate.email,
template='interview_invitation',
context={'invitation': invitation, 'slots': slots}
)
return invitation
-- 仕様が言うべきもの:
rule SendInvitation {
when: SendInvitation(candidacy, slots)
requires: slots.all(s => s.status = confirmed)
ensures:
for s in slots:
s.status = proposed
ensures: Invitation.created(
candidacy: candidacy,
slots: slots,
expires_at: now + 7.days,
status: pending
)
ensures: Email.created(
to: candidacy.candidate.email,
template: interview_invitation
)
}
削除したもの:
candidate_id: intは単なるcandidacyになったdb.session.query(...)は関係性トラバーサルになったsecrets.token_urlsafe(32)は完全に削除(トークンは実装)datetime.utcnow() + timedelta(...)はnow + 7.daysになったdb.session.add/commitはcreatedで暗示invitation.slots.append(slot)は関係性で暗示
「プロダクトオーナーは気にするか」と問う
コード内のすべての詳細について、次のように問うてください:
| コードの詳細 | プロダクトオーナーは気にするか? | 含める? |
|---|---|---|
| 招待は7日で有効期限切れ | はい、候補者体験に影響 | はい |
| トークンは32バイトのURL安全 | いいえ、セキュリティ実装 | いいえ |
| SQLAlchemy ORMを使用 | いいえ、永続化メカニズム | いいえ |
| メールテンプレート名 | 多分、テンプレートが設計決定の場合 | 多分 |
| スロットのステータスが「提案」に変更 | はい、候補者が見るものに影響 | はい |
| データベーストランザクションのコミット | いいえ、実装の詳細 | いいえ |
手段と目的を区別する
手段: コードが何かを達成する方法。 目的: システムが必要とする結果。
| 手段(コード) | 目的(仕様) |
|---|---|
requests.post('https://slack.com/api/...') | Notification.created(channel: slack) |
candidate.oauth_token = google.exchange(code) | Candidate authenticated |
redis.setex(f'session:{id}', 86400, data) | Session.created(expires: 24.hours) |
for slot in slots: slot.status = 'cancelled' | for s in slots: s.status = cancelled |
具体的な詳細の問題
最も難しい判断: 具体的な詳細がドメイン対単なる実装の一部かどうか?
Google OAuth例
このコードを見つけます:
OAUTH_PROVIDERS = {
'google': GoogleOAuthProvider(client_id=..., client_secret=...),
}
def authenticate(provider: str, code: str) -> User:
return OAUTH_PROVIDERS[provider].authenticate(code)
質問: 「Google OAuth」はドメインレベルまたは実装か?
実装の場合:
- Googleは単に選択された認証メカニズム
- あらゆるOAuthプロバイダーで置換可能
- ユーザーはどのプロバイダーを見たり気にしたりしない
- コードは汎用的に記述されている(プロバイダーはパラメータ)
ドメインレベルの場合:
- ユーザーはGoogleを明示的に選択する(対Microsoftなど)
- 「Googleでサインイン」は機能
- Google固有のスコープまたは権限が使用される
- 複数のプロバイダーが機能としてサポートされている
判断方法: UIとユーザーフローを見てください。ユーザーが「Googleでサインイン」を選択肢として見た場合、ドメインレベルです。「サインイン」だけを見て、Googleがその背後にたまたまある場合、実装です。
データベース選択例
PostgreSQL固有のコードを見つけます:
from sqlalchemy.dialects.postgresql import JSONB, ARRAY
class Candidate(Base):
skills = Column(ARRAY(String))
metadata = Column(JSONB)
ほぼ常に実装。 仕様は以下を言うべきです:
entity Candidate {
skills: Set<String>
metadata: String? -- or model specific fields
}
特定のデータベースがドメインレベルであることはめったにありません。例外: システムが明示的にPostgreSQLの互換性またはPostgreSQL固有の機能をユーザーに約束する場合。
サードパーティ統合例
Greenhouse ATS統合を見つけます:
class GreenhouseSync:
def import_candidate(self, greenhouse_id: str) -> Candidate:
data = self.client.get_candidate(greenhouse_id)
return Candidate(
name=data['name'],
email=data['email'],
greenhouse_id=greenhouse_id,
source='greenhouse'
)
どちらでもあり得る:
実装の場合:
- Greenhouseは候補者がたまたま来るから
- Lever、Workable、等に交換可能
- 統合は「候補者がインポートされている」の実装の詳細
仕様:
external entity Candidate {
name: String
email: String
source: CandidateSource
}
製品レベルの場合:
- 「Greenhouse統合」は売上の利点
- ユーザーはGreenhouseの接続を設定
- Greenhouse固有の機能が公開されている(フィードバックを同期し直すなど)
仕様:
external entity Candidate {
name: String
email: String
greenhouse_id: String? -- explicitly modeled
}
rule SyncFromGreenhouse {
when: GreenhouseWebhookReceived(candidate_data)
ensures: Candidate.created(
...
greenhouse_id: candidate_data.id
)
}
「複数の実装」ヒューリスティック
コードベースの変動を探します:
- OAuthプロバイダーが1つだけの場合、おそらく実装
- OAuthプロバイダーが複数ある場合、おそらくドメインレベル
- 通知チャネルが1つだけの場合、おそらく実装
- Slack と メール と SMS がある場合、おそらくドメインレベル
複数の実装の存在は、変動自体がドメイン懸念であることを示唆します。
蒸留プロセス
ステップ1: 領域をマップする
仕様を抽出する前に、コードベースの構造を理解してください:
- エントリポイントを特定します。 APIルート、CLIコマンド、メッセージハンドラー、スケジュールされたジョブ。
- ドメインモデルを見つけます。 通常
models/、entities/、domain/にあります。 - ビジネスロジックを見つけます。 サービス、ユースケース、ハンドラー。
- 外部統合を記述します。 それが話す第三者は誰ですか?
粗いマップを作成します:
Entry points:
- API: /api/candidates/*, /api/interviews/*, /api/invitations/*
- Webhooks: /webhooks/greenhouse, /webhooks/calendar
- Jobs: send_reminders, expire_invitations, sync_calendars
Models:
- Candidate, Interview, InterviewSlot, Invitation, Feedback
Services:
- SchedulingService, NotificationService, CalendarService
Integrations:
- Google Calendar, Slack, Greenhouse, SendGrid
ステップ2: エンティティ状態を抽出する
enumフィールドとステータス列を見てください:
class Invitation(Base):
status = Column(Enum('pending', 'accepted', 'declined', 'expired'))
になります:
entity Invitation {
status: pending | accepted | declined | expired
}
enum定義、ステータスまたは状態列、STATUS_PENDING = 'pending' のような定数、および状態マシンライブラリ(例 transitions、django-fsm)を探してください。
ステップ2.5: 候補プロセスを特定する
エンティティとそれらの状態を抽出した後、状態マシンをスキャンしてエンドツーエンドプロセスを示唆します。コードベース全体の各ステータス値が設定される場所をトレースします(status = 'interviewing' はどこで発生しますか?)。抽出されたプロセスをユーザーに検証用に提示します。「applied → screening → interviewing → deciding → hired/rejected の状態を持つエンティティが見えています。これはシステムがサポートすることを意図しているプロセスですか?」
またクロスエンティティのデータフローをトレースします。エンティティAのルールがエンティティBのフィールドを必要とする場合、チェーンをたどります: エンティティBのフィールドはどこで設定され、何がそれをトリガーしますか? チェーンを提示します: 「採用決定は background_check_status = clear が必要です。これは /api/webhooks/background-check のウェブフックハンドラーで設定されます。このチェーンは正しく見えますか?」
抽出されたルールから遷移グラフを生成します。グラフは、コードの派生ビューです。ギャップがある場合(ターミナルではない発信トランジションのない状態)、潜在的な問題としてそれらにフラグを立てます。
ステップ3: トランジションを抽出する
ステータスが変更される場所を見つけます:
def accept_invitation(invitation_id: int, slot_id: int):
invitation = get_invitation(invitation_id)
if invitation.status != 'pending':
raise InvalidStateError()
if invitation.expires_at < datetime.utcnow():
raise ExpiredError()
slot = get_slot(slot_id)
if slot not in invitation.slots:
raise InvalidSlotError()
invitation.status = 'accepted'
slot.status = 'booked'
# Release other slots
for other_slot in invitation.slots:
if other_slot.id != slot_id:
other_slot.status = 'available'
# Create the interview
interview = Interview(
candidate_id=invitation.candidate_id,
slot_id=slot_id,
status='scheduled'
)
notify_interviewers(interview)
send_confirmation_email(invitation.candidate, interview)
抽出します:
rule CandidateAcceptsInvitation {
when: CandidateAccepts(invitation, slot)
requires: invitation.status = pending
requires: invitation.expires_at > now
requires: slot in invitation.slots
ensures: invitation.status = accepted
ensures: slot.status = booked
ensures:
for s in invitation.slots:
if s != slot: s.status = available
ensures: Interview.created(
candidacy: invitation.candidacy,
slot: slot,
status: scheduled
)
ensures: Notification.created(to: slot.interviewers, ...)
ensures: Email.created(to: invitation.candidate.email, ...)
}
主要な抽出パターン:
| コードパターン | 仕様パターン |
|---|---|
if x.status != 'pending': raise | requires: x.status = pending |
if x.expires_at < now: raise | requires: x.expires_at > now |
if item not in collection: raise | requires: item in collection |
x.status = 'accepted' | ensures: x.status = accepted |
Model.create(...) | ensures: Model.created(...) |
send_email(...) | ensures: Email.created(...) |
notify(...) | ensures: Notification.created(...) |
コード内のアサーション、チェック、および検証(例 assert balance >= 0、クラスレベルのバリデーター)は、ルール固有の保護条件ではなく、式を持つ不変条件にマップする場合があります。システム全体のプロパティまたはルール固有のガードを説明しているかどうかを検討してください。
ステップ4: 時間トリガーを見つける
スケジュールされたジョブと時間ベースのロジックを探してください:
# In celery tasks or cron jobs
@app.task
def expire_invitations():
expired = Invitation.query.filter(
Invitation.status == 'pending',
Invitation.expires_at < datetime.utcnow()
).all()
for invitation in expired:
invitation.status = 'expired'
for slot in invitation.slots:
slot.status = 'available'
notify_candidate_expired(invitation)
@app.task
def send_reminders():
upcoming = Interview.query.filter(
Interview.status == 'scheduled',
Interview.slot.time.between(
datetime.utcnow() + timedelta(hours=1),
datetime.utcnow() + timedelta(hours=2)
)
).all()
for interview in upcoming:
send_reminder_notification(interview)
抽出します:
rule InvitationExpires {
when: invitation: Invitation.expires_at <= now
requires: invitation.status = pending
ensures: invitation.status = expired
ensures:
for s in invitation.slots:
s.status = available
ensures: CandidateInformed(candidate: invitation.candidate, about: invitation_expired)
}
rule InterviewReminder {
when: interview: Interview.slot.time - 1.hour <= now
requires: interview.status = scheduled
ensures: Notification.created(to: interview.interviewers, template: reminder)
}
ステップ5: 外部境界を特定する
サードパーティAPI呼び出し、ウェブフックハンドラー、インポート/エクスポート機能、および読み取られるが決して書き込まれないデータ(またはその逆)を探してください。
これらは多くの場合外部エンティティを示します:
# 候補者データはGreenhouseから来て、作成しません
def import_from_greenhouse(webhook_data):
candidate = Candidate.query.filter_by(
greenhouse_id=webhook_data['id']
).first()
if not candidate:
candidate = Candidate(greenhouse_id=webhook_data['id'])
candidate.name = webhook_data['name']
candidate.email = webhook_data['email']
示唆します:
external entity Candidate {
name: String
email: String
}
繰り返されるインターフェースパターンがサービス境界全体で表れる場合(例えば複数のコンシューマーが期待する同じシリアライゼーション契約)、これらはインラインオブリゲーションブロックを重複させるのではなく、再利用のための contract 宣言を示唆します。
ステップ5.5: 認証パターンからアクターを特定する
API エンドポイントのサーフェスを抽出した後、認証と認可パターンを調べてアクターを特定します。異なる認証コンテキストは異なるアクターを示唆します:
- API キー認証 → システムアクター(外部サービス)
- ロールベースアクセス(
user.role == 'admin')→ ロールごとに異なるアクター - スコープ付きアクセス(
user.org_id == resource.org_id)→withinスコープ付きアクター - 認証なしのエンドポイント → 公開向けアクターまたはシステムウェブフック
ユーザーに確認を求めます: 「このエンドポイントは管理者ロール認証が必要です。『Admin』は異なるアクターですか、またはこれは権限の昇格を持つ通常ユーザーと同じ人ですか?」
ステップ6: 実装を抽象化する
次に、抽出された仕様を通す1回のパスを実行し、実装の詳細を削除します。
前(具体的すぎる):
entity Invitation {
candidate_id: Integer
token: String(32)
created_at: DateTime
expires_at: DateTime
status: pending | accepted | declined | expired
}
後(ドメインレベル):
entity Invitation {
candidacy: Candidacy
created_at: Timestamp
expires_at: Timestamp
status: pending | accepted | declined | expired
is_expired: expires_at <= now
}
変更:
candidate_id: Integerはcandidacy: Candidacyになった(FK ではなく関係性)token: String(32)は削除(実装)DateTimeはTimestampになった(ドメイン型)- 明確にするために派生の
is_expiredを追加
他のコンフィグ値から導出されるコンフィグ値(例 extended_timeout = base_timeout * 2)は、独立したリテラル値ではなく、コンフィグブロック内の修飾参照または式形式のデフォルトを使用するべきです。
ステップ7: ステークホルダーで検証する
抽出された仕様は仮説です。検証してください:
- 元の開発者に仕様を示します。 「これはシステムが何をするかですか?」
- ステークホルダーに示します。 「これはシステムが何をすべきかですか?」
- ギャップを探します。 コードにはしばしばバグや欠落している機能があります。仕様がそれらを明らかにするかもしれません。
一般的な発見:
- 「ああ、そのリトライロジックはハック、削除すべき」
- 「実は X が欲しかったが、まだ構築していない」
- 「これら2つのコードパスは同じであるべきだが、そうではない」
さらなるチェックを実行する前に、仕様を評価する を読んで、蒸留された仕様の成熟度を測定してください。これは、仕様がプロセスレベルの分析の準備ができているか、または構造的な作業がまだ必要かどうかを示します。
Allium CLI が利用可能な場合、蒸留された仕様に対して allium check を実行して構造上の問題をキャッチし、その後 allium analyse を実行してプロセスレベルのギャップを特定します。analyse からの発見は検証の質問を駆動できます: 「蒸留された仕様には background_check.status = clear を必要とするルールがありますが、背景チェック結果をキャプチャするサーフェスがありません。これはコードベースのまだ調べていない部分で処理されていますか?」 発見の実行 を参照して、発見をドメイン質問に変換する方法を確認してください。
ライブラリ仕様の候補を認識する
蒸留中、アプリケーション固有のロジックではなく一般的な統合パターンを実装するコードに注意してください。これらはライブラリ仕様に属します。完全な決定フレームワークについては ライブラリ仕様の機会を認識する を参照してください(問うべき質問、処理方法、一般的な抽出)。
コードのシグナル
ライブラリ仕様を示唆するこれらのパターンを探してください:
サードパーティ統合モジュール:
class StripeWebhookHandler:
def handle_invoice_paid(self, event):
...
class GoogleOAuthProvider:
def exchange_code(self, code):
...
設定駆動の統合:
OAUTH_CONFIG = {
'google': {'client_id': ..., 'scopes': ...},
'microsoft': {'client_id': ..., 'scopes': ...},
}
特定のプロバイダーを持つ一般的なパターン: OAuth フロー、支払い処理、メール配信、カレンダー同期、ATS 統合、ファイルストレージ。
赤い旗: 仕様の統合ロジック
自分自身が以下のような仕様を書いていることに気づいた場合、停止して再検討してください:
-- TOO DETAILED - this is Stripe's domain, not yours
rule ProcessStripeWebhook {
when: WebhookReceived(payload, signature)
requires: verify_stripe_signature(payload, signature)
let event = parse_stripe_event(payload)
if event.type = "invoice.paid":
...
}
代わりに:
-- Application responds to payment events (integration handled elsewhere)
rule PaymentReceived {
when: stripe/InvoicePaid(invoice)
...
}
詳細な例については patterns.md パターン8 を参照してください。
一般的な蒸留の課題
課題: 重複した用語
同じコンセプトの2つの用語を見つけた場合(仕様全体、仕様内、仕様とコード間)、ブロッキング問題として扱ってください。
-- BAD: Acknowledges duplication without resolving it
-- Order vs Purchase
-- checkout.allium uses "Purchase" - these are equivalent concepts.
これは解決ではありません。コードベースの異なる部分が異なる仕様に対して構築されると、両方の用語が実装に終わります: 重複するモデル、冗長な結合テーブル、両方向を指す外部キー。
何をすべきか:
- 1つの用語を選択します。決定する前に関連する仕様をクロスリファレンスします。
- すべての参照を更新します。 コメントまたは「参照」メモに古い用語を残さないでください。
- 仕様自体ではなく、変更ログで名前変更を記述します。
コード内の警告サイン:
- 同じコンセプトを表す2つのモデル(
OrderとPurchase) - 両方のための結合テーブル(
order_items,purchase_items) - 「X と同等」または「Y と同じ」のようなコメント
抽出する仕様は1つの用語を選択する必要があります。他方を技術的負債として除外にフラグを立ててください。
課題: 暗黙の状態マシン
コードは多くの場合、モデル化されていない暗黙の状態を持っています:
# 明示的なステータスフィールドはありませんが、隠れた状態マシンがあります
class FeedbackRequest:
interview_id = Column(Integer)
interviewer_id = Column(Integer)
requested_at = Column(DateTime)
reminded_at = Column(DateTime, nullable=True)
feedback_id = Column(Integer, nullable=True) # FK to Feedback if submitted
暗黙の状態は:
pending: requested_at 設定、feedback_id null、reminded_at nullreminded: reminded_at 設定、feedback_id nullsubmitted: feedback_id 設定
明示的に抽出します:
entity FeedbackRequest {
interview: Interview
interviewer: Interviewer
requested_at: Timestamp
reminded_at: Timestamp?
status: pending | reminded | submitted
}
課題: 散在するロジック
同じ概念的なルールが複数の場所に散在しているかもしれません:
# In API handler
def accept_invitation(request):
if invitation.status != 'pending':
return error(400, "Already responded")
...
# In model
class Invitation:
def can_accept(self):
return self.expires_at > datetime.utcnow()
# In service
def process_acceptance(invitation, slot):
if slot not in invitation.slots:
raise InvalidSlot()
...
1つのルールに統合します:
rule CandidateAccepts {
when: CandidateAccepts(invitation, slot)
requires: invitation.status = pending
requires: invitation.expires_at > now
requires: slot in invitation.slots
...
}
課題: デッドコードと歴史的な事故
コードベースは、構築されたが使用されたことがない機能、現在修正されているバグの回避策、および決して実行されないコードパスを蓄積します。
これらを仕様に含めないでください。不確定な場合:
- コードが実際に到達可能かどうかを確認
- 開発者に意図的か尋ねる
- コンテキストについて git 履歴を確認
課題: 不完全なエラーハンドリング
コードはサイレントに失敗するか、不完全なエラーハンドリングを持つかもしれません:
def send_notification(user, message):
try:
slack.send(user.slack_id, message)
except SlackError:
pass # Silently ignore failures
仕様は現在の実装ではなく、意図した動作をキャプチャすべきです:
ensures: Notification.created(to: user, channel: slack)
現在の実装が失敗を適切に処理するかどうかは、システムが何をすべきかから独立しています。
課題: 過度に設計された抽象化
エンタープライズコードベースはしばしば、意図を隠す抽象化レイヤーを持っています:
public interface NotificationStrategy {
void notify(NotificationContext context);
}
public class SlackNotificationStrategy implements NotificationStrategy {
@Override
public void notify(NotificationContext context) {
// Actual Slack call buried 5 levels deep
}
}
実際の動作に切り通します。仕様は戦略パターン、依存性注入、または抽象ファクトリーを必要としません。ただし: ensures: Notification.created(channel: slack, ...)
チェックリスト: 十分に抽象化しましたか?
蒸留された仕様を確定する前に:
- データベース列型なし(Integer、VARCHAR など)
- ORM またはクエリ構文なし
- HTTP ステータスコードまたは API パスなし
- フレームワーク固有の概念なし(ミドルウェア、デコレーター など)
- プログラミング言語型なし(int、str、List など)
- コードの変数名なし(ドメイン用語を使用)
- インフラストラクチャなし(Redis、Kafka、S3 など)
- 外部キーは関係性に置き換え
- トークン/シークレット削除(アイデンティティの実装)
- タイムスタンプはドメイン Duration を使用し、timedelta/seconds ではない
残っている場合、「ステークホルダーは要件ドキュメントにこれを含めるだろうか?」と問うてください。
チェックリスト: 用語の一貫性
- 各コンセプトは仕様全体で正確に1つの名前を持つ
- 「別名」または「同等」コメントなし
- 競合する用語について関連する仕様を相互参照
- コード内の重複するモデルは技術的負債として削除にフラグを立て
蒸留後
抽出された仕様は出発点です。蒸留が構造化された発見が必要なギャップを明かす場合(不明確な要件、複雑なエンティティ関係、述べられていないビジネスルール)、elicit スキルを使用してそれらを埋めます。要件が進化するにつれて対象の変更については、tend スキルを使用してください。仕様と実装間の継続的な整合性をチェックするには、weed スキルを使用してください。
参考文献
言語リファレンス, 完全な Allium 構文仕様を評価する, 仕様の成熟度の評価と正しい分析レベルの選択方法発見の実行, チェッカーの発見をドメイン質問に変換実行例, Python、TypeScript、Java での完全なコードから仕様の例
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- juxt
- リポジトリ
- juxt/allium
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/juxt/allium / ライセンス: MIT
関連スキル
superfluid
Superfluidプロトコルおよびそのエコシステムに関するナレッジベースです。Superfluidについて情報を検索する際は、ウェブ検索の前にこちらを参照してください。対応キーワード:Superfluid、CFA、GDA、Super App、Super Token、stream、flow rate、real-time balance、pool(member/distributor)、IDA、sentinels、liquidation、TOGA、@sfpro/sdk、semantic money、yellowpaper、whitepaper
civ-finish-quotes
実質的なタスクが真に完了した際に、文明風の儀式的な引用句を追加します。ユーザーやエージェントが機能追加、リファクタリング、分析、設計ドキュメント、プロセス改善、レポート、執筆タスクといった実際の成果物を完成させるときに、明示的な依頼がなくても使用します。短い返信や小さな修正、未完成の作業には適用しません。
nookplot
Base(Ethereum L2)上のAIエージェント向け分散型調整ネットワークです。エージェントがオンチェーンアイデンティティを登録する、コンテンツを公開する、他のエージェントにメッセージを送る、マーケットプレイスで専門家を雇う、バウンティを投稿・請求する、レピュテーションを構築する、共有プロジェクトで協業する、リサーチチャレンジを解くことでNOOKをマイニングする、キュレーションされたナレッジを備えたスタンドアロンオンチェーンエージェントをデプロイする、またはアグリーメントとリワードで収益を得る場合に利用できます。エージェントネットワーク、エージェント調整、分散型エージェント、NOOKトークン、マイニングチャレンジ、ナレッジバンドル、エージェントレピュテーション、エージェントマーケットプレイス、ERC-2771メタトランザクション、Prepare-Sign-Relay、AgentFactory、またはNookplotが言及された場合にトリガーされます。
web3-polymarket
Polygon上でのPolymarket予測市場取引統合です。認証機能(L1 EIP-712、L2 HMAC-SHA256、ビルダーヘッダー)、注文発注(GTC/GTD/FOK/FAK、バッチ、ポストオンリー、ハートビート)、市場データ(Gamma API、Data API、オーダーブック、サブグラフ)、WebSocketストリーミング(市場・ユーザー・スポーツチャネル)、CTF操作(分割、統合、償却、ネガティブリスク)、ブリッジ機能(入金、出金、マルチチェーン)、およびガスレスリレイトランザクションに対応しています。AIエージェント、自動マーケットメーカー、予測市場UI、またはPolygraph上のPolymarketと統合するアプリケーション構築時に活用できます。
ethskills
Ethereum、EVM、またはブロックチェーン関連のリクエストに対応します。スマートコントラクト、dApps、ウォレット、DeFiプロトコルの構築、監査、デプロイ、インタラクションに適用されます。Solidityの開発、コントラクトアドレス、トークン規格(ERC-20、ERC-721、ERC-4626など)、Layer 2ネットワーク(Base、Arbitrum、Optimism、zkSync、Polygon)、Uniswap、Aave、Curveなどのプロトコルとの統合をカバーします。ガスコスト、コントラクトのデシマル設定、オラクルセキュリティ、リエントランシー、MEV、ブリッジング、ウォレット管理、オンチェーンデータの取得、本番環境へのデプロイ、プロトコル進化(EIPライフサイクル、フォーク追跡、今後の変更予定)といったトピックを含みます。
xxyy-trade
このスキルは、ユーザーが「トークン購入」「トークン売却」「トークンスワップ」「暗号資産取引」「取引ステータス確認」「トランザクション照会」「トークンスキャン」「フィード」「チェーン監視」「トークン照会」「トークン詳細」「トークン安全性確認」「ウォレット一覧表示」「マイウォレット」「AIスキャン」「自動スキャン」「ツイートスキャン」「オンボーディング」「IP確認」「IPホワイトリスト」「トークン発行」「自動売却」「損切り」「利益確定」「トレーリングストップ」「保有者」「トップホルダー」「KOLホルダー」などをリクエストした場合、またはSolana/ETH/BSC/BaseチェーンでXXYYを経由した取引について言及した場合に使用します。XXYY Open APIを通じてオンチェーン取引とデータ照会を実現します。