[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XCA9AA5KDER085677D8V48":3,"skill-same:01M6XCA9AA5KDER085677D8V48":83},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":34,"publishedAt":55,"linkOnly":56,"markdownContent":57,"markdownBody":58,"frontMatter":59,"refName":60,"commitSha":61,"skillDirectory":62,"forksCount":63,"repositoryHtmlUrl":64,"repositoryHomepageUrl":6,"ownerHtmlUrl":65,"ownerBlogUrl":66,"ownerDescription":67,"updatedAt":68,"install":69,"resources":82},"01M6XCA9AA5KDER085677D8V48","spec-writing",null,"How to write a design spec that downstream agents can implement and test against. Covers four composable methods: Use Case for interaction flows, Given-When-Then for behavioral scenarios, Design by Contract for function-level guarantees, and plain AC for simple tasks. Use when writing a spec, reviewing spec completeness, or when a dev\u002Ftest-gen agent reports the spec is too vague. Trigger on: spec, 设计文档, 需求文档, 接口设计, write spec, design doc, acceptance criteria, use case, given when then, design by contract, 契约, 用例.","Lincyaw\u002Fworkbuddy","Lincyaw","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F53161583?v=4","01M6XC87YTNETZ3AARKMHXJYJR","Aoyang Fang","01M6XC882ZSY5XBX2W6S88AGNH",2,"Apache-2.0","https:\u002F\u002Fgithub.com\u002FLincyaw\u002Fworkbuddy\u002Fblob\u002Fmain\u002Fdeploy\u002Fagentm\u002Fskills\u002Fspec-writing\u002FSKILL.md",[18,23,26,31],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",{"categoryNo":32,"categoryName":33,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000037","律师与法务",[35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54],"writing","spec","spec writing","when","design","vague","for","can","flows","how to","trigger","contract","methods","use","given when then","acceptance","dev","tasks","composable","implement","2026-11-10 10:12:30",false,"---\nname: spec-writing\ndescription: >\n  How to write a design spec that downstream agents can implement and\n  test against. Covers four composable methods: Use Case for interaction\n  flows, Given-When-Then for behavioral scenarios, Design by Contract\n  for function-level guarantees, and plain AC for simple tasks. Use when\n  writing a spec, reviewing spec completeness, or when a dev\u002Ftest-gen\n  agent reports the spec is too vague. Trigger on: spec, 设计文档,\n  需求文档, 接口设计, write spec, design doc, acceptance criteria,\n  use case, given when then, design by contract, 契约, 用例.\n---\n\n# Spec Writing Guide\n\nA spec is the single source of truth for what gets built. Tests are\nderived from it. Code is implemented against it. Review verifies\nconformance to it.\n\nThis guide covers four composable spec methods. They are not\nalternatives — most real tasks use two or three together. The choice\ndepends on what kind of thing you're specifying.\n\n## Choosing and combining methods\n\n| What you're specifying | Primary method | Often combined with |\n|---|---|---|\n| Multi-step interaction (registration, payment, approval flow) | **Use Case** | GWT for key scenarios, Contract for internal functions |\n| API endpoint \u002F event handler \u002F state transition | **Given-When-Then** | Contract for the functions behind the API |\n| Library function \u002F data structure \u002F algorithm | **Design by Contract** | GWT for integration scenarios |\n| Simple bug fix \u002F config change \u002F one-liner | **Plain AC** | — |\n| New module (API + internal logic + data model) | **All three combined** | Use Case at system level, GWT at API level, Contract at function level |\n\nThese methods describe the SAME system at different granularities.\nUse Case describes the flow between actors. GWT describes observable\nbehavior at the API boundary. Contract describes the guarantees of\neach function. They nest naturally:\n\n```\nUse Case: \"User submits order\"\n  ├── Step 3: \"system validates inventory\"\n  │     └── GWT scenario: Given item in stock, When order submitted, Then stock decremented\n  │           └── Contract: decrement_stock(item_id, qty)\n  │                 precondition:  qty > 0, item exists\n  │                 postcondition: stock[item_id] decreased by qty\n  │                 invariant:     stock[item_id] >= 0\n  ├── Extension 3a: \"item out of stock\"\n  │     └── GWT scenario: Given item stock = 0, When order submitted, Then 409 returned\n  ...\n```\n\n## Method 1: Use Case\n\nBest for: multi-step flows involving actors (users, systems,\nexternal services). The main value is systematic enumeration of\nexception branches — each Extension is a test case.\n\n### Template\n\n```markdown\n## Use Case: \u003CName>\n\n**Actor**: \u003Cwho initiates>\n**Preconditions**: \u003Cwhat must be true before this flow starts>\n\n### Main Success Scenario\n1. \u003CActor> does \u003Caction>\n2. System does \u003Cresponse>\n3. \u003CActor> does \u003Caction>\n4. System does \u003Cresponse>\n...\n\n### Extensions\n\u003Cstep>a. \u003Ccondition> →\n    1. System does \u003Calternative response>\n    2. \u003Coutcome>\n\u003Cstep>b. \u003Ccondition> →\n    1. System does \u003Calternative response>\n    2. \u003Coutcome>\n\n**Postconditions**: \u003Cwhat is true after successful completion>\n```\n\n### Example\n\n```markdown\n## Use Case: Create API Key\n\n**Actor**: Authenticated admin user\n**Preconditions**: User has admin role, API key quota not exhausted\n\n### Main Success Scenario\n1. Admin sends POST \u002Fapi\u002Fkeys with { \"name\": \"prod-key\", \"scopes\": [\"read\", \"write\"] }\n2. System validates scopes against allowed set\n3. System generates a 256-bit random key\n4. System stores key hash (not plaintext) with metadata\n5. System returns 201 { \"id\": \"key_xxx\", \"secret\": \"ak_...\", \"scopes\": [\"read\", \"write\"] }\n6. System logs key creation event\n\n### Extensions\n2a. Scope not in allowed set →\n    1. System returns 400 { \"error\": \"invalid scope: 'admin'\" }\n    2. No key created\n2b. Scopes array empty →\n    1. System returns 400 { \"error\": \"at least one scope required\" }\n3a. Random generation fails (entropy exhaustion) →\n    1. System returns 503 { \"error\": \"try again\" }\n    2. No key created, no partial state\n5a. Admin has reached key quota (10) →\n    1. System returns 429 { \"error\": \"key quota exhausted\" }\n    2. Checked BEFORE key generation (no wasted entropy)\n*a. Database unavailable at any step →\n    1. System returns 503\n    2. No partial state (transaction rolled back)\n\n**Postconditions**: New key exists in DB (hashed), plaintext returned\nexactly once, audit log entry written\n```\n\nEach extension (2a, 2b, 3a, 5a, *a) maps directly to a test case.\nThe asterisk `*a` means \"at any step\" — this produces a test that\ninjects a DB failure and verifies no partial state.\n\n### When to skip\n\nIf the task has no multi-step flow (e.g., \"add a pure function that\nparses config files\"), Use Case adds nothing. Use Contract instead.\n\n## Method 2: Given-When-Then (GWT)\n\nBest for: describing observable behavior at system boundaries —\nAPI responses, state transitions, event emissions. Each scenario\nis a self-contained test case.\n\n### Template\n\n```markdown\n## Scenario: \u003Cdescriptive name>\n  Given \u003Cinitial state \u002F precondition>\n  And \u003Cadditional setup>\n  When \u003Caction \u002F trigger>\n  Then \u003Cexpected outcome>\n  And \u003Cadditional assertions>\n```\n\n### Example\n\n```markdown\n## Scenario: Rate limit exceeded\n  Given a RateLimiter with max_requests=3, window_seconds=10\n  And client \"c1\" has made 3 requests in the last 5 seconds\n  When client \"c1\" makes a 4th request\n  Then check() returns False\n  And get_remaining(\"c1\") returns 0\n\n## Scenario: Window expiry restores quota\n  Given a RateLimiter with max_requests=3, window_seconds=10\n  And client \"c1\" has exhausted their quota\n  When 10 seconds elapse (via injected clock)\n  Then check(\"c1\") returns True\n  And get_remaining(\"c1\") returns 3\n\n## Scenario: Independent client counters\n  Given a RateLimiter with max_requests=1, window_seconds=60\n  And client \"c1\" has exhausted their quota\n  When client \"c2\" calls check()\n  Then check(\"c2\") returns True\n  And get_remaining(\"c1\") is still 0\n```\n\n### When to skip\n\nFor internal functions where the contract (pre\u002Fpost\u002Finvariant) is\nmore precise than a scenario. GWT is great for \"from the outside it\nlooks like...\" but awkward for \"this internal invariant always holds.\"\n\n## Method 3: Design by Contract\n\nBest for: library functions, data structures, algorithms. Each method\ngets three things: what the caller must guarantee (precondition), what\nthe method guarantees back (postcondition), and what is always true\n(invariant).\n\n### Template\n\n```markdown\n## Contract: \u003CClass or Module>\n\n### Invariants\n- \u003Cproperty that holds after every public method call>\n\n### \u003Cmethod signature>\n- **Precondition**: \u003Cwhat must be true when called>\n- **Postcondition**: \u003Cwhat is guaranteed after return>\n- **Raises**: \u003Cerror type>: \u003Cwhen>\n```\n\n### Example\n\n```markdown\n## Contract: OrderBook\n\n### Invariants\n- total_value = sum(order.price * order.qty for order in orders)\n- len(orders) == count\n- All orders have unique IDs\n- No order has qty \u003C= 0\n\n### add_order(order: Order) -> str\n- **Precondition**: order.qty > 0, order.price >= 0\n- **Postcondition**: returned ID is in orders, count increased by 1,\n  total_value increased by order.price * order.qty\n- **Raises**: ValueError: qty \u003C= 0 or price \u003C 0\n- **Raises**: DuplicateError: order.id already exists\n\n### cancel_order(order_id: str) -> Order\n- **Precondition**: order_id exists in orders\n- **Postcondition**: order_id no longer in orders, count decreased by 1,\n  total_value decreased by cancelled order's value, returned order\n  matches the cancelled one\n- **Raises**: KeyError: order_id not found\n\n### get_total_value() -> Decimal\n- **Precondition**: none\n- **Postcondition**: returns current total_value (same as invariant)\n```\n\n**Why invariants matter for testing**: the invariant is the most\npowerful test oracle. Instead of testing specific inputs and outputs,\nyou test that the invariant holds after EVERY operation, with random\nsequences of operations (property-based testing). If `total_value`\never diverges from the sum of order values, something is broken —\nregardless of which specific sequence of adds and cancels caused it.\n\n### When to skip\n\nFor tasks that are purely about wiring (configuration, routing,\ndeployment) where there's no computation to contract over.\n\n## Method 4: Plain AC\n\nFor simple tasks where the other three are overhead. Just numbered\nconditions.\n\n```markdown\n## Acceptance Criteria\n\nAC-1: Config file `logging.yaml` is loaded at startup\nAC-2: If `logging.yaml` is missing, default to INFO level\nAC-3: Log level change takes effect without restart (SIGHUP handler)\n```\n\nUse when:\n- Bug fix with obvious scope\n- Config change\n- One function, few edge cases\n- The task is small enough that a 3-line spec is complete\n\n## Composing methods in practice\n\nA real spec for a new module might look like:\n\n```markdown\n# Spec: Rate Limiting Service\n\n## Use Case: API Rate Limiting\n\n**Actor**: API client (authenticated via API key)\n**Preconditions**: Client has a valid API key\n\n### Main Success Scenario\n1. Client sends request with API key in header\n2. System looks up rate limit config for the key's tier\n3. System checks current request count against limit\n4. Request count is below limit → request proceeds\n5. System increments counter, sets\u002Fextends TTL\n\n### Extensions\n3a. No rate limit config for this tier →\n    1. System applies default limit (100 req\u002Fmin)\n4a. Request count >= limit →\n    1. System returns 429 with Retry-After header\n    2. Counter is NOT incremented (rejected requests don't count)\n*a. Redis unavailable →\n    1. System allows request (fail-open policy)\n    2. System logs warning with client_id and timestamp\n\n## Scenarios (Given-When-Then)\n\n### Scenario: Retry-After header value\n  Given client has exceeded their rate limit\n  And their window resets in 23 seconds\n  When they receive a 429 response\n  Then the Retry-After header value is \"23\"\n\n### Scenario: Tier upgrade takes effect immediately\n  Given client \"c1\" is on tier \"free\" (100 req\u002Fmin)\n  And client \"c1\" has made 80 requests this minute\n  When client \"c1\" is upgraded to tier \"pro\" (1000 req\u002Fmin)\n  Then client \"c1\"'s next request succeeds (80 \u003C 1000)\n\n## Contracts\n\n### RateLimiter(redis: Redis, config: TierConfig)\n\n#### Invariants\n- request_count[client] >= 0 for all clients\n- request_count[client] \u003C= tier_limit[client.tier] after any\n  successful check() (rejected requests don't increment)\n\n#### check(client_id: str) -> RateLimitResult\n- **Precondition**: client_id is non-empty\n- **Postcondition (allowed)**: request_count[client_id] incremented\n  by 1, returns RateLimitResult(allowed=True, remaining=N)\n- **Postcondition (rejected)**: request_count unchanged, returns\n  RateLimitResult(allowed=False, retry_after=seconds)\n- **Raises**: ValueError: client_id is empty\n\n### Design Notes\n- Redis INCR + EXPIRE for atomic counter management\n- Fail-open on Redis failure (availability > strictness)\n- Counter key format: `rl:{client_id}:{minute_bucket}`\n- TTL = window size + 1 second (avoid off-by-one on bucket boundary)\n```\n\nThis spec uses all three methods at appropriate levels: Use Case for\nthe overall flow, GWT for specific behavioral scenarios, Contract\nfor the core function's guarantees.\n\n## Spec completeness checklist\n\nBefore marking a spec as ready for review:\n\n```\n[ ] Every public function\u002Fendpoint has defined input types and\n    return types (Interface)\n[ ] Every error condition has a named error type and trigger\n    condition\n[ ] Every Use Case extension is a specific, named exception path\n    (not \"something goes wrong\")\n[ ] Every GWT scenario has concrete values (not \"some input\")\n[ ] Every Contract has at least one invariant\n[ ] Preconditions are stated (what the caller must guarantee)\n[ ] Out-of-scope is explicit (what this spec does NOT cover)\n[ ] No subjective criteria (\"clean\", \"fast\", \"good\") without\n    quantification\n```\n","# Spec Writing Guide\n\nA spec is the single source of truth for what gets built. Tests are\nderived from it. Code is implemented against it. Review verifies\nconformance to it.\n\nThis guide covers four composable spec methods. They are not\nalternatives — most real tasks use two or three together. The choice\ndepends on what kind of thing you're specifying.\n\n## Choosing and combining methods\n\n| What you're specifying | Primary method | Often combined with |\n|---|---|---|\n| Multi-step interaction (registration, payment, approval flow) | **Use Case** | GWT for key scenarios, Contract for internal functions |\n| API endpoint \u002F event handler \u002F state transition | **Given-When-Then** | Contract for the functions behind the API |\n| Library function \u002F data structure \u002F algorithm | **Design by Contract** | GWT for integration scenarios |\n| Simple bug fix \u002F config change \u002F one-liner | **Plain AC** | — |\n| New module (API + internal logic + data model) | **All three combined** | Use Case at system level, GWT at API level, Contract at function level |\n\nThese methods describe the SAME system at different granularities.\nUse Case describes the flow between actors. GWT describes observable\nbehavior at the API boundary. Contract describes the guarantees of\neach function. They nest naturally:\n\n```\nUse Case: \"User submits order\"\n  ├── Step 3: \"system validates inventory\"\n  │     └── GWT scenario: Given item in stock, When order submitted, Then stock decremented\n  │           └── Contract: decrement_stock(item_id, qty)\n  │                 precondition:  qty > 0, item exists\n  │                 postcondition: stock[item_id] decreased by qty\n  │                 invariant:     stock[item_id] >= 0\n  ├── Extension 3a: \"item out of stock\"\n  │     └── GWT scenario: Given item stock = 0, When order submitted, Then 409 returned\n  ...\n```\n\n## Method 1: Use Case\n\nBest for: multi-step flows involving actors (users, systems,\nexternal services). The main value is systematic enumeration of\nexception branches — each Extension is a test case.\n\n### Template\n\n```markdown\n## Use Case: \u003CName>\n\n**Actor**: \u003Cwho initiates>\n**Preconditions**: \u003Cwhat must be true before this flow starts>\n\n### Main Success Scenario\n1. \u003CActor> does \u003Caction>\n2. System does \u003Cresponse>\n3. \u003CActor> does \u003Caction>\n4. System does \u003Cresponse>\n...\n\n### Extensions\n\u003Cstep>a. \u003Ccondition> →\n    1. System does \u003Calternative response>\n    2. \u003Coutcome>\n\u003Cstep>b. \u003Ccondition> →\n    1. System does \u003Calternative response>\n    2. \u003Coutcome>\n\n**Postconditions**: \u003Cwhat is true after successful completion>\n```\n\n### Example\n\n```markdown\n## Use Case: Create API Key\n\n**Actor**: Authenticated admin user\n**Preconditions**: User has admin role, API key quota not exhausted\n\n### Main Success Scenario\n1. Admin sends POST \u002Fapi\u002Fkeys with { \"name\": \"prod-key\", \"scopes\": [\"read\", \"write\"] }\n2. System validates scopes against allowed set\n3. System generates a 256-bit random key\n4. System stores key hash (not plaintext) with metadata\n5. System returns 201 { \"id\": \"key_xxx\", \"secret\": \"ak_...\", \"scopes\": [\"read\", \"write\"] }\n6. System logs key creation event\n\n### Extensions\n2a. Scope not in allowed set →\n    1. System returns 400 { \"error\": \"invalid scope: 'admin'\" }\n    2. No key created\n2b. Scopes array empty →\n    1. System returns 400 { \"error\": \"at least one scope required\" }\n3a. Random generation fails (entropy exhaustion) →\n    1. System returns 503 { \"error\": \"try again\" }\n    2. No key created, no partial state\n5a. Admin has reached key quota (10) →\n    1. System returns 429 { \"error\": \"key quota exhausted\" }\n    2. Checked BEFORE key generation (no wasted entropy)\n*a. Database unavailable at any step →\n    1. System returns 503\n    2. No partial state (transaction rolled back)\n\n**Postconditions**: New key exists in DB (hashed), plaintext returned\nexactly once, audit log entry written\n```\n\nEach extension (2a, 2b, 3a, 5a, *a) maps directly to a test case.\nThe asterisk `*a` means \"at any step\" — this produces a test that\ninjects a DB failure and verifies no partial state.\n\n### When to skip\n\nIf the task has no multi-step flow (e.g., \"add a pure function that\nparses config files\"), Use Case adds nothing. Use Contract instead.\n\n## Method 2: Given-When-Then (GWT)\n\nBest for: describing observable behavior at system boundaries —\nAPI responses, state transitions, event emissions. Each scenario\nis a self-contained test case.\n\n### Template\n\n```markdown\n## Scenario: \u003Cdescriptive name>\n  Given \u003Cinitial state \u002F precondition>\n  And \u003Cadditional setup>\n  When \u003Caction \u002F trigger>\n  Then \u003Cexpected outcome>\n  And \u003Cadditional assertions>\n```\n\n### Example\n\n```markdown\n## Scenario: Rate limit exceeded\n  Given a RateLimiter with max_requests=3, window_seconds=10\n  And client \"c1\" has made 3 requests in the last 5 seconds\n  When client \"c1\" makes a 4th request\n  Then check() returns False\n  And get_remaining(\"c1\") returns 0\n\n## Scenario: Window expiry restores quota\n  Given a RateLimiter with max_requests=3, window_seconds=10\n  And client \"c1\" has exhausted their quota\n  When 10 seconds elapse (via injected clock)\n  Then check(\"c1\") returns True\n  And get_remaining(\"c1\") returns 3\n\n## Scenario: Independent client counters\n  Given a RateLimiter with max_requests=1, window_seconds=60\n  And client \"c1\" has exhausted their quota\n  When client \"c2\" calls check()\n  Then check(\"c2\") returns True\n  And get_remaining(\"c1\") is still 0\n```\n\n### When to skip\n\nFor internal functions where the contract (pre\u002Fpost\u002Finvariant) is\nmore precise than a scenario. GWT is great for \"from the outside it\nlooks like...\" but awkward for \"this internal invariant always holds.\"\n\n## Method 3: Design by Contract\n\nBest for: library functions, data structures, algorithms. Each method\ngets three things: what the caller must guarantee (precondition), what\nthe method guarantees back (postcondition), and what is always true\n(invariant).\n\n### Template\n\n```markdown\n## Contract: \u003CClass or Module>\n\n### Invariants\n- \u003Cproperty that holds after every public method call>\n\n### \u003Cmethod signature>\n- **Precondition**: \u003Cwhat must be true when called>\n- **Postcondition**: \u003Cwhat is guaranteed after return>\n- **Raises**: \u003Cerror type>: \u003Cwhen>\n```\n\n### Example\n\n```markdown\n## Contract: OrderBook\n\n### Invariants\n- total_value = sum(order.price * order.qty for order in orders)\n- len(orders) == count\n- All orders have unique IDs\n- No order has qty \u003C= 0\n\n### add_order(order: Order) -> str\n- **Precondition**: order.qty > 0, order.price >= 0\n- **Postcondition**: returned ID is in orders, count increased by 1,\n  total_value increased by order.price * order.qty\n- **Raises**: ValueError: qty \u003C= 0 or price \u003C 0\n- **Raises**: DuplicateError: order.id already exists\n\n### cancel_order(order_id: str) -> Order\n- **Precondition**: order_id exists in orders\n- **Postcondition**: order_id no longer in orders, count decreased by 1,\n  total_value decreased by cancelled order's value, returned order\n  matches the cancelled one\n- **Raises**: KeyError: order_id not found\n\n### get_total_value() -> Decimal\n- **Precondition**: none\n- **Postcondition**: returns current total_value (same as invariant)\n```\n\n**Why invariants matter for testing**: the invariant is the most\npowerful test oracle. Instead of testing specific inputs and outputs,\nyou test that the invariant holds after EVERY operation, with random\nsequences of operations (property-based testing). If `total_value`\never diverges from the sum of order values, something is broken —\nregardless of which specific sequence of adds and cancels caused it.\n\n### When to skip\n\nFor tasks that are purely about wiring (configuration, routing,\ndeployment) where there's no computation to contract over.\n\n## Method 4: Plain AC\n\nFor simple tasks where the other three are overhead. Just numbered\nconditions.\n\n```markdown\n## Acceptance Criteria\n\nAC-1: Config file `logging.yaml` is loaded at startup\nAC-2: If `logging.yaml` is missing, default to INFO level\nAC-3: Log level change takes effect without restart (SIGHUP handler)\n```\n\nUse when:\n- Bug fix with obvious scope\n- Config change\n- One function, few edge cases\n- The task is small enough that a 3-line spec is complete\n\n## Composing methods in practice\n\nA real spec for a new module might look like:\n\n```markdown\n# Spec: Rate Limiting Service\n\n## Use Case: API Rate Limiting\n\n**Actor**: API client (authenticated via API key)\n**Preconditions**: Client has a valid API key\n\n### Main Success Scenario\n1. Client sends request with API key in header\n2. System looks up rate limit config for the key's tier\n3. System checks current request count against limit\n4. Request count is below limit → request proceeds\n5. System increments counter, sets\u002Fextends TTL\n\n### Extensions\n3a. No rate limit config for this tier →\n    1. System applies default limit (100 req\u002Fmin)\n4a. Request count >= limit →\n    1. System returns 429 with Retry-After header\n    2. Counter is NOT incremented (rejected requests don't count)\n*a. Redis unavailable →\n    1. System allows request (fail-open policy)\n    2. System logs warning with client_id and timestamp\n\n## Scenarios (Given-When-Then)\n\n### Scenario: Retry-After header value\n  Given client has exceeded their rate limit\n  And their window resets in 23 seconds\n  When they receive a 429 response\n  Then the Retry-After header value is \"23\"\n\n### Scenario: Tier upgrade takes effect immediately\n  Given client \"c1\" is on tier \"free\" (100 req\u002Fmin)\n  And client \"c1\" has made 80 requests this minute\n  When client \"c1\" is upgraded to tier \"pro\" (1000 req\u002Fmin)\n  Then client \"c1\"'s next request succeeds (80 \u003C 1000)\n\n## Contracts\n\n### RateLimiter(redis: Redis, config: TierConfig)\n\n#### Invariants\n- request_count[client] >= 0 for all clients\n- request_count[client] \u003C= tier_limit[client.tier] after any\n  successful check() (rejected requests don't increment)\n\n#### check(client_id: str) -> RateLimitResult\n- **Precondition**: client_id is non-empty\n- **Postcondition (allowed)**: request_count[client_id] incremented\n  by 1, returns RateLimitResult(allowed=True, remaining=N)\n- **Postcondition (rejected)**: request_count unchanged, returns\n  RateLimitResult(allowed=False, retry_after=seconds)\n- **Raises**: ValueError: client_id is empty\n\n### Design Notes\n- Redis INCR + EXPIRE for atomic counter management\n- Fail-open on Redis failure (availability > strictness)\n- Counter key format: `rl:{client_id}:{minute_bucket}`\n- TTL = window size + 1 second (avoid off-by-one on bucket boundary)\n```\n\nThis spec uses all three methods at appropriate levels: Use Case for\nthe overall flow, GWT for specific behavioral scenarios, Contract\nfor the core function's guarantees.\n\n## Spec completeness checklist\n\nBefore marking a spec as ready for review:\n\n```\n[ ] Every public function\u002Fendpoint has defined input types and\n    return types (Interface)\n[ ] Every error condition has a named error type and trigger\n    condition\n[ ] Every Use Case extension is a specific, named exception path\n    (not \"something goes wrong\")\n[ ] Every GWT scenario has concrete values (not \"some input\")\n[ ] Every Contract has at least one invariant\n[ ] Preconditions are stated (what the caller must guarantee)\n[ ] Out-of-scope is explicit (what this spec does NOT cover)\n[ ] No subjective criteria (\"clean\", \"fast\", \"good\") without\n    quantification\n```\n",{"name":5,"description":7},"main","b9778c800f442939b879c9a556dfb8d6f4a40971","deploy\u002Fagentm\u002Fskills\u002Fspec-writing",1,"https:\u002F\u002Fgithub.com\u002FLincyaw\u002Fworkbuddy","https:\u002F\u002Fgithub.com\u002FLincyaw","http:\u002F\u002Fprofile.aoyangfang.top\u002F","Student in The Chinese University of Hong Kong, Shenzhen","2026-06-08 13:33:51",{"source":8,"skillName":5,"commands":70,"prompt":80,"zipAvailable":81},[71,74,77],{"tool":72,"command":73},"npx","npx skills add Lincyaw\u002Fworkbuddy --skill spec-writing",{"tool":75,"command":76},"bunx","bunx skills add Lincyaw\u002Fworkbuddy --skill spec-writing",{"tool":78,"command":79},"pnpm","pnpm dlx skills add Lincyaw\u002Fworkbuddy --skill spec-writing","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FLincyaw\u002Fworkbuddy\u002Fblob\u002Fmain\u002Fdeploy\u002Fagentm\u002Fskills\u002Fspec-writing\u002FSKILL.md",true,[],[84,110,144,169,188,211],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":90,"publishedAt":55},"01M6XCBSW1MMEAWEF38VX9NH3E","workbuddy-guide","Explain and operate workbuddy: what it is, which command to reach for, and where to find deeper references. **Trigger whenever the user mentions 'workbuddy' in any context** — including operational requests (register\u002Fonboard a repo, add\u002Fbind a worker, 注册仓库, 加仓库, 接入新仓库, onboard repo, set up worker), explanatory questions (how to use workbuddy, workbuddy guide, teach me workbuddy, 怎么用workbuddy, 使用指南), deployment\u002Frunning\u002Foperating\u002Fdebugging workbuddy, and questions about how issues\u002Ftasks are scheduled or how dependencies between issues work (depends_on, blocked, dependency gate, 依赖, 调度, 排队, '为什么没派发'), and questions about parallel-development workflows (rollouts:N fan-out, synthesize\u002Fsynthesis reduce step, cherry-pick, '并行开发', '多个方案', 'rollout'). Invoke this skill before running workbuddy CLI commands so the documented onboarding flow (coordinator register + worker repos add) and the dependency\u002Fscheduling model aren't re-derived from scratch.","https:\u002F\u002Fgithub.com\u002FLincyaw\u002Fworkbuddy\u002Fblob\u002Fmain\u002Fplugins\u002Fworkbuddy\u002Fskills\u002Fworkbuddy-guide\u002FSKILL.md",[],[91,92,93,94,95,96,97,98,99,100,101,102,44,103,104,105,106,107,108,109],"guide","workbuddy guide","debugging","issues","SET","model","pick","up","rollouts","deeper","worker","bind","operating","coordinator","re","operate","which","scheduled","invoke",{"skillNo":111,"name":112,"nameZh":6,"description":113,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":125,"publishedAt":55},"01M6XCBM7B95PB3Q6PHZ3TCM7F","web-debug","Validate the workbuddy webui (SPA + auth + JSON API) end-to-end on the same machine workbuddy runs on, using browser-use's headless harness. Use when the user says 'verify the frontend', 'test the SPA', 'check dashboard loads', '前端验证', '验下 webui', or after merging changes that touch internal\u002Fwebui, web\u002F, or the auth\u002Fcookie path.","https:\u002F\u002Fgithub.com\u002FLincyaw\u002Fworkbuddy\u002Fblob\u002Fmain\u002Fplugins\u002Fworkbuddy\u002Fskills\u002Fweb-debug\u002FSKILL.md",[116,119,122],{"categoryNo":117,"categoryName":118,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":120,"categoryName":121,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":123,"categoryName":124,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000030","开发工程师",[126,127,128,129,130,131,132,133,134,48,135,136,137,138,139,140,141,142,143,38],"web","debug","web debug","frontend","auth","browser","harness","api","check","webui","spa","end","machine","touch","loads","headless","browser-use","using browser",{"skillNo":145,"name":146,"nameZh":6,"description":147,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":148,"categories":149,"keywords":153,"publishedAt":55},"01M6XCBFR925WJSWEYVHGSTT56","setup-repo","Configure a GitHub repository for workbuddy orchestration — creates labels, the two canonical agent configs (dev + review), workflow definitions, issue templates, and config.yaml. Use when the user says 'set up repo', 'configure repo for workbuddy', 'initialize workbuddy', '配置仓库', '初始化workbuddy', or wants to onboard a new repo.","https:\u002F\u002Fgithub.com\u002FLincyaw\u002Fworkbuddy\u002Fblob\u002Fmain\u002Fplugins\u002Fworkbuddy\u002Fskills\u002Fsetup-repo\u002FSKILL.md",[150],{"categoryNo":151,"categoryName":152,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[154,155,156,157,158,159,160,51,161,95,162,163,98,164,165,166,167,41,168,38],"setup","setup repo","repo","review","orchestration","workflow","config","repository","configure","initialize","issue","new repo","初始化","canonical","labels",{"skillNo":170,"name":171,"nameZh":6,"description":172,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":173,"categories":174,"keywords":175,"publishedAt":55},"01M6XCBAEPZQTAS5FZYCPDEGAG","pipeline-monitor","Monitor the workbuddy agent pipeline, detect stuck issues, zombie processes, and missed redispatches. Works for both the 3-unit bundle (supervisor + coordinator + worker) and single-process `serve`. Use when the user says 'monitor the pipeline', 'check workbuddy status', '监工', '看看跑完了没', or asks to watch issue progress.","https:\u002F\u002Fgithub.com\u002FLincyaw\u002Fworkbuddy\u002Fblob\u002Fmain\u002Fplugins\u002Fworkbuddy\u002Fskills\u002Fpipeline-monitor\u002FSKILL.md",[],[176,177,178,179,94,180,134,48,164,181,101,182,183,184,185,104,186,41,187,38],"monitor","pipeline","pipeline monitor","status","supervisor","missed","watch","progress","detect","unit","agent pipeline","single",{"skillNo":189,"name":190,"nameZh":6,"description":191,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":192,"categories":193,"keywords":195,"publishedAt":55},"01M6XCB5RTYNH7QK9RQ5Z4B6NK","merge-flow","Merge a batch of approved workbuddy PRs into main: order them, resolve conflicts against design intent, validate post-merge health, and report. Use when the user asks to 'merge the batch', 'merge approved PRs', 'integrate finished issues', '批量合并', '处理冲突'; or when the merge-agent is invoked.","https:\u002F\u002Fgithub.com\u002FLincyaw\u002Fworkbuddy\u002Fblob\u002Fmain\u002Fplugins\u002Fworkbuddy\u002Fskills\u002Fmerge-flow\u002FSKILL.md",[194],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[196,197,198,39,199,200,94,201,202,203,48,204,205,206,207,60,208,209,210,38],"merge","flow","merge flow","validate","report","batch","health","user","integrate","resolve conflicts","conflicts","intent","post","design intent","prs",{"skillNo":212,"name":213,"nameZh":6,"description":214,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":215,"categories":216,"keywords":220,"publishedAt":55},"01M6XCB29F96RJ02Z3BXTAVYSB","deploy","Workbuddy deployment topology guide — explains the 3-unit bundle (supervisor + coordinator + worker), the rolling-restart property, and the cutover from the legacy single-process `serve` install. Use when the user says 'how should I deploy workbuddy', 'install workbuddy as a service', 'switch to bundle', 'rolling restart', '部署 workbuddy', '升级到 bundle'.","https:\u002F\u002Fgithub.com\u002FLincyaw\u002Fworkbuddy\u002Fblob\u002Fmain\u002Fplugins\u002Fworkbuddy\u002Fskills\u002Fdeploy\u002FSKILL.md",[217],{"categoryNo":218,"categoryName":219,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000011","运维与部署",[213,221,222,223,180,224,225,91,48,226,227,101,228,229,230,231,185,104,187,38],"deployment","topology","how","process","install","Legacy","switch","service","as","bundle","serve"]