[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M7AFHFSFZVTRE7C15TZAVFDB":3,"skill-same:01M7AFHFSFZVTRE7C15TZAVFDB":51},{"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":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":30,"commitSha":31,"skillDirectory":32,"forksCount":33,"repositoryHtmlUrl":34,"repositoryHomepageUrl":34,"ownerHtmlUrl":35,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":36,"install":37,"resources":50},"01M7AFHFSFZVTRE7C15TZAVFDB","trove-lambda",null,"AWS Lambda handler patterns with CDK infrastructure conventions. Auto-activates when working with Lambda handler or CDK files. Covers handler structure, SQS\u002FSNS parsing, IPC patterns, Powertools logging, and CDK best practices.","bravew\u002Ftrove","bravew","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F10855844?v=4","01M7ACJ91JZSQYHT81XPVYR7VA","Yong","01M7AFERNR92HDPGNGP5QA16DM",10,"MIT","https:\u002F\u002Fgithub.com\u002Fbravew\u002Ftrove\u002Fblob\u002Fmain\u002Fplugins\u002Ftrove-dev\u002F.agents\u002Fskills\u002Ftrove-lambda\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: trove-lambda\ndescription: \"AWS Lambda handler patterns with CDK infrastructure conventions. Auto-activates when working with Lambda handler or CDK files. Covers handler structure, SQS\u002FSNS parsing, IPC patterns, Powertools logging, and CDK best practices.\"\npaths:\n  - \"**\u002Flambda_handler.py\"\n  - \"**\u002Flambda_function.py\"\nmetadata:\n  trove-category: servers-cloud\n  trove-category-label: \"Servers & cloud\"\n---\n\u003C!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->\n\u003C!-- Regenerate: bun run build:skills -->\n\n## Session Init\n\nThis skill ships Trove conventions. Prefer existing project patterns over generic best practices when they conflict.\n\n# AWS Lambda Conventions\n\n## Handler Structure\n\n```python\nfrom aws_lambda_powertools import Logger\nfrom aws_lambda_powertools.utilities.typing import LambdaContext\n\nlogger = Logger()\n\n@logger.inject_lambda_context(log_event=True)\ndef handler(event: dict, context: LambdaContext) -> dict:\n    records = parse_event(event)\n    for record in records:\n        process_record(record)\n    return {\"statusCode\": 200}\n```\n\n## SQS\u002FSNS Event Parsing\n\n```python\ndef parse_sqs_event(event: dict) -> list[dict]:\n    return [json.loads(record[\"body\"]) for record in event[\"Records\"]]\n\ndef parse_sns_via_sqs(event: dict) -> list[dict]:\n    messages = []\n    for record in event[\"Records\"]:\n        body = json.loads(record[\"body\"])\n        message = json.loads(body[\"Message\"])\n        messages.append(message)\n    return messages\n\ndef parse_sns_direct(event: dict) -> list[dict]:\n    # A Lambda subscribed to the topic directly gets no `body`.\n    return [json.loads(record[\"Sns\"][\"Message\"]) for record in event[\"Records\"]]\n```\n\nCheck which shape the trigger delivers before parsing. SNS through SQS puts the\nSNS envelope in `body`. A direct subscription puts it under `Sns`.\n\n## Logging\n\nFollow the same logging rules as trove-python, plus:\n- Use `@logger.inject_lambda_context(log_event=True)` on handlers\n- Use `logger.append_keys()` for request-scoped context\n- Use `logger.exception()` in error handlers (captures stack trace for Sentry)\n- Keep messages static and put values in `extra`:\n  `logger.warning(\"IPC token rejected\", extra={\"source\": source})`\n\n## IPC Pattern (Backend Communication)\n\nInbound: an event that claims to come from the backend is untrusted until its\ntoken matches `BACKEND_IPC_TOKEN`. Check it before parsing the payload or doing\nany work, compare in constant time, and never log the token itself.\n\n```python\nimport hmac\n\ndef ipc_token_valid(presented: str | None) -> bool:\n    expected = os.environ[\"BACKEND_IPC_TOKEN\"]  # injected from Secrets Manager\n    return presented is not None and hmac.compare_digest(presented, expected)\n```\n\nAn HTTP-triggered handler reads it from the `Authorization` header and returns\n401 on a mismatch. A queued event carries it where the backend put it, such as\nan SNS message attribute, and a mismatch is logged and dropped, not retried.\n\nOutbound:\n\n```python\nasync def call_backend(endpoint: str, payload: dict) -> dict:\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            f\"{BACKEND_URL}\u002Fapi\u002Fipc\u002F{endpoint}\",\n            json=payload,\n            headers={\"Authorization\": f\"Bearer {IPC_TOKEN}\"},\n            timeout=30.0,\n        )\n        response.raise_for_status()\n        return response.json()\n```\n\n## CDK Patterns\n\n- One construct per Lambda function\n- Use `PythonFunction` from `@aws-cdk\u002Faws-lambda-python-alpha`\n- Environment variables via CDK `environment` prop, secrets via Secrets Manager\n- Set `memorySize`, `timeout`, and `reservedConcurrentExecutions`\n\n## AI Gotchas\n\n- **Cold starts**: Keep handler imports minimal; use lazy initialization\n- **Timeout**: Default 3s is too low — set explicitly (30s for API, 300s for batch)\n- **Event source**: Always validate event structure before processing\n- **Idempotency**: SQS may deliver messages more than once — design for at-least-once\n","\u003C!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->\n\u003C!-- Regenerate: bun run build:skills -->\n\n## Session Init\n\nThis skill ships Trove conventions. Prefer existing project patterns over generic best practices when they conflict.\n\n# AWS Lambda Conventions\n\n## Handler Structure\n\n```python\nfrom aws_lambda_powertools import Logger\nfrom aws_lambda_powertools.utilities.typing import LambdaContext\n\nlogger = Logger()\n\n@logger.inject_lambda_context(log_event=True)\ndef handler(event: dict, context: LambdaContext) -> dict:\n    records = parse_event(event)\n    for record in records:\n        process_record(record)\n    return {\"statusCode\": 200}\n```\n\n## SQS\u002FSNS Event Parsing\n\n```python\ndef parse_sqs_event(event: dict) -> list[dict]:\n    return [json.loads(record[\"body\"]) for record in event[\"Records\"]]\n\ndef parse_sns_via_sqs(event: dict) -> list[dict]:\n    messages = []\n    for record in event[\"Records\"]:\n        body = json.loads(record[\"body\"])\n        message = json.loads(body[\"Message\"])\n        messages.append(message)\n    return messages\n\ndef parse_sns_direct(event: dict) -> list[dict]:\n    # A Lambda subscribed to the topic directly gets no `body`.\n    return [json.loads(record[\"Sns\"][\"Message\"]) for record in event[\"Records\"]]\n```\n\nCheck which shape the trigger delivers before parsing. SNS through SQS puts the\nSNS envelope in `body`. A direct subscription puts it under `Sns`.\n\n## Logging\n\nFollow the same logging rules as trove-python, plus:\n- Use `@logger.inject_lambda_context(log_event=True)` on handlers\n- Use `logger.append_keys()` for request-scoped context\n- Use `logger.exception()` in error handlers (captures stack trace for Sentry)\n- Keep messages static and put values in `extra`:\n  `logger.warning(\"IPC token rejected\", extra={\"source\": source})`\n\n## IPC Pattern (Backend Communication)\n\nInbound: an event that claims to come from the backend is untrusted until its\ntoken matches `BACKEND_IPC_TOKEN`. Check it before parsing the payload or doing\nany work, compare in constant time, and never log the token itself.\n\n```python\nimport hmac\n\ndef ipc_token_valid(presented: str | None) -> bool:\n    expected = os.environ[\"BACKEND_IPC_TOKEN\"]  # injected from Secrets Manager\n    return presented is not None and hmac.compare_digest(presented, expected)\n```\n\nAn HTTP-triggered handler reads it from the `Authorization` header and returns\n401 on a mismatch. A queued event carries it where the backend put it, such as\nan SNS message attribute, and a mismatch is logged and dropped, not retried.\n\nOutbound:\n\n```python\nasync def call_backend(endpoint: str, payload: dict) -> dict:\n    async with httpx.AsyncClient() as client:\n        response = await client.post(\n            f\"{BACKEND_URL}\u002Fapi\u002Fipc\u002F{endpoint}\",\n            json=payload,\n            headers={\"Authorization\": f\"Bearer {IPC_TOKEN}\"},\n            timeout=30.0,\n        )\n        response.raise_for_status()\n        return response.json()\n```\n\n## CDK Patterns\n\n- One construct per Lambda function\n- Use `PythonFunction` from `@aws-cdk\u002Faws-lambda-python-alpha`\n- Environment variables via CDK `environment` prop, secrets via Secrets Manager\n- Set `memorySize`, `timeout`, and `reservedConcurrentExecutions`\n\n## AI Gotchas\n\n- **Cold starts**: Keep handler imports minimal; use lazy initialization\n- **Timeout**: Default 3s is too low — set explicitly (30s for API, 300s for batch)\n- **Event source**: Always validate event structure before processing\n- **Idempotency**: SQS may deliver messages more than once — design for at-least-once\n",{"name":5,"paths":24,"metadata":27,"description":7},[25,26],"**\u002Flambda_handler.py","**\u002Flambda_function.py",{"trove-category":28,"trove-category-label":29},"servers-cloud","Servers & cloud","main","f88db1f1cf0e0703dd962051ebc142db961d7799","plugins\u002Ftrove-dev\u002F.agents\u002Fskills\u002Ftrove-lambda",1,"https:\u002F\u002Fgithub.com\u002Fbravew\u002Ftrove","https:\u002F\u002Fgithub.com\u002Fbravew","2026-11-12 07:56:32",{"source":8,"skillName":5,"commands":38,"prompt":48,"zipAvailable":49},[39,42,45],{"tool":40,"command":41},"npx","npx skills add bravew\u002Ftrove --skill trove-lambda",{"tool":43,"command":44},"bunx","bunx skills add bravew\u002Ftrove --skill trove-lambda",{"tool":46,"command":47},"pnpm","pnpm dlx skills add bravew\u002Ftrove --skill trove-lambda","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fbravew\u002Ftrove\u002Fblob\u002Fmain\u002Fplugins\u002Ftrove-dev\u002F.agents\u002Fskills\u002Ftrove-lambda\u002FSKILL.md",true,[],[52,59,66,73,80,87],{"skillNo":53,"name":54,"nameZh":6,"description":55,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":56,"categories":57,"keywords":58,"publishedAt":19},"01M7AGJDHTQAS1VK6P7VJAV79G","using-trove","Skill discipline anchor for the Trove marketplace. Loaded once per session; routes the agent to the right trove-* skill before code, planning, review, or git actions.","https:\u002F\u002Fgithub.com\u002Fbravew\u002Ftrove\u002Fblob\u002Fmain\u002Fskills\u002Fworkflow\u002Fusing-trove\u002FSKILL.md",[],[],{"skillNo":60,"name":61,"nameZh":6,"description":62,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":63,"categories":64,"keywords":65,"publishedAt":19},"01M7AGJAS4V4TXE6TV6F839BGZ","trove-write-skill","Skill authoring workflow. Use when creating or updating a Codex or Trove skill, including rubrics, tasks, metadata, and validation.","https:\u002F\u002Fgithub.com\u002Fbravew\u002Ftrove\u002Fblob\u002Fmain\u002Fskills\u002Fworkflow\u002Ftrove-write-skill\u002FSKILL.md",[],[],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":72,"publishedAt":19},"01M7AGJ87K41ZAC8DDBAV17QFW","trove-worktree","Git worktree workflow for spikes, risky experiments, and broad refactors that should not disturb the user's current branch.","https:\u002F\u002Fgithub.com\u002Fbravew\u002Ftrove\u002Fblob\u002Fmain\u002Fskills\u002Fworkflow\u002Ftrove-worktree\u002FSKILL.md",[],[],{"skillNo":74,"name":75,"nameZh":6,"description":76,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":79,"publishedAt":19},"01M7AGJ32V58VYBFYMS9EMHRY4","trove-why","Reconstruct design rationale and decision history from evidence — source control, issue tracker (Linear), docs (Notion), chat (Slack), error tracking (Sentry) — with calibrated confidence. Use for \"why does X work this way\", \"why did we pick Y\", regressions, and postmortems. Use trove-explain for how it works now.","https:\u002F\u002Fgithub.com\u002Fbravew\u002Ftrove\u002Fblob\u002Fmain\u002Fskills\u002Fworkflow\u002Ftrove-why\u002FSKILL.md",[],[],{"skillNo":81,"name":82,"nameZh":6,"description":83,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":86,"publishedAt":19},"01M7AGJ0FHJSRR8MMXC3V8XNHF","trove-verify","Completion verification workflow. Use before claiming done, fixed, complete, ready, or shipped.","https:\u002F\u002Fgithub.com\u002Fbravew\u002Ftrove\u002Fblob\u002Fmain\u002Fskills\u002Fworkflow\u002Ftrove-verify\u002FSKILL.md",[],[],{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":93,"publishedAt":19},"01M7AGHWV4B9SDS60RH5W0B0K5","trove-unslop","Cut AI tells from writing — PR descriptions, release notes, specs, commit bodies, and replies. Use before finalizing prose, or when the user asks to clean up, de-slop, or make text sound human.","https:\u002F\u002Fgithub.com\u002Fbravew\u002Ftrove\u002Fblob\u002Fmain\u002Fskills\u002Fworkflow\u002Ftrove-unslop\u002FSKILL.md",[],[]]