跳到主内容
智客 ZICQ

技能库 智客分类:Agent 工作流 clanker-discipline

纪律

捕捉到状态bloat,抓取-包模型,以及AI编码剂的突变模糊. 用于审查状态类型,布尔旗,可选域模型,或可变数据模式.

6596 安装量

官方网址:skills.sh

技能介绍

先看中文介绍;官方 description 原文单独保留,不改写 SKILL.md。

做什么

捕捉到状态bloat,抓包模型,以及来自AI编码剂的突变模糊性

何时用

审查状态类型、布尔旗、可选域模型或可变数据模式

代理如何加载

按 Agent Skills 渐进披露:启动时只加载 name 与 description(约 100 token);任务匹配后才读入整份 SKILL.md 正文;scripts/、references/、assets/ 仅在需要时再读。 本文件正文结构:Clanker Discipline、1. Derive, don't store、Before: cached flags、After: derive from evidence、When NOT to derive、If you cannot derive, encapsulate。

文件分析

文件分析:这是一份仅含 SKILL.md 的指令型技能,代理激活后整份正文进入上下文。

官方 description(原文)

Catches state bloat, grab-bag models, and mutation ambiguity from AI coding agents. Use when reviewing state types, boolean flags, optional-field models, or mutable data patterns.

Clanker Discipline1. Derive, don't storeBefore: cached flagsAfter: derive from evidenceWhen NOT to deriveIf you cannot derive, encapsulateThe debugging payoff2. Make wrong states impossibleDiscriminated unions over optional bagsNull over sentinelsPhased composition over grab-bagsBrand identical primitives

来源分类:skills.sh agent-skill

SKILL.md 与 Agent 调用

官方规范 ↗
name
clanker-discipline
description
Catches state bloat, grab-bag models, and mutation ambiguity from AI coding agents. Use when reviewing state types, boolean flags, optional-field models, or mutable data patterns.
  1. 发现技能客户端向 Agent 提供名称与描述目录。
  2. 匹配与调用用户指定或任务匹配后,载入 SKILL.md 指令。
  3. 按需加载按步骤读取参考文档、使用脚本与素材。

具体调用语法与可用工具以目标 Agent 客户端为准。 查看调用机制说明 ↗

安装这个技能

Skills CLI ↗

先选择目标 Agent 和安装范围,保留技能包的附属文件,安装后检查客户端能否发现该技能。

交给 Agent 安装

复制安装指令给支持 Agent Skills 的代理,确认其中的目标目录与客户端匹配。

把 Agent Skill「clanker-discipline」安装到我的项目:SKILL.md 原文与官方 description 见 https://zicq.com/zh/skills/skl-af5c17b12a0c2ad6-%E7%BA%AA%E5%BE%8B.html
请存为 .cursor/skills/clanker-discipline/SKILL.md 或 .claude/skills/clanker-discipline/SKILL.md,frontmatter 的 name 与 description 保持原样,不要改写。

GitHub 完整包 ↗

终端安装 · Skills CLI

需要 Node.js 与 npx。先查看仓库技能列表,确认实际名称。

npx skills add 'https://github.com/gbasin/clanker-discipline' --list

npx skills add 'https://github.com/gbasin/clanker-discipline' --skill 'clanker-discipline'

CLI 会交互选择目标 Agent,默认安装到项目;用户级安装使用 -g。先通过查看命令核对仓库内容,再用 npx skills list 检查已安装技能。

阅读排版
--- name: clanker-discipline description: Catches state bloat, grab-bag models, and mutation ambiguity from AI coding agents. Use when reviewing state types, boolean flags, optional-field models, or mutable data patterns. --- # Clanker Discipline Apply these rules when writing or reviewing state types, data models, and functions that manage application state. Agents tend to add flags, optional fields, and special cases that compound into state nobody intended — catch that before it lands. When you find violations, refactor fully. The goal is clean, maintainable code, not minimal diffs. Rip out the flags, reshape the types, restructure the functions. A bigger diff now is better than layering workarounds that compound later. --- ## 1. Derive, don't store Every boolean you add doubles the theoretical state space. When a value can be derived from data you already have, do not store it. The best source to derive from is an event stream: a log of what happened. ### Before: cached flags An agent was asked to show a footer only when the assistant finishes naturally. It invented four flags: ```ts type ThreadState = { wasInterrupted: boolean; didAssistantFinish: boolean; didAssistantError: boolean; wasToolCallOnly: boolean; }; function shouldShowFooter(state: ThreadState): boolean { return state.didAssistantFinish && !state.wasInterrupted && !state.didAssistantError && !state.wasToolCallOnly; } ``` Four fields to answer one question, with four mutation sites elsewhere keeping them in sync. ### After: derive from evidence ```ts function shouldShowFooter(events: SessionEvent[]): boolean { const latest = getLatestAssistantMessage(events); if (!latest) return false; return latest.completed && !latest.error && latest.finish !== 'tool-calls'; } ``` The answer is now computed from events that already exist. ### When NOT to derive - The domain genuinely has a state machine with ordered transitions. A checkout step is not a cached conclusion; it IS the state. - A field contains temporal or external data that cannot be rederived (timestamps from async processes, API responses needed downstream). - The derivation would be more complex than the stored value. ### If you cannot derive, encapsulate If mutable state must exist, trap it in the smallest possible scope. A closure is better than a class field: ```ts // Bad: state visible to the whole class class Writer { private debounceTimeout: ReturnType | null = null; queueSend(text: string) { /* can touch debounceTimeout */ } flushNow() { /* can touch debounceTimeout */ } somethingElse() { /* can also touch debounceTimeout */ } } // Good: state trapped in a closure function createDebouncedAction(callback: () => void, delayMs = 300) { let timeout: ReturnType | null = null; return { trigger() { clearTimeout(timeout!); timeout = setTimeout(() => { timeout = null; callback(); }, delayMs); }, clear() { if (timeout) { clearTimeout(timeout); timeout = null; } }, }; } ``` Nothing outside the closure can touch the timer. ### The debugging payoff When state is derived from evidence, debugging becomes data-in, answer-out: ```ts test('footer is hidden for aborted runs', () => { const events = loadEvents('./fixtures/aborted-session.jsonl'); expect(shouldShowFooter(events)).toBe(false); }); ``` No mocking or timing reproduction. The bug is in the events or in the pure function. --- ## 2. Make wrong states impossible Every optional field is a question the rest of the codebase must answer every time it touches that data. ### Discriminated unions over optional bags ```ts // Bad: when status is 'idle', should gateway/transactionId exist? The type doesn't say. type PaymentState = { status: 'idle' | 'processing' | 'settled'; gateway?: 'stripe' | 'paypal'; transactionId?: string; initiatedAt?: string; settledAt?: string; }; // Good: each status carries exactly the fields it needs. type PaymentState = | { status: 'idle' } | { status: 'processing'; gateway: 'stripe' | 'paypal'; transactionId: string; initiatedAt: string } | { status: 'settled'; gateway: 'stripe' | 'paypal'; transactionId: string; settledAt: string }; ``` ### Null over sentinels ```ts // Bad: 'none' is not an action. It is the absence of one. type PendingAction = 'none' | 'confirm-address' | 'select-shipping'; // Good type PendingAction = 'confirm-address' | 'select-shipping'; type OrderState = { pendingAction: PendingAction | null }; ``` ### Phased composition over grab-bags ```ts // Bad: 20+ optional fields. Every consumer does profile.firstName ?? defaults.firstName. type UserProfile = { firstName?: string; lastName?: string; email?: string; phone?: string; company?: string; jobTitle?: string; billingAddress?: string; cardLast4?: string; // ... more }; // Good: check one optional instead of eight. When identity exists, all its fields are present. type UserProfile = { identity?: { firstName: string; lastName: string; email: string }; billing?: { address: string; cardLast4: string }; }; ``` ### Brand identical primitives ```ts // Bad: a function accepting UserId will happily take a TeamId. type UserId = string; type TeamId = string; // Good type UserId = string & { readonly __brand: 'user' }; type TeamId = string & { readonly __brand: 'team' }; ``` ### Delete dead variants If a type has a variant that is never constructed, delete it. A `status: 'open' | 'completed'` where `'completed'` is never set suggests a lifecycle that does not exist. --- ## 3. Enforce function contracts ### Never add side effects to a pure function When a pure function quietly gains a side effect, every callsite inherits behavior it did not ask for. If a function needs side effects, extract them into a separate orchestrator. - **Semantic functions** are small, pure, and self-describing. All inputs in, all outputs out, no hidden effects. - **Pragmatic functions** are orchestrators. They compose semantic functions and contain messy domain glue. ### Before: semantic function that grew into a pragmatic one ```ts function handleWebhook(state, eventType, payload, receivedAt): WebhookResult { switch (eventType) { case 'payment.captured': { const receipt = buildReceipt(payload); // data creation state.order.paymentStatus = 'captured'; // mutation state.order.receipt = receipt; // mutation state.user.lastPurchaseAt = receivedAt; // mutation state.user.lifetimeSpend += receipt.amount; // mutation clearPendingAction(state); // side effect const notifications = buildPaymentNotifs(state); // notification state.notifications.push(...notifications); // mutation recalculateDashboard(state); // derivation return { state, output: receipt, notifications }; } // ... 12 more cases, same pattern } } ``` ### After: composed from semantic functions ```ts function handlePaymentCaptured(state: AppState, payload: PaymentPayload, receivedAt: string): WebhookResult { const receipt = buildReceipt(payload); const updatedOrder = applyPaymentToOrder(state.order, receipt); const updatedUser = applyPurchaseToUser(state.user, receipt, receivedAt); const notifications = buildPaymentNotifs(state, receipt); return { state: { ...state, order: updatedOrder, user: updatedUser }, output: receipt, notifications, }; } ``` ### Pick a mutation contract If a function mutates its input, return `void`. If it returns a value, clone first. Never mutate the input and return the same reference — callers cannot tell whether to use the return value or the original. ```ts // Bad: mutates AND returns the same object function withPendingAction(state: AppState, action: string): AppState { state.pendingAction = action; return state; } // Good: mutate, return void function applyPendingAction(state: AppState, action: string): void { state.pendingAction = action; } // Also good: clone, return new function withPendingAction(state: AppState, action: string): AppState { return { ...state, pendingAction: action }; } ``` --- ## 4. Data over procedure When a long if-chain returns a similar shape from every branch, the logic is a lookup table encoded as code. Convert it to data. ### Before: if-chain ```ts function getStepInfo(step: string): StepInfo | null { if (step === 'verify-email') { return { tone: 'action', title: 'Verify your email', detail: 'Check your inbox' }; } if (step === 'add-payment') { return { tone: 'action', title: 'Add payment method', detail: 'Enter card details' }; } if (step === 'review-order') { return { tone: 'confirm', title: 'Review your order', detail: 'Check totals' }; } // ... 10 more branches return null; } ``` ### After: declarative table ```ts const STEP_INFO: Array<{ match: (step: string) => boolean; info: StepInfo; }> = [ { match: (s) => s === 'verify-email', info: { tone: 'action', title: 'Verify your email', detail: 'Check your inbox' } }, { match: (s) => s === 'add-payment', info: { tone: 'action', title: 'Add payment method', detail: 'Enter card details' } }, { match: (s) => s === 'review-order', info: { tone: 'confirm', title: 'Review your order', detail: 'Check totals' } }, // data, not code ]; function getStepInfo(step: string): StepInfo | null { return STEP_INFO.find(({ match }) => match(step))?.info ?? null; } ``` Easier to scan, extend, and test. An agent adding a new step adds a data entry, not a branch in a control flow. ### When NOT to convert If branches have different control flow — not just different return values — keep them as code. A table maps inputs to outputs; it cannot express "call X then conditionally call Y." --- ## Checklist When reviewing code (yours or an agent's): - [ ] Can any new field be derived from existing state? Derive it. - [ ] Is mutable state visible beyond its minimal scope? Trap it in a closure. - [ ] Do any models allow field combinations that should be impossible? Discriminated union. - [ ] Are there sentinel values (`'none'`, `'unknown'`, `-1`) where `null` would work? Use null. - [ ] Are there identical type aliases for different domain concepts? Brand or eliminate. - [ ] Does any function both mutate its input and return it? Pick one contract. - [ ] Has a semantic function grown side effects? Extract them. - [ ] Is there an if-chain where every branch returns a similar shape? Make it a table. - [ ] Are there dead type variants never constructed? Delete them.

相关技能

Agent 工作流

技能创建者Skill Creator

创造有效技能指南。 当用户想创造出新的技能(或更新现有的技能),以专业知识,工作流程,或工具集成来扩展克洛德的能力时,应该使用这种技能.

Agent 工作流

克劳德胡布Clawdhub

使用ClawdHub CLI搜索,安装,更新并发布从taladhub.com的代理技能. 需要获取苍蝇上的新技能时使用,将安装的技能同步到最新版本或特定版本,或者发布 npm-instainddhub CLI 的新/更新的技能文件夹.

Agent 工作流

团队指挥Agent Team Orchestration

管弦乐团多代理团队,任务设定周期,交接协议,审查工作流程. 使用时间: (1)建立2+特派员队伍,具有不同专业,(2)确定任务路线和生命周期(收录框_ spec_建设_审查_完成),(3)在特派员之间制定交接协议,(4)建立审查和质量关口,(5)管理特派员之间的交流和文物共享.

Agent 工作流

超级力量Superpowers

Spec-first,TDD,子代理驱动的软件开发工作流程. 当:(1)构建任何新功能或应用——触发脑暴_计划_子代理执行回路,(2)调试出一个bug或测试失败——触发系统性的根起过程,(3)用户说"让我们构建","帮助我计划","我想添加X",或"这个被打破",(4)完成一个功…