[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CV23MY7X7GXTK51Z4J16R4":3,"skill-same:01M6CV23MY7X7GXTK51Z4J16R4":79},{"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":28,"publishedAt":46,"linkOnly":47,"markdownContent":48,"markdownBody":49,"frontMatter":50,"refName":51,"commitSha":52,"skillDirectory":53,"forksCount":54,"repositoryHtmlUrl":55,"repositoryHomepageUrl":6,"ownerHtmlUrl":56,"ownerBlogUrl":6,"ownerDescription":57,"updatedAt":58,"install":59,"resources":72},"01M6CV23MY7X7GXTK51Z4J16R4","add-api-endpoint",null,"Add or change a Crucible API endpoint, request or response model, pagination path, repository cache, or scientific metadata write. Use when touching data\u002Fapi, data\u002Frepository, API serialization, or create and edit network flows.","roncofaber\u002Fcrucible-lens","roncofaber","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F48035351?v=4","01M6CV1Y8G75MY5ZBY47GZXYGD","Fabrice Roncoroni","01M6CV1YCNR0JBT13J84NGB3QC",1,"BSD-3-Clause","https:\u002F\u002Fgithub.com\u002Froncofaber\u002Fcrucible-lens\u002Fblob\u002Fmain\u002Fagent-skills\u002Fadd-api-endpoint\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],"endpoint","add api endpoint","api endpoint","api","serialization","repository","network","data","change","cache","model","write","edit","use","pagination","metadata","create","2026-11-10 10:12:30",false,"---\nname: add-api-endpoint\ndescription: Add or change a Crucible API endpoint, request or response model, pagination path, repository cache, or scientific metadata write. Use when touching data\u002Fapi, data\u002Frepository, API serialization, or create and edit network flows.\n---\n\n# Add a Crucible API endpoint\n\n## Service layer\n\nAdd the method in `data\u002Fapi\u002FCrucibleApiService.kt` and wrap it with `safeCall` so non-2xx responses become `ApiResult.Error` values. The shared client uses `expectSuccess = true`; do not bypass that behavior.\n\n```kotlin\nsuspend fun getWidget(widgetId: String): ApiResult\u003CWidget> = safeCall {\n    client.get(\"$baseUrl\u002Fwidgets\u002F$widgetId\").body()\n}\n```\n\nSend `Authorization: Bearer {apiKey}` on authenticated requests. Handle both `ApiResult.Success` and `ApiResult.Error` at call sites.\n\nChoose pagination from the endpoint response:\n\n| Response | Helper |\n|---|---|\n| Offset and limit list | `fetchAllPages` |\n| Sample or dataset keyset cursor list | `fetchAllPagesCursor` |\n| Flat search result | No pagination helper |\n\nUse explicit `@SerialName` values for wire fields that must remain stable, including single-word names whose Kotlin property might later be renamed.\n\n## Scientific metadata\n\nSample and dataset create\u002Fupdate DTOs do not accept `scientific_metadata`; the server silently drops it. Create and edit flows therefore make a structural request and a separate metadata request.\n\n- Metadata `POST` and `PATCH` return `ApiResult\u003CJsonObject>`, not `ApiResult\u003CUnit>`.\n- On create, use a plain metadata `POST`.\n- On edit, use `diffMetadataWrite` from `ui\u002Fcommon\u002FMetadataEditor.kt`.\n- If no key was deleted, `PATCH` only the changed top-level keys.\n- If a key was deleted, `POST ?overwrite=true` with the complete object because `PATCH` cannot express deletion.\n\n## Repository and cache\n\nUse `CrucibleRepository` for data that should be cached or observed by multiple consumers. Back it with `ObservableCache`, using `Unit` with `maxSize = 1` for a whole collection or an entity ID for per-entity data. Let `ObservableCache` own TTL and LRU behavior.\n\nExpose observation when screens must react to cache updates. Do not copy an observed repository object into long-lived Compose state.\n\nSkip the repository for a one-shot mutation with nothing to cache. Existing join-request mutations intentionally call `apiClient.service` from their ViewModels; pending join-request counts remain in the repository because several screens consume them.\n\n## Documentation and tests\n\n- Update the endpoint and caching sections of `dev\u002Farchitecture.md`.\n- Add an `Unreleased` changelog entry only for a user-visible change.\n- Add a common test for new deterministic branching, parsing, sorting, or cache logic. A direct Ktor wrapper without app logic does not require a unit test.\n\n## Verify\n\n```bash\n.\u002Fscripts\u002Fverify-change.sh\n```\n","# Add a Crucible API endpoint\n\n## Service layer\n\nAdd the method in `data\u002Fapi\u002FCrucibleApiService.kt` and wrap it with `safeCall` so non-2xx responses become `ApiResult.Error` values. The shared client uses `expectSuccess = true`; do not bypass that behavior.\n\n```kotlin\nsuspend fun getWidget(widgetId: String): ApiResult\u003CWidget> = safeCall {\n    client.get(\"$baseUrl\u002Fwidgets\u002F$widgetId\").body()\n}\n```\n\nSend `Authorization: Bearer {apiKey}` on authenticated requests. Handle both `ApiResult.Success` and `ApiResult.Error` at call sites.\n\nChoose pagination from the endpoint response:\n\n| Response | Helper |\n|---|---|\n| Offset and limit list | `fetchAllPages` |\n| Sample or dataset keyset cursor list | `fetchAllPagesCursor` |\n| Flat search result | No pagination helper |\n\nUse explicit `@SerialName` values for wire fields that must remain stable, including single-word names whose Kotlin property might later be renamed.\n\n## Scientific metadata\n\nSample and dataset create\u002Fupdate DTOs do not accept `scientific_metadata`; the server silently drops it. Create and edit flows therefore make a structural request and a separate metadata request.\n\n- Metadata `POST` and `PATCH` return `ApiResult\u003CJsonObject>`, not `ApiResult\u003CUnit>`.\n- On create, use a plain metadata `POST`.\n- On edit, use `diffMetadataWrite` from `ui\u002Fcommon\u002FMetadataEditor.kt`.\n- If no key was deleted, `PATCH` only the changed top-level keys.\n- If a key was deleted, `POST ?overwrite=true` with the complete object because `PATCH` cannot express deletion.\n\n## Repository and cache\n\nUse `CrucibleRepository` for data that should be cached or observed by multiple consumers. Back it with `ObservableCache`, using `Unit` with `maxSize = 1` for a whole collection or an entity ID for per-entity data. Let `ObservableCache` own TTL and LRU behavior.\n\nExpose observation when screens must react to cache updates. Do not copy an observed repository object into long-lived Compose state.\n\nSkip the repository for a one-shot mutation with nothing to cache. Existing join-request mutations intentionally call `apiClient.service` from their ViewModels; pending join-request counts remain in the repository because several screens consume them.\n\n## Documentation and tests\n\n- Update the endpoint and caching sections of `dev\u002Farchitecture.md`.\n- Add an `Unreleased` changelog entry only for a user-visible change.\n- Add a common test for new deterministic branching, parsing, sorting, or cache logic. A direct Ktor wrapper without app logic does not require a unit test.\n\n## Verify\n\n```bash\n.\u002Fscripts\u002Fverify-change.sh\n```\n",{"name":5,"description":7},"main","9ceaf87c9c62e4b7eb416b5f5516465f8c08443d","agent-skills\u002Fadd-api-endpoint",0,"https:\u002F\u002Fgithub.com\u002Froncofaber\u002Fcrucible-lens","https:\u002F\u002Fgithub.com\u002Froncofaber","🐍 Python is my rock, and I am ready to roll! 🪨 ","2026-09-17 22:32:31",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add roncofaber\u002Fcrucible-lens --skill add-api-endpoint",{"tool":65,"command":66},"bunx","bunx skills add roncofaber\u002Fcrucible-lens --skill add-api-endpoint",{"tool":68,"command":69},"pnpm","pnpm dlx skills add roncofaber\u002Fcrucible-lens --skill add-api-endpoint","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Froncofaber\u002Fcrucible-lens\u002Fblob\u002Fmain\u002Fagent-skills\u002Fadd-api-endpoint\u002FSKILL.md",true,[73],{"path":74,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":77,"sourceHtmlUrl":78},"reference.md","REFERENCE","参考文档",5647,"https:\u002F\u002Fgithub.com\u002FFDU-INS\u002FInsurance-Skills\u002Fblob\u002Fmain\u002FSkills\u002Fflare-fdc\u002Freference.md",[80,95,114,130,136],{"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":46},"01M6CV2B46QMSS5HNCHPX2XY5Z","release","Cut, build, sign, tag, or publish a Crucible Lens release. Use only when explicitly asked to release, ship, publish, tag, bump the version, or produce signed release artifacts. Follow the ordered safeguards without improvising.","https:\u002F\u002Fgithub.com\u002Froncofaber\u002Fcrucible-lens\u002Fblob\u002Fmain\u002Fagent-skills\u002Frelease\u002FSKILL.md",[],[82,87,88,89,90,91,42,92,93,94],"build","artifacts","produce","publish","ship","version","only","bump",{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":46},"01M6CV28FNZJBJM7SAKY4ZWQ8T","audit-docs","Audit AGENTS.md, README.md, project skills, and dev documentation against the repository, then repair factual drift and unnecessary duplication. Use only when explicitly asked to audit, refresh, migrate, or verify project documentation.","https:\u002F\u002Fgithub.com\u002Froncofaber\u002Fcrucible-lens\u002Fblob\u002Fmain\u002Fagent-skills\u002Faudit-docs\u002FSKILL.md",[],[102,103,104,105,106,42,93,107,108,109,110,111,112,34,113],"audit","docs","audit docs","README","refresh","duplication","drift","repair","migrate","documentation","dev","verify",{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":120,"publishedAt":46},"01M6CV268BH7084XE3ASMXFC37","add-screen","Add a Compose Multiplatform feature screen with its route, encoded arguments, NavGraph entry, ViewModel, Koin registration, shared UI conventions, and validation. Use when adding a screen or changing navigation and screen-level dependency wiring.","https:\u002F\u002Fgithub.com\u002Froncofaber\u002Fcrucible-lens\u002Fblob\u002Fmain\u002Fagent-skills\u002Fadd-screen\u002FSKILL.md",[],[121,122,123,124,42,125,126,127,128,129],"add screen","compose","validation","ui","dependency","navigation","registration","route","conventions",{"skillNo":131,"name":82,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":135,"publishedAt":46},"01M6CV224Y9A83VEVXPMW6AEHP","Cut, build, sign, tag, or publish a Crucible Lens release when explicitly requested.","https:\u002F\u002Fgithub.com\u002Froncofaber\u002Fcrucible-lens\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Frelease\u002FSKILL.md",[],[82,90,87],{"skillNo":137,"name":97,"nameZh":6,"description":138,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":141,"publishedAt":46},"01M6CV20VEDHVRPM7JPV1G5WBB","Audit and refresh project documentation when explicitly requested.","https:\u002F\u002Fgithub.com\u002Froncofaber\u002Fcrucible-lens\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Faudit-docs\u002FSKILL.md",[],[103,102,104,111,106]]