From be476a3fea4fd5d5ec31115433bbf1646f2510eb Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sat, 29 Nov 2025 18:29:15 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 17 + README.md | 3 + plugin.lock.json | 333 ++++++ skills/api-design-standards/SKILL.md | 288 +++++ .../checklists/api-design-checklist.md | 118 ++ .../checklists/security-review.md | 84 ++ skills/api-design-standards/examples/INDEX.md | 17 + .../examples/fastapi-crud.md | 332 ++++++ .../examples/pagination.md | 38 + .../examples/pydantic-schemas.md | 149 +++ .../examples/tanstack-start.md | 71 ++ .../api-design-standards/examples/testing.md | 22 + .../api-design-standards/reference/INDEX.md | 11 + .../reference/authentication.md | 24 + .../reference/cors-rate-limiting.md | 26 + .../reference/error-handlers.md | 17 + .../reference/fastapi-setup.md | 34 + .../api-design-standards/reference/openapi.md | 17 + .../templates/error-handler.py | 115 ++ .../templates/fastapi-crud-endpoint.py | 130 +++ .../templates/pydantic-schemas.py | 61 + .../templates/rate-limiter.py | 82 ++ .../templates/repository-pattern.py | 101 ++ .../templates/tanstack-server-function.ts | 186 +++ skills/code-style/EXAMPLES.md | 1023 +++++++++++++++++ skills/code-style/REFERENCE.md | 586 ++++++++++ skills/code-style/SKILL.md | 199 ++++ skills/code-style/checklists/python-review.md | 141 +++ .../checklists/typescript-review.md | 113 ++ skills/code-style/templates/.eslintrc.json | 18 + skills/code-style/templates/.prettierrc.json | 9 + .../code-style/templates/python-endpoint.py | 186 +++ skills/code-style/templates/python-model.py | 126 ++ skills/code-style/templates/ruff.toml | 73 ++ .../templates/typescript-component.tsx | 85 ++ .../templates/typescript-server-function.ts | 92 ++ skills/documentation-architecture/SKILL.md | 25 + .../documentation-quality-checklist.md | 152 +++ .../examples/INDEX.md | 67 ++ .../examples/architecture-docs.md | 442 +++++++ .../examples/coverage-validation.md | 411 +++++++ .../examples/openapi-generation.md | 437 +++++++ .../reference/INDEX.md | 55 + .../reference/documentation-standards.md | 496 ++++++++ .../reference/mermaid-diagrams.md | 500 ++++++++ .../reference/openapi-patterns.md | 491 ++++++++ .../templates/INDEX.md | 59 + .../templates/api-endpoint.md | 191 +++ .../templates/architecture-doc.md | 307 +++++ .../templates/openapi-spec.yaml | 429 +++++++ skills/onboarding-coordination/SKILL.md | 25 + .../onboarding-coordination/examples/INDEX.md | 67 ++ .../reference/INDEX.md | 59 + .../templates/INDEX.md | 41 + skills/ontological-documentation/README.md | 68 ++ skills/ontological-documentation/SKILL.md | 399 +++++++ .../ontology-documentation-checklist.md | 236 ++++ .../examples/INDEX.md | 69 ++ .../examples/ecommerce-ontology.md | 98 ++ .../reference/INDEX.md | 43 + .../reference/README.md | 55 + .../reference/concept_extraction_guide.md | 271 +++++ .../reference/documentation_templates.md | 447 +++++++ .../reference/ontology_patterns.md | 230 ++++ .../scripts/README.md | 114 ++ .../scripts/extract_concepts.py | 259 +++++ .../scripts/generate_ontology_diagram.py | 288 +++++ .../templates/assets-README.md | 79 ++ .../templates/domain-ontology.md | 35 + skills/pr-template/SKILL.md | 242 ++++ skills/pr-template/checklists/pr-checklist.md | 73 ++ .../pr-template/templates/feature-template.md | 53 + skills/project-structure/SKILL.md | 327 ++++++ .../checklists/project-setup-checklist.md | 165 +++ skills/project-structure/examples/INDEX.md | 48 + skills/project-structure/reference/INDEX.md | 32 + 76 files changed, 12812 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 plugin.lock.json create mode 100644 skills/api-design-standards/SKILL.md create mode 100644 skills/api-design-standards/checklists/api-design-checklist.md create mode 100644 skills/api-design-standards/checklists/security-review.md create mode 100644 skills/api-design-standards/examples/INDEX.md create mode 100644 skills/api-design-standards/examples/fastapi-crud.md create mode 100644 skills/api-design-standards/examples/pagination.md create mode 100644 skills/api-design-standards/examples/pydantic-schemas.md create mode 100644 skills/api-design-standards/examples/tanstack-start.md create mode 100644 skills/api-design-standards/examples/testing.md create mode 100644 skills/api-design-standards/reference/INDEX.md create mode 100644 skills/api-design-standards/reference/authentication.md create mode 100644 skills/api-design-standards/reference/cors-rate-limiting.md create mode 100644 skills/api-design-standards/reference/error-handlers.md create mode 100644 skills/api-design-standards/reference/fastapi-setup.md create mode 100644 skills/api-design-standards/reference/openapi.md create mode 100644 skills/api-design-standards/templates/error-handler.py create mode 100644 skills/api-design-standards/templates/fastapi-crud-endpoint.py create mode 100644 skills/api-design-standards/templates/pydantic-schemas.py create mode 100644 skills/api-design-standards/templates/rate-limiter.py create mode 100644 skills/api-design-standards/templates/repository-pattern.py create mode 100644 skills/api-design-standards/templates/tanstack-server-function.ts create mode 100644 skills/code-style/EXAMPLES.md create mode 100644 skills/code-style/REFERENCE.md create mode 100644 skills/code-style/SKILL.md create mode 100644 skills/code-style/checklists/python-review.md create mode 100644 skills/code-style/checklists/typescript-review.md create mode 100644 skills/code-style/templates/.eslintrc.json create mode 100644 skills/code-style/templates/.prettierrc.json create mode 100644 skills/code-style/templates/python-endpoint.py create mode 100644 skills/code-style/templates/python-model.py create mode 100644 skills/code-style/templates/ruff.toml create mode 100644 skills/code-style/templates/typescript-component.tsx create mode 100644 skills/code-style/templates/typescript-server-function.ts create mode 100644 skills/documentation-architecture/SKILL.md create mode 100644 skills/documentation-architecture/checklists/documentation-quality-checklist.md create mode 100644 skills/documentation-architecture/examples/INDEX.md create mode 100644 skills/documentation-architecture/examples/architecture-docs.md create mode 100644 skills/documentation-architecture/examples/coverage-validation.md create mode 100644 skills/documentation-architecture/examples/openapi-generation.md create mode 100644 skills/documentation-architecture/reference/INDEX.md create mode 100644 skills/documentation-architecture/reference/documentation-standards.md create mode 100644 skills/documentation-architecture/reference/mermaid-diagrams.md create mode 100644 skills/documentation-architecture/reference/openapi-patterns.md create mode 100644 skills/documentation-architecture/templates/INDEX.md create mode 100644 skills/documentation-architecture/templates/api-endpoint.md create mode 100644 skills/documentation-architecture/templates/architecture-doc.md create mode 100644 skills/documentation-architecture/templates/openapi-spec.yaml create mode 100644 skills/onboarding-coordination/SKILL.md create mode 100644 skills/onboarding-coordination/examples/INDEX.md create mode 100644 skills/onboarding-coordination/reference/INDEX.md create mode 100644 skills/onboarding-coordination/templates/INDEX.md create mode 100644 skills/ontological-documentation/README.md create mode 100644 skills/ontological-documentation/SKILL.md create mode 100644 skills/ontological-documentation/checklists/ontology-documentation-checklist.md create mode 100644 skills/ontological-documentation/examples/INDEX.md create mode 100644 skills/ontological-documentation/examples/ecommerce-ontology.md create mode 100644 skills/ontological-documentation/reference/INDEX.md create mode 100644 skills/ontological-documentation/reference/README.md create mode 100644 skills/ontological-documentation/reference/concept_extraction_guide.md create mode 100644 skills/ontological-documentation/reference/documentation_templates.md create mode 100644 skills/ontological-documentation/reference/ontology_patterns.md create mode 100644 skills/ontological-documentation/scripts/README.md create mode 100755 skills/ontological-documentation/scripts/extract_concepts.py create mode 100755 skills/ontological-documentation/scripts/generate_ontology_diagram.py create mode 100644 skills/ontological-documentation/templates/assets-README.md create mode 100644 skills/ontological-documentation/templates/domain-ontology.md create mode 100644 skills/pr-template/SKILL.md create mode 100644 skills/pr-template/checklists/pr-checklist.md create mode 100644 skills/pr-template/templates/feature-template.md create mode 100644 skills/project-structure/SKILL.md create mode 100644 skills/project-structure/checklists/project-setup-checklist.md create mode 100644 skills/project-structure/examples/INDEX.md create mode 100644 skills/project-structure/reference/INDEX.md diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..e680eae --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,17 @@ +{ + "name": "developer-experience", + "description": "Developer experience and team collaboration tools including onboarding automation, documentation generation, async standups with Linear integration, and knowledge management", + "version": "1.0.0", + "author": { + "name": "Grey Haven Studio" + }, + "skills": [ + "./skills/api-design-standards", + "./skills/code-style", + "./skills/documentation-architecture", + "./skills/onboarding-coordination", + "./skills/ontological-documentation", + "./skills/pr-template", + "./skills/project-structure" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8664961 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# developer-experience + +Developer experience and team collaboration tools including onboarding automation, documentation generation, async standups with Linear integration, and knowledge management diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..86db4af --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,333 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:greyhaven-ai/claude-code-config:grey-haven-plugins/developer-experience", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "382f952f867a3240da5793cf4e0969e10d519654", + "treeHash": "51c21b2c4157042854383f81ae706ca51846c25f90b6d4865b3a80b9f950e16c", + "generatedAt": "2025-11-28T10:17:03.858002Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "developer-experience", + "description": "Developer experience and team collaboration tools including onboarding automation, documentation generation, async standups with Linear integration, and knowledge management", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "4625c80929ebcbca5dd49347eddbe882c4b970ad04b910932d7ef12cdb96eafd" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "f3e99e441ec7836e0752274f5f93051be3855979922b8ae87730e2ba2d893508" + }, + { + "path": "skills/api-design-standards/SKILL.md", + "sha256": "502488f938236df62ca739706a8fb22af156df310d292fa092cae2dd579ffc1b" + }, + { + "path": "skills/api-design-standards/checklists/security-review.md", + "sha256": "3a239b4edb905c3418f342504c842e4e9a620a99c51534e18122f1ba582dbab9" + }, + { + "path": "skills/api-design-standards/checklists/api-design-checklist.md", + "sha256": "1db3c2c9e2d45b0f797b45643104092e5b4ccf1bcd5310df03fce562bfae90eb" + }, + { + "path": "skills/api-design-standards/examples/pydantic-schemas.md", + "sha256": "06d10bb37ff544bb19ea0c104eeb342767cc93dd6d40611a6a4171fb31172457" + }, + { + "path": "skills/api-design-standards/examples/fastapi-crud.md", + "sha256": "48ed7e7554e0668d59508749f5650889d813ca29f51c68b8e70b874b2155f065" + }, + { + "path": "skills/api-design-standards/examples/pagination.md", + "sha256": "a3d5bb435548e6123270f825e8acaf549d66a96ab361021822586bedb4714fc0" + }, + { + "path": "skills/api-design-standards/examples/testing.md", + "sha256": "60f8a0ebb121d27627f2899001dc95f416f42743d72ce87de735df194a9a4bd6" + }, + { + "path": "skills/api-design-standards/examples/tanstack-start.md", + "sha256": "4c20160b0a93c2b2b03e73a54036aa319818344f369e8ec043c71790efd82dd8" + }, + { + "path": "skills/api-design-standards/examples/INDEX.md", + "sha256": "59f8b71dc0e5ad1c47b35bca28b2a06832424efb4d4f9f8c00b7e4777b11df7c" + }, + { + "path": "skills/api-design-standards/templates/repository-pattern.py", + "sha256": "d3918975b70d5721c98e975d66ad8ba1b2c58b8658b50639abac166652a310e2" + }, + { + "path": "skills/api-design-standards/templates/rate-limiter.py", + "sha256": "e973f344999d39e1877a8e242c461c4cff0b2813a35a91ba8dc13100b34e9859" + }, + { + "path": "skills/api-design-standards/templates/pydantic-schemas.py", + "sha256": "7ca11e5145c0f7c63cff08b0d891c88a658779b0257c0f397088dc6a8e76eb6f" + }, + { + "path": "skills/api-design-standards/templates/fastapi-crud-endpoint.py", + "sha256": "fb287b8e6eb79e1a3f8c975e894e95c2acc20df8c64c942205542e9b3104f971" + }, + { + "path": "skills/api-design-standards/templates/error-handler.py", + "sha256": "0de1a732dcb7b67d7f9d9119566cec7f37b8826ce80343da34973bf4b26eb3e6" + }, + { + "path": "skills/api-design-standards/templates/tanstack-server-function.ts", + "sha256": "027ba481af0ad8c62610002ad51ba9dfb3018cf400724c86cbe4fdf53e13df56" + }, + { + "path": "skills/api-design-standards/reference/authentication.md", + "sha256": "ab85a5deeec98e237f76334a702ecbc78c7a1eb7a4c3b1235f796ce7dc58d6b3" + }, + { + "path": "skills/api-design-standards/reference/cors-rate-limiting.md", + "sha256": "218aab01fca085c60f94bb5d76246899a1ee6c40ed207375e8c5915935fc0f25" + }, + { + "path": "skills/api-design-standards/reference/fastapi-setup.md", + "sha256": "ba9187313da1e02bbc85ab172482cef5f140145b666bfbc1625bd93ee2dd84de" + }, + { + "path": "skills/api-design-standards/reference/error-handlers.md", + "sha256": "4644e1baa08893c0b157cc4034963bfddee5c5b0ffd6f295fbbc54d5f65477fb" + }, + { + "path": "skills/api-design-standards/reference/INDEX.md", + "sha256": "7c252ae328f28a82c9ab9c695a956145c4f5f492f4058b754996c4591733bf52" + }, + { + "path": "skills/api-design-standards/reference/openapi.md", + "sha256": "715654b6c57c511476f8205e0406ba1040470fad7b1df27bacaa4e0df81b8c02" + }, + { + "path": "skills/pr-template/SKILL.md", + "sha256": "36d4187f82e46c82a69e7d55c0ecb59e7a123dc532e82aa2d40057f2170cac44" + }, + { + "path": "skills/pr-template/checklists/pr-checklist.md", + "sha256": "fae708a5e5ba6945c510dd1ac558f2e1b1198a779dea2353c66a95fca2222677" + }, + { + "path": "skills/pr-template/templates/feature-template.md", + "sha256": "4001ad70a77de76e351b66509af3c897433055f7e9c3eae9bff14f1c3f2a8b52" + }, + { + "path": "skills/ontological-documentation/README.md", + "sha256": "8e762699f91f2e33a621fca42d0445f6f7bacaaa6d192c0c8b06bdc1d149b9ca" + }, + { + "path": "skills/ontological-documentation/SKILL.md", + "sha256": "3d6b80205d3322a338e17047c3ddfb72e9a712a8b417d0c9ee2c6dafb26d464f" + }, + { + "path": "skills/ontological-documentation/checklists/ontology-documentation-checklist.md", + "sha256": "42b3b947b7a29445e825a3945f40a64255f9567ff26c8670b18f05b28daddf23" + }, + { + "path": "skills/ontological-documentation/examples/ecommerce-ontology.md", + "sha256": "2a6f18dbc8464bcb18c4610146293da86d5e433d3613f265a99edc97b5f2b413" + }, + { + "path": "skills/ontological-documentation/examples/INDEX.md", + "sha256": "494611f1b8e5918aa0e404afc3bd2cd4151122eb83a0071cb511b2bddf304d3c" + }, + { + "path": "skills/ontological-documentation/scripts/generate_ontology_diagram.py", + "sha256": "72b0e84ab906ce9cadbd11087365bae19d4eec3d72ec54276080fc66d061b30f" + }, + { + "path": "skills/ontological-documentation/scripts/README.md", + "sha256": "f1fbe7e1f1204a804b53af53b6fb21552e5f966f75813744358573e884152e8c" + }, + { + "path": "skills/ontological-documentation/scripts/extract_concepts.py", + "sha256": "67397467c86c3b1f2eeba7ffe38ab53223244aa358430d61c3597f4d9caacdbf" + }, + { + "path": "skills/ontological-documentation/templates/assets-README.md", + "sha256": "33fa54d4da095c36cb98324276261bff799f1b487b1b66464d58016726a20480" + }, + { + "path": "skills/ontological-documentation/templates/domain-ontology.md", + "sha256": "54f6faaba617f709915963ea744851c539522b94b94267ef8eae2ee0185e2da2" + }, + { + "path": "skills/ontological-documentation/reference/concept_extraction_guide.md", + "sha256": "831e0ba24dda053a2e1d6ad049beef5f2b8e464a28d197b8a3ccfa9370891a81" + }, + { + "path": "skills/ontological-documentation/reference/ontology_patterns.md", + "sha256": "36145f294e1d316198dea178f9a6c64605120144cb58adf69dcc8934bd1389ee" + }, + { + "path": "skills/ontological-documentation/reference/README.md", + "sha256": "df8fc60c65ddf4e94b75b1514eb70a03a979fa21f5912ce91c1b65868a125d95" + }, + { + "path": "skills/ontological-documentation/reference/INDEX.md", + "sha256": "b0d715f473f6a618bf01c3824b07fcaa0ea36b4eb14bed6269a0bfac42e78ba2" + }, + { + "path": "skills/ontological-documentation/reference/documentation_templates.md", + "sha256": "59a120c9b812338c15c008522a5e0e99308b8df1ac55eee9999afef2450d0068" + }, + { + "path": "skills/project-structure/SKILL.md", + "sha256": "76e6c568af7efe357041e2525f546dbaa8a79c3689934860f6e9434402f3b396" + }, + { + "path": "skills/project-structure/checklists/project-setup-checklist.md", + "sha256": "7b54cca9b77c13a82ea2f6a1b9d3892ad778046b5275dec88fb21353ebb91a0c" + }, + { + "path": "skills/project-structure/examples/INDEX.md", + "sha256": "62b81c66e19647bf2e0518111abb020cb75604740750beb94b22c3edd3f0c75e" + }, + { + "path": "skills/project-structure/reference/INDEX.md", + "sha256": "1674663009d90e565e8cdb4b13495be9532cc9f567ad6841da379bc9da276f10" + }, + { + "path": "skills/onboarding-coordination/SKILL.md", + "sha256": "35c2a48b6d53865e832e52047049461a55efdf3283a75ba4915d52ade0b0388e" + }, + { + "path": "skills/onboarding-coordination/examples/INDEX.md", + "sha256": "839ac38874b65f569d05cf84b743fd63f837bf974c903ee48c2d9f8a7146dbc4" + }, + { + "path": "skills/onboarding-coordination/templates/INDEX.md", + "sha256": "2af8827d1ea2943368ed7be7dcc13963b1d38b6b673844b142d819d47c2ce8a7" + }, + { + "path": "skills/onboarding-coordination/reference/INDEX.md", + "sha256": "72833a433e47c814286dc13ef10785fd035a7e95ec23f418c1060561b3d11d29" + }, + { + "path": "skills/code-style/EXAMPLES.md", + "sha256": "675ffc254bd74e019a0ec5d7b51bfb5e27c659d8f9f0aeaade42bcdbeb2e21fa" + }, + { + "path": "skills/code-style/REFERENCE.md", + "sha256": "43904b5d8597b676677641de845a667a4f5c3f89a7ea47a50eba565105c8e7d9" + }, + { + "path": "skills/code-style/SKILL.md", + "sha256": "6602a54886c8315705f9654a6768b92ee2dc93a1ec6af53d482a1bc2a8b8fb19" + }, + { + "path": "skills/code-style/checklists/python-review.md", + "sha256": "8810802c882e73d4f9a00e16eb9f95e62a505e41a0f0856d58ba555e82e30726" + }, + { + "path": "skills/code-style/checklists/typescript-review.md", + "sha256": "b23cc57b91cfb41a49fd694ba6a2f0666e46ebd7da9a8ea76fa52749ff829d16" + }, + { + "path": "skills/code-style/templates/typescript-component.tsx", + "sha256": "1270d35dddeb7a6dffc125b47dfc60ef00838f48e868470177010ff78048a74a" + }, + { + "path": "skills/code-style/templates/python-model.py", + "sha256": "6a575992f2b25993d7f005a71022b6280640b44342c51c953c61b31fc8d3119a" + }, + { + "path": "skills/code-style/templates/typescript-server-function.ts", + "sha256": "c91202362eb2257be052319bfe3c023cba144ed0736cc5980224cf2e57060bbe" + }, + { + "path": "skills/code-style/templates/python-endpoint.py", + "sha256": "199749bee12cc9894c0067bd9c135ce762cc3fd02efdc039963c971673bc3505" + }, + { + "path": "skills/code-style/templates/.prettierrc.json", + "sha256": "dfbd47294d39568b0e933089c4c5be3d91ddd12a7b385a6d68d5337aafc512b5" + }, + { + "path": "skills/code-style/templates/ruff.toml", + "sha256": "7b1e9d695e4e3a455acd61b98bc31ff86d936989daf16a783e7089a504537cc7" + }, + { + "path": "skills/code-style/templates/.eslintrc.json", + "sha256": "57db390995cc0938f81298248d272e9686b96e9c6eabf85b26e6581db3f2c39c" + }, + { + "path": "skills/documentation-architecture/SKILL.md", + "sha256": "46ea8d28041c321c367e937b6d61bb62ad1e4a4a3dd5b9deed0a8bcd8ddcd8cf" + }, + { + "path": "skills/documentation-architecture/checklists/documentation-quality-checklist.md", + "sha256": "f144397d848f7d67ba1926da2ef8e22c1568914bfdfd0835069722d71502e7d4" + }, + { + "path": "skills/documentation-architecture/examples/architecture-docs.md", + "sha256": "3bbbba3c9b52429e395ab116f73545c3dd1631a690d05a5035dbbef3a64edf5a" + }, + { + "path": "skills/documentation-architecture/examples/openapi-generation.md", + "sha256": "402c402e3afe373677fa81fff14759d2c14cc003b26e4b5815c2f01933a86fc9" + }, + { + "path": "skills/documentation-architecture/examples/INDEX.md", + "sha256": "f6854bb2df3a7f4277db7372a7328e6a95d62c20e8a6abe6b646571674c0d7c7" + }, + { + "path": "skills/documentation-architecture/examples/coverage-validation.md", + "sha256": "a52da791f52accdacb3d5f9a5fda0861d0495935d86d979dbc99ee6f3024d877" + }, + { + "path": "skills/documentation-architecture/templates/openapi-spec.yaml", + "sha256": "9971ea0aec2ba505cc7d86cdbe203d235cbd016f18be62eaf1c79b5051c452bb" + }, + { + "path": "skills/documentation-architecture/templates/api-endpoint.md", + "sha256": "ae4d3d8e08b300f3f6570d4e985edffe0a54a310feb1a8b662b8b5bc83274323" + }, + { + "path": "skills/documentation-architecture/templates/architecture-doc.md", + "sha256": "7e207ef69ec2c8b7fcf9dabfc1c94ab202d74a815d1cb43c2b6b9b6a172cece0" + }, + { + "path": "skills/documentation-architecture/templates/INDEX.md", + "sha256": "09e59e94be27f582c75c0d5579d1bb8a7508b148e1a1c4582e1a1383f4e73f71" + }, + { + "path": "skills/documentation-architecture/reference/mermaid-diagrams.md", + "sha256": "b43cedd8370a2556d79976f7fb44514de9e458bdf0a6b21beac6171c19290960" + }, + { + "path": "skills/documentation-architecture/reference/documentation-standards.md", + "sha256": "3e5ed6c0887bf43fe1fadf53420269f20d449007953d74b32018979de27c8fe7" + }, + { + "path": "skills/documentation-architecture/reference/INDEX.md", + "sha256": "f78452c5dc51406cde72499d4bd65af8bc59d16d5c3b5c5ac5d96dead9ec8e87" + }, + { + "path": "skills/documentation-architecture/reference/openapi-patterns.md", + "sha256": "2ae229c7f3415cde4dfc0384911259dedc037fc9f1f7b46e7cdcb743b49b3728" + } + ], + "dirSha256": "51c21b2c4157042854383f81ae706ca51846c25f90b6d4865b3a80b9f950e16c" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/api-design-standards/SKILL.md b/skills/api-design-standards/SKILL.md new file mode 100644 index 0000000..dc75bcb --- /dev/null +++ b/skills/api-design-standards/SKILL.md @@ -0,0 +1,288 @@ +--- +name: grey-haven-api-design +description: "Design RESTful APIs following Grey Haven standards - FastAPI routes, Pydantic schemas, HTTP status codes, pagination, filtering, error responses, OpenAPI docs, and multi-tenant patterns. Use when creating API endpoints, designing REST resources, implementing server functions, configuring FastAPI, writing Pydantic schemas, setting up error handling, implementing pagination, or when user mentions 'API', 'endpoint', 'REST', 'FastAPI', 'Pydantic', 'server function', 'OpenAPI', 'pagination', 'validation', 'error handling', 'rate limiting', 'CORS', or 'authentication'." +--- + +# Grey Haven API Design Standards + +**RESTful API design for FastAPI backends and TanStack Start server functions.** + +Follow these standards when creating API endpoints, defining schemas, and handling errors in Grey Haven projects. + +## Supporting Documentation + +- **[examples/](examples/)** - Complete endpoint examples (all files <500 lines) + - [fastapi-crud.md](examples/fastapi-crud.md) - CRUD endpoints with repository pattern + - [pydantic-schemas.md](examples/pydantic-schemas.md) - Request/response schemas + - [tanstack-start.md](examples/tanstack-start.md) - Server functions + - [pagination.md](examples/pagination.md) - Pagination patterns + - [testing.md](examples/testing.md) - API testing +- **[reference/](reference/)** - Configuration references (all files <500 lines) + - [fastapi-setup.md](reference/fastapi-setup.md) - Main app configuration + - [openapi.md](reference/openapi.md) - OpenAPI customization + - [error-handlers.md](reference/error-handlers.md) - Exception handlers + - [authentication.md](reference/authentication.md) - JWT configuration + - [cors-rate-limiting.md](reference/cors-rate-limiting.md) - CORS and rate limiting +- **[templates/](templates/)** - Copy-paste ready endpoint templates +- **[checklists/](checklists/)** - API design and security checklists + +## Quick Reference + +### RESTful Resource Design + +**URL Patterns:** +- ✅ `/api/v1/users` (plural nouns, lowercase with hyphens) +- ✅ `/api/v1/organizations/{org_id}/teams` (hierarchical) +- ❌ `/api/v1/getUsers` (no verbs in URLs) +- ❌ `/api/v1/user_profiles` (no underscores) + +**HTTP Verbs:** +- `GET` - Retrieve resources +- `POST` - Create new resources +- `PUT` - Update entire resource +- `PATCH` - Update partial resource +- `DELETE` - Remove resource + +### HTTP Status Codes + +**Success:** +- `200 OK` - GET, PUT, PATCH requests +- `201 Created` - POST request (resource created) +- `204 No Content` - DELETE request + +**Client Errors:** +- `400 Bad Request` - Invalid request data +- `401 Unauthorized` - Missing/invalid authentication +- `403 Forbidden` - Insufficient permissions +- `404 Not Found` - Resource doesn't exist +- `409 Conflict` - Duplicate resource, concurrent update +- `422 Unprocessable Entity` - Validation errors + +**Server Errors:** +- `500 Internal Server Error` - Unhandled exception +- `503 Service Unavailable` - Database/service down + +### Multi-Tenant Isolation + +**Always enforce tenant isolation:** +```python +# Extract tenant_id from JWT +repository = UserRepository(db, tenant_id=current_user.tenant_id) + +# All queries automatically filtered by tenant_id +users = await repository.list() # Only returns users in this tenant +``` + +### FastAPI Route Pattern + +```python +from fastapi import APIRouter, Depends, HTTPException, status + +router = APIRouter(prefix="/api/v1/users", tags=["users"]) + +@router.post("", response_model=UserRead, status_code=status.HTTP_201_CREATED) +async def create_user( + user_data: UserCreate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> UserRead: + """Create a new user in the current tenant.""" + repository = UserRepository(db, tenant_id=current_user.tenant_id) + user = await repository.create(user_data) + return user +``` + +**See [examples/fastapi-crud.md](examples/fastapi-crud.md) for complete CRUD endpoints.** + +### Pydantic Schema Pattern + +```python +from pydantic import BaseModel, EmailStr, Field, ConfigDict + +class UserCreate(BaseModel): + """Schema for creating a new user.""" + email: EmailStr + full_name: str = Field(..., min_length=1, max_length=255) + password: str = Field(..., min_length=8) + +class UserRead(BaseModel): + """Schema for reading user data (public fields only).""" + id: str + tenant_id: str + email: EmailStr + full_name: str + created_at: datetime + + model_config = ConfigDict(from_attributes=True) +``` + +**See [examples/pydantic-schemas.md](examples/pydantic-schemas.md) for validation patterns.** + +### TanStack Start Server Functions + +```typescript +// app/routes/api/users.ts +import { createServerFn } from "@tanstack/start"; +import { z } from "zod"; + +const createUserSchema = z.object({ + email: z.string().email(), + fullName: z.string().min(1).max(255), +}); + +export const createUser = createServerFn({ method: "POST" }) + .validator(createUserSchema) + .handler(async ({ data, context }) => { + const authUser = await getAuthUser(context); + // Create user with tenant isolation + }); +``` + +**See [examples/tanstack-start.md](examples/tanstack-start.md) for complete examples.** + +### Error Response Format + +```json +{ + "error": "User with ID abc123 not found", + "status_code": 404 +} +``` + +**Validation errors:** +```json +{ + "error": "Validation error", + "detail": [ + { + "field": "email", + "message": "value is not a valid email address", + "code": "value_error.email" + } + ], + "status_code": 422 +} +``` + +**See [reference/error-handlers.md](reference/error-handlers.md) for exception handlers.** + +### Pagination + +**Offset-based (simple):** +```python +@router.get("", response_model=PaginatedResponse[UserRead]) +async def list_users(skip: int = 0, limit: int = 100): + users = await repository.list(skip=skip, limit=limit) + total = await repository.count() + return PaginatedResponse(items=users, total=total, skip=skip, limit=limit) +``` + +**Cursor-based (recommended for large datasets):** +```python +@router.get("") +async def list_users(cursor: Optional[str] = None, limit: int = 100): + users = await repository.list_cursor(cursor=cursor, limit=limit) + next_cursor = users[-1].id if len(users) == limit else None + return {"items": users, "next_cursor": next_cursor} +``` + +**See [examples/pagination.md](examples/pagination.md) for complete implementations.** + +## Core Principles + +### 1. Repository Pattern + +**Always use tenant-aware repositories:** +- Extract `tenant_id` from JWT claims +- Pass to repository constructor +- All queries automatically filtered +- Prevents cross-tenant data leaks + +### 2. Pydantic Validation + +**Define schemas for all requests/responses:** +- `{Model}Create` - Fields for creation +- `{Model}Read` - Public fields for responses +- `{Model}Update` - Optional fields for updates +- Never return password hashes or sensitive data + +### 3. OpenAPI Documentation + +**FastAPI auto-generates docs:** +- Add docstrings to all endpoints +- Use `summary`, `description`, `response_description` +- Document all parameters and responses +- Available at `/docs` (Swagger UI) and `/redoc` (ReDoc) + +**See [reference/openapi.md](reference/openapi.md) for customization.** + +### 4. Rate Limiting + +**Protect public endpoints:** +```python +from app.core.rate_limit import rate_limit + +@router.get("", dependencies=[Depends(rate_limit)]) +async def list_users(): + """List users (rate limited to 100 req/min).""" + pass +``` + +**See [templates/rate-limiter.py](templates/rate-limiter.py) for Upstash Redis implementation.** + +### 5. CORS Configuration + +**Use Doppler for allowed origins:** +```python +# NEVER hardcode origins in production! +allowed_origins = os.getenv("CORS_ALLOWED_ORIGINS", "").split(",") + +app.add_middleware( + CORSMiddleware, + allow_origins=allowed_origins, + allow_credentials=True, +) +``` + +**See [reference/cors-rate-limiting.md](reference/cors-rate-limiting.md) for complete setup.** + +## When to Apply This Skill + +Use this skill when: +- ✅ Creating new FastAPI endpoints or TanStack Start server functions +- ✅ Designing RESTful resource hierarchies +- ✅ Writing Pydantic schemas for validation +- ✅ Implementing pagination, filtering, or sorting +- ✅ Configuring error response formats +- ✅ Setting up OpenAPI documentation +- ✅ Implementing rate limiting or CORS +- ✅ Designing multi-tenant API isolation +- ✅ Testing API endpoints with pytest +- ✅ Reviewing API design in pull requests +- ✅ User mentions: "API", "endpoint", "REST", "FastAPI", "Pydantic", "server function", "OpenAPI", "pagination", "validation" + +## Template References + +These API design patterns come from Grey Haven's actual templates: +- **Backend**: `cvi-backend-template` (FastAPI + SQLModel + Repository Pattern) +- **Frontend**: `cvi-template` (TanStack Start server functions) + +## Critical Reminders + +1. **Repository pattern** - Always use tenant-aware repositories for multi-tenant isolation +2. **Pydantic schemas** - Never return password hashes or sensitive fields in responses +3. **HTTP status codes** - 201 for create, 204 for delete, 404 for not found, 422 for validation errors +4. **Pagination** - Use cursor-based for large datasets (better performance than offset) +5. **Error format** - Consistent error structure with `error`, `detail`, and `status_code` fields +6. **OpenAPI docs** - Document all parameters, responses, and errors with docstrings +7. **Rate limiting** - Protect public endpoints with Upstash Redis (100 req/min default) +8. **CORS** - Use Doppler for allowed origins, never hardcode in production +9. **JWT authentication** - Extract `tenant_id` from JWT claims for multi-tenant isolation +10. **Testing** - Use FastAPI TestClient with `doppler run --config test -- pytest` + +## Next Steps + +- **Need endpoint examples?** See [examples/](examples/) for FastAPI CRUD and TanStack Start +- **Need configurations?** See [reference/](reference/) for OpenAPI, CORS, error handlers +- **Need templates?** See [templates/](templates/) for copy-paste ready endpoint code +- **Need checklists?** Use [checklists/](checklists/) for systematic API design reviews diff --git a/skills/api-design-standards/checklists/api-design-checklist.md b/skills/api-design-standards/checklists/api-design-checklist.md new file mode 100644 index 0000000..f696cd2 --- /dev/null +++ b/skills/api-design-standards/checklists/api-design-checklist.md @@ -0,0 +1,118 @@ +# API Design Checklist + +**Use this checklist before creating PR for new API endpoints.** + +## RESTful Design + +- [ ] URLs use plural nouns (`/users` not `/user`) +- [ ] URLs use lowercase with hyphens (`/user-profiles` not `/userProfiles`) +- [ ] No verbs in URLs (`/users` not `/getUsers`) +- [ ] Hierarchical resources follow pattern `/parent/{id}/child` +- [ ] HTTP verbs used correctly (GET=read, POST=create, PUT=update, DELETE=delete) + +## Multi-Tenant Isolation + +- [ ] All queries filtered by `tenant_id` from JWT +- [ ] Repository pattern used with automatic tenant filtering +- [ ] No direct database queries bypassing repository +- [ ] Cross-tenant access blocked (except superuser endpoints) +- [ ] Test cases verify tenant isolation + +## Request/Response Schemas + +- [ ] Pydantic schemas defined for all requests +- [ ] Pydantic schemas defined for all responses +- [ ] Password hashes never returned in responses +- [ ] Sensitive fields excluded from public schemas +- [ ] Validation rules enforced (min/max length, format, etc.) +- [ ] Field-level validation implemented where needed +- [ ] Model-level validation for cross-field rules + +## HTTP Status Codes + +- [ ] 200 OK for successful GET/PUT/PATCH +- [ ] 201 Created for successful POST +- [ ] 204 No Content for successful DELETE +- [ ] 400 Bad Request for invalid data +- [ ] 401 Unauthorized for missing/invalid auth +- [ ] 403 Forbidden for insufficient permissions +- [ ] 404 Not Found for missing resources +- [ ] 409 Conflict for duplicates +- [ ] 422 Validation Error for schema failures + +## Error Handling + +- [ ] Consistent error response format (`error`, `status_code`, `detail`) +- [ ] Custom exception handlers registered +- [ ] Validation errors return field-level details +- [ ] Integrity errors handled gracefully (409 Conflict) +- [ ] Generic exceptions caught and logged + +## Pagination + +- [ ] List endpoints support pagination (`skip`, `limit`) +- [ ] Maximum limit enforced (100 default) +- [ ] Paginated response includes `total`, `has_more` +- [ ] Cursor-based pagination for large datasets +- [ ] Pagination tested with edge cases + +## Authentication + +- [ ] JWT authentication required on protected endpoints +- [ ] `tenant_id` extracted from JWT claims +- [ ] Superuser flag checked for admin endpoints +- [ ] Public endpoints explicitly marked (no auth) +- [ ] Authentication tested in integration tests + +## OpenAPI Documentation + +- [ ] Endpoint docstrings describe purpose +- [ ] All parameters documented +- [ ] Response schemas documented +- [ ] Error responses documented (409, 422, etc.) +- [ ] Examples provided for complex schemas +- [ ] Tags assigned for logical grouping + +## Rate Limiting + +- [ ] Public endpoints have rate limiting +- [ ] Critical endpoints (create, update) have stricter limits +- [ ] Rate limit uses Upstash Redis +- [ ] Rate limit headers included in response + +## Testing + +- [ ] Unit tests for repository methods +- [ ] Integration tests for all CRUD operations +- [ ] Tenant isolation verified in tests +- [ ] Duplicate detection tested (409 Conflict) +- [ ] Validation errors tested (422 Unprocessable) +- [ ] Authentication tested (401 Unauthorized) +- [ ] Tests run with Doppler (`doppler run --config test`) + +## Security + +- [ ] No SQL injection vulnerabilities (using ORM) +- [ ] No hardcoded secrets or credentials +- [ ] CORS origins from Doppler (not hardcoded) +- [ ] Input validation on all fields +- [ ] Output encoding prevents XSS +- [ ] Rate limiting protects against abuse + +## Performance + +- [ ] Database queries use indexes +- [ ] N+1 queries avoided +- [ ] Pagination prevents loading all records +- [ ] Heavy operations use background jobs +- [ ] Caching implemented where appropriate + +## Before Merging + +- [ ] All tests pass (`pytest`) +- [ ] Coverage >80% for new code +- [ ] OpenAPI docs reviewed at `/docs` +- [ ] Tested locally with Doppler +- [ ] Code reviewed by teammate +- [ ] No console.log or debug prints +- [ ] Migration created if schema changed diff --git a/skills/api-design-standards/checklists/security-review.md b/skills/api-design-standards/checklists/security-review.md new file mode 100644 index 0000000..1fca28f --- /dev/null +++ b/skills/api-design-standards/checklists/security-review.md @@ -0,0 +1,84 @@ +# API Security Review Checklist + +**Security-focused review for API endpoints.** + +## Authentication & Authorization + +- [ ] JWT required on all protected endpoints +- [ ] Token expiration enforced +- [ ] Superuser flag checked for admin operations +- [ ] No authentication bypass vulnerabilities +- [ ] Session management secure + +## Multi-Tenant Security + +- [ ] All queries filter by `tenant_id` +- [ ] No cross-tenant data leaks possible +- [ ] Repository pattern enforces isolation +- [ ] Admin endpoints verify superuser +- [ ] Test cases prove isolation + +## Input Validation + +- [ ] All inputs validated with Pydantic +- [ ] SQL injection prevented (using ORM) +- [ ] XSS prevented (output encoding) +- [ ] File uploads validated (type, size) +- [ ] Email format validated +- [ ] URL format validated +- [ ] Integer ranges validated +- [ ] String lengths validated + +## Sensitive Data + +- [ ] Passwords hashed with bcrypt +- [ ] Password hashes never returned +- [ ] Secrets from Doppler (not hardcoded) +- [ ] PII properly handled +- [ ] No sensitive data in logs +- [ ] No sensitive data in error messages + +## Rate Limiting + +- [ ] Public endpoints rate limited +- [ ] Login endpoints strictly rate limited +- [ ] Rate limit uses Redis +- [ ] Rate limit tested + +## CORS + +- [ ] Allowed origins from Doppler +- [ ] No `allow_origins=["*"]` in production +- [ ] Credentials allowed only for trusted origins +- [ ] Preflight requests handled + +## Error Handling + +- [ ] No stack traces in production responses +- [ ] Generic errors for security issues +- [ ] Detailed errors only in dev/test +- [ ] Errors logged server-side + +## OWASP Top 10 + +- [ ] A01: Broken Access Control - ✅ Tenant isolation +- [ ] A02: Cryptographic Failures - ✅ Bcrypt, Doppler +- [ ] A03: Injection - ✅ ORM, validation +- [ ] A04: Insecure Design - ✅ Repository pattern +- [ ] A05: Security Misconfiguration - ✅ CORS, secrets +- [ ] A06: Vulnerable Components - ✅ Updated dependencies +- [ ] A07: Identification Failures - ✅ JWT, rate limiting +- [ ] A08: Integrity Failures - ✅ Input validation +- [ ] A09: Logging Failures - ✅ Error logging +- [ ] A10: SSRF - ✅ URL validation + +## Before Production Deploy + +- [ ] Security scan passed +- [ ] No hardcoded secrets +- [ ] Doppler secrets configured +- [ ] CORS origins configured +- [ ] Rate limiting enabled +- [ ] HTTPS enforced +- [ ] Error handling tested +- [ ] Penetration test completed diff --git a/skills/api-design-standards/examples/INDEX.md b/skills/api-design-standards/examples/INDEX.md new file mode 100644 index 0000000..b4d171b --- /dev/null +++ b/skills/api-design-standards/examples/INDEX.md @@ -0,0 +1,17 @@ +# API Design Examples Index + +**All example files are under 500 lines for optimal loading.** + +## Available Examples + +- **[fastapi-crud.md](fastapi-crud.md)** - Complete CRUD endpoints with repository pattern (332 lines) +- **[pydantic-schemas.md](pydantic-schemas.md)** - Request/response schemas with validation +- **[tanstack-start.md](tanstack-start.md)** - TanStack Start server functions +- **[pagination.md](pagination.md)** - Offset and cursor-based pagination +- **[testing.md](testing.md)** - API endpoint testing with pytest + +## Quick Links + +- **Templates**: See [../templates/](../templates/) for copy-paste ready code +- **Reference**: See [../reference/](../reference/) for complete configurations +- **Checklists**: See [../checklists/](../checklists/) for API design validation diff --git a/skills/api-design-standards/examples/fastapi-crud.md b/skills/api-design-standards/examples/fastapi-crud.md new file mode 100644 index 0000000..80e6138 --- /dev/null +++ b/skills/api-design-standards/examples/fastapi-crud.md @@ -0,0 +1,332 @@ +# FastAPI CRUD Endpoints + +**Complete CRUD endpoint examples with repository pattern and tenant isolation.** + +## Complete User CRUD + +```python +# app/api/routes/users.py +from fastapi import APIRouter, Depends, HTTPException, status +from sqlmodel import Session +from typing import Optional + +from app.core.dependencies import get_current_user, get_db +from app.models.user import User +from app.repositories.user_repository import UserRepository +from app.schemas.user import UserCreate, UserRead, UserUpdate, PaginatedResponse + +router = APIRouter(prefix="/api/v1/users", tags=["users"]) + + +@router.get("", response_model=PaginatedResponse[UserRead], status_code=status.HTTP_200_OK) +async def list_users( + skip: int = 0, + limit: int = 100, + is_active: Optional[bool] = None, + email_contains: Optional[str] = None, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> PaginatedResponse[UserRead]: + """ + List all users in the current tenant with pagination and filtering. + + - **skip**: Number of records to skip (pagination offset) + - **limit**: Maximum number of records to return (max 100) + - **is_active**: Filter by active status (optional) + - **email_contains**: Filter by email substring (optional) + - **Returns**: Paginated list of users with public fields only + """ + repository = UserRepository(db, tenant_id=current_user.tenant_id) + + # Build filter criteria + filters = {} + if is_active is not None: + filters["is_active"] = is_active + if email_contains: + filters["email_contains"] = email_contains + + users = await repository.list(filters=filters, skip=skip, limit=limit) + total = await repository.count(filters=filters) + + return PaginatedResponse( + items=users, + total=total, + skip=skip, + limit=limit, + has_more=(skip + limit) < total, + ) + + +@router.get("/{user_id}", response_model=UserRead, status_code=status.HTTP_200_OK) +async def get_user( + user_id: str, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> UserRead: + """Get a single user by ID (tenant-isolated).""" + repository = UserRepository(db, tenant_id=current_user.tenant_id) + user = await repository.get_by_id(user_id) + if not user: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"User with ID {user_id} not found", + ) + return user + + +@router.post("", response_model=UserRead, status_code=status.HTTP_201_CREATED) +async def create_user( + user_data: UserCreate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> UserRead: + """ + Create a new user in the current tenant. + + - **email**: Valid email address (unique per tenant) + - **full_name**: User's full name (1-255 characters) + - **password**: At least 8 characters + - **Returns**: Created user with ID, timestamps, and public fields + + **Errors**: + - 409 Conflict: Email already exists in tenant + - 422 Validation Error: Invalid email or weak password + """ + repository = UserRepository(db, tenant_id=current_user.tenant_id) + + try: + user = await repository.create(user_data) + return user + except IntegrityError: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"User with email {user_data.email} already exists", + ) + + +@router.put("/{user_id}", response_model=UserRead, status_code=status.HTTP_200_OK) +async def update_user( + user_id: str, + user_data: UserUpdate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> UserRead: + """ + Update an existing user (tenant-isolated). + + All fields are optional - only provided fields will be updated. + """ + repository = UserRepository(db, tenant_id=current_user.tenant_id) + user = await repository.update(user_id, user_data) + if not user: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"User with ID {user_id} not found", + ) + return user + + +@router.patch("/{user_id}", response_model=UserRead, status_code=status.HTTP_200_OK) +async def partial_update_user( + user_id: str, + user_data: UserUpdate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> UserRead: + """ + Partially update an existing user (tenant-isolated). + + Same as PUT but semantically indicates partial update. + """ + repository = UserRepository(db, tenant_id=current_user.tenant_id) + user = await repository.update(user_id, user_data) + if not user: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"User with ID {user_id} not found", + ) + return user + + +@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete_user( + user_id: str, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> None: + """ + Soft-delete a user (tenant-isolated). + + Returns 204 No Content on success (no response body). + """ + repository = UserRepository(db, tenant_id=current_user.tenant_id) + success = await repository.delete(user_id) + if not success: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"User with ID {user_id} not found", + ) +``` + +## Repository Pattern with Tenant Isolation + +```python +# app/repositories/user_repository.py +from sqlmodel import Session, select, func +from typing import Optional +from datetime import datetime + +from app.models.user import User +from app.schemas.user import UserCreate, UserUpdate + + +class UserRepository: + """Repository with automatic tenant filtering.""" + + def __init__(self, db: Session, tenant_id: str): + self.db = db + self.tenant_id = tenant_id + + async def list( + self, filters: dict = None, skip: int = 0, limit: int = 100 + ) -> list[User]: + """List users (automatically filtered by tenant_id).""" + statement = select(User).where(User.tenant_id == self.tenant_id) + + # Apply additional filters + if filters: + if "is_active" in filters: + statement = statement.where(User.is_active == filters["is_active"]) + if "email_contains" in filters: + statement = statement.where(User.email.contains(filters["email_contains"])) + + statement = statement.offset(skip).limit(limit).order_by(User.created_at.desc()) + + result = await self.db.execute(statement) + return result.scalars().all() + + async def count(self, filters: dict = None) -> int: + """Count users (tenant-isolated).""" + statement = select(func.count(User.id)).where(User.tenant_id == self.tenant_id) + + # Apply same filters as list() + if filters: + if "is_active" in filters: + statement = statement.where(User.is_active == filters["is_active"]) + if "email_contains" in filters: + statement = statement.where(User.email.contains(filters["email_contains"])) + + result = await self.db.execute(statement) + return result.scalar_one() + + async def get_by_id(self, user_id: str) -> User | None: + """Get user by ID (tenant-isolated).""" + statement = select(User).where( + User.id == user_id, + User.tenant_id == self.tenant_id, + ) + result = await self.db.execute(statement) + return result.scalar_one_or_none() + + async def create(self, user_data: UserCreate) -> User: + """Create a new user in the current tenant.""" + from app.utils.password import hash_password + + user = User( + email=user_data.email, + full_name=user_data.full_name, + hashed_password=hash_password(user_data.password), + tenant_id=self.tenant_id, # Automatic tenant assignment + ) + self.db.add(user) + await self.db.commit() + await self.db.refresh(user) + return user + + async def update(self, user_id: str, user_data: UserUpdate) -> User | None: + """Update an existing user (tenant-isolated).""" + user = await self.get_by_id(user_id) + if not user: + return None + + # Update only provided fields + update_data = user_data.model_dump(exclude_unset=True) + for field, value in update_data.items(): + setattr(user, field, value) + + await self.db.commit() + await self.db.refresh(user) + return user + + async def delete(self, user_id: str) -> bool: + """Soft-delete a user (tenant-isolated).""" + user = await self.get_by_id(user_id) + if not user: + return False + + # Soft delete by setting deleted_at timestamp + user.deleted_at = datetime.utcnow() + await self.db.commit() + return True +``` + +## Nested Resources + +```python +# app/api/routes/organizations.py +from fastapi import APIRouter, Depends, HTTPException, status + +router = APIRouter(prefix="/api/v1/organizations", tags=["organizations"]) + + +@router.get("/{org_id}/teams", response_model=list[TeamRead]) +async def list_organization_teams( + org_id: str, + skip: int = 0, + limit: int = 100, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> list[TeamRead]: + """List all teams in an organization (tenant-isolated).""" + # Verify organization exists and belongs to tenant + org_repo = OrganizationRepository(db, tenant_id=current_user.tenant_id) + org = await org_repo.get_by_id(org_id) + if not org: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Organization with ID {org_id} not found", + ) + + # Fetch teams for organization + team_repo = TeamRepository(db, tenant_id=current_user.tenant_id) + teams = await team_repo.list_by_organization(org_id, skip=skip, limit=limit) + return teams + + +@router.post("/{org_id}/teams", response_model=TeamRead, status_code=status.HTTP_201_CREATED) +async def create_organization_team( + org_id: str, + team_data: TeamCreate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> TeamRead: + """Create a new team in an organization.""" + # Verify organization exists + org_repo = OrganizationRepository(db, tenant_id=current_user.tenant_id) + org = await org_repo.get_by_id(org_id) + if not org: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Organization with ID {org_id} not found", + ) + + # Create team + team_repo = TeamRepository(db, tenant_id=current_user.tenant_id) + team = await team_repo.create(team_data, organization_id=org_id) + return team +``` + +**See also:** +- [pydantic-schemas.md](pydantic-schemas.md) - Request/response schema patterns +- [pagination.md](pagination.md) - Pagination and filtering examples +- [../templates/fastapi-crud-endpoint.py](../templates/fastapi-crud-endpoint.py) - CRUD template diff --git a/skills/api-design-standards/examples/pagination.md b/skills/api-design-standards/examples/pagination.md new file mode 100644 index 0000000..3fa5d5a --- /dev/null +++ b/skills/api-design-standards/examples/pagination.md @@ -0,0 +1,38 @@ +# Pagination Patterns + +**Offset-based and cursor-based pagination examples.** + +## Offset-Based Pagination + +```python +class PaginatedResponse[T](BaseModel): + items: list[T] + total: int + skip: int + limit: int + has_more: bool + +@router.get("", response_model=PaginatedResponse[UserRead]) +async def list_users(skip: int = 0, limit: int = 100): + repository = UserRepository(db, tenant_id=current_user.tenant_id) + users = await repository.list(skip=skip, limit=limit) + total = await repository.count() + return PaginatedResponse(items=users, total=total, skip=skip, limit=limit, has_more=(skip + limit) < total) +``` + +## Cursor-Based Pagination (Recommended) + +```python +class CursorPaginatedResponse[T](BaseModel): + items: list[T] + next_cursor: Optional[str] + has_more: bool + +@router.get("") +async def list_users(cursor: Optional[str] = None, limit: int = 100): + users = await repository.list_cursor(cursor=cursor, limit=limit) + next_cursor = users[-1].id if len(users) == limit else None + return CursorPaginatedResponse(items=users, next_cursor=next_cursor, has_more=next_cursor is not None) +``` + +**See also:** [fastapi-crud.md](fastapi-crud.md) for complete examples diff --git a/skills/api-design-standards/examples/pydantic-schemas.md b/skills/api-design-standards/examples/pydantic-schemas.md new file mode 100644 index 0000000..b70b00c --- /dev/null +++ b/skills/api-design-standards/examples/pydantic-schemas.md @@ -0,0 +1,149 @@ +# Pydantic Schema Examples + +**Complete Pydantic schema patterns for request/response validation.** + +## Request/Response Schemas + +```python +# app/schemas/user.py +from pydantic import BaseModel, EmailStr, Field, ConfigDict, field_validator, model_validator +from datetime import datetime +from typing import Optional + + +class UserBase(BaseModel): + """Shared fields for User schemas.""" + email: EmailStr + full_name: str = Field(..., min_length=1, max_length=255) + is_active: bool = True + + +class UserCreate(UserBase): + """Schema for creating a new user.""" + password: str = Field(..., min_length=8, max_length=100) + password_confirm: str = Field(..., min_length=8, max_length=100) + + @field_validator("password") + @classmethod + def validate_password_strength(cls, v: str) -> str: + """Ensure password meets complexity requirements.""" + if len(v) < 8: + raise ValueError("Password must be at least 8 characters") + if not any(char.isdigit() for char in v): + raise ValueError("Password must contain at least one digit") + if not any(char.isupper() for char in v): + raise ValueError("Password must contain at least one uppercase letter") + if not any(char.islower() for char in v): + raise ValueError("Password must contain at least one lowercase letter") + return v + + @model_validator(mode="after") + def passwords_match(self) -> "UserCreate": + """Ensure password and password_confirm match.""" + if self.password != self.password_confirm: + raise ValueError("Passwords do not match") + return self + + +class UserUpdate(BaseModel): + """Schema for updating an existing user (all fields optional).""" + email: Optional[EmailStr] = None + full_name: Optional[str] = Field(None, min_length=1, max_length=255) + is_active: Optional[bool] = None + + +class UserRead(UserBase): + """Schema for reading user data (public fields only).""" + id: str + tenant_id: str + created_at: datetime + updated_at: datetime + + model_config = ConfigDict(from_attributes=True) + + +class PaginatedResponse[T](BaseModel): + """Generic paginated response.""" + items: list[T] + total: int + skip: int + limit: int + has_more: bool +``` + +## Nested Schemas + +```python +# app/schemas/organization.py +from pydantic import BaseModel, ConfigDict, Field +from datetime import datetime +from typing import Optional +from app.schemas.team import TeamRead + + +class OrganizationBase(BaseModel): + """Shared fields for Organization schemas.""" + name: str = Field(..., min_length=1, max_length=255) + description: Optional[str] = None + + +class OrganizationCreate(OrganizationBase): + """Schema for creating a new organization.""" + pass + + +class OrganizationRead(OrganizationBase): + """Organization with nested teams.""" + id: str + tenant_id: str + teams: list[TeamRead] = [] # Nested teams + created_at: datetime + updated_at: datetime + + model_config = ConfigDict(from_attributes=True) +``` + +## Custom Validation + +```python +# app/schemas/user.py +from pydantic import field_validator, model_validator +import re + + +class UserCreate(BaseModel): + email: EmailStr + username: str + password: str + + @field_validator("username") + @classmethod + def validate_username(cls, v: str) -> str: + """Ensure username is alphanumeric.""" + if not re.match(r"^[a-zA-Z0-9_-]+$", v): + raise ValueError("Username must contain only letters, numbers, hyphens, and underscores") + if len(v) < 3: + raise ValueError("Username must be at least 3 characters") + return v + + @field_validator("email") + @classmethod + def validate_email_domain(cls, v: str) -> str: + """Ensure email is from allowed domain.""" + allowed_domains = ["example.com", "greyhaven.studio"] + domain = v.split("@")[1] + if domain not in allowed_domains: + raise ValueError(f"Email must be from {', '.join(allowed_domains)}") + return v + + @model_validator(mode="after") + def validate_username_not_in_email(self) -> "UserCreate": + """Ensure username is not part of email.""" + if self.username.lower() in self.email.lower(): + raise ValueError("Username cannot be part of email address") + return self +``` + +**See also:** +- [fastapi-crud.md](fastapi-crud.md) - CRUD endpoints using these schemas +- [../templates/pydantic-schemas.py](../templates/pydantic-schemas.py) - Schema template diff --git a/skills/api-design-standards/examples/tanstack-start.md b/skills/api-design-standards/examples/tanstack-start.md new file mode 100644 index 0000000..6690f8c --- /dev/null +++ b/skills/api-design-standards/examples/tanstack-start.md @@ -0,0 +1,71 @@ +# TanStack Start Server Functions + +**Complete server function examples with Drizzle ORM and tenant isolation.** + +See [../templates/tanstack-server-function.ts](../templates/tanstack-server-function.ts) for full template. + +## Complete CRUD Server Functions + +```typescript +// app/routes/api/users.ts +import { createServerFn } from "@tanstack/start"; +import { z } from "zod"; +import { db } from "~/utils/db.server"; +import { usersTable } from "~/db/schema"; +import { getAuthUser } from "~/utils/auth.server"; +import { eq, and, like, count, desc } from "drizzle-orm"; +import { hashPassword } from "~/utils/password.server"; + +// Validation schemas +const createUserSchema = z.object({ + email: z.string().email(), + fullName: z.string().min(1).max(255), + password: z.string().min(8), +}); + +const updateUserSchema = z.object({ + email: z.string().email().optional(), + fullName: z.string().min(1).max(255).optional(), + isActive: z.boolean().optional(), +}); + +// List users +export const listUsers = createServerFn({ method: "GET" }) + .validator(z.object({ skip: z.number().min(0).default(0), limit: z.number().min(1).max(100).default(100) })) + .handler(async ({ data, context }) => { + const authUser = await getAuthUser(context); + if (!authUser) throw new Error("Unauthorized", { status: 401 }); + + const users = await db.select().from(usersTable) + .where(eq(usersTable.tenantId, authUser.tenantId)) + .orderBy(desc(usersTable.createdAt)) + .limit(data.limit).offset(data.skip); + + const [{ count: total }] = await db.select({ count: count() }) + .from(usersTable).where(eq(usersTable.tenantId, authUser.tenantId)); + + return { + items: users.map(({ hashedPassword, ...user }) => user), + total, skip: data.skip, limit: data.limit, + hasMore: data.skip + data.limit < total, + }; + }); + +// Create user +export const createUser = createServerFn({ method: "POST" }) + .validator(createUserSchema) + .handler(async ({ data, context }) => { + const authUser = await getAuthUser(context); + if (!authUser) throw new Error("Unauthorized", { status: 401 }); + + const [user] = await db.insert(usersTable).values({ + ...data, hashedPassword: await hashPassword(data.password), + tenantId: authUser.tenantId, + }).returning(); + + const { hashedPassword: _, ...userPublic } = user; + return userPublic; + }); +``` + +**See also:** [fastapi-crud.md](fastapi-crud.md) for FastAPI examples diff --git a/skills/api-design-standards/examples/testing.md b/skills/api-design-standards/examples/testing.md new file mode 100644 index 0000000..47194d7 --- /dev/null +++ b/skills/api-design-standards/examples/testing.md @@ -0,0 +1,22 @@ +# API Testing Examples + +**pytest examples for FastAPI endpoints.** + +```python +# tests/api/test_users.py +import pytest +from fastapi.testclient import TestClient + +@pytest.mark.integration +def test_create_user(test_token: str): + response = client.post("/api/v1/users", json={"email": "test@example.com", "full_name": "Test", "password": "Pass123"}, headers={"Authorization": f"Bearer {test_token}"}) + assert response.status_code == 201 + assert "id" in response.json() + +@pytest.mark.integration +def test_tenant_isolation(test_token: str): + response = client.get(f"/api/v1/users/{other_tenant_user_id}", headers={"Authorization": f"Bearer {test_token}"}) + assert response.status_code == 404 # Cannot access other tenant's data +``` + +Run with: `doppler run --config test -- pytest tests/api/ -v` diff --git a/skills/api-design-standards/reference/INDEX.md b/skills/api-design-standards/reference/INDEX.md new file mode 100644 index 0000000..61d11c6 --- /dev/null +++ b/skills/api-design-standards/reference/INDEX.md @@ -0,0 +1,11 @@ +# API Design Reference Index + +**All reference files are under 500 lines for optimal loading.** + +## Available References + +- **[fastapi-setup.md](fastapi-setup.md)** - Main FastAPI configuration +- **[openapi.md](openapi.md)** - OpenAPI customization +- **[error-handlers.md](error-handlers.md)** - Exception handlers +- **[authentication.md](authentication.md)** - JWT configuration +- **[cors-rate-limiting.md](cors-rate-limiting.md)** - CORS and rate limiting diff --git a/skills/api-design-standards/reference/authentication.md b/skills/api-design-standards/reference/authentication.md new file mode 100644 index 0000000..8d1ce71 --- /dev/null +++ b/skills/api-design-standards/reference/authentication.md @@ -0,0 +1,24 @@ +# Authentication Configuration + +**JWT setup with bcrypt password hashing.** + +```python +# app/core/auth.py +import jwt +from passlib.context import CryptContext +from datetime import datetime, timedelta +import os + +JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY") +JWT_ALGORITHM = "HS256" +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +def create_access_token(user_id: str, tenant_id: str) -> str: + expire = datetime.utcnow() + timedelta(minutes=30) + return jwt.encode({"sub": user_id, "tenant_id": tenant_id, "exp": expire}, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM) + +def hash_password(password: str) -> str: + return pwd_context.hash(password) +``` + +**Doppler:** `JWT_SECRET_KEY` must be set in Doppler secrets. diff --git a/skills/api-design-standards/reference/cors-rate-limiting.md b/skills/api-design-standards/reference/cors-rate-limiting.md new file mode 100644 index 0000000..05a4379 --- /dev/null +++ b/skills/api-design-standards/reference/cors-rate-limiting.md @@ -0,0 +1,26 @@ +# CORS and Rate Limiting + +**CORS middleware and Upstash Redis rate limiter.** + +## CORS + +```python +allowed_origins = os.getenv("CORS_ALLOWED_ORIGINS", "").split(",") +app.add_middleware(CORSMiddleware, allow_origins=allowed_origins, allow_credentials=True) +``` + +**Doppler:** `CORS_ALLOWED_ORIGINS="https://app.example.com"` + +## Rate Limiting + +See [../templates/rate-limiter.py](../templates/rate-limiter.py) for full implementation. + +```python +from app.core.rate_limit import rate_limit_normal + +@router.get("", dependencies=[Depends(rate_limit_normal)]) +async def list_users(): + pass # Rate limited to 100 req/min +``` + +**Doppler:** `REDIS_URL` must be set for Upstash Redis. diff --git a/skills/api-design-standards/reference/error-handlers.md b/skills/api-design-standards/reference/error-handlers.md new file mode 100644 index 0000000..be03443 --- /dev/null +++ b/skills/api-design-standards/reference/error-handlers.md @@ -0,0 +1,17 @@ +# Error Handlers + +**Complete exception handler configuration.** + +See [../templates/error-handler.py](../templates/error-handler.py) for full implementation. + +```python +async def http_exception_handler(request, exc): + return JSONResponse(status_code=exc.status_code, content={"error": exc.detail, "status_code": exc.status_code}) + +async def validation_exception_handler(request, exc): + errors = [{"field": ".".join(str(loc) for loc in e["loc"]), "message": e["msg"], "code": e["type"]} for e in exc.errors()] + return JSONResponse(status_code=422, content={"error": "Validation error", "detail": errors, "status_code": 422}) + +app.add_exception_handler(HTTPException, http_exception_handler) +app.add_exception_handler(RequestValidationError, validation_exception_handler) +``` diff --git a/skills/api-design-standards/reference/fastapi-setup.md b/skills/api-design-standards/reference/fastapi-setup.md new file mode 100644 index 0000000..8d6d87b --- /dev/null +++ b/skills/api-design-standards/reference/fastapi-setup.md @@ -0,0 +1,34 @@ +# FastAPI Setup + +**Complete main application configuration.** + +```python +# app/main.py +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +import os + +app = FastAPI( + title="Grey Haven API", + description="RESTful API for multi-tenant SaaS", + version="1.0.0", + docs_url="/docs", + redoc_url="/redoc", +) + +# CORS +allowed_origins = os.getenv("CORS_ALLOWED_ORIGINS", "").split(",") +app.add_middleware(CORSMiddleware, allow_origins=allowed_origins, allow_credentials=True) + +# Exception handlers (see error-handlers.md) +# app.add_exception_handler(...) + +# Include routers +app.include_router(users.router) + +@app.get("/health") +async def health_check(): + return {"status": "healthy"} +``` + +**Doppler config:** `doppler run --config dev -- uvicorn app.main:app --reload` diff --git a/skills/api-design-standards/reference/openapi.md b/skills/api-design-standards/reference/openapi.md new file mode 100644 index 0000000..b0788ff --- /dev/null +++ b/skills/api-design-standards/reference/openapi.md @@ -0,0 +1,17 @@ +# OpenAPI Customization + +**Custom OpenAPI schema with security.** + +```python +def custom_openapi(app): + openapi_schema = get_openapi(title=app.title, version=app.version, routes=app.routes) + openapi_schema["components"]["securitySchemes"] = { + "BearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"} + } + openapi_schema["security"] = [{"BearerAuth": []}] + return openapi_schema + +app.openapi = lambda: custom_openapi(app) +``` + +Access docs at `/docs` (Swagger UI) or `/redoc` (ReDoc). diff --git a/skills/api-design-standards/templates/error-handler.py b/skills/api-design-standards/templates/error-handler.py new file mode 100644 index 0000000..d8d7827 --- /dev/null +++ b/skills/api-design-standards/templates/error-handler.py @@ -0,0 +1,115 @@ +# Grey Haven Studio - Error Handler Template +# Add this to app/core/exceptions.py + +from fastapi import HTTPException, Request, status +from fastapi.responses import JSONResponse +from fastapi.exceptions import RequestValidationError +from sqlalchemy.exc import IntegrityError, OperationalError +import logging + +logger = logging.getLogger(__name__) + + +async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse: + """Handle HTTPException with standard error format.""" + logger.warning(f"HTTP exception: {exc.status_code} - {exc.detail}") + + return JSONResponse( + status_code=exc.status_code, + content={ + "error": exc.detail, + "status_code": exc.status_code, + }, + headers=exc.headers, + ) + + +async def validation_exception_handler( + request: Request, exc: RequestValidationError +) -> JSONResponse: + """Handle Pydantic validation errors.""" + errors = [] + for error in exc.errors(): + field_path = ".".join(str(loc) for loc in error["loc"] if loc != "body") + errors.append( + { + "field": field_path if field_path else None, + "message": error["msg"], + "code": error["type"], + } + ) + + logger.warning(f"Validation error: {errors}") + + return JSONResponse( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + content={ + "error": "Validation error", + "detail": errors, + "status_code": 422, + }, + ) + + +async def integrity_error_handler( + request: Request, exc: IntegrityError +) -> JSONResponse: + """Handle database integrity errors.""" + logger.error(f"Integrity error: {exc}") + + error_detail = "A resource with this unique value already exists" + if "foreign key" in str(exc).lower(): + error_detail = "Referenced resource does not exist" + + return JSONResponse( + status_code=status.HTTP_409_CONFLICT, + content={ + "error": error_detail, + "status_code": 409, + }, + ) + + +async def operational_error_handler( + request: Request, exc: OperationalError +) -> JSONResponse: + """Handle database operational errors.""" + logger.error(f"Database operational error: {exc}") + + return JSONResponse( + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + content={ + "error": "Service temporarily unavailable", + "detail": "Database connection error. Please try again later.", + "status_code": 503, + }, + ) + + +async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse: + """Catch-all handler for unexpected exceptions.""" + logger.exception(f"Unhandled exception: {exc}") + + return JSONResponse( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + content={ + "error": "Internal server error", + "status_code": 500, + }, + ) + + +# Register in main.py: +# from app.core.exceptions import ( +# http_exception_handler, +# validation_exception_handler, +# integrity_error_handler, +# operational_error_handler, +# generic_exception_handler, +# ) +# +# app.add_exception_handler(HTTPException, http_exception_handler) +# app.add_exception_handler(RequestValidationError, validation_exception_handler) +# app.add_exception_handler(IntegrityError, integrity_error_handler) +# app.add_exception_handler(OperationalError, operational_error_handler) +# app.add_exception_handler(Exception, generic_exception_handler) diff --git a/skills/api-design-standards/templates/fastapi-crud-endpoint.py b/skills/api-design-standards/templates/fastapi-crud-endpoint.py new file mode 100644 index 0000000..a81ebf4 --- /dev/null +++ b/skills/api-design-standards/templates/fastapi-crud-endpoint.py @@ -0,0 +1,130 @@ +# Grey Haven Studio - FastAPI CRUD Endpoint Template +# Copy this template for new resource endpoints + +from fastapi import APIRouter, Depends, HTTPException, status +from sqlmodel import Session +from typing import Optional + +from app.core.dependencies import get_current_user, get_db +from app.models.user import User +from app.repositories.resource_repository import ResourceRepository # TODO: Update import +from app.schemas.resource import ResourceCreate, ResourceRead, ResourceUpdate, PaginatedResponse # TODO: Update import + +# TODO: Update prefix, tags, and model name +router = APIRouter(prefix="/api/v1/resources", tags=["resources"]) + + +@router.get("", response_model=PaginatedResponse[ResourceRead], status_code=status.HTTP_200_OK) +async def list_resources( + skip: int = 0, + limit: int = 100, + # TODO: Add filter parameters as needed + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> PaginatedResponse[ResourceRead]: + """ + List all resources in the current tenant with pagination. + + - **skip**: Number of records to skip (pagination offset) + - **limit**: Maximum number of records to return (max 100) + - **Returns**: Paginated list of resources + """ + repository = ResourceRepository(db, tenant_id=current_user.tenant_id) + + # TODO: Build filter criteria if needed + filters = {} + + resources = await repository.list(filters=filters, skip=skip, limit=limit) + total = await repository.count(filters=filters) + + return PaginatedResponse( + items=resources, + total=total, + skip=skip, + limit=limit, + has_more=(skip + limit) < total, + ) + + +@router.get("/{resource_id}", response_model=ResourceRead, status_code=status.HTTP_200_OK) +async def get_resource( + resource_id: str, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> ResourceRead: + """Get a single resource by ID (tenant-isolated).""" + repository = ResourceRepository(db, tenant_id=current_user.tenant_id) + resource = await repository.get_by_id(resource_id) + if not resource: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Resource with ID {resource_id} not found", + ) + return resource + + +@router.post("", response_model=ResourceRead, status_code=status.HTTP_201_CREATED) +async def create_resource( + resource_data: ResourceCreate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> ResourceRead: + """ + Create a new resource in the current tenant. + + **Errors**: + - 409 Conflict: Duplicate resource + - 422 Validation Error: Invalid data + """ + repository = ResourceRepository(db, tenant_id=current_user.tenant_id) + + try: + resource = await repository.create(resource_data) + return resource + except IntegrityError: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Resource with this identifier already exists", + ) + + +@router.put("/{resource_id}", response_model=ResourceRead, status_code=status.HTTP_200_OK) +async def update_resource( + resource_id: str, + resource_data: ResourceUpdate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> ResourceRead: + """ + Update an existing resource (tenant-isolated). + + All fields are optional - only provided fields will be updated. + """ + repository = ResourceRepository(db, tenant_id=current_user.tenant_id) + resource = await repository.update(resource_id, resource_data) + if not resource: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Resource with ID {resource_id} not found", + ) + return resource + + +@router.delete("/{resource_id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete_resource( + resource_id: str, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> None: + """ + Soft-delete a resource (tenant-isolated). + + Returns 204 No Content on success (no response body). + """ + repository = ResourceRepository(db, tenant_id=current_user.tenant_id) + success = await repository.delete(resource_id) + if not success: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Resource with ID {resource_id} not found", + ) diff --git a/skills/api-design-standards/templates/pydantic-schemas.py b/skills/api-design-standards/templates/pydantic-schemas.py new file mode 100644 index 0000000..f27a996 --- /dev/null +++ b/skills/api-design-standards/templates/pydantic-schemas.py @@ -0,0 +1,61 @@ +# Grey Haven Studio - Pydantic Schema Template +# Copy this template for new resource schemas + +from pydantic import BaseModel, EmailStr, Field, ConfigDict, field_validator +from datetime import datetime +from typing import Optional + + +# TODO: Update model name +class ResourceBase(BaseModel): + """Shared fields for Resource schemas.""" + + # TODO: Add your base fields here + name: str = Field(..., min_length=1, max_length=255) + description: Optional[str] = None + is_active: bool = True + + +class ResourceCreate(ResourceBase): + """Schema for creating a new resource.""" + + # TODO: Add creation-specific fields + # Example: password, external_id, etc. + + @field_validator("name") + @classmethod + def validate_name(cls, v: str) -> str: + """Add custom validation if needed.""" + if not v.strip(): + raise ValueError("Name cannot be empty or whitespace") + return v.strip() + + +class ResourceUpdate(BaseModel): + """Schema for updating an existing resource (all fields optional).""" + + # TODO: Add updateable fields (all optional) + name: Optional[str] = Field(None, min_length=1, max_length=255) + description: Optional[str] = None + is_active: Optional[bool] = None + + +class ResourceRead(ResourceBase): + """Schema for reading resource data (public fields only).""" + + id: str + tenant_id: str + created_at: datetime + updated_at: datetime + + model_config = ConfigDict(from_attributes=True) + + +class PaginatedResponse[T](BaseModel): + """Generic paginated response.""" + + items: list[T] + total: int + skip: int + limit: int + has_more: bool diff --git a/skills/api-design-standards/templates/rate-limiter.py b/skills/api-design-standards/templates/rate-limiter.py new file mode 100644 index 0000000..e5c70fd --- /dev/null +++ b/skills/api-design-standards/templates/rate-limiter.py @@ -0,0 +1,82 @@ +# Grey Haven Studio - Rate Limiter Template +# Add this to app/core/rate_limit.py + +from fastapi import Request, HTTPException, status +from upstash_redis import Redis +import os + +# Doppler provides REDIS_URL +redis = Redis.from_url(os.getenv("REDIS_URL")) + + +class RateLimiter: + """ + Rate limiter using Upstash Redis. + + Usage: + rate_limit_strict = RateLimiter(max_requests=10, window=60) + rate_limit_normal = RateLimiter(max_requests=100, window=60) + + @router.post("", dependencies=[Depends(rate_limit_strict)]) + async def create_resource(): + pass + """ + + def __init__(self, max_requests: int = 100, window: int = 60): + """ + Initialize rate limiter. + + Args: + max_requests: Maximum requests allowed in window + window: Time window in seconds + """ + self.max_requests = max_requests + self.window = window + + async def __call__(self, request: Request): + """Check rate limit for current request.""" + # Get client identifier (IP address or user ID from JWT) + client_id = self._get_client_id(request) + + # Rate limit key + key = f"rate_limit:{client_id}:{request.url.path}" + + # Increment counter + count = redis.incr(key) + + # Set expiration on first request + if count == 1: + redis.expire(key, self.window) + + # Check if limit exceeded + if count > self.max_requests: + retry_after = redis.ttl(key) + raise HTTPException( + status_code=status.HTTP_429_TOO_MANY_REQUESTS, + detail=f"Rate limit exceeded. Try again in {retry_after} seconds.", + headers={"Retry-After": str(retry_after)}, + ) + + def _get_client_id(self, request: Request) -> str: + """Get client identifier (IP or user ID).""" + # Prefer user ID from JWT if available + if hasattr(request.state, "user") and request.state.user: + return f"user:{request.state.user.id}" + + # Fallback to IP address + return f"ip:{request.client.host}" + + +# Rate limit configurations +rate_limit_strict = RateLimiter(max_requests=10, window=60) # 10 req/min +rate_limit_normal = RateLimiter(max_requests=100, window=60) # 100 req/min +rate_limit_relaxed = RateLimiter(max_requests=1000, window=60) # 1000 req/min + + +# Apply to routes: +# from app.core.rate_limit import rate_limit_strict, rate_limit_normal +# +# @router.post("", dependencies=[Depends(rate_limit_strict)]) +# async def create_user(): +# """Create user (10 req/min limit).""" +# pass diff --git a/skills/api-design-standards/templates/repository-pattern.py b/skills/api-design-standards/templates/repository-pattern.py new file mode 100644 index 0000000..320b7d0 --- /dev/null +++ b/skills/api-design-standards/templates/repository-pattern.py @@ -0,0 +1,101 @@ +# Grey Haven Studio - Repository Pattern Template +# Copy this template for new resource repositories + +from sqlmodel import Session, select, func +from typing import Optional +from uuid import UUID + +from app.models.resource import Resource # TODO: Update import +from app.schemas.resource import ResourceCreate, ResourceUpdate # TODO: Update import + + +# TODO: Update class name +class ResourceRepository: + """Repository with automatic tenant filtering for Resource model.""" + + def __init__(self, db: Session, tenant_id: str): + self.db = db + self.tenant_id = tenant_id + + async def list( + self, filters: dict = None, skip: int = 0, limit: int = 100 + ) -> list[Resource]: + """List resources (automatically filtered by tenant_id).""" + statement = select(Resource).where(Resource.tenant_id == self.tenant_id) + + # Apply additional filters + if filters: + # TODO: Add your filter logic here + # Example: + # if "is_active" in filters: + # statement = statement.where(Resource.is_active == filters["is_active"]) + pass + + statement = statement.offset(skip).limit(limit).order_by(Resource.created_at.desc()) + + result = await self.db.execute(statement) + return result.scalars().all() + + async def count(self, filters: dict = None) -> int: + """Count resources (tenant-isolated).""" + statement = select(func.count(Resource.id)).where( + Resource.tenant_id == self.tenant_id + ) + + # Apply same filters as list() + if filters: + # TODO: Add same filter logic as list() + pass + + result = await self.db.execute(statement) + return result.scalar_one() + + async def get_by_id(self, resource_id: str) -> Resource | None: + """Get resource by ID (tenant-isolated).""" + statement = select(Resource).where( + Resource.id == resource_id, + Resource.tenant_id == self.tenant_id, + ) + result = await self.db.execute(statement) + return result.scalar_one_or_none() + + async def create(self, resource_data: ResourceCreate) -> Resource: + """Create a new resource in the current tenant.""" + resource = Resource( + **resource_data.model_dump(), + tenant_id=self.tenant_id, # Automatic tenant assignment + ) + self.db.add(resource) + await self.db.commit() + await self.db.refresh(resource) + return resource + + async def update( + self, resource_id: str, resource_data: ResourceUpdate + ) -> Resource | None: + """Update an existing resource (tenant-isolated).""" + resource = await self.get_by_id(resource_id) + if not resource: + return None + + # Update only provided fields + update_data = resource_data.model_dump(exclude_unset=True) + for field, value in update_data.items(): + setattr(resource, field, value) + + await self.db.commit() + await self.db.refresh(resource) + return resource + + async def delete(self, resource_id: str) -> bool: + """Soft-delete a resource (tenant-isolated).""" + resource = await self.get_by_id(resource_id) + if not resource: + return False + + # Soft delete by setting deleted_at timestamp + from datetime import datetime + + resource.deleted_at = datetime.utcnow() + await self.db.commit() + return True diff --git a/skills/api-design-standards/templates/tanstack-server-function.ts b/skills/api-design-standards/templates/tanstack-server-function.ts new file mode 100644 index 0000000..324e259 --- /dev/null +++ b/skills/api-design-standards/templates/tanstack-server-function.ts @@ -0,0 +1,186 @@ +// Grey Haven Studio - TanStack Start Server Function Template +// Copy this template for new TanStack Start server functions + +import { createServerFn } from "@tanstack/start"; +import { z } from "zod"; +import { db } from "~/utils/db.server"; +import { resourcesTable } from "~/db/schema"; // TODO: Update import +import { getAuthUser } from "~/utils/auth.server"; +import { eq, and, like, count, desc } from "drizzle-orm"; + +// TODO: Update validation schemas +const createResourceSchema = z.object({ + name: z.string().min(1).max(255), + description: z.string().optional(), +}); + +const updateResourceSchema = z.object({ + name: z.string().min(1).max(255).optional(), + description: z.string().optional(), + isActive: z.boolean().optional(), +}); + +const listResourcesSchema = z.object({ + skip: z.number().min(0).default(0), + limit: z.number().min(1).max(100).default(100), + // TODO: Add filter fields +}); + +// List resources +export const listResources = createServerFn({ method: "GET" }) + .validator(listResourcesSchema) + .handler(async ({ data, context }) => { + const authUser = await getAuthUser(context); + if (!authUser) { + throw new Error("Unauthorized", { status: 401 }); + } + + // Build query with tenant filter + let query = db + .select() + .from(resourcesTable) + .where(eq(resourcesTable.tenantId, authUser.tenantId)); + + // TODO: Apply additional filters + + // Apply pagination + const resources = await query + .orderBy(desc(resourcesTable.createdAt)) + .limit(data.limit) + .offset(data.skip); + + // Get total count + const [{ count: total }] = await db + .select({ count: count() }) + .from(resourcesTable) + .where(eq(resourcesTable.tenantId, authUser.tenantId)); + + return { + items: resources, + total, + skip: data.skip, + limit: data.limit, + hasMore: data.skip + data.limit < total, + }; + }); + +// Get resource by ID +export const getResource = createServerFn({ method: "GET" }) + .validator(z.object({ resourceId: z.string() })) + .handler(async ({ data, context }) => { + const authUser = await getAuthUser(context); + if (!authUser) { + throw new Error("Unauthorized", { status: 401 }); + } + + const [resource] = await db + .select() + .from(resourcesTable) + .where( + and( + eq(resourcesTable.id, data.resourceId), + eq(resourcesTable.tenantId, authUser.tenantId) + ) + ) + .limit(1); + + if (!resource) { + throw new Error("Resource not found", { status: 404 }); + } + + return resource; + }); + +// Create resource +export const createResource = createServerFn({ method: "POST" }) + .validator(createResourceSchema) + .handler(async ({ data, context }) => { + const authUser = await getAuthUser(context); + if (!authUser) { + throw new Error("Unauthorized", { status: 401 }); + } + + // TODO: Check for duplicates if needed + + // Create resource (tenant_id from auth context) + const [resource] = await db + .insert(resourcesTable) + .values({ + ...data, + tenantId: authUser.tenantId, + }) + .returning(); + + return resource; + }); + +// Update resource +export const updateResource = createServerFn({ method: "PUT" }) + .validator( + z.object({ + resourceId: z.string(), + data: updateResourceSchema, + }) + ) + .handler(async ({ data, context }) => { + const authUser = await getAuthUser(context); + if (!authUser) { + throw new Error("Unauthorized", { status: 401 }); + } + + // Verify resource exists and belongs to tenant + const [existing] = await db + .select() + .from(resourcesTable) + .where( + and( + eq(resourcesTable.id, data.resourceId), + eq(resourcesTable.tenantId, authUser.tenantId) + ) + ) + .limit(1); + + if (!existing) { + throw new Error("Resource not found", { status: 404 }); + } + + // Update resource + const [resource] = await db + .update(resourcesTable) + .set({ + ...data.data, + updatedAt: new Date(), + }) + .where(eq(resourcesTable.id, data.resourceId)) + .returning(); + + return resource; + }); + +// Delete resource +export const deleteResource = createServerFn({ method: "DELETE" }) + .validator(z.object({ resourceId: z.string() })) + .handler(async ({ data, context }) => { + const authUser = await getAuthUser(context); + if (!authUser) { + throw new Error("Unauthorized", { status: 401 }); + } + + // Soft delete by setting deletedAt + const result = await db + .update(resourcesTable) + .set({ deletedAt: new Date() }) + .where( + and( + eq(resourcesTable.id, data.resourceId), + eq(resourcesTable.tenantId, authUser.tenantId) + ) + ) + .returning(); + + if (result.length === 0) { + throw new Error("Resource not found", { status: 404 }); + } + + return { success: true }; + }); diff --git a/skills/code-style/EXAMPLES.md b/skills/code-style/EXAMPLES.md new file mode 100644 index 0000000..4de69cb --- /dev/null +++ b/skills/code-style/EXAMPLES.md @@ -0,0 +1,1023 @@ +# Code Examples + +Copy-paste ready examples from Grey Haven Studio production templates. + +## TypeScript/React Examples + +### React Component Structure + +Complete example from [cvi-template/src/routes/settings/profile.tsx](https://github.com/greyhaven-ai/cvi-template): + +```typescript +import { createFileRoute } from "@tanstack/react-router"; +import { useRef, useState } from "react"; +import { useAuth } from "~/components/auth/provider"; +import { ImageUploadDialog } from "~/components/settings/image-upload-dialog"; +import { SettingsWrapper } from "~/components/settings/wrapper"; +import { Button } from "~/components/ui/button"; +import { Input } from "~/components/ui/input"; +import { SmartAvatar } from "~/components/ui/smart-avatar"; +import authClient from "~/utils/auth-client"; + +export const Route = createFileRoute("/settings/profile")({ + component: RouteComponent, +}); + +function RouteComponent() { + // 1. Custom hooks + const { user } = useAuth(); + + // 2. State management + const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false); + const urlInputRef = useRef(null); + + // 3. Event handlers + const saveChanges = async (data: Record, any>) => { + await authClient.updateUser({ name: data.name, image: data.image }); + location.reload(); + return true; + }; + + // 4. Main render with form fields + return ( + <> + { + if (!name || name.length < 3) { + return { error: "Has to be at least 3 characters long" }; + } + }, + component: ({ value, disabled, update, error }) => ( + <> + Username + update(e.currentTarget.value)} + placeholder="Your user name" + className={`max-w-[70%] ${error && "border-red-400"}`} + /> + + ), + }, + { + id: "image", + type: "data", + value: { defaultValue: user.image }, + component: ({ value, disabled, update }) => ( + <> + Avatar +
+ update(e.currentTarget.value)} + placeholder="Your avatar url" + className="flex-1" + /> + +
+ + ), + }, + ]} + /> + + ); +} +``` + +### Custom React Hook + +Example from [cvi-template/src/hooks/use-plan.ts](https://github.com/greyhaven-ai/cvi-template): + +```typescript +import type { CustomerState } from "@polar-sh/sdk/models/components/customerstate.js"; +import { useQuery } from "@tanstack/react-query"; +import { polarProducts } from "~/config/polar-config"; +import { queryClient } from "~/lib/query-client"; +import authClient from "~/utils/auth-client"; + +export type PlanType = "free" | string; + +export interface UsePlanReturn { + planType: PlanType; + activeSubscription: any | null; + customerState: CustomerState | null; + isLoading: boolean; + error: any; +} + +export function usePlan(): UsePlanReturn { + // TanStack Query with proper configuration + const { + data: customerState, + isPending, + error, + } = useQuery( + { + queryKey: ["customerState"], + queryFn: async () => { + const { data: customerState, error } = await authClient.customer.state(); + if (error) throw error; + return customerState; + }, + }, + queryClient, + ); + + // Helper function + const getPlanType = (): PlanType => { + if ( + !customerState?.activeSubscriptions || + customerState.activeSubscriptions.length === 0 + ) { + return "free"; + } + + const type = polarProducts.find( + (p) => p.productId === customerState.activeSubscriptions[0].productId, + )?.slug; + + return type || "free"; + }; + + const planType = getPlanType(); + const activeSubscription = customerState?.activeSubscriptions?.[0] || null; + + return { + planType, + activeSubscription, + customerState: customerState || null, + isLoading: isPending, + error, + }; +} +``` + +### Database Schema (Drizzle with snake_case) + +Example from [cvi-template/src/server/schema/auth.schema.ts](https://github.com/greyhaven-ai/cvi-template): + +```typescript +import { SQL, sql } from "drizzle-orm"; +import { + AnyPgColumn, + boolean, + index, + pgEnum, + pgPolicy, + pgTable, + text, + timestamp, + unique, +} from "drizzle-orm/pg-core"; + +// Helper functions for RLS policies +const getUserId = (): SQL => sql`current_setting('app.user_id', true)`; +const getTenantId = (): SQL => sql`current_setting('app.tenant_id', true)`; +const getUserRole = (): SQL => sql`current_setting('app.user_role', true)`; + +// Helper to check if user ID matches +export const authUid = (userIdColumn: AnyPgColumn): SQL => + sql`${getUserId()} = ${userIdColumn}::text`; + +// Helper for tenant isolation +export const isSameTenant = (tenantIdCol: AnyPgColumn): SQL => + sql`(${getTenantId()} = ${tenantIdCol}::text)`; + +export const isAppAdmin = (): SQL => sql`${getUserRole()} = 'app_admin'::text`; + +export const inSameTenant = (tenantIdCol: AnyPgColumn, query: SQL): SQL => + sql`${isSameTenant(tenantIdCol)} and (${query})`; + +// User role enum +export const userRoleEnum = pgEnum("role", ["user", "org_admin", "app_admin"]); + +// User table with multi-tenant support and RLS policies +export const user = pgTable( + "users", + { + id: text("id").primaryKey(), + name: text("name").notNull(), + email: text("email").notNull(), + emailVerified: boolean("email_verified").notNull(), + image: text("image"), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at"), + role: userRoleEnum().default("user"), + userId: text("user_id"), + tenantId: text("tenant_id").notNull().default("dev"), // Multi-tenant field + serviceId: text("service_id").default("default"), + }, + (table) => [ + // Indexes for query performance + index("user_email_idx").on(table.email), + index("user_user_id_idx").on(table.userId), + index("user_tenant_id_idx").on(table.tenantId), + index("user_service_id_idx").on(table.serviceId), + unique("user_email_tenant_id_unique").on(table.email, table.tenantId), + + // RLS Policy: Authenticated users can read their own record or if they're app admin, within same tenant + pgPolicy("user_authenticated_select", { + for: "select", + to: "public", + using: inSameTenant( + table.tenantId, + sql`(${getUserId()} = ${table.id}::text) or ${isAppAdmin()}`, + ), + }), + + // RLS Policy: Insert with tenant isolation + pgPolicy("user_authenticated_insert", { + for: "insert", + to: "public", + withCheck: inSameTenant( + table.tenantId, + sql`${authUid(table.id)} or ${isAppAdmin()}`, + ), + }), + + // RLS Policy: Update own record or admin + pgPolicy("user_authenticated_update", { + for: "update", + to: "public", + using: inSameTenant(table.tenantId, sql`${authUid(table.id)} or ${isAppAdmin()}`), + withCheck: inSameTenant( + table.tenantId, + sql`${authUid(table.id)} or ${isAppAdmin()}`, + ), + }), + + // RLS Policy: Delete own record or admin + pgPolicy("user_authenticated_delete", { + for: "delete", + to: "public", + using: inSameTenant(table.tenantId, sql`${authUid(table.id)} or ${isAppAdmin()}`), + }), + ], +); +``` + +### Environment Variables with Validation + +Example from [cvi-template/src/utils/env.ts](https://github.com/greyhaven-ai/cvi-template): + +```typescript +import { createEnv } from "@t3-oss/env-core"; +import { z } from "zod"; + +/** + * Client-side environment variables accessible in the browser. + * All client variables must be prefixed with VITE_ for Vite to expose them. + */ +const clientVariables = { + /** Application display name shown in UI components */ + VITE_APP_NAME: z.string(), + + /** Base URL of the application */ + VITE_BASE_URL: z.string().url(), + + /** Feature flag to enable/disable billing features */ + VITE_ENABLE_BILLING: z.enum(["true", "false"]), + + /** Cloudflare Turnstile site key for CAPTCHA verification */ + VITE_TURNSTILE_SITE_KEY: z.string(), + + /** PostHog API key for analytics */ + VITE_POSTHOG_KEY: z.string(), + + /** PostHog host URL */ + VITE_POSTHOG_HOST: z.string(), + + /** Tenant identifier for multi-tenant authentication */ + VITE_TENANT_ID: z.string(), + + /** Service identifier for API authentication */ + VITE_SERVICE_ID: z.string(), + + /** API mode switch for demo vs production */ + VITE_API_MODE: z.enum(["demo", "prod"]), + + /** Production API base URL */ + VITE_PROD_BASE_URL: z.string().url(), +}; + +/** + * Server-side environment variables configuration. + * These variables are only accessible on the server and contain sensitive data. + */ +export const env = createEnv({ + server: { + /** Application name used in emails and server-side operations */ + APP_NAME: z.string(), + + /** Secret key for Better Auth session management (min 25 chars) */ + BETTER_AUTH_SECRET: z.string().min(25), + + /** Better Auth base URL */ + BETTER_AUTH_URL: z.string().url(), + + /** PostgreSQL database connection URL */ + DATABASE_URL: z.string().url(), + + /** Redis connection URL for session storage */ + REDIS_URL: z.string().url(), + + /** Google OAuth client ID */ + GOOGLE_CLIENT_ID: z.string(), + + /** Google OAuth client secret */ + GOOGLE_CLIENT_SECRET: z.string(), + + /** AWS S3 bucket name for file storage */ + AWS_S3_BUCKET: z.string(), + + /** AWS region */ + AWS_REGION: z.string(), + + /** AWS access key ID */ + AWS_ACCESS_KEY_ID: z.string(), + + /** AWS secret access key */ + AWS_SECRET_ACCESS_KEY: z.string(), + }, + client: clientVariables, + runtimeEnv: import.meta.env, + skipValidation: import.meta.env.NODE_ENV === "test", +}); + +// Usage in code +import { env } from "~/utils/env"; + +const dbUrl = env.DATABASE_URL; // Fully typed and validated! +const apiUrl = env.VITE_API_URL; // Fully typed and validated! +``` + +## Python/FastAPI Examples + +### FastAPI Router Structure + +Example from [cvi-backend-template/app/routers/accounts.py](https://github.com/greyhaven-ai/cvi-backend-template): + +```python +import uuid +from app.db.models.account import AccountDB +from app.db.repositories import AccountRepository, TenantRepository +from app.dependencies import get_account_repository, get_tenant_repository, verify_api_key_with_tenant +from app.schemas.accounts import ( + AccountCreate, + AccountResponse, + AccountUpdate, + AccountWithTenants, + TenantResponse, +) +from fastapi import APIRouter, Depends, HTTPException, Query, status +from typing import Annotated + +router = APIRouter( + prefix="/accounts", + tags=["accounts"], + dependencies=[Depends(verify_api_key_with_tenant)], +) + + +@router.post("/", response_model=AccountResponse, status_code=status.HTTP_201_CREATED) +async def create_account( + account_data: AccountCreate, + account_repo: Annotated[AccountRepository, Depends(get_account_repository)], +) -> AccountResponse: + """Create a new account with optional Polar customer creation.""" + # Check if slug already exists + existing = await account_repo.get_by_slug(account_data.slug) + if existing: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"Account with slug '{account_data.slug}' already exists", + ) + + # Create account + account = AccountDB(id=f"acc-{uuid.uuid4()}", **account_data.model_dump()) + + created_account = await account_repo.create(account) + return AccountResponse.model_validate(created_account) + + +@router.get("/{account_id}", response_model=AccountWithTenants) +async def get_account( + account_id: str, + account_repo: Annotated[AccountRepository, Depends(get_account_repository)], + tenant_repo: Annotated[TenantRepository, Depends(get_tenant_repository)], +) -> AccountWithTenants: + """Get account details with associated tenants.""" + account = await account_repo.get_by_id(account_id) + if not account: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Account '{account_id}' not found", + ) + + # Get associated tenants + tenants = await tenant_repo.list_by_account(account_id, include_inactive=True) + + return AccountWithTenants( + **AccountResponse.model_validate(account).model_dump(), + tenants=[TenantResponse.model_validate(t) for t in tenants], + tenant_count=len(tenants), + ) + + +@router.get("/", response_model=list[AccountResponse]) +async def list_accounts( + account_repo: Annotated[AccountRepository, Depends(get_account_repository)], + limit: int = Query(100, ge=1, le=1000), + offset: int = Query(0, ge=0), +) -> list[AccountResponse]: + """List all active accounts.""" + accounts = await account_repo.list_active(limit=limit, offset=offset) + return [AccountResponse.model_validate(a) for a in accounts] + + +@router.patch("/{account_id}", response_model=AccountResponse) +async def update_account( + account_id: str, + update_data: AccountUpdate, + account_repo: Annotated[AccountRepository, Depends(get_account_repository)], +) -> AccountResponse: + """Update account details.""" + account = await account_repo.get_by_id(account_id) + if not account: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Account '{account_id}' not found", + ) + + # Update fields + update_dict = update_data.model_dump(exclude_unset=True) + for field, value in update_dict.items(): + setattr(account, field, value) + + updated_account = await account_repo.update(account) + return AccountResponse.model_validate(updated_account) +``` + +### SQLModel Database Models + +Example from [cvi-backend-template/app/db/models/tenant.py](https://github.com/greyhaven-ai/cvi-backend-template): + +```python +from __future__ import annotations + +import secrets +from app.db.db_types import UTCDateTime, utc_now +from datetime import datetime +from sqlalchemy import Column as SAColumn +from sqlmodel import JSON, Column, Field, SQLModel +from typing import Any + + +def generate_api_key() -> str: + """Generate a secure API key for tenant authentication.""" + return f"sk_tenant_{secrets.token_urlsafe(32)}" + + +class TenantDB(SQLModel, table=True): # type: ignore[call-arg] + """Database model representing a tenant (frontend application instance). + + Each tenant is a specific frontend app owned by an account. Tenants have + their own API keys, users, and service configurations. This is the primary + isolation boundary for data access. + """ + + __tablename__ = "tenants" + + # Primary identification + id: str = Field( + primary_key=True, + description="Unique tenant identifier, e.g. 'tenant-acme-healthcare'", + ) + account_id: str = Field( + foreign_key="accounts.id", + index=True, + description="The account that owns this tenant", + ) + + # Tenant information + name: str = Field( + description="Friendly name for the frontend app, e.g. 'Acme Healthcare Portal'" + ) + slug: str = Field(index=True, description="URL-friendly identifier") + description: str | None = Field( + default=None, description="Optional description of the tenant's purpose" + ) + + # Authentication + api_key: str = Field( + default_factory=generate_api_key, + unique=True, + index=True, + description="API key for backend authentication", + ) + api_key_provider_id: str | None = Field( + default=None, + index=True, + description="External API key provider ID (e.g., Unkey key ID)", + ) + + # Access control + allowed_origins: list[str] | None = Field( + default=None, + sa_column=Column(JSON), + description="Allowed CORS origins for this tenant", + ) + allowed_ip_ranges: list[str] | None = Field( + default=None, + sa_column=Column(JSON), + description="Optional IP allowlist in CIDR notation", + ) + + # Status flags + is_active: bool = Field( + default=True, description="Whether tenant is active and can make API calls" + ) + is_demo: bool = Field( + default=False, description="Whether this is a demo/sandbox tenant" + ) + + # Timestamps + created_at: datetime = Field( + default_factory=utc_now, + sa_column=SAColumn(UTCDateTime, nullable=False), + description="Tenant creation timestamp", + ) + updated_at: datetime = Field( + default_factory=utc_now, + sa_column=SAColumn(UTCDateTime, nullable=False, onupdate=utc_now), + description="Last update timestamp", + ) +``` + +### User Model with Multi-Tenant Support + +Example from [cvi-backend-template/app/db/models/user.py](https://github.com/greyhaven-ai/cvi-backend-template): + +```python +from app.db.db_types import UTCDateTime, utc_now +from datetime import datetime +from sqlalchemy import Column as SAColumn +from sqlmodel import Field, SQLModel + + +class UserDB(SQLModel, table=True): # type: ignore[call-arg] + """User database model with multi-tenant support.""" + + __tablename__ = "users" + + id: str = Field(primary_key=True, description="User's unique identifier") + name: str = Field(description="User's full name") + email: str = Field(index=True, unique=True, description="User's email address") + email_verified: bool = Field( + default=False, description="Whether the user's email has been verified" + ) + image: str | None = Field(default=None, description="URL of the user's profile image") + + role: str = Field( + default="user", description="Role of the user (e.g., user, org_admin, cvi_admin)" + ) + + # Multi-tenant fields (CRITICAL) + tenant_id: str = Field(index=True, description="Owning tenant/organisation identifier") + service_id: str | None = Field( + default=None, index=True, description="Logical domain (e.g. healthcare, education)" + ) + + # Timestamps + created_at: datetime = Field( + default_factory=utc_now, sa_column=SAColumn(UTCDateTime, nullable=False) + ) + updated_at: datetime | None = Field( + default_factory=utc_now, + sa_column=SAColumn(UTCDateTime, nullable=True, onupdate=utc_now), + ) +``` + +### Pydantic Request/Response Schemas + +Example from [cvi-backend-template/app/schemas/accounts.py](https://github.com/greyhaven-ai/cvi-backend-template): + +```python +import re +from datetime import datetime +from pydantic import BaseModel, ConfigDict, Field, field_validator +from typing import Any + + +class AccountBase(BaseModel): + """Base account schema with shared fields.""" + + name: str = Field(..., description="Company/Organization name") + slug: str = Field(..., description="URL-friendly identifier") + billing_tier: str = Field(default="starter", description="Subscription tier") + billing_email: str = Field(..., description="Primary billing contact email") + billing_address: dict[str, Any] | None = Field( + default=None, description="Billing address" + ) + + # Limits + max_tenants: int = Field(default=3, ge=1, description="Maximum number of tenants") + max_users_per_tenant: int = Field(default=100, ge=1, description="Maximum users per tenant") + max_api_calls_per_month: int = Field( + default=1000000, ge=0, description="Monthly API call limit" + ) + + # Metadata + account_metadata: dict[str, Any] | None = Field( + default=None, description="Arbitrary metadata" + ) + + @field_validator("slug") + def validate_slug(cls, v: str) -> str: + """Validate slug format.""" + if not re.match(r"^[a-z0-9-]+$", v): + raise ValueError("Slug must contain only lowercase letters, numbers, and hyphens") + return v + + @field_validator("billing_email") + def validate_email(cls, v: str) -> str: + """Validate email format.""" + if not re.match(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", v): + raise ValueError("Invalid email format") + return v + + +class AccountCreate(AccountBase): + """Schema for creating an account.""" + + pass + + +class AccountUpdate(BaseModel): + """Schema for updating an account (all fields optional).""" + + name: str | None = None + billing_tier: str | None = None + billing_email: str | None = None + billing_address: dict[str, Any] | None = None + billing_provider_id: str | None = None + + # Limits + max_tenants: int | None = Field(None, ge=1) + max_users_per_tenant: int | None = Field(None, ge=1) + max_api_calls_per_month: int | None = Field(None, ge=0) + + # Metadata + account_metadata: dict[str, Any] | None = None + + +class AccountResponse(AccountBase): + """Account response schema.""" + + id: str + billing_provider_id: str | None = None + polar_subscription_id: str | None = None + billing_status: str = "pending" + billing_period_end: datetime | None = None + + # Status + is_active: bool + is_trial: bool + trial_ends_at: datetime | None = None + suspended_at: datetime | None = None + suspension_reason: str | None = None + + # Timestamps + created_at: datetime + updated_at: datetime + + model_config = ConfigDict(from_attributes=True) +``` + +### Repository Pattern with Tenant Isolation + +Example from [cvi-backend-template/app/db/repositories/tenant_repository.py](https://github.com/greyhaven-ai/cvi-backend-template): + +```python +from __future__ import annotations + +from app.db.models.tenant import TenantDB, generate_api_key +from datetime import UTC, datetime +from sqlalchemy import and_, select +from sqlalchemy.ext.asyncio import AsyncSession + + +class TenantRepository: + """Repository for tenant-related database operations.""" + + def __init__(self, session: AsyncSession): + self.session = session + + async def create(self, tenant: TenantDB) -> TenantDB: + """Create a new tenant.""" + self.session.add(tenant) + await self.session.commit() + await self.session.refresh(tenant) + return tenant + + async def get_by_id(self, tenant_id: str) -> TenantDB | None: + """Get tenant by ID.""" + stmt = select(TenantDB).where(TenantDB.id == tenant_id) + result = await self.session.execute(stmt) + return result.scalars().first() + + async def get_by_api_key(self, api_key: str) -> TenantDB | None: + """Get tenant by API key.""" + stmt = select(TenantDB).where(TenantDB.api_key == api_key) + result = await self.session.execute(stmt) + return result.scalars().first() + + async def get_by_slug(self, account_id: str, slug: str) -> TenantDB | None: + """Get tenant by slug within an account (tenant isolation).""" + stmt = select(TenantDB).where( + and_(TenantDB.account_id == account_id, TenantDB.slug == slug) + ) + result = await self.session.execute(stmt) + return result.scalars().first() + + async def list_by_account( + self, account_id: str, include_inactive: bool = False + ) -> list[TenantDB]: + """List all tenants for an account.""" + stmt = select(TenantDB).where(TenantDB.account_id == account_id) + if not include_inactive: + stmt = stmt.where(TenantDB.is_active == True) + stmt = stmt.order_by(TenantDB.created_at.desc()) + + result = await self.session.execute(stmt) + return list(result.scalars().all()) + + async def update(self, tenant: TenantDB) -> TenantDB: + """Update an existing tenant.""" + await self.session.commit() + await self.session.refresh(tenant) + return tenant + + async def rotate_api_key(self, tenant_id: str) -> TenantDB | None: + """Rotate the API key for a tenant.""" + tenant = await self.get_by_id(tenant_id) + if tenant: + tenant.api_key = generate_api_key() + return await self.update(tenant) + return None + + async def suspend(self, tenant_id: str, reason: str) -> TenantDB | None: + """Suspend a tenant.""" + tenant = await self.get_by_id(tenant_id) + if tenant: + tenant.is_active = False + tenant.suspended_at = datetime.now(UTC) + tenant.suspension_reason = reason + return await self.update(tenant) + return None + + async def reactivate(self, tenant_id: str) -> TenantDB | None: + """Reactivate a suspended tenant.""" + tenant = await self.get_by_id(tenant_id) + if tenant: + tenant.is_active = True + tenant.suspended_at = None + tenant.suspension_reason = None + return await self.update(tenant) + return None +``` + +### Pytest Unit Tests + +Example from [cvi-backend-template/tests/unit/repositories/test_tenant_repository.py](https://github.com/greyhaven-ai/cvi-backend-template): + +```python +"""Unit tests for TenantRepository.""" +import pytest +from app.db.models.tenant import TenantDB, generate_api_key +from app.db.repositories.tenant_repository import TenantRepository +from datetime import UTC, datetime +from sqlalchemy.ext.asyncio import AsyncSession +from unittest.mock import AsyncMock, MagicMock + + +class TestTenantRepository: + """Test TenantRepository methods.""" + + @pytest.fixture + def mock_session(self): + """Create a mock database session.""" + session = AsyncMock(spec=AsyncSession) + session.add = MagicMock() + session.commit = AsyncMock() + session.refresh = AsyncMock() + session.execute = AsyncMock() + return session + + @pytest.fixture + def repository(self, mock_session): + """Create repository instance with mock session.""" + return TenantRepository(mock_session) + + @pytest.fixture + def sample_tenant(self): + """Create a sample tenant for testing.""" + return TenantDB( + id="tenant-123", + account_id="acc-123", + slug="test-tenant", + name="Test Tenant", + api_key="sk_tenant_test123", + is_active=True, + created_at=datetime.now(UTC), + updated_at=datetime.now(UTC), + ) + + @pytest.mark.asyncio + async def test_create_tenant(self, repository, mock_session, sample_tenant): + """Test creating a new tenant.""" + # Act + result = await repository.create(sample_tenant) + + # Assert + mock_session.add.assert_called_once_with(sample_tenant) + mock_session.commit.assert_called_once() + mock_session.refresh.assert_called_once_with(sample_tenant) + assert result == sample_tenant + + @pytest.mark.asyncio + async def test_get_by_id_found(self, repository, mock_session, sample_tenant): + """Test getting tenant by ID when it exists.""" + # Arrange + mock_result = MagicMock() + mock_result.scalars.return_value.first.return_value = sample_tenant + mock_session.execute.return_value = mock_result + + # Act + result = await repository.get_by_id("tenant-123") + + # Assert + assert result == sample_tenant + + @pytest.mark.asyncio + async def test_get_by_slug_with_tenant_isolation(self, repository, mock_session, sample_tenant): + """Test getting tenant by slug within an account (tenant isolation).""" + # Arrange + mock_result = MagicMock() + mock_result.scalars.return_value.first.return_value = sample_tenant + mock_session.execute.return_value = mock_result + + # Act + result = await repository.get_by_slug("acc-123", "test-tenant") + + # Assert + assert result == sample_tenant + # Verify both account_id and slug are in query + query = mock_session.execute.call_args[0][0] + query_str = str(query) + assert "account_id" in query_str + assert "slug" in query_str + + @pytest.mark.asyncio + async def test_suspend(self, repository, mock_session, sample_tenant): + """Test suspending a tenant.""" + # Arrange + mock_result = MagicMock() + mock_result.scalars.return_value.first.return_value = sample_tenant + mock_session.execute.return_value = mock_result + + # Act + result = await repository.suspend("tenant-123", "Violation of terms") + + # Assert + assert result is not None + assert result.is_active is False + assert result.suspended_at is not None + assert result.suspension_reason == "Violation of terms" + mock_session.commit.assert_called_once() + + +class TestGenerateApiKey: + """Test the generate_api_key function.""" + + def test_generate_api_key_format(self): + """Test that generated API keys have correct format.""" + key = generate_api_key() + + assert key.startswith("sk_tenant_") + suffix = key[len("sk_tenant_") :] + assert len(suffix) >= 40 + assert len(suffix) <= 50 + + def test_generate_api_key_uniqueness(self): + """Test that generated API keys are unique.""" + keys = [generate_api_key() for _ in range(100)] + + # All keys should be unique + assert len(set(keys)) == 100 +``` + +## Before/After Comparisons + +### Wrong: camelCase in Database Schema + +```typescript +// ❌ WRONG - Don't do this +export const users = pgTable("users", { + id: uuid("id"), + createdAt: timestamp("createdAt"), // Wrong! + tenantId: uuid("tenantId"), // Wrong! + emailAddress: text("emailAddress"), // Wrong! + displayName: text("displayName"), // Wrong! + isActive: boolean("isActive"), // Wrong! +}); +``` + +### Correct: snake_case in Database Schema + +```typescript +// ✅ CORRECT - Always use snake_case +export const users = pgTable("users", { + id: uuid("id"), + created_at: timestamp("created_at"), // Correct! + tenant_id: uuid("tenant_id"), // Correct! + email_address: text("email_address"), // Correct! + display_name: text("display_name"), // Correct! + is_active: boolean("is_active"), // Correct! +}); +``` + +### Wrong: Missing Type Hints (Python) + +```python +# ❌ WRONG - Don't do this +def get_user(user_id): # Missing type hints! + return db.query(User).filter(User.id == user_id).first() + +async def create_user(data): # Missing type hints! + user = User(**data) + db.add(user) + await db.commit() + return user +``` + +### Correct: Type Hints Required (Python) + +```python +# ✅ CORRECT - Always include type hints +from typing import Optional + +def get_user(user_id: str) -> Optional[User]: # Type hints! + """Get user by ID.""" + return db.query(User).filter(User.id == user_id).first() + +async def create_user(data: UserCreate) -> User: # Type hints! + """Create new user.""" + user = User(**data.model_dump()) + db.add(user) + await db.commit() + return user +``` + +## Key Patterns Summary + +### TypeScript/React + +- ✅ Imports auto-sorted by prettier-plugin-organize-imports +- ✅ Custom hooks use `use-` prefix +- ✅ TanStack Query with `staleTime: 60000` (1 minute default) +- ✅ Database fields use `snake_case` (even in TypeScript schemas) +- ✅ Environment validation with @t3-oss/env-core and Zod +- ✅ RLS policies for multi-tenant isolation + +### Python/FastAPI + +- ✅ Type hints with `Annotated` for dependency injection +- ✅ Proper HTTP status codes and error handling +- ✅ Repository pattern for data access +- ✅ Pydantic schemas with `field_validator` +- ✅ SQLModel with `snake_case` fields +- ✅ Pytest with `@pytest.mark.asyncio` and fixtures +- ✅ Multi-tenant isolation in all queries diff --git a/skills/code-style/REFERENCE.md b/skills/code-style/REFERENCE.md new file mode 100644 index 0000000..770b440 --- /dev/null +++ b/skills/code-style/REFERENCE.md @@ -0,0 +1,586 @@ +# Technical Reference + +Complete configuration files and detailed technical information for Grey Haven coding standards. + +## TypeScript/React Configuration Files + +### .prettierrc + +Complete Prettier configuration from cvi-template: + +```json +{ + "tabWidth": 2, + "semi": true, + "printWidth": 90, + "singleQuote": false, + "endOfLine": "lf", + "trailingComma": "all", + "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"] +} +``` + +**Field Explanations:** + +- `tabWidth: 2` - Use 2 spaces for indentation (NOT 4) +- `semi: true` - Always add semicolons at the end of statements +- `printWidth: 90` - Wrap lines at 90 characters (NOT 80 or 120) +- `singleQuote: false` - Use double quotes for strings +- `endOfLine: "lf"` - Use Unix-style line endings (\\n) +- `trailingComma: "all"` - Add trailing commas wherever possible +- `prettier-plugin-organize-imports` - Auto-organize imports by type +- `prettier-plugin-tailwindcss` - Auto-sort Tailwind CSS classes + +### .eslintrc + +Complete ESLint configuration from cvi-template: + +```json +{ + "root": true, + "env": { "browser": true, "es2020": true }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["react-refresh"], + "rules": { + "react-hooks/exhaustive-deps": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off", + "no-unused-vars": "off", + "react-refresh/only-export-components": "off" + } +} +``` + +**Rule Explanations:** + +- `react-hooks/exhaustive-deps: "off"` - Don't enforce exhaustive deps in useEffect (manage manually) +- `@typescript-eslint/no-explicit-any: "off"` - Allow `any` type for flexibility +- `@typescript-eslint/no-unused-vars: "off"` - Don't error on unused variables (clean up manually) +- `no-unused-vars: "off"` - Same as above for non-TypeScript files +- `react-refresh/only-export-components: "off"` - Allow exporting non-components from module + +**Philosophy:** Grey Haven takes a pragmatic approach over pedantic enforcement. The codebase values developer velocity and allows `any` types, unused variables, and manual dependency management when appropriate. + +### tsconfig.json + +Complete TypeScript configuration from cvi-template: + +```json +{ + "compilerOptions": { + "strict": true, + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "esModuleInterop": true, + "isolatedModules": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./src/*"] + }, + "noEmit": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", ".wrangler", ".output"] +} +``` + +**Key Settings:** + +- `strict: true` - Enable all strict type checking options +- `target: "ES2022"` - Compile to ES2022 JavaScript +- `module: "ESNext"` - Use ESNext module syntax +- `moduleResolution: "Bundler"` - Use bundler resolution for Vite/TanStack +- `jsx: "react-jsx"` - Use React 17+ JSX transform +- `paths: { "~/*": ["./src/*"] }` - Path alias for imports (e.g., `import { foo } from "~/lib/utils"`) +- `noEmit: true` - Don't emit compiled files (Vite handles this) + +### package.json Scripts + +Standard scripts from cvi-template: + +```json +{ + "scripts": { + "dev": "vinxi dev", + "build": "vinxi build", + "start": "vinxi start", + "lint": "eslint .", + "format": "prettier --write .", + "format:check": "prettier --check .", + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:push": "drizzle-kit push", + "db:studio": "drizzle-kit studio", + "prepare": "husky" + } +} +``` + +## Python/FastAPI Configuration Files + +### pyproject.toml (Ruff) + +Complete Ruff configuration from cvi-backend-template: + +```toml +[tool.ruff] +fix-only = true +show-fixes = true +indent-width = 4 +line-length = 130 # NOT 80 or 88! + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "UP", # pyupgrade +] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +line-ending = "lf" + +[tool.ruff.lint.isort] +known-first-party = ["app"] +``` + +**Field Explanations:** + +- `line-length = 130` - Wrap lines at 130 characters (CRITICAL: NOT 80 or 88!) +- `indent-width = 4` - Use 4 spaces for indentation +- `fix-only = true` - Auto-fix issues without showing unfixable errors +- `show-fixes = true` - Show what was fixed +- `select = [...]` - Enable specific linter rules: + - `E`, `W` - pycodestyle style enforcement + - `F` - pyflakes error detection + - `I` - isort import sorting + - `B` - flake8-bugbear bug detection + - `C4` - flake8-comprehensions list/dict comprehension improvements + - `UP` - pyupgrade automatic Python version upgrades + +### pyproject.toml (MyPy) + +Complete MyPy configuration from cvi-backend-template: + +```toml +[tool.mypy] +python_version = "3.11" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = true +check_untyped_defs = true +ignore_missing_imports = false +strict_optional = true +warn_redundant_casts = true +warn_unused_ignores = true +warn_no_return = true +warn_unreachable = true +``` + +**Key Settings:** + +- `disallow_untyped_defs: true` - **CRITICAL**: Require type hints on all function definitions +- `python_version = "3.11"` - Target Python 3.11+ (projects use 3.12+) +- `strict_optional: true` - Strict checking of Optional types +- `warn_return_any: true` - Warn when returning Any from typed functions +- `warn_unused_configs: true` - Warn about unused mypy config + +**Philosophy:** Type hints are **required** in Grey Haven Python projects. Unlike TypeScript's relaxed rules, Python enforces strict typing. + +### pyproject.toml (Pytest) + +Complete pytest configuration from cvi-backend-template: + +```toml +[tool.pytest.ini_options] +pythonpath = ["."] +asyncio_mode = "auto" +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +markers = [ + "unit: Unit tests (fast, isolated, mocked)", + "integration: Integration tests (database, external services)", + "e2e: End-to-end tests (full system)", + "benchmark: Performance benchmark tests", + "slow: Tests that take > 1 second", +] +addopts = [ + "-ra", # Show summary of all test outcomes + "--strict-markers", # Require markers to be registered + "--strict-config", # Raise on config warnings + "--tb=short", # Shorter traceback format +] + +[tool.coverage.run] +source = ["app"] +omit = ["tests/*", "*/migrations/*"] + +[tool.coverage.report] +precision = 2 +show_missing = true +skip_covered = false +``` + +**Marker Explanations:** + +- `@pytest.mark.unit` - Fast, isolated tests with mocked dependencies +- `@pytest.mark.integration` - Tests with database or external services +- `@pytest.mark.e2e` - Full system end-to-end tests +- `@pytest.mark.benchmark` - Performance measurement tests +- `@pytest.mark.slow` - Tests taking over 1 second + +**Coverage:** Aim for >80% code coverage across the project. + +## Project Structure Details + +### TypeScript/React Structure (cvi-template) + +```plaintext +cvi-template/ +├── src/ +│ ├── routes/ # TanStack Router file-based routing +│ │ ├── __root.tsx # Root layout +│ │ ├── index.tsx # Homepage (/) +│ │ ├── _authenticated/ # Protected routes (requires auth) +│ │ │ └── dashboard.tsx # /dashboard +│ │ └── settings/ +│ │ ├── index.tsx # /settings +│ │ └── profile.tsx # /settings/profile +│ │ +│ ├── lib/ +│ │ ├── components/ # React components +│ │ │ ├── ui/ # Shadcn UI components (button, card, etc.) +│ │ │ ├── auth/ # Authentication components +│ │ │ └── layout/ # Layout components (header, sidebar) +│ │ │ +│ │ ├── server/ # Server-side code +│ │ │ ├── schema/ # Drizzle database schemas (snake_case!) +│ │ │ ├── functions/ # TanStack Start server functions +│ │ │ ├── auth.ts # Better-auth configuration +│ │ │ └── db.ts # Database connection with RLS +│ │ │ +│ │ ├── config/ # Configuration files +│ │ │ └── env.ts # Environment validation (@t3-oss/env-core) +│ │ │ +│ │ ├── hooks/ # Custom React hooks (use-* naming) +│ │ ├── utils/ # Utility functions +│ │ └── types/ # TypeScript type definitions +│ │ +│ ├── clients/ # API client code +│ ├── data/ # Zod schemas for data validation +│ ├── middleware/ # Route middleware and guards +│ ├── services/ # Business logic services +│ ├── workers/ # Cloudflare Workers code +│ ├── index.css # Global styles (Tailwind) +│ └── router.tsx # Router configuration +│ +├── public/ # Static assets +├── tests/ # Test files +├── .prettierrc # Prettier config +├── .eslintrc # ESLint config +├── tsconfig.json # TypeScript config +├── drizzle.config.ts # Drizzle ORM config +├── vite.config.ts # Vite bundler config +├── wrangler.jsonc # Cloudflare Workers config +└── package.json # Dependencies and scripts +``` + +### Python/FastAPI Structure (cvi-backend-template) + +```plaintext +cvi-backend-template/ +├── app/ +│ ├── config/ # Application configuration +│ │ └── settings.py # Pydantic settings with env vars +│ │ +│ ├── db/ # Database layer +│ │ ├── models/ # SQLModel database models (snake_case!) +│ │ │ ├── account.py # Account model +│ │ │ ├── tenant.py # Tenant model (multi-tenant) +│ │ │ └── user.py # User model +│ │ │ +│ │ ├── repositories/ # Repository pattern (data access) +│ │ │ ├── base.py # Base repository +│ │ │ ├── account_repository.py +│ │ │ ├── tenant_repository.py +│ │ │ └── user_repository.py +│ │ │ +│ │ ├── db_types.py # Custom database types (UTCDateTime) +│ │ └── session.py # Database session management +│ │ +│ ├── routers/ # FastAPI routers (endpoints) +│ │ ├── accounts.py # /accounts endpoints +│ │ ├── tenants.py # /tenants endpoints +│ │ └── users.py # /users endpoints +│ │ +│ ├── services/ # Business logic layer +│ │ ├── auth_service.py # Authentication service +│ │ └── billing_service.py # Billing service +│ │ +│ ├── schemas/ # Pydantic schemas (API contracts) +│ │ ├── accounts.py # Account request/response schemas +│ │ ├── tenants.py # Tenant request/response schemas +│ │ └── users.py # User request/response schemas +│ │ +│ ├── utils/ # Utility functions +│ │ ├── logging.py # Logging configuration +│ │ └── security.py # Security utilities +│ │ +│ ├── dependencies.py # FastAPI dependencies +│ └── main.py # FastAPI app entry point +│ +├── tests/ # Test files +│ ├── unit/ # Unit tests (@pytest.mark.unit) +│ ├── integration/ # Integration tests (@pytest.mark.integration) +│ ├── e2e/ # E2E tests (@pytest.mark.e2e) +│ ├── conftest.py # Pytest fixtures +│ └── __init__.py +│ +├── alembic/ # Database migrations (if using Alembic) +├── pyproject.toml # Python project config (Ruff, MyPy, pytest) +├── requirements.txt # Python dependencies +└── .env.example # Example environment variables +``` + +## Database Naming Standards + +### Field Naming Rules + +**ALWAYS use snake_case for database columns:** + +```text +✅ CORRECT ❌ WRONG +created_at createdAt +tenant_id tenantId +email_address emailAddress +is_active isActive +first_name firstName +last_name lastName +phone_number phoneNumber +billing_tier billingTier +max_retries maxRetries +api_key_hash apiKeyHash +``` + +### Table Naming Rules + +**Use lowercase plural names:** + +```text +✅ CORRECT ❌ WRONG +users User, Users, user +accounts Account, ACCOUNTS +tenants Tenant, Tenants +organizations Organization +subscriptions Subscription +``` + +### Index Naming Rules + +**Use descriptive index names:** + +```text +Format: {table}_{column}_idx + +✅ CORRECT ❌ WRONG +user_email_idx idx_1, email_index +user_tenant_id_idx tenant_idx +organization_slug_idx slug +``` + +### Foreign Key Naming Rules + +**Reference the parent table:** + +```text +Format: {parent_table}_{singular}_id + +✅ CORRECT ❌ WRONG +tenant_id tenant +account_id acc_id, accountId +organization_id org_id, orgId +user_id userId, uid +``` + +## Multi-Tenant Architecture + +### Tenant Isolation Levels + +Grey Haven projects use **row-level** tenant isolation: + +1. **Every table** includes a `tenant_id` or `account_id` field +2. **Every query** filters by tenant ID +3. **Row Level Security (RLS)** policies enforce tenant boundaries +4. **Repository pattern** centralizes tenant filtering + +### RLS Policy Pattern + +Standard RLS policy structure: + +```typescript +// TypeScript/Drizzle RLS helper +export const inSameTenant = (tenantIdCol: AnyPgColumn, query: SQL): SQL => + sql`${isSameTenant(tenantIdCol)} and (${query})`; + +// Apply to table +pgPolicy("table_name_select", { + for: "select", + to: "public", + using: inSameTenant(table.tenantId, sql`true`), +}); +``` + +```sql +-- SQL RLS Policy +CREATE POLICY tenant_isolation_policy ON users + USING (tenant_id = current_setting('app.tenant_id')::text); +``` + +### Repository Tenant Filtering + +Every repository method filters by tenant: + +```python +# Python repository pattern +async def get_by_id(self, id: UUID, tenant_id: UUID) -> Optional[Model]: + """Get record by ID with tenant isolation.""" + result = await self.session.execute( + select(self.model) + .where(self.model.id == id) + .where(self.model.tenant_id == tenant_id) # Tenant filter! + ) + return result.scalar_one_or_none() +``` + +## Import Organization + +### TypeScript Import Order + +Auto-organized by `prettier-plugin-organize-imports`: + +```typescript +// 1. External libraries +import { useState, useEffect } from "react"; +import { useQuery } from "@tanstack/react-query"; + +// 2. Internal modules (path alias) +import { Button } from "~/lib/components/ui/button"; +import { useAuth } from "~/lib/hooks/use-auth"; +import { env } from "~/lib/config/env"; + +// 3. Relative imports +import { helpers } from "./helpers"; +``` + +### Python Import Order + +Auto-organized by Ruff `isort`: + +```python +# 1. Standard library imports +import os +from datetime import datetime +from typing import Optional + +# 2. Third-party imports +from fastapi import APIRouter, Depends, HTTPException +from sqlmodel import select +from pydantic import BaseModel + +# 3. Local imports (app.*) +from app.db.models.user import User +from app.db.repositories.user_repository import UserRepository +from app.schemas.user import UserCreate, UserResponse +``` + +## Line Length Reasoning + +### TypeScript: 90 Characters + +**Why 90?** + +- Comfortable reading width on modern displays +- Allows two editor panes side-by-side +- Balances readability with code density +- TailwindCSS classes can be long - 90 gives room +- Standard 80 is too restrictive for modern development + +### Python: 130 Characters + +**Why 130?** + +- Type hints can be verbose: `Annotated[AccountRepository, Depends(get_account_repository)]` +- Pydantic field definitions: `Field(default=None, description="Long description here")` +- Allows descriptive variable names without constant wrapping +- FastAPI decorators are long: `@router.post("/endpoint", response_model=ResponseSchema)` +- PEP 8's 79 is outdated for modern development + +## Pre-commit Hooks + +Both templates use pre-commit hooks for code quality: + +### TypeScript Pre-commit + +```bash +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +# Run linting +npm run lint + +# Run formatting check +npm run format:check + +# Run type checking +npx tsc --noEmit +``` + +### Python Pre-commit + +```bash +#!/usr/bin/env sh + +# Activate virtual environment +source .venv/bin/activate + +# Run Ruff formatter +ruff format . + +# Run Ruff linter +ruff check --fix . + +# Run MyPy type checking +mypy app/ + +# Run tests +pytest -m "not slow" +``` + +## Additional Resources + +- **Frontend Template**: [cvi-template](https://github.com/greyhaven-ai/cvi-template) - TanStack Start + React 19 + Drizzle +- **Backend Template**: [cvi-backend-template](https://github.com/greyhaven-ai/cvi-backend-template) - FastAPI + SQLModel + PostgreSQL +- **Prettier Docs**: https://prettier.io/docs/en/configuration.html +- **ESLint Docs**: https://eslint.org/docs/latest/use/configure/ +- **Ruff Docs**: https://docs.astral.sh/ruff/ +- **Drizzle ORM**: https://orm.drizzle.team/docs/overview +- **TanStack Start**: https://tanstack.com/router/latest/docs/framework/react/start/overview diff --git a/skills/code-style/SKILL.md b/skills/code-style/SKILL.md new file mode 100644 index 0000000..5a8184b --- /dev/null +++ b/skills/code-style/SKILL.md @@ -0,0 +1,199 @@ +--- +name: grey-haven-code-style +description: "Apply Grey Haven Studio's TypeScript/React and Python/FastAPI coding standards from production templates. Use when writing code, reviewing PRs, fixing linting errors, formatting files, or when the user mentions 'code standards', 'Grey Haven style', 'linting', 'Prettier', 'ESLint', 'Ruff', 'formatting rules', or 'coding conventions'. Includes exact Prettier/ESLint/Ruff configs, naming conventions, project structure, and multi-tenant database patterns." +--- + +# Grey Haven Code Style Standards + +**Actual coding standards from Grey Haven Studio production templates.** + +Follow these exactly when working on Grey Haven codebases. This skill provides navigation to detailed examples, reference configs, and templates. + +## Supporting Documentation + +- **[EXAMPLES.md](EXAMPLES.md)** - Copy-paste code examples for TypeScript and Python +- **[REFERENCE.md](REFERENCE.md)** - Complete config files and detailed rule explanations +- **[templates/](templates/)** - Ready-to-use starter files +- **[checklists/](checklists/)** - Code review checklists + +## Quick Reference + +### TypeScript/React (Frontend) + +Based on `cvi-template` - TanStack Start + React 19 + +**Key Settings:** + +- **Line width:** 90 characters +- **Tab width:** 2 spaces +- **Quotes:** Double quotes +- **Semicolons:** Required +- **Trailing commas:** Always +- **ESLint:** Pragmatic (allows `any`, unused vars) +- **Path alias:** `~/` maps to `./src/*` + +**Naming Conventions:** + +- Variables/Functions: `camelCase` (`getUserData`, `isAuthenticated`) +- Components: `PascalCase` (`UserProfile`, `AuthProvider`) +- Constants: `UPPER_SNAKE_CASE` (`API_BASE_URL`, `MAX_RETRIES`) +- Types/Interfaces: `PascalCase` (`User`, `AuthConfig`) +- **Database fields:** `snake_case` (`user_id`, `created_at`, `tenant_id`) ⚠️ CRITICAL + +**Project Structure:** + +```plaintext +src/ +├── routes/ # File-based routing (TanStack Router) +├── lib/ +│ ├── components/ # UI components (grouped by feature) +│ ├── server/ # Server functions and DB schema +│ ├── config/ # Environment validation +│ ├── hooks/ # Custom React hooks (use-* naming) +│ ├── utils/ # Utility functions +│ └── types/ # TypeScript definitions +└── public/ # Static assets +``` + +### Python/FastAPI (Backend) + +Based on `cvi-backend-template` - FastAPI + SQLModel + +**Key Settings:** + +- **Line length:** 130 characters +- **Indent:** 4 spaces +- **Type hints:** Required on all functions +- **Auto-fix:** Ruff fixes issues automatically + +**Naming Conventions:** + +- Functions/Variables: `snake_case` (`get_user_data`, `is_authenticated`) +- Classes: `PascalCase` (`UserRepository`, `AuthService`) +- Constants: `UPPER_SNAKE_CASE` (`API_BASE_URL`, `MAX_RETRIES`) +- **Database fields:** `snake_case` (`user_id`, `created_at`, `tenant_id`) ⚠️ CRITICAL +- Boolean fields: Prefix with `is_` or `has_` (`is_active`, `has_access`) + +**Project Structure:** + +```plaintext +app/ +├── config/ # Application settings +├── db/ +│ ├── models/ # SQLModel entities +│ └── repositories/ # Repository pattern (tenant isolation) +├── routers/ # FastAPI endpoints +├── services/ # Business logic +├── schemas/ # Pydantic models (API contracts) +└── utils/ # Utilities +``` + +## Database Field Convention (CRITICAL) + +**ALWAYS use `snake_case` for database column names** - this is non-negotiable in Grey Haven projects. + +✅ **Correct:** + +```typescript +// TypeScript - Drizzle schema +export const users = pgTable("users", { + id: uuid("id").primaryKey(), + created_at: timestamp("created_at").defaultNow(), + tenant_id: uuid("tenant_id").notNull(), + email_address: text("email_address").notNull(), + is_active: boolean("is_active").default(true), +}); +``` + +```python +# Python - SQLModel +class User(SQLModel, table=True): + id: UUID = Field(default_factory=uuid4, primary_key=True) + created_at: datetime = Field(default_factory=datetime.utcnow) + tenant_id: UUID = Field(foreign_key="tenants.id", index=True) + email_address: str = Field(unique=True, index=True) + is_active: bool = Field(default=True) +``` + +❌ **Wrong:** + +```typescript +// DON'T use camelCase in database schemas +export const users = pgTable("users", { + id: uuid("id"), + createdAt: timestamp("createdAt"), // WRONG! + tenantId: uuid("tenantId"), // WRONG! + emailAddress: text("emailAddress"), // WRONG! +}); +``` + +**See [EXAMPLES.md](EXAMPLES.md#database-schemas) for complete examples.** + +## Multi-Tenant Architecture + +**Every database table must include tenant isolation:** + +- **Field name:** `tenant_id` (snake_case in DB) or `tenantId` (camelCase in TypeScript code) +- **Type:** UUID foreign key to tenants table +- **Index:** Always indexed for query performance +- **RLS:** Use Row Level Security policies for tenant isolation +- **Repository pattern:** All queries filter by `tenant_id` + +**See [EXAMPLES.md](EXAMPLES.md#multi-tenant-patterns) for implementation patterns.** + +## Virtual Environment (Python Projects) + +**⚠️ ALWAYS activate virtual environment before running Python commands:** + +```bash +source .venv/bin/activate +``` + +Required for: + +- Running tests (`pytest`) +- Running pre-commit hooks +- Using task commands (`task test`, `task format`) +- Any Python script execution + +## When to Apply This Skill + +Use this skill when: + +- ✅ Writing new TypeScript/React or Python/FastAPI code +- ✅ Reviewing code in pull requests +- ✅ Fixing linting or formatting errors +- ✅ Setting up new projects from templates +- ✅ Configuring Prettier, ESLint, or Ruff +- ✅ Creating database schemas +- ✅ Implementing multi-tenant features +- ✅ User mentions: "code standards", "linting rules", "Grey Haven style", "formatting" + +## Template References + +These standards come from actual Grey Haven production templates: + +- **Frontend:** `cvi-template` (TanStack Start + React 19 + Drizzle) +- **Backend:** `cvi-backend-template` (FastAPI + SQLModel + PostgreSQL) + +When in doubt, reference these templates for patterns and configurations. + +## Critical Reminders + +1. **Line lengths:** TypeScript=90, Python=130 (NOT 80/88) +2. **Database fields:** ALWAYS `snake_case` (both TypeScript and Python schemas) +3. **`any` type:** ALLOWED in Grey Haven TypeScript (pragmatic approach) +4. **Double quotes:** TypeScript uses double quotes (`singleQuote: false`) +5. **Type hints:** REQUIRED in Python (`disallow_untyped_defs: true`) +6. **Virtual env:** MUST activate before Python commands +7. **Multi-tenant:** Every table has `tenant_id`/`tenantId` +8. **Path aliases:** Use `~/` for TypeScript imports from `src/` +9. **Trailing commas:** ALWAYS in TypeScript (`trailingComma: "all"`) +10. **Pre-commit hooks:** Run before every commit (both projects) + +## Next Steps + +- **Need examples?** See [EXAMPLES.md](EXAMPLES.md) for copy-paste code +- **Need configs?** See [REFERENCE.md](REFERENCE.md) for complete config files +- **Need templates?** See [templates/](templates/) for starter files +- **Reviewing code?** Use [checklists/](checklists/) for systematic reviews diff --git a/skills/code-style/checklists/python-review.md b/skills/code-style/checklists/python-review.md new file mode 100644 index 0000000..3209f61 --- /dev/null +++ b/skills/code-style/checklists/python-review.md @@ -0,0 +1,141 @@ +# Python/FastAPI Code Review Checklist + +Use this checklist when reviewing Python/FastAPI code or creating pull requests for Grey Haven projects. + +## Formatting & Style + +- [ ] **Line length**: Code lines do not exceed 130 characters (CRITICAL!) +- [ ] **Indentation**: Uses 4 spaces (not tabs or 2 spaces) +- [ ] **Quotes**: Uses double quotes for strings (Ruff default) +- [ ] **Line endings**: Uses Unix-style line endings (\\n) +- [ ] **Ruff formatted**: Code is formatted with Ruff (`ruff format .`) +- [ ] **Ruff linting**: No Ruff linting errors (`ruff check .`) + +## Type Hints + +- [ ] **Function signatures**: ALL functions have type hints (CRITICAL!) + - Parameters: `def get_user(user_id: str) -> Optional[User]:` + - Return types: Always include return type annotation +- [ ] **MyPy passing**: `mypy app/` has no type errors +- [ ] **Optional types**: Uses `Optional[T]` or `T | None` for nullable values +- [ ] **Generic types**: Uses proper generic types (`list[str]`, `dict[str, Any]`) +- [ ] **Type imports**: Imports types from `typing` module + +## Database Models (SQLModel) + +- [ ] **snake_case fields**: ALL database column names use snake_case (CRITICAL!) + - ✅ `created_at`, `tenant_id`, `email_address`, `is_active` + - ❌ `createdAt`, `tenantId`, `emailAddress`, `isActive` +- [ ] **Multi-tenant**: Models include `tenant_id` field +- [ ] **Field descriptions**: All fields have `description` parameter +- [ ] **Indexes**: Frequently queried fields have `index=True` +- [ ] **Constraints**: Foreign keys, unique constraints properly defined +- [ ] **Timestamps**: Uses UTC datetime (`UTCDateTime` type if available) +- [ ] **Table names**: Uses `__tablename__` with lowercase plural names + +## Pydantic Schemas + +- [ ] **Schema hierarchy**: Follows Base/Create/Update/Response pattern +- [ ] **Validators**: Uses `@field_validator` for custom validation +- [ ] **ConfigDict**: Response schemas have `model_config = ConfigDict(from_attributes=True)` +- [ ] **Field constraints**: Uses appropriate constraints (`ge`, `le`, `max_length`, etc.) +- [ ] **Optional fields**: Update schemas use `| None` for optional fields +- [ ] **Descriptions**: All fields have descriptions in `Field(..., description="...")` + +## FastAPI Endpoints + +- [ ] **Type hints**: ALL endpoint functions fully typed with `Annotated` +- [ ] **Docstrings**: Endpoints have comprehensive docstrings (Args, Returns, Raises) +- [ ] **Status codes**: Uses appropriate HTTP status codes from `status` module +- [ ] **Response models**: Endpoints specify `response_model` +- [ ] **Dependencies**: Uses Depends() for repository and auth dependencies +- [ ] **Error handling**: Raises HTTPException with proper status codes and messages +- [ ] **Router prefix**: Router has appropriate prefix and tags + +## Repository Pattern + +- [ ] **Tenant isolation**: ALL queries filter by `tenant_id` (CRITICAL!) +- [ ] **Type hints**: Repository methods fully typed +- [ ] **Async/await**: Uses async/await for database operations +- [ ] **Session management**: Properly commits and refreshes after changes +- [ ] **Error handling**: Handles database errors appropriately +- [ ] **CRUD methods**: Implements standard create, read, update, delete methods + +## Multi-Tenant Architecture + +- [ ] **Tenant filtering**: All queries include tenant_id filter +- [ ] **Repository methods**: Accept `tenant_id` parameter +- [ ] **Validation**: Validates user has access to requested tenant +- [ ] **Isolation**: No cross-tenant data leakage possible +- [ ] **Foreign keys**: Multi-tenant relationships properly enforced + +## Imports Organization + +- [ ] **Import order**: Follows Ruff isort rules: + 1. Standard library imports + 2. Third-party imports + 3. Local imports (app.*) +- [ ] **Absolute imports**: Uses absolute imports (not relative) +- [ ] **Grouped imports**: Related imports grouped together +- [ ] **No unused imports**: All imports are used + +## Testing (Pytest) + +- [ ] **Tests exist**: Endpoints/functions have corresponding tests +- [ ] **Test markers**: Uses pytest markers (@pytest.mark.unit, @pytest.mark.integration) +- [ ] **Fixtures**: Uses pytest fixtures for setup +- [ ] **Async tests**: Async tests decorated with `@pytest.mark.asyncio` +- [ ] **Mocking**: Uses AsyncMock/MagicMock for external dependencies +- [ ] **Coverage**: Maintains or improves test coverage (aim for >80%) +- [ ] **Assertions**: Tests have clear, specific assertions + +## Security + +- [ ] **Input validation**: Uses Pydantic schemas for input validation +- [ ] **SQL injection**: Uses parameterized queries (SQLModel handles this) +- [ ] **Authentication**: Endpoints require authentication via dependencies +- [ ] **Authorization**: Validates user has permission for actions +- [ ] **Secrets**: Uses environment variables for secrets (never hardcode) +- [ ] **Rate limiting**: Critical endpoints have rate limiting + +## Error Handling + +- [ ] **HTTPException**: Raises HTTPException with proper status codes +- [ ] **Error messages**: Error messages are descriptive and user-friendly +- [ ] **Logging**: Errors are logged appropriately +- [ ] **Validation errors**: Pydantic validation errors return 422 +- [ ] **Not found**: Returns 404 for missing resources + +## Performance + +- [ ] **Database queries**: Queries are efficient (no N+1 problems) +- [ ] **Indexes**: Frequently filtered columns have indexes +- [ ] **Pagination**: List endpoints implement pagination (limit/offset) +- [ ] **Async operations**: Uses async/await for I/O operations +- [ ] **Connection pooling**: Database uses connection pooling + +## Documentation + +- [ ] **Docstrings**: All functions have comprehensive docstrings +- [ ] **OpenAPI docs**: FastAPI auto-docs are accurate and complete +- [ ] **Type annotations**: Type hints serve as documentation +- [ ] **README updated**: README reflects any new features/changes +- [ ] **API examples**: Complex endpoints have usage examples + +## Pre-commit Checks + +- [ ] **Virtual env active**: Ran commands with `source .venv/bin/activate` +- [ ] **Ruff formatting**: `ruff format .` applied +- [ ] **Ruff linting**: `ruff check --fix .` passing +- [ ] **MyPy**: `mypy app/` passing with no type errors +- [ ] **Tests passing**: `pytest` passes all tests +- [ ] **Coverage**: Test coverage meets threshold (>80%) +- [ ] **Pre-commit hooks**: All pre-commit hooks pass + +## API Design + +- [ ] **RESTful**: Endpoints follow REST principles +- [ ] **Naming**: Endpoints use clear, descriptive names +- [ ] **Versioning**: API versioned appropriately (`/v1/`) +- [ ] **Consistency**: Similar endpoints have consistent patterns +- [ ] **CRUD complete**: Resource has full CRUD operations if needed diff --git a/skills/code-style/checklists/typescript-review.md b/skills/code-style/checklists/typescript-review.md new file mode 100644 index 0000000..29e41aa --- /dev/null +++ b/skills/code-style/checklists/typescript-review.md @@ -0,0 +1,113 @@ +# TypeScript/React Code Review Checklist + +Use this checklist when reviewing TypeScript/React code or creating pull requests for Grey Haven projects. + +## Formatting & Style + +- [ ] **Line width**: Code lines do not exceed 90 characters +- [ ] **Indentation**: Uses 2 spaces (not tabs or 4 spaces) +- [ ] **Semicolons**: All statements end with semicolons +- [ ] **Quotes**: Uses double quotes for strings (not single quotes) +- [ ] **Trailing commas**: Has trailing commas in objects, arrays, function parameters +- [ ] **Prettier formatted**: Code is formatted with Prettier (`npm run format`) +- [ ] **ESLint passing**: No ESLint errors (`npm run lint`) + +## TypeScript + +- [ ] **Type safety**: No `any` types used unnecessarily (but allowed when appropriate) +- [ ] **Type annotations**: Complex types have proper interfaces/types defined +- [ ] **Imports organized**: Imports are auto-sorted (external → internal → relative) +- [ ] **Path aliases**: Uses `~/` path alias for src imports (not `../..`) +- [ ] **tsconfig compliance**: Follows strict TypeScript configuration + +## Database Schema (Drizzle) + +- [ ] **snake_case fields**: ALL database column names use snake_case (CRITICAL!) + - ✅ `created_at`, `tenant_id`, `email_address` + - ❌ `createdAt`, `tenantId`, `emailAddress` +- [ ] **Multi-tenant**: Tables include `tenant_id` or `tenantId` field +- [ ] **Indexes**: Frequently queried fields have indexes +- [ ] **RLS policies**: Row Level Security policies defined for multi-tenant isolation +- [ ] **Foreign keys**: Relationships use proper foreign key constraints + +## React Components + +- [ ] **Component structure**: Follows Grey Haven component pattern: + 1. Imports (auto-sorted) + 2. Types/Interfaces + 3. Component definition + 4. State management (hooks first) + 5. Queries/Mutations + 6. Effects + 7. Event handlers + 8. Conditional renders + 9. Main render +- [ ] **Naming**: Components use PascalCase (`UserProfile.tsx`) +- [ ] **Props typed**: Component props have TypeScript interfaces +- [ ] **Hooks named**: Custom hooks start with `use-` prefix +- [ ] **Default export**: Route components export default + +## TanStack Query + +- [ ] **Query keys**: Uses descriptive, unique query keys +- [ ] **staleTime**: Sets appropriate staleTime (default: 60000ms / 1 minute) +- [ ] **Error handling**: Handles loading and error states +- [ ] **Mutations**: Uses useMutation for data updates +- [ ] **Invalidation**: Invalidates queries after mutations + +## Environment Variables + +- [ ] **Validation**: Environment variables validated with @t3-oss/env-core and Zod +- [ ] **VITE_ prefix**: Client variables prefixed with `VITE_` +- [ ] **Types**: All env variables have proper Zod schemas +- [ ] **Documentation**: Env variables have JSDoc comments + +## Multi-Tenant Architecture + +- [ ] **Tenant isolation**: All queries filter by `tenant_id` / `tenantId` +- [ ] **RLS enabled**: Row Level Security policies enforce tenant boundaries +- [ ] **Context usage**: Uses tenant context from auth provider +- [ ] **API calls**: Includes tenant ID in API requests + +## Testing + +- [ ] **Tests exist**: Components/functions have corresponding tests +- [ ] **Coverage**: Maintains or improves test coverage (aim for >80%) +- [ ] **Test structure**: Tests follow Arrange-Act-Assert pattern +- [ ] **Mocking**: External dependencies are properly mocked + +## Security + +- [ ] **Input validation**: User inputs are validated (Zod schemas) +- [ ] **XSS prevention**: No dangerouslySetInnerHTML without sanitization +- [ ] **API security**: API endpoints require authentication +- [ ] **Secrets**: No secrets or API keys in code (use env variables) + +## Accessibility + +- [ ] **Semantic HTML**: Uses appropriate HTML elements +- [ ] **ARIA labels**: Interactive elements have accessible labels +- [ ] **Keyboard nav**: Interactive elements are keyboard accessible +- [ ] **Color contrast**: Text has sufficient color contrast + +## Performance + +- [ ] **Code splitting**: Large components use lazy loading if appropriate +- [ ] **Memoization**: Expensive calculations use useMemo/useCallback +- [ ] **Query optimization**: Database queries are efficient (no N+1) +- [ ] **Bundle size**: No unnecessary dependencies added + +## Documentation + +- [ ] **JSDoc comments**: Complex functions have JSDoc comments +- [ ] **README updated**: README reflects any new features/changes +- [ ] **Type exports**: Exported types are documented +- [ ] **Examples**: Complex patterns have usage examples + +## Pre-commit Checks + +- [ ] **Build passes**: `npm run build` completes without errors +- [ ] **Linting passes**: `npm run lint` has no errors +- [ ] **Type checking**: `npx tsc --noEmit` has no errors +- [ ] **Tests passing**: `npm test` passes all tests +- [ ] **Pre-commit hooks**: Husky pre-commit hooks all pass diff --git a/skills/code-style/templates/.eslintrc.json b/skills/code-style/templates/.eslintrc.json new file mode 100644 index 0000000..00e197a --- /dev/null +++ b/skills/code-style/templates/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "root": true, + "env": { "browser": true, "es2020": true }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["react-refresh"], + "rules": { + "react-hooks/exhaustive-deps": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off", + "no-unused-vars": "off", + "react-refresh/only-export-components": "off" + } +} diff --git a/skills/code-style/templates/.prettierrc.json b/skills/code-style/templates/.prettierrc.json new file mode 100644 index 0000000..9da68af --- /dev/null +++ b/skills/code-style/templates/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "tabWidth": 2, + "semi": true, + "printWidth": 90, + "singleQuote": false, + "endOfLine": "lf", + "trailingComma": "all", + "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"] +} diff --git a/skills/code-style/templates/python-endpoint.py b/skills/code-style/templates/python-endpoint.py new file mode 100644 index 0000000..6cbf241 --- /dev/null +++ b/skills/code-style/templates/python-endpoint.py @@ -0,0 +1,186 @@ +"""Example FastAPI Router Template. + +Copy and adapt this for new Grey Haven API endpoints. +""" + +from datetime import datetime +from typing import Annotated +from uuid import UUID + +from fastapi import APIRouter, Depends, HTTPException, Query, status +from pydantic import BaseModel, Field + +# Replace with your actual imports +from app.db.models.example import ExampleDB +from app.db.repositories.example_repository import ExampleRepository +from app.dependencies import get_current_user, get_example_repository +from app.schemas.example import ExampleCreate, ExampleResponse, ExampleUpdate + +# 1. Create router with tags and dependencies +router = APIRouter( + prefix="/examples", + tags=["examples"], + dependencies=[Depends(get_current_user)], # Require authentication +) + + +# 2. POST endpoint - Create resource +@router.post("/", response_model=ExampleResponse, status_code=status.HTTP_201_CREATED) +async def create_example( + data: ExampleCreate, + repo: Annotated[ExampleRepository, Depends(get_example_repository)], + current_user: Annotated[dict, Depends(get_current_user)], +) -> ExampleResponse: + """ + Create a new example resource. + + Args: + data: Example creation data + repo: Example repository dependency + current_user: Currently authenticated user + + Returns: + ExampleResponse: Created example + + Raises: + HTTPException: If creation fails + """ + # Create resource with tenant isolation + example = await repo.create(data, tenant_id=current_user["tenant_id"]) + return ExampleResponse.model_validate(example) + + +# 3. GET endpoint - Retrieve single resource +@router.get("/{example_id}", response_model=ExampleResponse) +async def get_example( + example_id: UUID, + repo: Annotated[ExampleRepository, Depends(get_example_repository)], + current_user: Annotated[dict, Depends(get_current_user)], +) -> ExampleResponse: + """ + Get example by ID with tenant isolation. + + Args: + example_id: Example UUID + repo: Example repository dependency + current_user: Currently authenticated user + + Returns: + ExampleResponse: Example data + + Raises: + HTTPException: If not found + """ + # Get with tenant isolation + example = await repo.get_by_id(example_id, tenant_id=current_user["tenant_id"]) + + if not example: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Example not found" + ) + + return ExampleResponse.model_validate(example) + + +# 4. GET endpoint - List resources with pagination +@router.get("/", response_model=list[ExampleResponse]) +async def list_examples( + repo: Annotated[ExampleRepository, Depends(get_example_repository)], + current_user: Annotated[dict, Depends(get_current_user)], + limit: int = Query(100, ge=1, le=1000, description="Max items to return"), + offset: int = Query(0, ge=0, description="Number of items to skip"), + is_active: bool | None = Query(None, description="Filter by active status"), +) -> list[ExampleResponse]: + """ + List examples with pagination and filtering. + + Args: + repo: Example repository dependency + current_user: Currently authenticated user + limit: Maximum number of items to return + offset: Number of items to skip + is_active: Optional filter by active status + + Returns: + list[ExampleResponse]: List of examples + """ + # List with tenant isolation + examples = await repo.list_by_tenant( + tenant_id=current_user["tenant_id"], + limit=limit, + offset=offset, + is_active=is_active, + ) + + return [ExampleResponse.model_validate(e) for e in examples] + + +# 5. PATCH endpoint - Update resource +@router.patch("/{example_id}", response_model=ExampleResponse) +async def update_example( + example_id: UUID, + data: ExampleUpdate, + repo: Annotated[ExampleRepository, Depends(get_example_repository)], + current_user: Annotated[dict, Depends(get_current_user)], +) -> ExampleResponse: + """ + Update example by ID with tenant isolation. + + Args: + example_id: Example UUID + data: Update data (partial fields) + repo: Example repository dependency + current_user: Currently authenticated user + + Returns: + ExampleResponse: Updated example + + Raises: + HTTPException: If not found + """ + # Get existing example with tenant isolation + example = await repo.get_by_id(example_id, tenant_id=current_user["tenant_id"]) + + if not example: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Example not found" + ) + + # Update fields (exclude_unset to only update provided fields) + update_dict = data.model_dump(exclude_unset=True) + for field, value in update_dict.items(): + setattr(example, field, value) + + # Save updates + updated = await repo.update(example) + return ExampleResponse.model_validate(updated) + + +# 6. DELETE endpoint - Delete resource +@router.delete("/{example_id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete_example( + example_id: UUID, + repo: Annotated[ExampleRepository, Depends(get_example_repository)], + current_user: Annotated[dict, Depends(get_current_user)], +) -> None: + """ + Delete example by ID with tenant isolation. + + Args: + example_id: Example UUID + repo: Example repository dependency + current_user: Currently authenticated user + + Raises: + HTTPException: If not found + """ + # Get existing example with tenant isolation + example = await repo.get_by_id(example_id, tenant_id=current_user["tenant_id"]) + + if not example: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Example not found" + ) + + # Delete + await repo.delete(example_id, tenant_id=current_user["tenant_id"]) diff --git a/skills/code-style/templates/python-model.py b/skills/code-style/templates/python-model.py new file mode 100644 index 0000000..1f284ce --- /dev/null +++ b/skills/code-style/templates/python-model.py @@ -0,0 +1,126 @@ +"""Example SQLModel Database Model Template. + +Copy and adapt this for new Grey Haven database models. +""" + +from __future__ import annotations + +from datetime import datetime +from uuid import UUID, uuid4 + +from sqlalchemy import Column as SAColumn +from sqlmodel import JSON, Column, Field, SQLModel + + +def utc_now() -> datetime: + """Return current UTC datetime.""" + from datetime import UTC + + return datetime.now(UTC) + + +class ExampleDB(SQLModel, table=True): # type: ignore[call-arg] + """ + Example database model with multi-tenant support. + + This model demonstrates Grey Haven's database conventions: + - snake_case field names + - Multi-tenant isolation with tenant_id + - UTC timestamps + - Proper indexes + - Comprehensive docstrings + """ + + __tablename__ = "examples" + + # Primary identification + id: UUID = Field( + default_factory=uuid4, primary_key=True, description="Unique example identifier" + ) + + # Multi-tenant field (CRITICAL - always include!) + tenant_id: UUID = Field( + foreign_key="tenants.id", index=True, description="Owning tenant identifier" + ) + + # Example fields - all snake_case! + name: str = Field(index=True, max_length=255, description="Example name") + description: str | None = Field( + default=None, max_length=1000, description="Optional description" + ) + + # Relationships (foreign keys) + owner_id: UUID | None = Field( + default=None, foreign_key="users.id", index=True, description="Owner user ID" + ) + + # Status flags + is_active: bool = Field(default=True, description="Whether example is active") + is_archived: bool = Field(default=False, description="Whether example is archived") + + # JSON metadata field + metadata: dict | None = Field( + default=None, + sa_column=Column(JSON), + description="Flexible JSON metadata storage", + ) + + # Numerical fields + priority: int = Field( + default=0, ge=0, le=10, description="Priority level (0-10)" + ) + max_retries: int = Field( + default=3, ge=0, description="Maximum number of retry attempts" + ) + + # Timestamps (UTC) + created_at: datetime = Field( + default_factory=utc_now, description="Creation timestamp (UTC)" + ) + updated_at: datetime = Field( + default_factory=utc_now, description="Last update timestamp (UTC)" + ) + archived_at: datetime | None = Field( + default=None, description="Archive timestamp (UTC)" + ) + + # Uncomment if using custom UTCDateTime type + # from app.db.db_types import UTCDateTime + # created_at: datetime = Field( + # default_factory=utc_now, + # sa_column=SAColumn(UTCDateTime, nullable=False) + # ) + # updated_at: datetime = Field( + # default_factory=utc_now, + # sa_column=SAColumn(UTCDateTime, nullable=False, onupdate=utc_now) + # ) + + +# Pydantic schemas for API (in separate schemas file) +# class ExampleBase(BaseModel): +# """Base example schema with shared fields.""" +# name: str = Field(..., max_length=255) +# description: str | None = None +# is_active: bool = True +# priority: int = Field(default=0, ge=0, le=10) +# +# class ExampleCreate(ExampleBase): +# """Schema for creating an example.""" +# tenant_id: UUID +# +# class ExampleUpdate(BaseModel): +# """Schema for updating an example (all fields optional).""" +# name: str | None = None +# description: str | None = None +# is_active: bool | None = None +# priority: int | None = Field(None, ge=0, le=10) +# +# class ExampleResponse(ExampleBase): +# """Example response schema.""" +# id: UUID +# tenant_id: UUID +# owner_id: UUID | None +# created_at: datetime +# updated_at: datetime +# +# model_config = ConfigDict(from_attributes=True) diff --git a/skills/code-style/templates/ruff.toml b/skills/code-style/templates/ruff.toml new file mode 100644 index 0000000..719403d --- /dev/null +++ b/skills/code-style/templates/ruff.toml @@ -0,0 +1,73 @@ +# Grey Haven Studio - Ruff Configuration Template +# Copy this to your project root as pyproject.toml or ruff.toml + +[tool.ruff] +# CRITICAL: Line length is 130, not 80 or 88! +line-length = 130 +indent-width = 4 + +# Auto-fix issues without showing unfixable errors +fix-only = true +show-fixes = true + +# Python version +target-version = "py312" + +[tool.ruff.lint] +# Enable specific linter rules +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort (import sorting) + "B", # flake8-bugbear (bug detection) + "C4", # flake8-comprehensions + "UP", # pyupgrade (automatic upgrades) +] + +# Ignore specific rules if needed +ignore = [] + +[tool.ruff.format] +# Use double quotes for strings +quote-style = "double" + +# Use spaces for indentation +indent-style = "space" + +# Use Unix-style line endings +line-ending = "lf" + +[tool.ruff.lint.isort] +# Configure import sorting +known-first-party = ["app"] +section-order = [ + "future", + "standard-library", + "third-party", + "first-party", + "local-folder", +] + +# MyPy Configuration (add to pyproject.toml) +# [tool.mypy] +# python_version = "3.12" +# warn_return_any = true +# warn_unused_configs = true +# disallow_untyped_defs = true # REQUIRED: Type hints on all functions! +# check_untyped_defs = true +# ignore_missing_imports = false +# strict_optional = true +# warn_redundant_casts = true +# warn_unused_ignores = true + +# Pytest Configuration (add to pyproject.toml) +# [tool.pytest.ini_options] +# pythonpath = ["."] +# asyncio_mode = "auto" +# testpaths = ["tests"] +# markers = [ +# "unit: Unit tests", +# "integration: Integration tests", +# "e2e: End-to-end tests", +# ] diff --git a/skills/code-style/templates/typescript-component.tsx b/skills/code-style/templates/typescript-component.tsx new file mode 100644 index 0000000..85863c9 --- /dev/null +++ b/skills/code-style/templates/typescript-component.tsx @@ -0,0 +1,85 @@ +// Example React Component Template +// Copy and adapt this for new Grey Haven components + +import { useState } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { Button } from "~/lib/components/ui/button"; +import { Card } from "~/lib/components/ui/card"; +import { queryClient } from "~/lib/query-client"; + +// 1. Define your types/interfaces +interface MyComponentProps { + id: string; + onUpdate?: (data: MyData) => void; +} + +interface MyData { + id: string; + name: string; + created_at: Date; // snake_case for database fields + is_active: boolean; +} + +// 2. Component (default export for routes) +export default function MyComponent({ id, onUpdate }: MyComponentProps) { + // 3. State management + const [isEditing, setIsEditing] = useState(false); + + // 4. Queries with TanStack Query + const { data, isLoading, error } = useQuery( + { + queryKey: ["myData", id], + queryFn: async () => { + // Replace with your API call + const response = await fetch(`/api/data/${id}`); + return response.json(); + }, + staleTime: 60000, // 1 minute - Grey Haven default + }, + queryClient, + ); + + // 5. Event handlers + const handleSave = async () => { + // Replace with your save logic + console.log("Saving..."); + setIsEditing(false); + onUpdate?.(data); + }; + + // 6. Conditional renders + if (isLoading) { + return
Loading...
; + } + + if (error) { + return
Error loading data
; + } + + if (!data) { + return
No data found
; + } + + // 7. Main render + return ( + +

{data.name}

+ + {isEditing ? ( +
+ {/* Edit mode UI */} + + +
+ ) : ( +
+ {/* View mode UI */} +

Status: {data.is_active ? "Active" : "Inactive"}

+ +
+ )} +
+ ); +} diff --git a/skills/code-style/templates/typescript-server-function.ts b/skills/code-style/templates/typescript-server-function.ts new file mode 100644 index 0000000..f085341 --- /dev/null +++ b/skills/code-style/templates/typescript-server-function.ts @@ -0,0 +1,92 @@ +// Example TanStack Start Server Function Template +// Copy and adapt this for new Grey Haven server functions + +import { createServerFn } from "@tanstack/start"; +import { db } from "~/lib/server/db"; +import { users } from "~/lib/server/schema/users"; +import { eq } from "drizzle-orm"; +import { z } from "zod"; + +// 1. Define input schema with Zod +const getUserInputSchema = z.object({ + userId: z.string().uuid(), + tenantId: z.string(), // Always include tenant_id for multi-tenant isolation +}); + +// 2. Define output type +interface UserOutput { + id: string; + name: string; + email: string; + created_at: Date; + tenant_id: string; +} + +// 3. Create server function +export const getUser = createServerFn("GET", async (input: unknown): Promise => { + // Validate input + const { userId, tenantId } = getUserInputSchema.parse(input); + + // Query database with tenant isolation + const user = await db.query.users.findFirst({ + where: eq(users.id, userId) && eq(users.tenant_id, tenantId), // Tenant filtering! + }); + + if (!user) { + throw new Error("User not found"); + } + + // Return typed result + return { + id: user.id, + name: user.name, + email: user.email, + created_at: user.created_at, + tenant_id: user.tenant_id, + }; +}); + +// 4. Mutation example +const updateUserInputSchema = z.object({ + userId: z.string().uuid(), + tenantId: z.string(), + name: z.string().min(1), + email: z.string().email(), +}); + +export const updateUser = createServerFn( + "POST", + async (input: unknown): Promise => { + // Validate input + const { userId, tenantId, name, email } = updateUserInputSchema.parse(input); + + // Update with tenant isolation + const [updatedUser] = await db + .update(users) + .set({ name, email, updated_at: new Date() }) + .where(eq(users.id, userId) && eq(users.tenant_id, tenantId)) // Tenant filtering! + .returning(); + + if (!updatedUser) { + throw new Error("User not found or update failed"); + } + + return { + id: updatedUser.id, + name: updatedUser.name, + email: updatedUser.email, + created_at: updatedUser.created_at, + tenant_id: updatedUser.tenant_id, + }; + }, +); + +// 5. Usage in client components +// import { useQuery } from "@tanstack/react-query"; +// import { getUser } from "~/lib/server/functions/users"; +// +// const { data: user } = useQuery({ +// queryKey: ["user", userId], +// queryFn: () => getUser({ userId, tenantId }), +// staleTime: 60000, +// }); diff --git a/skills/documentation-architecture/SKILL.md b/skills/documentation-architecture/SKILL.md new file mode 100644 index 0000000..4007270 --- /dev/null +++ b/skills/documentation-architecture/SKILL.md @@ -0,0 +1,25 @@ +# Documentation Architecture Skill + +Comprehensive documentation systems design including structure, generation, maintenance, and automation. + +## Description + +Design and maintain scalable documentation architectures with automated generation, versioning, and search. + +## What's Included + +- **Examples**: Documentation structures, automated generation +- **Reference**: Documentation best practices, architecture patterns +- **Templates**: Documentation templates, style guides + +## Use When + +- Designing documentation systems +- Automating documentation +- Large-scale doc projects + +## Related Agents + +- `docs-architect` + +**Skill Version**: 1.0 diff --git a/skills/documentation-architecture/checklists/documentation-quality-checklist.md b/skills/documentation-architecture/checklists/documentation-quality-checklist.md new file mode 100644 index 0000000..990868c --- /dev/null +++ b/skills/documentation-architecture/checklists/documentation-quality-checklist.md @@ -0,0 +1,152 @@ +# Documentation Quality Checklist + +**Use when creating or reviewing documentation.** + +## Accuracy + +- [ ] All code examples are tested and verified +- [ ] API contracts match actual implementation +- [ ] Version numbers are correct and up-to-date +- [ ] External links are valid (not 404) +- [ ] Screenshots reflect current UI +- [ ] Configuration examples are accurate +- [ ] Environment variables are correctly documented +- [ ] Dependency versions match package.json/requirements.txt + +## Completeness + +- [ ] All public APIs are documented +- [ ] All required parameters are explained +- [ ] All optional parameters have defaults documented +- [ ] Response formats are specified with examples +- [ ] Error cases are covered with status codes +- [ ] Authentication requirements are clear +- [ ] Rate limiting is documented +- [ ] Deprecation notices are included where needed +- [ ] Migration guides for breaking changes +- [ ] Changelog is up-to-date + +## API Documentation + +- [ ] OpenAPI 3.1 specification is valid +- [ ] All endpoints have descriptions +- [ ] Request/response schemas are complete +- [ ] Multi-language code examples (TypeScript, Python, cURL) +- [ ] Authentication flows documented +- [ ] Error responses documented +- [ ] Interactive documentation (Swagger UI/Redoc) works +- [ ] Try-it-now functionality tested + +## Architecture Documentation + +- [ ] Executive summary for stakeholders +- [ ] Mermaid system architecture diagram +- [ ] Sequence diagrams for key flows +- [ ] Data flow diagrams +- [ ] Integration points documented +- [ ] Security model explained +- [ ] ADRs (Architectural Decision Records) included +- [ ] Database schema documented + +## Code Examples + +- [ ] TypeScript examples follow Grey Haven patterns +- [ ] Python examples follow Grey Haven patterns +- [ ] cURL examples are complete and correct +- [ ] Examples use realistic data +- [ ] Examples show error handling +- [ ] Examples demonstrate authentication +- [ ] Examples are syntax-highlighted +- [ ] Examples are copy-paste ready + +## Consistency + +- [ ] Uniform terminology throughout +- [ ] Consistent formatting (headings, lists, code blocks) +- [ ] Standard code example format +- [ ] Unified voice and tone +- [ ] Consistent naming conventions +- [ ] Cross-references use standard format +- [ ] Diagrams follow consistent style + +## Accessibility + +- [ ] Content is searchable +- [ ] Clear navigation structure +- [ ] Mobile-responsive design +- [ ] WCAG 2.1 AA compliant +- [ ] Alt text for images and diagrams +- [ ] Keyboard navigation works +- [ ] Color contrast meets standards +- [ ] Screen reader compatible + +## Usability + +- [ ] Progressive disclosure (simple → complex) +- [ ] Practical examples and use cases +- [ ] Troubleshooting guides included +- [ ] Quick reference sections provided +- [ ] Table of contents for long docs +- [ ] Search functionality works +- [ ] Clear call-to-action buttons +- [ ] Getting started guide present + +## Documentation Coverage + +- [ ] Function coverage >80% +- [ ] API coverage >80% +- [ ] Type coverage >80% +- [ ] No critical gaps in documentation +- [ ] Coverage report generated +- [ ] CI/CD validation passes + +## Grey Haven Standards + +- [ ] Cloudflare Workers patterns documented +- [ ] TanStack Start patterns included +- [ ] FastAPI patterns covered +- [ ] Multi-tenant patterns explained +- [ ] tenant_id filtering documented +- [ ] RLS policies explained +- [ ] Doppler secrets management documented +- [ ] bun package manager (NOT npm!) + +## CI/CD Integration + +- [ ] Documentation generates automatically on merge +- [ ] Pre-commit hooks validate coverage +- [ ] Broken link checker runs +- [ ] Code examples are tested +- [ ] Coverage threshold enforced (<80% fails build) +- [ ] Deployment to Cloudflare Pages configured + +## Deployment + +- [ ] Deployed to correct environment (staging/production) +- [ ] Custom domain configured +- [ ] SSL certificate valid +- [ ] Redirects configured (if needed) +- [ ] Analytics tracking enabled +- [ ] Search indexing configured +- [ ] CDN caching configured + +## Maintenance + +- [ ] Quarterly documentation audit scheduled +- [ ] User feedback collection in place +- [ ] Version support matrix documented +- [ ] Deprecation timeline clear +- [ ] Migration guides for breaking changes +- [ ] Contact information for support +- [ ] Contribution guidelines present + +## Pre-Release + +- [ ] All checklist items above completed +- [ ] Documentation reviewed by team +- [ ] Examples tested on staging +- [ ] Coverage meets >80% threshold +- [ ] No broken links +- [ ] Mobile testing completed +- [ ] Accessibility audit passed +- [ ] User testing feedback incorporated diff --git a/skills/documentation-architecture/examples/INDEX.md b/skills/documentation-architecture/examples/INDEX.md new file mode 100644 index 0000000..4024b81 --- /dev/null +++ b/skills/documentation-architecture/examples/INDEX.md @@ -0,0 +1,67 @@ +# Documentation Architecture Examples + +Real-world examples of comprehensive documentation generation, architecture documentation, and coverage validation. + +## Available Examples + +### [OpenAPI 3.1 Generation from FastAPI](openapi-generation.md) + +Complete workflow for automatic API documentation generation from FastAPI codebase. + +**Scenario**: E-commerce API with 47 undocumented endpoints causing 12 integration issues/week + +**Solution**: Enhanced OpenAPI generation, multi-language examples, interactive Swagger UI, CI/CD auto-generation + +**Results**: Integration issues 12/week → 0.5/week (96% reduction), manual doc time 4hrs → 0 (automated) + +**Key Techniques**: FastAPI OpenAPI customization, Pydantic v2 field validators, example generation scripts + +--- + +### [System Architecture Documentation with Mermaid](architecture-docs.md) + +Comprehensive system architecture documentation reducing onboarding time from 3-4 weeks to 4-5 days. + +**Scenario**: No architecture docs, tribal knowledge spread across 8 developers, 3-4 week onboarding + +**Solution**: 8 Mermaid diagrams, Architecture Decision Records, progressive disclosure, version-controlled + +**Results**: Onboarding 3-4 weeks → 4-5 days (75% reduction), architecture questions 15hrs/week → 2hrs/week + +**Key Techniques**: Mermaid diagrams (system, sequence, data flow, ER), ADR template, multi-tenant flow docs + +--- + +### [Documentation Coverage Validation](coverage-validation.md) + +Automated documentation coverage analysis with 80% threshold enforcement in CI/CD. + +**Scenario**: Unknown coverage, 147 undocumented functions, no visibility into gaps + +**Solution**: TypeScript coverage (ts-morph), Python coverage (AST), HTML reports, CI/CD enforcement + +**Results**: TS 42% → 87%, Python 38% → 91%, API 51% → 95%, undocumented 147 → 18 + +**Key Techniques**: AST parsing, OpenAPI schema analysis, coverage threshold enforcement, HTML reports + +--- + +## Common Patterns + +1. **Automation First**: All documentation generated/validated automatically +2. **CI/CD Integration**: Updates on every commit, coverage checks block PRs +3. **Multi-Language Support**: Examples in TypeScript, Python, cURL +4. **Visual Documentation**: Mermaid diagrams for architecture, sequences, data models +5. **Progressive Disclosure**: Start with overview, drill into details + +## Quick Reference + +| Need | Example | Key Tool | +|------|---------|----------| +| API Documentation | [openapi-generation.md](openapi-generation.md) | FastAPI + Pydantic v2 | +| System Architecture | [architecture-docs.md](architecture-docs.md) | Mermaid + ADRs | +| Coverage Analysis | [coverage-validation.md](coverage-validation.md) | ts-morph + Python AST | + +--- + +Related: [Reference Guides](../reference/INDEX.md) | [Templates](../templates/) | [Return to Agent](../docs-architect.md) diff --git a/skills/documentation-architecture/examples/architecture-docs.md b/skills/documentation-architecture/examples/architecture-docs.md new file mode 100644 index 0000000..7ec9f98 --- /dev/null +++ b/skills/documentation-architecture/examples/architecture-docs.md @@ -0,0 +1,442 @@ +# Example: System Architecture Documentation with Mermaid Diagrams + +Complete workflow for creating comprehensive system architecture documentation for a distributed Grey Haven application. + +## Context + +**Project**: Multi-Tenant SaaS Platform (TanStack Start + Cloudflare Workers + FastAPI + PostgreSQL) +**Problem**: New developers taking 3-4 weeks to understand system architecture, high onboarding cost +**Goal**: Create comprehensive architecture documentation that reduces onboarding time to <1 week + +**Initial State**: +- No architecture documentation +- Tribal knowledge spread across 8 senior developers +- New hires asking same questions repeatedly +- 3-4 weeks until new developer productive +- Architecture decisions not documented (ADRs missing) + +## Step 1: System Overview with Mermaid + +### High-Level Architecture Diagram + +```mermaid +graph TB + subgraph "Client Layer" + Browser[Web Browser] + Mobile[Mobile App] + end + + subgraph "Edge Layer (Cloudflare Workers)" + Gateway[API Gateway] + Auth[Auth Service] + Cache[KV Cache] + end + + subgraph "Application Layer" + Frontend[TanStack Start
React 19] + Backend[FastAPI Backend
Python 3.12] + end + + subgraph "Data Layer" + PostgreSQL[(PostgreSQL
PlanetScale)] + Redis[(Redis Cache
Upstash)] + S3[(R2 Object Storage
Cloudflare)] + end + + subgraph "External Services" + Stripe[Stripe
Payments] + SendGrid[SendGrid
Email] + DataDog[DataDog
Monitoring] + end + + Browser --> Gateway + Mobile --> Gateway + Gateway --> Auth + Gateway --> Frontend + Gateway --> Backend + Auth --> Cache + Frontend --> PostgreSQL + Backend --> PostgreSQL + Backend --> Redis + Backend --> S3 + Backend --> Stripe + Backend --> SendGrid + Backend -.telemetry.-> DataDog +``` + +## Step 2: Request Flow Sequence Diagrams + +### User Authentication Flow + +```mermaid +sequenceDiagram + actor User + participant Browser + participant Gateway as API Gateway
(Cloudflare Worker) + participant Auth as Auth Service
(Cloudflare Worker) + participant KV as KV Cache + participant DB as PostgreSQL + + User->>Browser: Enter email/password + Browser->>Gateway: POST /auth/login + Gateway->>Auth: Validate credentials + Auth->>DB: Query user by email + DB-->>Auth: User record + + alt Valid Credentials + Auth->>Auth: Hash password & verify + Auth->>Auth: Generate JWT token + Auth->>KV: Store session (token -> user_id) + KV-->>Auth: OK + Auth-->>Gateway: {token, user} + Gateway-->>Browser: 200 OK {token, user} + Browser->>Browser: Store token in localStorage + Browser-->>User: Redirect to dashboard + else Invalid Credentials + Auth-->>Gateway: 401 Unauthorized + Gateway-->>Browser: {error: "INVALID_CREDENTIALS"} + Browser-->>User: Show error message + end +``` + +### Multi-Tenant Data Access Flow + +```mermaid +sequenceDiagram + participant Client + participant Gateway + participant Backend as FastAPI Backend + participant DB as PostgreSQL
(Row-Level Security) + + Client->>Gateway: GET /api/orders
Authorization: Bearer + Gateway->>Gateway: Validate JWT token + Gateway->>Gateway: Extract tenant_id from token + Gateway->>Backend: Forward request
X-Tenant-ID: tenant_123 + + Backend->>Backend: Set session context
SET app.tenant_id = 'tenant_123' + Backend->>DB: SELECT * FROM orders
(RLS automatically filters by tenant) + + Note over DB: Row-Level Security Policy:
CREATE POLICY tenant_isolation ON orders
FOR SELECT USING (tenant_id = current_setting('app.tenant_id')) + + DB-->>Backend: Orders for tenant_123 only + Backend-->>Gateway: {orders: [...]} + Gateway-->>Client: 200 OK {orders: [...]} +``` + +## Step 3: Data Flow Diagram + +### Order Processing Data Flow + +```mermaid +flowchart LR + User[User Creates Order] --> Validation[Validate Order Data] + Validation --> Stock{Check Stock
Availability} + + Stock -->|Insufficient| Error[Return 400 Error] + Stock -->|Available| Reserve[Reserve Inventory] + + Reserve --> Payment[Process Payment
via Stripe] + Payment -->|Failed| Release[Release Reservation] + Release --> Error + + Payment -->|Success| CreateOrder[Create Order
in Database] + CreateOrder --> Queue[Queue Email
Confirmation] + Queue --> Cache[Invalidate
User Cache] + Cache --> Success[Return Order] + + Success --> Async[Async: Send Email
via SendGrid] + Success --> Metrics[Update Metrics
in DataDog] +``` + +## Step 4: Database Schema ER Diagram + +```mermaid +erDiagram + TENANT ||--o{ USER : has + TENANT ||--o{ ORDER : has + USER ||--o{ ORDER : places + ORDER ||--|{ ORDER_ITEM : contains + PRODUCT ||--o{ ORDER_ITEM : included_in + TENANT ||--o{ PRODUCT : owns + + TENANT { + uuid id PK + string name + string subdomain UK + timestamp created_at + } + + USER { + uuid id PK + uuid tenant_id FK + string email UK + string hashed_password + string role + timestamp created_at + } + + PRODUCT { + uuid id PK + uuid tenant_id FK + string name + decimal price + int stock + } + + ORDER { + uuid id PK + uuid tenant_id FK + uuid user_id FK + decimal subtotal + decimal tax + decimal total + string status + timestamp created_at + } + + ORDER_ITEM { + uuid id PK + uuid order_id FK + uuid product_id FK + int quantity + decimal unit_price + } +``` + +## Step 5: Deployment Architecture + +```mermaid +graph TB + subgraph "Development" + DevBranch[Feature Branch] + DevEnv[Dev Environment
Cloudflare Preview] + end + + subgraph "Staging" + MainBranch[Main Branch] + StageEnv[Staging Environment
staging.greyhaven.com] + StageDB[(Staging PostgreSQL)] + end + + subgraph "Production" + Release[Release Tag] + ProdWorkers[Cloudflare Workers
300+ Datacenters] + ProdDB[(Production PostgreSQL
PlanetScale)] + ProdCache[(Redis Cache
Upstash)] + end + + DevBranch -->|git push| CI1[GitHub Actions] + CI1 -->|Deploy| DevEnv + + DevBranch -->|PR Merged| MainBranch + MainBranch -->|Deploy| CI2[GitHub Actions] + CI2 -->|Run Tests| TestSuite + TestSuite -->|Success| StageEnv + StageEnv --> StageDB + + MainBranch -->|git tag v1.0.0| Release + Release -->|Deploy| CI3[GitHub Actions] + CI3 -->|Canary 10%| ProdWorkers + CI3 -->|Monitor 10 min| Metrics + Metrics -->|Success| FullDeploy[100% Rollout] + FullDeploy --> ProdWorkers + ProdWorkers --> ProdDB + ProdWorkers --> ProdCache +``` + +## Step 6: State Machine Diagram for Order Status + +```mermaid +stateDiagram-v2 + [*] --> Pending: Order Created + Pending --> Processing: Payment Confirmed + Pending --> Cancelled: Payment Failed + + Processing --> Shipped: Fulfillment Complete + Processing --> Cancelled: Out of Stock + + Shipped --> Delivered: Tracking Confirmed + Shipped --> Returned: Customer Return + + Delivered --> Returned: Return Requested + Returned --> Refunded: Return Approved + + Cancelled --> [*] + Delivered --> [*] + Refunded --> [*] + + note right of Pending + Inventory reserved + Payment processing + end note + + note right of Processing + Items picked + Preparing shipment + end note + + note right of Shipped + Tracking number assigned + In transit + end note +``` + +## Step 7: Architecture Decision Records (ADRs) + +### ADR-001: Choose Cloudflare Workers for Edge Computing + +```markdown +# ADR-001: Use Cloudflare Workers for API Gateway and Auth + +**Date**: 2024-01-15 +**Status**: Accepted +**Decision Makers**: Engineering Team + +## Context + +We need an edge computing platform for API gateway, authentication, and caching that: +- Provides global low latency (<50ms p95) +- Scales automatically without management +- Integrates with our CDN infrastructure +- Supports multi-tenant architecture + +## Decision + +We will use Cloudflare Workers for edge computing with KV for session storage. + +## Alternatives Considered + +1. **AWS Lambda@Edge**: Good performance but vendor lock-in, higher cost +2. **Traditional Load Balancer**: Single region, no edge caching +3. **Self-hosted Edge Nodes**: Complex deployment, maintenance overhead + +## Consequences + +**Positive**: +- Global deployment (300+ datacenters) with <50ms latency worldwide +- Auto-scaling to zero cost when idle +- Built-in DDoS protection and WAF +- KV storage for session caching (sub-millisecond reads) +- 1ms CPU time limit forces efficient code + +**Negative**: +- 1ms CPU time limit requires careful optimization +- Cold starts (though <10ms typically) +- Limited to JavaScript/TypeScript/Rust/Python (via Pyodide) +- No native PostgreSQL driver (must use HTTP-based client) + +## Implementation + +- API Gateway: Handles routing, CORS, rate limiting +- Auth Service: JWT validation, session management (KV) +- Cache Layer: API response caching (KV + Cache API) + +## Monitoring + +- Worker CPU time (aim for <500μs p95) +- KV cache hit rate (aim for >95%) +- Edge response time (aim for <50ms p95) +``` + +### ADR-002: PostgreSQL with Row-Level Security for Multi-Tenancy + +```markdown +# ADR-002: PostgreSQL Row-Level Security (RLS) for Multi-Tenant Isolation + +**Date**: 2024-01-20 +**Status**: Accepted + +## Context + +Multi-tenant SaaS requires strict data isolation. Accidental cross-tenant data access would be a critical security breach. + +## Decision + +Use PostgreSQL Row-Level Security (RLS) policies to enforce tenant isolation at the database level. + +## Implementation + +```sql +-- Enable RLS on all tables +ALTER TABLE orders ENABLE ROW LEVEL SECURITY; + +-- Create policy that filters by session tenant_id +CREATE POLICY tenant_isolation ON orders + FOR ALL + USING (tenant_id = current_setting('app.tenant_id', true)::uuid); + +-- Application sets tenant context per request +SET app.tenant_id = ''; +``` + +## Consequences + +**Positive**: +- Database-level enforcement (cannot be bypassed by application bugs) +- Automatic filtering on all queries (including ORMs) +- Performance: RLS uses indexes efficiently + +**Negative**: +- Requires setting session context per connection +- Slightly more complex query plans + +## Monitoring + +- Weekly audit: Check for tables missing RLS +- Quarterly penetration test: Attempt cross-tenant access +``` + +## Results + +### Before + +- No architecture documentation +- 3-4 weeks until new developer productive +- 15+ hours/week answering architecture questions +- Architecture decisions lost to time +- Difficult to identify bottlenecks + +### After + +- Comprehensive architecture docs with 8 Mermaid diagrams +- 5 Architecture Decision Records documenting key choices +- Documentation in Git (versioned, reviewed) +- Interactive diagrams (clickable, navigable) + +### Improvements + +- Onboarding time: 3-4 weeks → 4-5 days (75% reduction) +- Architecture questions: 15 hrs/week → 2 hrs/week (87% reduction) +- New developer productivity: Week 4 → Week 1 +- Time to understand data flow: 2 weeks → 1 day + +### Developer Feedback + +- "The sequence diagrams made auth flow crystal clear" +- "ERD diagram helped me understand relationships immediately" +- "ADRs answered 'why did we choose X?' questions" + +## Key Lessons + +1. **Mermaid Diagrams**: Version-controlled, reviewable, always up-to-date +2. **Multiple Perspectives**: System, sequence, data flow, deployment diagrams all needed +3. **ADRs are Critical**: "Why" is as important as "what" +4. **Progressive Disclosure**: Overview first, then drill into details +5. **Keep Diagrams Simple**: One concept per diagram, not everything at once + +## Prevention Measures + +**Implemented**: +- [x] All architecture docs in Git (versioned) +- [x] Mermaid diagrams (not static images) +- [x] ADR template for all major decisions +- [x] Onboarding checklist includes reading architecture docs + +**Ongoing**: +- [ ] Auto-generate diagrams from code (infrastructure as code) +- [ ] Quarterly architecture review (docs up-to-date?) +- [ ] New ADR for every major technical decision + +--- + +Related: [openapi-generation.md](openapi-generation.md) | [coverage-validation.md](coverage-validation.md) | [Return to INDEX](INDEX.md) diff --git a/skills/documentation-architecture/examples/coverage-validation.md b/skills/documentation-architecture/examples/coverage-validation.md new file mode 100644 index 0000000..12eb9b9 --- /dev/null +++ b/skills/documentation-architecture/examples/coverage-validation.md @@ -0,0 +1,411 @@ +# Example: Documentation Coverage Validation and Gap Analysis + +Complete workflow for analyzing documentation coverage, identifying gaps, and establishing quality gates in CI/CD. + +## Context + +**Project**: FastAPI + TanStack Start SaaS Platform +**Problem**: Documentation coverage unknown, many functions and API endpoints undocumented +**Goal**: Establish 80% documentation coverage with CI/CD enforcement + +**Initial State**: +- No visibility into documentation coverage +- 147 undocumented functions and 23 undocumented API endpoints +- New code merged without documentation requirements +- Partners complained about missing API documentation + +## Step 1: TypeScript Documentation Coverage Analysis + +```typescript +// scripts/analyze-ts-coverage.ts +import { Project } from "ts-morph"; + +function analyzeTypeScriptCoverage(projectPath: string) { + const project = new Project({ tsConfigFilePath: `${projectPath}/tsconfig.json` }); + + const result = { total: 0, documented: 0, undocumented: [] }; + + project.getSourceFiles().forEach((sourceFile) => { + // Analyze exported functions + sourceFile.getFunctions().filter((fn) => fn.isExported()).forEach((fn) => { + result.total++; + const jsDocs = fn.getJsDocs(); + + if (jsDocs.length > 0 && jsDocs[0].getDescription().trim().length > 0) { + result.documented++; + } else { + result.undocumented.push({ + name: fn.getName() || "(anonymous)", + location: `${sourceFile.getFilePath()}:${fn.getStartLineNumber()}`, + }); + } + }); + + // Analyze interfaces + sourceFile.getInterfaces().forEach((iface) => { + if (!iface.isExported()) return; + result.total++; + if (iface.getJsDocs().length > 0) { + result.documented++; + } else { + result.undocumented.push({ + name: iface.getName(), + location: `${sourceFile.getFilePath()}:${iface.getStartLineNumber()}`, + }); + } + }); + }); + + const coverage = (result.documented / result.total) * 100; + + console.log(`TypeScript Coverage: ${coverage.toFixed(1)}%`); + console.log(`Documented: ${result.documented} / ${result.total}`); + + if (result.undocumented.length > 0) { + console.log("\nMissing documentation:"); + result.undocumented.forEach((item) => console.log(` - ${item.name} (${item.location})`)); + } + + if (coverage < 80) { + console.error(`❌ Coverage ${coverage.toFixed(1)}% below threshold 80%`); + process.exit(1); + } + + console.log(`✅ Coverage ${coverage.toFixed(1)}% meets threshold`); +} + +analyzeTypeScriptCoverage("./app"); +``` + +## Step 2: Python Documentation Coverage Analysis + +```python +# scripts/analyze_py_coverage.py +import ast +from pathlib import Path +from typing import List, Dict + +class DocstringAnalyzer(ast.NodeVisitor): + def __init__(self): + self.total = 0 + self.documented = 0 + self.undocumented: List[Dict] = [] + self.current_file = "" + + def visit_FunctionDef(self, node: ast.FunctionDef): + if node.name.startswith("_"): # Skip private functions + return + + self.total += 1 + docstring = ast.get_docstring(node) + + if docstring and len(docstring.strip()) > 10: + self.documented += 1 + else: + self.undocumented.append({ + "name": node.name, + "type": "function", + "location": f"{self.current_file}:{node.lineno}" + }) + self.generic_visit(node) + + def visit_ClassDef(self, node: ast.ClassDef): + self.total += 1 + docstring = ast.get_docstring(node) + + if docstring and len(docstring.strip()) > 10: + self.documented += 1 + else: + self.undocumented.append({ + "name": node.name, + "type": "class", + "location": f"{self.current_file}:{node.lineno}" + }) + self.generic_visit(node) + +def analyze_python_coverage(project_path: str): + analyzer = DocstringAnalyzer() + + for py_file in Path(project_path).rglob("*.py"): + if "__pycache__" in str(py_file): + continue + + analyzer.current_file = str(py_file) + with open(py_file, "r") as f: + try: + tree = ast.parse(f.read()) + analyzer.visit(tree) + except SyntaxError: + print(f"⚠️ Syntax error in {py_file}") + + coverage = (analyzer.documented / analyzer.total * 100) if analyzer.total > 0 else 0 + + print(f"Python Coverage: {coverage:.1f}%") + print(f"Documented: {analyzer.documented} / {analyzer.total}") + + if analyzer.undocumented: + print("\nMissing documentation:") + for item in analyzer.undocumented: + print(f" - {item['type']} {item['name']} ({item['location']})") + + if coverage < 80: + print(f"❌ Coverage {coverage:.1f}% below threshold 80%") + exit(1) + + print(f"✅ Coverage {coverage:.1f}% meets threshold") + +analyze_python_coverage("./app") +``` + +## Step 3: API Endpoint Documentation Coverage + +```python +# scripts/analyze_api_coverage.py +from fastapi import FastAPI + +def analyze_api_documentation(app: FastAPI): + result = {"total_endpoints": 0, "documented": 0, "undocumented": []} + + openapi = app.openapi() + + for path, methods in openapi["paths"].items(): + for method, details in methods.items(): + result["total_endpoints"] += 1 + + has_summary = bool(details.get("summary")) + has_description = bool(details.get("description")) + + if has_summary and has_description: + result["documented"] += 1 + else: + missing = [] + if not has_summary: missing.append("summary") + if not has_description: missing.append("description") + + result["undocumented"].append({ + "method": method.upper(), + "path": path, + "missing": missing + }) + + coverage = (result["documented"] / result["total_endpoints"] * 100) + + print(f"API Coverage: {coverage:.1f}%") + print(f"Documented: {result['documented']} / {result['total_endpoints']}") + + if result["undocumented"]: + print("\nMissing documentation:") + for endpoint in result["undocumented"]: + missing = ", ".join(endpoint["missing"]) + print(f" - {endpoint['method']} {endpoint['path']} (missing: {missing})") + + if coverage < 80: + print(f"❌ Coverage {coverage:.1f}% below threshold 80%") + exit(1) + + print(f"✅ Coverage {coverage:.1f}% meets threshold") + +from app.main import app +analyze_api_documentation(app) +``` + +## Step 4: Comprehensive HTML Coverage Report + +```python +# scripts/generate_coverage_report.py +from jinja2 import Template +from datetime import datetime + +def generate_coverage_report(ts_coverage, py_coverage, api_coverage): + template = Template(''' + + + + Documentation Coverage Report + + + +

Documentation Coverage Report

+

Generated: {{ timestamp }}

+ +
+
+

TypeScript

+
{{ "%.1f"|format(ts_coverage.coverage) }}%
+

{{ ts_coverage.documented }} / {{ ts_coverage.total }}

+
+
+

Python

+
{{ "%.1f"|format(py_coverage.coverage) }}%
+

{{ py_coverage.documented }} / {{ py_coverage.total }}

+
+
+

API

+
{{ "%.1f"|format(api_coverage.coverage) }}%
+

{{ api_coverage.documented }} / {{ api_coverage.total_endpoints }}

+
+
+ + {% for section in [ts_coverage, py_coverage] %} + {% if section.undocumented %} +
+

{{ section.name }} - Missing Documentation

+
    + {% for item in section.undocumented %} +
  • {{ item.name }} - {{ item.location }}
  • + {% endfor %} +
+
+ {% endif %} + {% endfor %} + + + ''') + + html = template.render( + timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + ts_coverage=ts_coverage, + py_coverage=py_coverage, + api_coverage=api_coverage + ) + + with open("docs/coverage-report.html", "w") as f: + f.write(html) + + print("📊 Coverage report generated: docs/coverage-report.html") +``` + +## Step 5: CI/CD Integration + +```yaml +# .github/workflows/documentation-coverage.yml +name: Documentation Coverage + +on: + pull_request: + push: + branches: [main] + +jobs: + documentation-coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + npm install + pip install -r requirements.txt jinja2 + + - name: Check TypeScript coverage + run: npx ts-node scripts/analyze-ts-coverage.ts + + - name: Check Python coverage + run: python scripts/analyze_py_coverage.py + + - name: Check API coverage + run: python scripts/analyze_api_coverage.py + + - name: Generate report + if: always() + run: python scripts/generate_coverage_report.py + + - name: Upload report + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: docs/coverage-report.html + + - name: Comment on PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '📊 Documentation coverage report generated. Check artifacts.' + }); +``` + +## Results + +### Before + +- Documentation coverage: unknown +- No visibility into gaps +- 147 undocumented functions +- 23 undocumented API endpoints +- New code merged without docs +- Partners complained about missing docs + +### After + +- TypeScript coverage: 42% → 87% +- Python coverage: 38% → 91% +- API endpoint coverage: 51% → 95% +- CI/CD enforcement (fails build if <80%) +- Automated HTML reports + +### Improvements + +- Undocumented functions: 147 → 18 (88% reduction) +- Undocumented endpoints: 23 → 1 (96% reduction) +- Time to find function docs: 15 min → instant +- Partner onboarding: 2 weeks → 3 days +- Documentation debt: eliminated weekly + +### Developer Feedback + +- "Coverage reports made it clear what needed docs" +- "CI/CD enforcement prevented new undocumented code" +- "HTML report showed exactly what was missing" +- "80% threshold is challenging but achievable" + +## Key Lessons + +1. **Automated Analysis**: Manual tracking doesn't scale +2. **CI/CD Enforcement**: Prevents documentation regression +3. **Visibility**: Reports show exactly what's missing +4. **Threshold-Based**: 80% coverage is achievable and meaningful +5. **Multi-Language**: Each language needs appropriate tooling (ts-morph, AST, OpenAPI) +6. **HTML Reports**: Visual representation drives action + +## Prevention Measures + +**Implemented**: +- [x] TypeScript coverage analysis (ts-morph) +- [x] Python coverage analysis (AST) +- [x] API endpoint documentation check +- [x] HTML coverage reports +- [x] CI/CD integration (fails below 80%) +- [x] PR comments with coverage status + +**Ongoing**: +- [ ] Pre-commit hooks (warn if adding undocumented code) +- [ ] Dashboard showing coverage trends over time +- [ ] Team documentation KPIs (quarterly review) +- [ ] Automated "most undocumented files" weekly report + +--- + +Related: [openapi-generation.md](openapi-generation.md) | [architecture-docs.md](architecture-docs.md) | [Return to INDEX](INDEX.md) diff --git a/skills/documentation-architecture/examples/openapi-generation.md b/skills/documentation-architecture/examples/openapi-generation.md new file mode 100644 index 0000000..e9ae7e7 --- /dev/null +++ b/skills/documentation-architecture/examples/openapi-generation.md @@ -0,0 +1,437 @@ +# Example: OpenAPI 3.1 Generation from FastAPI Codebase + +Complete workflow showing automatic OpenAPI specification generation from a FastAPI codebase with Pydantic v2 models. + +## Context + +**Project**: E-commerce API (FastAPI + Pydantic v2 + SQLModel) +**Problem**: Manual API documentation was 3 months out of date, causing integration failures for 2 partner teams +**Goal**: Generate comprehensive OpenAPI 3.1 spec automatically from code with multi-language examples + +**Initial State**: +- 47 API endpoints with no documentation +- 12 integration issues per week from stale documentation +- Manual doc updates taking 4+ hours per release +- Partners blocked waiting for updated contracts + +## Step 1: Pydantic v2 Models with Rich Schemas + +```python +# app/models/orders.py +from pydantic import BaseModel, Field +from typing import List +from datetime import datetime + +class OrderItem(BaseModel): + product_id: str = Field(..., description="Product identifier") + quantity: int = Field(..., gt=0, description="Quantity to order") + unit_price: float = Field(..., gt=0, description="Price per unit in USD") + +class OrderCreate(BaseModel): + """Create a new order for the authenticated user.""" + items: List[OrderItem] = Field(..., min_length=1, description="Order line items") + shipping_address_id: str = Field(..., description="ID of shipping address") + + model_config = { + "json_schema_extra": { + "examples": [{ + "items": [{"product_id": "prod_123", "quantity": 2, "unit_price": 29.99}], + "shipping_address_id": "addr_456" + }] + } + } + +class Order(BaseModel): + """Order with calculated totals.""" + id: str + user_id: str + items: List[OrderItem] + subtotal: float = Field(..., description="Sum of all item prices") + tax: float = Field(..., description="Calculated tax amount") + total: float = Field(..., description="Final order total") + status: str = Field(..., description="pending, processing, shipped, delivered, cancelled") + created_at: datetime +``` + +## Step 2: Enhanced OpenAPI Generation + +```python +# app/main.py +from fastapi import FastAPI +from fastapi.openapi.utils import get_openapi + +app = FastAPI() + +def custom_openapi(): + if app.openapi_schema: + return app.openapi_schema + + openapi_schema = get_openapi( + title="Grey Haven E-Commerce API", + version="1.0.0", + description="E-commerce API with JWT auth. Rate limit: 1000 req/hour (authenticated).", + routes=app.routes, + ) + + # Add security schemes + openapi_schema["components"]["securitySchemes"] = { + "BearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "JWT token from /auth/login" + } + } + openapi_schema["security"] = [{"BearerAuth": []}] + + # Add error response schema + openapi_schema["components"]["schemas"]["ErrorResponse"] = { + "type": "object", + "required": ["error", "message"], + "properties": { + "error": {"type": "string", "example": "INSUFFICIENT_STOCK"}, + "message": {"type": "string", "example": "Product has insufficient stock"}, + "details": {"type": "object", "additionalProperties": True} + } + } + + # Add rate limit headers + openapi_schema["components"]["headers"] = { + "X-RateLimit-Limit": {"description": "Request limit per hour", "schema": {"type": "integer"}}, + "X-RateLimit-Remaining": {"description": "Remaining requests", "schema": {"type": "integer"}}, + "X-RateLimit-Reset": {"description": "Reset timestamp", "schema": {"type": "integer"}} + } + + app.openapi_schema = openapi_schema + return app.openapi_schema + +app.openapi = custom_openapi +``` + +## Step 3: FastAPI Route with Complete Documentation + +```python +# app/routers/orders.py +from fastapi import APIRouter, Depends, HTTPException + +router = APIRouter(prefix="/api/v1/orders", tags=["orders"]) + +@router.post("/", response_model=Order, status_code=201) +async def create_order( + order_data: OrderCreate, + current_user: User = Depends(get_current_user), + session: Session = Depends(get_session) +) -> Order: + """ + Create a new order for the authenticated user. + + The order will be created in 'pending' status and total calculated + including applicable taxes based on shipping address. + + **Requires**: + - Valid JWT authentication token + - At least one item in the order + - Valid shipping address ID owned by the user + + **Returns**: Created order with calculated totals + + **Raises**: + - **401 Unauthorized**: If user is not authenticated + - **404 Not Found**: If shipping address not found + - **400 Bad Request**: If product stock insufficient or validation fails + - **429 Too Many Requests**: If rate limit exceeded + """ + # Validate shipping address belongs to user + address = session.get(ShippingAddress, order_data.shipping_address_id) + if not address or address.user_id != current_user.id: + raise HTTPException(404, detail="Shipping address not found") + + # Check stock availability + for item in order_data.items: + product = session.get(Product, item.product_id) + if not product or product.stock < item.quantity: + raise HTTPException( + 400, + detail={ + "error": "INSUFFICIENT_STOCK", + "message": f"Product {item.product_id} has insufficient stock", + "details": { + "product_id": item.product_id, + "requested": item.quantity, + "available": product.stock if product else 0 + } + } + ) + + # Create order and calculate totals + order = Order( + user_id=current_user.id, + items=order_data.items, + subtotal=sum(item.quantity * item.unit_price for item in order_data.items) + ) + order.tax = order.subtotal * 0.08 # 8% tax + order.total = order.subtotal + order.tax + order.status = "pending" + + session.add(order) + session.commit() + return order +``` + +## Step 4: Multi-Language Code Examples + +### Automated Example Generation + +```python +# scripts/generate_examples.py +def generate_examples(openapi_spec): + """Generate TypeScript, Python, and cURL examples for each endpoint.""" + + examples = {} + + for path, methods in openapi_spec["paths"].items(): + for method, details in methods.items(): + operation_id = details.get("operationId", f"{method}_{path}") + + # TypeScript example + examples[f"{operation_id}_typescript"] = f''' +const response = await fetch('https://api.greyhaven.com{path}', {{ + method: '{method.upper()}', + headers: {{ + 'Authorization': 'Bearer YOUR_API_TOKEN', + 'Content-Type': 'application/json' + }}, + body: JSON.stringify({{ + items: [{{ product_id: "prod_123", quantity: 2, unit_price: 29.99 }}], + shipping_address_id: "addr_456" + }}) +}}); +const order = await response.json(); +''' + + # Python example + examples[f"{operation_id}_python"] = f''' +import requests + +response = requests.{method}( + 'https://api.greyhaven.com{path}', + headers={{'Authorization': 'Bearer YOUR_API_TOKEN'}}, + json={{ + 'items': [{{'product_id': 'prod_123', 'quantity': 2, 'unit_price': 29.99}}], + 'shipping_address_id': 'addr_456' + }} +) +order = response.json() +''' + + # cURL example + examples[f"{operation_id}_curl"] = f''' +curl -X {method.upper()} https://api.greyhaven.com{path} \\ + -H "Authorization: Bearer YOUR_API_TOKEN" \\ + -H "Content-Type: application/json" \\ + -d '{{"items": [{{"product_id": "prod_123", "quantity": 2, "unit_price": 29.99}}], "shipping_address_id": "addr_456"}}' +''' + + return examples +``` + +## Step 5: Interactive Swagger UI + +```python +# app/main.py (enhanced) +from fastapi.openapi.docs import get_swagger_ui_html + +@app.get("/docs", include_in_schema=False) +async def custom_swagger_ui_html(): + return get_swagger_ui_html( + openapi_url="/openapi.json", + title=f"{app.title} - API Documentation", + swagger_js_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js", + swagger_css_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css", + swagger_ui_parameters={ + "persistAuthorization": True, # Remember auth token + "displayRequestDuration": True, # Show request timing + "filter": True, # Enable filtering + "tryItOutEnabled": True # Enable try-it-out by default + } + ) +``` + +## Step 6: CI/CD Auto-Generation + +```yaml +# .github/workflows/generate-docs.yml +name: Generate API Documentation + +on: + push: + branches: [main] + paths: ['app/**/*.py'] + +jobs: + generate-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Generate OpenAPI spec + run: | + pip install -r requirements.txt + python -c " + from app.main import app + import json + with open('docs/openapi.json', 'w') as f: + json.dump(app.openapi(), f, indent=2) + " + + - name: Generate code examples + run: python scripts/generate_examples.py + + - name: Validate OpenAPI + run: npx @redocly/cli lint docs/openapi.json + + - name: Deploy to Cloudflare Pages + run: | + npm install -g wrangler + wrangler pages deploy docs/ --project-name=api-docs + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} +``` + +## Generated OpenAPI Specification (Excerpt) + +```yaml +openapi: 3.1.0 +info: + title: Grey Haven E-Commerce API + version: 1.0.0 + description: E-commerce API with JWT auth. Rate limit: 1000 req/hour. + +servers: + - url: https://api.greyhaven.com + description: Production + +paths: + /api/v1/orders: + post: + summary: Create a new order + description: Create order in 'pending' status with calculated totals + operationId: createOrder + tags: [orders] + security: + - BearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OrderCreate' + responses: + '201': + description: Order created successfully + headers: + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Validation error or insufficient stock + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized (invalid token) + '429': + description: Rate limit exceeded + +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + schemas: + OrderItem: + type: object + required: [product_id, quantity, unit_price] + properties: + product_id: + type: string + example: "prod_123" + quantity: + type: integer + minimum: 1 + example: 2 + unit_price: + type: number + minimum: 0.01 + example: 29.99 +``` + +## Results + +### Before + +- Manual documentation 3 months out of date +- 47 endpoints with no docs +- 12 integration issues per week +- 4+ hours manual doc updates per release +- Partners blocked waiting for updated contracts + +### After + +- OpenAPI spec auto-generated on every commit +- 100% endpoint coverage with examples +- Interactive Swagger UI with try-it-out +- Multi-language examples (TypeScript, Python, cURL) +- Complete error response documentation + +### Improvements + +- Integration issues: 12/week → 0.5/week (96% reduction) +- Doc update time: 4 hours → 0 minutes (automated) +- Partner satisfaction: 45% → 98% +- Time-to-integration: 2 weeks → 2 days + +### Partner Feedback + +- "The interactive docs with try-it-out saved us days of testing" +- "Code examples in our language made integration trivial" +- "Error responses are fully documented - no guesswork" + +## Key Lessons + +1. **Automation is Critical**: Manual docs will always drift from code +2. **Pydantic v2 Schema**: Excellent OpenAPI generation with field validators +3. **Multi-Language Examples**: Dramatically improved partner integration speed +4. **Interactive Docs**: Try-it-out functionality reduced support tickets +5. **CI/CD Integration**: Documentation stays current automatically +6. **Error Documentation**: Complete error schemas eliminated guesswork + +## Prevention Measures + +**Implemented**: +- [x] Auto-generation on every commit (GitHub Actions) +- [x] OpenAPI spec validation in CI/CD +- [x] Interactive Swagger UI deployed to Cloudflare Pages +- [x] Multi-language code examples generated +- [x] Complete error response schemas +- [x] Rate limiting documentation + +**Ongoing**: +- [ ] SDK auto-generation from OpenAPI spec (TypeScript, Python clients) +- [ ] Contract testing (validate API matches OpenAPI spec) +- [ ] Changelog generation from git commits + +--- + +Related: [architecture-docs.md](architecture-docs.md) | [coverage-validation.md](coverage-validation.md) | [Return to INDEX](INDEX.md) diff --git a/skills/documentation-architecture/reference/INDEX.md b/skills/documentation-architecture/reference/INDEX.md new file mode 100644 index 0000000..0e78945 --- /dev/null +++ b/skills/documentation-architecture/reference/INDEX.md @@ -0,0 +1,55 @@ +# Documentation Reference + +Quick-lookup guides for OpenAPI patterns, Mermaid diagrams, and documentation quality standards. + +## Available References + +### [OpenAPI 3.1 Patterns](openapi-patterns.md) + +Comprehensive OpenAPI 3.1 specification patterns for Grey Haven stack. + +**Covers**: Complete schemas, authentication (JWT/OAuth2/API Key), error responses, pagination, multi-language examples, webhooks, FastAPI integration + +**Use for**: API documentation generation, OpenAPI schema creation, endpoint documentation standards + +--- + +### [Mermaid Diagram Templates](mermaid-diagrams.md) + +Complete templates for all Mermaid diagram types with Grey Haven examples. + +**Covers**: System architecture, sequence diagrams, data flow, ER diagrams, state machines, deployment diagrams, class diagrams + +**Use for**: Architecture documentation, visualizing data flows, documenting state transitions, database schemas + +--- + +### [Documentation Standards](documentation-standards.md) + +Quality standards and best practices for technical documentation. + +**Covers**: Writing style, code examples, API documentation templates, function documentation (JSDoc/docstrings), README structure, quality checklist + +**Use for**: Ensuring consistency, setting quality thresholds, creating maintainable documentation + +--- + +## Common Patterns + +1. **API Documentation**: Use OpenAPI patterns + documentation standards +2. **Architecture Docs**: Use Mermaid diagrams + ADR template from standards +3. **Coverage Validation**: Use standards checklist + automated tools + +## Quick Reference + +| Need | Reference | Key Section | +|------|-----------|-------------| +| API endpoint docs | [openapi-patterns.md](openapi-patterns.md) | Endpoint Documentation Template | +| System diagrams | [mermaid-diagrams.md](mermaid-diagrams.md) | Architecture Diagrams | +| Quality checklist | [documentation-standards.md](documentation-standards.md) | Quality Checklist | +| Code examples | [documentation-standards.md](documentation-standards.md) | Code Examples | +| Error responses | [openapi-patterns.md](openapi-patterns.md) | Error Response Schema | + +--- + +Related: [Examples](../examples/INDEX.md) | [Templates](../templates/) | [Return to Agent](../docs-architect.md) diff --git a/skills/documentation-architecture/reference/documentation-standards.md b/skills/documentation-architecture/reference/documentation-standards.md new file mode 100644 index 0000000..6dda260 --- /dev/null +++ b/skills/documentation-architecture/reference/documentation-standards.md @@ -0,0 +1,496 @@ +# Documentation Standards and Quality Guidelines + +Comprehensive standards for creating high-quality technical documentation for Grey Haven projects. + +## Documentation Principles + +### 1. Progressive Disclosure +Start with overview, provide details on demand. + +**Good**: +```markdown +# User Authentication + +Quick overview: Our authentication uses JWT tokens with refresh rotation. + +## Getting Started +[Simple example] + +## Advanced Usage +[Detailed configuration options] + +## Security Considerations +[Deep dive into security] +``` + +### 2. Show, Don't Tell +Use code examples instead of lengthy explanations. + +**Bad**: "To create a user, you need to instantiate a User class with email and password, then call the save method." + +**Good**: +```python +user = User(email="user@example.com", password="secure123") +user.save() +``` + +### 3. Keep It Current +Documentation that's out of date is worse than no documentation. + +Use automation: +- Auto-generate API docs from code +- CI/CD validation (fail if docs outdated) +- Link to code for truth source + +## Writing Style + +### Voice and Tone + +**Use Active Voice**: +- ❌ "The order will be processed by the system" +- ✅ "The system processes the order" + +**Be Direct**: +- ❌ "It might be a good idea to consider using..." +- ✅ "Use X when Y" + +**Avoid Jargon**: +- ❌ "Leverage our enterprise-grade synergistic platform" +- ✅ "Use our API to manage users" + +### Structure + +**Every Page Should Have**: +1. **Title**: Clear, descriptive +2. **Summary**: 1-2 sentence overview +3. **Prerequisites**: What user needs to know/have +4. **Step-by-Step**: Numbered instructions +5. **Code Examples**: Working, copy-paste ready +6. **Troubleshooting**: Common errors and solutions +7. **Next Steps**: Where to go next + +### Code Examples + +**Always Include**: +- ✅ Complete, working examples +- ✅ Expected output/result +- ✅ Error handling +- ✅ Comments explaining why, not what + +**Example Template**: +```python +# Create a new user +# Requires: Admin authentication +# Returns: User object or raises ValidationError + +try: + user = User.objects.create( + email="user@example.com", + password="secure123", + role="member" # Default role for new users + ) + print(f"User created: {user.id}") +except ValidationError as e: + print(f"Validation failed: {e.message}") +``` + +## API Documentation Standards + +### Endpoint Documentation Template + +```markdown +## POST /api/v1/users + +Create a new user account. + +### Authentication +Requires: Admin JWT token in Authorization header + +### Request + +**Headers**: +- `Authorization: Bearer ` (required) +- `Content-Type: application/json` (required) + +**Body**: +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| email | string | Yes | Valid email address | +| password | string | Yes | Min 8 characters | +| role | string | No | Default: "member" | + +**Example**: +```json +{ + "email": "user@example.com", + "password": "secure123", + "role": "member" +} +``` + +### Response + +**Success (201 Created)**: +```json +{ + "id": "usr_123abc", + "email": "user@example.com", + "role": "member", + "created_at": "2024-01-15T10:30:00Z" +} +``` + +**Errors**: +- `400 Bad Request`: Validation failed (email invalid, password too short) +- `401 Unauthorized`: Missing or invalid auth token +- `403 Forbidden`: User lacks admin role +- `409 Conflict`: Email already exists +- `429 Too Many Requests`: Rate limit exceeded + +**Error Response**: +```json +{ + "error": "VALIDATION_ERROR", + "message": "Email address is invalid", + "details": { + "field": "email", + "value": "invalid-email" + } +} +``` + +### Rate Limiting +- Authenticated: 1000 requests/hour +- Unauthenticated: 100 requests/hour + +### Code Examples + +**TypeScript**: +```typescript +const response = await fetch("https://api.greyhaven.com/users", { + method: "POST", + headers: { + "Authorization": `Bearer ${token}`, + "Content-Type": "application/json" + }, + body: JSON.stringify({ + email: "user@example.com", + password: "secure123" + }) +}); + +if (!response.ok) { + const error = await response.json(); + throw new Error(error.message); +} + +const user = await response.json(); +console.log(`User created: ${user.id}`); +``` + +**Python**: +```python +import httpx + +async with httpx.AsyncClient() as client: + response = await client.post( + "https://api.greyhaven.com/users", + headers={"Authorization": f"Bearer {token}"}, + json={"email": "user@example.com", "password": "secure123"} + ) + response.raise_for_status() + user = response.json() + print(f"User created: {user['id']}") +``` +``` + +## Function Documentation Standards + +### JSDoc (TypeScript) + +```typescript +/** + * Calculate order total including tax and shipping. + * + * @param items - Array of order items with quantity and price + * @param shippingAddress - Address for tax calculation + * @returns Total amount in USD cents + * @throws {ValidationError} If items array is empty + * @throws {TaxCalculationError} If tax lookup fails + * + * @example + * const total = calculateTotal( + * [{ quantity: 2, price: 2999 }], + * { zip: "94105", country: "US" } + * ); + * // Returns: 6398 (5998 + 400 tax + 0 shipping) + */ +export function calculateTotal( + items: OrderItem[], + shippingAddress: Address +): number { + if (items.length === 0) { + throw new ValidationError("Items array cannot be empty"); + } + + const subtotal = items.reduce((sum, item) => + sum + (item.quantity * item.price), 0 + ); + + const tax = calculateTax(subtotal, shippingAddress); + const shipping = calculateShipping(items, shippingAddress); + + return subtotal + tax + shipping; +} +``` + +### Python Docstrings (Google Style) + +```python +def calculate_total(items: List[OrderItem], shipping_address: Address) -> int: + """Calculate order total including tax and shipping. + + Args: + items: Array of order items with quantity and price. + shipping_address: Address for tax calculation. + + Returns: + Total amount in USD cents. + + Raises: + ValidationError: If items array is empty. + TaxCalculationError: If tax lookup fails. + + Example: + >>> items = [OrderItem(quantity=2, price=2999)] + >>> address = Address(zip="94105", country="US") + >>> total = calculate_total(items, address) + >>> print(total) + 6398 # 5998 + 400 tax + 0 shipping + """ + if not items: + raise ValidationError("Items array cannot be empty") + + subtotal = sum(item.quantity * item.price for item in items) + tax = calculate_tax(subtotal, shipping_address) + shipping = calculate_shipping(items, shipping_address) + + return subtotal + tax + shipping +``` + +## README Structure + +Every project should have a comprehensive README: + +```markdown +# Project Name + +One-line description of what this project does. + +## Quick Start + +```bash +npm install +npm run dev +``` + +Visit http://localhost:3000 + +## Features + +- Feature 1: Brief description +- Feature 2: Brief description +- Feature 3: Brief description + +## Installation + +### Prerequisites +- Node.js 20+ +- PostgreSQL 14+ +- Redis (optional) + +### Steps + +1. Clone repository +```bash +git clone https://github.com/greyhaven/project.git +cd project +``` + +2. Install dependencies +```bash +npm install +``` + +3. Configure environment +```bash +cp .env.example .env +# Edit .env with your values +``` + +4. Run migrations +```bash +npm run migrate +``` + +5. Start development server +```bash +npm run dev +``` + +## Configuration + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `DATABASE_URL` | Yes | - | PostgreSQL connection string | +| `REDIS_URL` | No | - | Redis connection string | +| `API_KEY` | Yes | - | API key for external service | + +## Architecture + +[Link to architecture docs or include Mermaid diagram] + +## Development + +### Running Tests +```bash +npm test +``` + +### Code Quality +```bash +npm run lint +npm run type-check +``` + +### Building +```bash +npm run build +``` + +## Deployment + +[Link to deployment guide or include basic steps] + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) + +## License + +MIT License - see [LICENSE](LICENSE) +``` + +## Documentation Coverage + +### Minimum Requirements + +**Code Coverage**: +- Public functions: 100% +- Exported types: 100% +- API endpoints: 100% + +**Content Coverage**: +- Every function has description +- Every parameter documented +- Return value documented +- Errors/exceptions documented +- At least one example + +### Validation + +Use automated tools: +- TypeScript: ts-morph for AST analysis +- Python: AST module for docstring coverage +- API: OpenAPI schema validation + +```bash +# Check coverage +npm run docs:coverage + +# Expected output +TypeScript: 87% (124/142 documented) +Python: 91% (98/108 documented) +API Endpoints: 95% (42/44 documented) +``` + +## Quality Checklist + +Before publishing documentation: + +- [ ] All code examples work (copy-paste tested) +- [ ] Links are valid (no 404s) +- [ ] Screenshots are current +- [ ] Version numbers are correct +- [ ] Prerequisite versions are accurate +- [ ] Examples use realistic data +- [ ] Error messages match actual errors +- [ ] Spelling and grammar checked +- [ ] Follows style guide +- [ ] Reviewed by another person + +## Common Mistakes + +### 1. Outdated Examples + +❌ **Bad**: Uses deprecated API +```typescript +// This was removed in v2.0 +const user = User.create({ email, password }); +``` + +✅ **Good**: Current API with version note +```typescript +// As of v2.0, use createUser instead of User.create +const user = await createUser({ email, password }); +``` + +### 2. Missing Error Handling + +❌ **Bad**: Happy path only +```typescript +const user = await api.getUser(id); +console.log(user.email); +``` + +✅ **Good**: Error handling included +```typescript +try { + const user = await api.getUser(id); + console.log(user.email); +} catch (error) { + if (error.code === 'NOT_FOUND') { + console.error(`User ${id} not found`); + } else { + throw error; + } +} +``` + +### 3. Vague Instructions + +❌ **Bad**: "Configure the database" + +✅ **Good**: Specific steps +```markdown +1. Create database: `createdb myapp` +2. Run migrations: `npm run migrate` +3. Verify: `psql myapp -c "\dt"` +``` + +## Best Practices + +1. **Update docs with code**: Documentation changes in same PR as code changes +2. **Link to code**: Reference specific files and line numbers +3. **Version everything**: Document which version each feature was added +4. **Test examples**: All code examples must be tested +5. **Screenshots with captions**: Always explain what image shows +6. **Consistent terminology**: Use same terms throughout +7. **Mobile-friendly**: Documentation should work on phones +8. **Search-optimized**: Use descriptive headings and keywords +9. **Accessible**: Alt text for images, semantic HTML +10. **Feedback loops**: Easy way for users to report doc issues + +--- + +Related: [openapi-patterns.md](openapi-patterns.md) | [mermaid-diagrams.md](mermaid-diagrams.md) | [Return to INDEX](INDEX.md) diff --git a/skills/documentation-architecture/reference/mermaid-diagrams.md b/skills/documentation-architecture/reference/mermaid-diagrams.md new file mode 100644 index 0000000..dcabfb4 --- /dev/null +++ b/skills/documentation-architecture/reference/mermaid-diagrams.md @@ -0,0 +1,500 @@ +# Mermaid Diagram Templates for Architecture Documentation + +Comprehensive guide to Mermaid diagram types for visualizing system architecture, data flows, and interactions. + +## Why Mermaid? + +- **Version Controlled**: Diagrams in code, reviewable in PRs +- **Always Up-to-Date**: Easy to update alongside code changes +- **No Image Files**: Rendered dynamically in documentation +- **GitHub Native**: Renders in README.md and issues +- **Interactive**: Clickable links, zooming + +## System Architecture Diagrams + +### Basic Architecture + +```mermaid +graph TB + subgraph "Frontend" + UI[React UI] + end + + subgraph "Backend" + API[FastAPI] + DB[(PostgreSQL)] + end + + UI --> API + API --> DB +``` + +### Multi-Tier Architecture + +```mermaid +graph TB + subgraph "Client" + Browser[Web Browser] + Mobile[Mobile App] + end + + subgraph "Edge (Cloudflare)" + Gateway[API Gateway] + Cache[KV Cache] + end + + subgraph "Application" + Frontend[TanStack Start] + Backend[FastAPI] + end + + subgraph "Data" + DB[(PostgreSQL)] + Redis[(Redis)] + R2[(Object Storage)] + end + + Browser --> Gateway + Mobile --> Gateway + Gateway --> Frontend + Gateway --> Backend + Gateway --> Cache + Backend --> DB + Backend --> Redis + Backend --> R2 +``` + +### Microservices Architecture + +```mermaid +graph LR + Gateway[API Gateway] + + subgraph "Services" + Auth[Auth Service] + Users[User Service] + Orders[Order Service] + Payments[Payment Service] + end + + subgraph "Data" + AuthDB[(Auth DB)] + UserDB[(User DB)] + OrderDB[(Order DB)] + end + + Gateway --> Auth + Gateway --> Users + Gateway --> Orders + Gateway --> Payments + + Auth --> AuthDB + Users --> UserDB + Orders --> OrderDB + Payments -.Stripe.-> External[External API] +``` + +## Sequence Diagrams + +### Authentication Flow + +```mermaid +sequenceDiagram + actor User + participant Browser + participant Gateway + participant Auth + participant DB + + User->>Browser: Enter credentials + Browser->>Gateway: POST /auth/login + Gateway->>Auth: Validate credentials + Auth->>DB: Query user + DB-->>Auth: User record + + alt Valid + Auth->>Auth: Generate JWT + Auth-->>Gateway: {token, user} + Gateway-->>Browser: 200 OK + Browser-->>User: Redirect + else Invalid + Auth-->>Gateway: 401 + Gateway-->>Browser: Error + Browser-->>User: Show error + end +``` + +### API Request Flow + +```mermaid +sequenceDiagram + participant Client + participant Gateway + participant Backend + participant DB + participant Cache + + Client->>Gateway: GET /users/123 + Gateway->>Gateway: Validate JWT + Gateway->>Cache: Check cache + + alt Cache Hit + Cache-->>Gateway: User data + Gateway-->>Client: 200 OK (cached) + else Cache Miss + Gateway->>Backend: Forward request + Backend->>DB: Query user + DB-->>Backend: User data + Backend-->>Gateway: Response + Gateway->>Cache: Store in cache + Gateway-->>Client: 200 OK + end +``` + +### Payment Processing + +```mermaid +sequenceDiagram + participant Client + participant API + participant PaymentSvc + participant Stripe + participant DB + + Client->>API: POST /orders + API->>PaymentSvc: Process payment + PaymentSvc->>Stripe: Create payment intent + Stripe-->>PaymentSvc: Payment intent + PaymentSvc-->>API: Intent created + API-->>Client: {client_secret} + + Client->>Stripe: Confirm payment + Stripe->>PaymentSvc: Webhook: payment.succeeded + PaymentSvc->>DB: Update order status + PaymentSvc->>API: Notify completion + API->>Client: Send confirmation email +``` + +## Data Flow Diagrams + +### Order Processing Flow + +```mermaid +flowchart LR + Start[User Creates Order] --> Validate[Validate Data] + Validate --> Stock{Check Stock} + + Stock -->|Insufficient| Error[Return Error] + Stock -->|Available| Reserve[Reserve Items] + + Reserve --> Payment[Process Payment] + Payment -->|Failed| Release[Release Items] + Release --> Error + + Payment -->|Success| Create[Create Order] + Create --> Queue[Queue Email] + Queue --> Cache[Invalidate Cache] + Cache --> Success[Return Order] + + Success --> Async[Async: Send Email] +``` + +### Data Transformation Pipeline + +```mermaid +flowchart TD + Raw[Raw Data] --> Extract[Extract] + Extract --> Transform[Transform] + Transform --> Validate{Validate} + + Validate -->|Invalid| Log[Log Error] + Validate -->|Valid| Enrich[Enrich Data] + + Enrich --> Normalize[Normalize] + Normalize --> Store[(Store in DB)] + Store --> Index[Update Search Index] + Index --> Cache[Update Cache] +``` + +## Entity Relationship Diagrams + +### Multi-Tenant E-Commerce + +```mermaid +erDiagram + TENANT ||--o{ USER : has + TENANT ||--o{ ORDER : has + TENANT ||--o{ PRODUCT : has + USER ||--o{ ORDER : places + ORDER ||--|{ ORDER_ITEM : contains + PRODUCT ||--o{ ORDER_ITEM : included_in + + TENANT { + uuid id PK + string name + string subdomain UK + timestamp created_at + } + + USER { + uuid id PK + uuid tenant_id FK + string email UK + string role + } + + PRODUCT { + uuid id PK + uuid tenant_id FK + string name + decimal price + int stock + } + + ORDER { + uuid id PK + uuid tenant_id FK + uuid user_id FK + decimal total + string status + } + + ORDER_ITEM { + uuid id PK + uuid order_id FK + uuid product_id FK + int quantity + decimal unit_price + } +``` + +### User Authentication Schema + +```mermaid +erDiagram + USER ||--o{ SESSION : has + USER ||--o{ API_KEY : has + USER ||--o{ OAUTH_TOKEN : has + USER }|--|| USER_PROFILE : has + + USER { + uuid id PK + string email UK + string hashed_password + bool email_verified + } + + SESSION { + uuid id PK + uuid user_id FK + string token UK + timestamp expires_at + } + + API_KEY { + uuid id PK + uuid user_id FK + string key_hash UK + string name + timestamp last_used + } +``` + +## State Diagrams + +### Order State Machine + +```mermaid +stateDiagram-v2 + [*] --> Pending: Order Created + Pending --> Processing: Payment Confirmed + Pending --> Cancelled: Payment Failed + + Processing --> Shipped: Fulfillment Complete + Processing --> Cancelled: Out of Stock + + Shipped --> Delivered: Tracking Confirmed + Shipped --> Returned: Customer Return + + Delivered --> Returned: Return Requested + Returned --> Refunded: Return Approved + + Cancelled --> [*] + Delivered --> [*] + Refunded --> [*] +``` + +### User Lifecycle + +```mermaid +stateDiagram-v2 + [*] --> Invited: User Invited + Invited --> Active: Accept Invitation + Invited --> Expired: 7 Days Passed + + Active --> Suspended: Policy Violation + Active --> Inactive: 90 Days No Login + + Suspended --> Active: Appeal Approved + Inactive --> Active: User Logs In + + Active --> Deleted: User Deletes Account + Suspended --> Deleted: Admin Deletes + + Expired --> [*] + Deleted --> [*] +``` + +## Deployment Diagrams + +### CI/CD Pipeline + +```mermaid +graph LR + Dev[Feature Branch] -->|PR| CI[GitHub Actions] + CI -->|Tests| Tests{Tests Pass?} + + Tests -->|No| Fail[❌ Fail] + Tests -->|Yes| Build[Build] + + Build --> Stage[Deploy to Staging] + Stage -->|Smoke Tests| SmokeTest{Pass?} + + SmokeTest -->|No| Fail + SmokeTest -->|Yes| Approve{Manual Approve?} + + Approve -->|No| Wait[Wait] + Approve -->|Yes| Canary[Canary Deploy 10%] + + Canary -->|Monitor| Monitor{Healthy?} + Monitor -->|No| Rollback[Rollback] + Monitor -->|Yes| Prod[Deploy 100%] +``` + +### Multi-Region Deployment + +```mermaid +graph TB + subgraph "Region: US-East" + USWorker[Cloudflare Workers] + USDB[(Primary DB)] + end + + subgraph "Region: Europe" + EUWorker[Cloudflare Workers] + EUDB[(Read Replica)] + end + + subgraph "Region: Asia" + AsiaWorker[Cloudflare Workers] + AsiaDB[(Read Replica)] + end + + subgraph "Global" + DNS[Global DNS] + CDN[Cloudflare CDN] + end + + DNS --> USWorker + DNS --> EUWorker + DNS --> AsiaWorker + + CDN --> USWorker + CDN --> EUWorker + CDN --> AsiaWorker + + USDB -.replication.-> EUDB + USDB -.replication.-> AsiaDB +``` + +## Class Diagrams (TypeScript/Python) + +### Service Architecture + +```mermaid +classDiagram + class OrderService { + -repository: OrderRepository + -payment: PaymentService + +createOrder(data) Order + +getOrder(id) Order + +cancelOrder(id) void + } + + class OrderRepository { + -db: Database + +save(order) Order + +findById(id) Order + +findByUser(userId) Order[] + } + + class PaymentService { + -stripe: StripeClient + +processPayment(amount) PaymentResult + +refund(paymentId) void + } + + class Order { + +id: string + +userId: string + +total: number + +status: OrderStatus + } + + OrderService --> OrderRepository + OrderService --> PaymentService + OrderRepository --> Order +``` + +## Best Practices + +1. **Keep Diagrams Simple**: One concept per diagram +2. **Use Subgraphs**: Group related components +3. **Consistent Naming**: Use same names as code +4. **Color Coding**: Use colors sparingly for emphasis +5. **Labels**: Add descriptive labels to edges +6. **Legend**: Include legend for complex diagrams +7. **Direction**: LR (left-right) or TB (top-bottom) based on flow +8. **Update Regularly**: Keep in sync with code changes + +## Rendering in Documentation + +### GitHub Markdown + +````markdown +```mermaid +graph TB + A[Start] --> B[Process] + B --> C[End] +``` +```` + +### Docusaurus + +Install plugin: +```bash +npm install @docusaurus/theme-mermaid +``` + +### MkDocs + +Install plugin: +```bash +pip install mkdocs-mermaid2-plugin +``` + +## Common Patterns + +### Request/Response Flow +Use sequence diagrams with alt/opt for error handling + +### Data Relationships +Use ER diagrams with proper cardinality (||--o{) + +### State Transitions +Use state diagrams for order status, user lifecycle + +### System Overview +--- + +Related: [openapi-patterns.md](openapi-patterns.md) | [documentation-standards.md](documentation-standards.md) | [Return to INDEX](INDEX.md) diff --git a/skills/documentation-architecture/reference/openapi-patterns.md b/skills/documentation-architecture/reference/openapi-patterns.md new file mode 100644 index 0000000..8d5b7d2 --- /dev/null +++ b/skills/documentation-architecture/reference/openapi-patterns.md @@ -0,0 +1,491 @@ +# OpenAPI 3.1 Patterns and Best Practices + +Comprehensive guide to OpenAPI 3.1 specification patterns for Grey Haven stack (FastAPI + TanStack Start). + +## OpenAPI 3.1 Overview + +OpenAPI 3.1 is fully compatible with JSON Schema Draft 2020-12. + +**Key Differences from 3.0**: Full JSON Schema compatibility, `examples` replaces `example`, `webhooks` support, better discriminator + +## Basic Structure + +```yaml +openapi: 3.1.0 +info: + title: Grey Haven API + version: 1.0.0 + +servers: + - url: https://api.greyhaven.com + +paths: + /users: + get: + operationId: listUsers + tags: [users] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' +``` + +## Authentication + +### JWT Bearer + +```yaml +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + +security: + - BearerAuth: [] +``` + +### OAuth2 + +```yaml +components: + securitySchemes: + OAuth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: https://auth.greyhaven.com/oauth/authorize + tokenUrl: https://auth.greyhaven.com/oauth/token + scopes: + read:users: Read user data +``` + +### API Key + +```yaml +components: + securitySchemes: + ApiKey: + type: apiKey + in: header + name: X-API-Key +``` + +## Schema Patterns + +### Pydantic v2 to OpenAPI + +```python +from pydantic import BaseModel, Field +from typing import Literal + +class User(BaseModel): + id: str = Field(..., pattern="^usr_[a-z0-9]{16}$") + email: str = Field(..., examples=["user@example.com"]) + role: Literal["admin", "member", "guest"] = "member" +``` + +Generates: + +```yaml +User: + type: object + required: [id, email] + properties: + id: + type: string + pattern: ^usr_[a-z0-9]{16}$ + email: + type: string + examples: ["user@example.com"] + role: + type: string + enum: [admin, member, guest] + default: member +``` + +### Nullable and Optional + +```yaml +# Optional (can be omitted) +username: + type: string + +# Nullable (can be null) +middle_name: + type: [string, 'null'] + +# Both +nickname: + type: [string, 'null'] +``` + +### Discriminated Unions + +```yaml +PaymentMethod: + type: object + required: [type] + discriminator: + propertyName: type + mapping: + card: '#/components/schemas/CardPayment' + bank: '#/components/schemas/BankPayment' + +CardPayment: + allOf: + - $ref: '#/components/schemas/PaymentMethod' + - type: object + properties: + card_number: + type: string + pattern: ^\d{16}$ +``` + +## Response Patterns + +### Error Response + +```yaml +ErrorResponse: + type: object + required: [error, message] + properties: + error: + type: string + examples: ["VALIDATION_ERROR"] + message: + type: string + details: + type: object + additionalProperties: true + +# Use in responses +responses: + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' +``` + +### Paginated Response + +```yaml +PaginatedUsers: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/User' + pagination: + type: object + properties: + page: + type: integer + minimum: 1 + per_page: + type: integer + minimum: 1 + maximum: 100 + total: + type: integer + total_pages: + type: integer +``` + +### Multiple Status Codes + +```yaml +responses: + '201': + description: Created + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '202': + description: Accepted (async) + content: + application/json: + schema: + type: object + properties: + job_id: + type: string +``` + +## Request Body + +### Required vs Optional + +```yaml +requestBody: + required: true + content: + application/json: + schema: + type: object + required: [email, password] # Required + properties: + email: + type: string + format: email + password: + type: string + minLength: 8 + name: + type: string # Optional +``` + +### File Upload + +```yaml +requestBody: + content: + multipart/form-data: + schema: + properties: + file: + type: string + format: binary +``` + +## Parameters + +### Path + +```yaml +parameters: + - name: user_id + in: path + required: true + schema: + type: string + pattern: ^usr_[a-z0-9]{16}$ +``` + +### Query + +```yaml +parameters: + - name: status + in: query + schema: + type: string + enum: [pending, processing, shipped] + - name: created_after + in: query + schema: + type: string + format: date-time + - name: sort + in: query + schema: + type: string + enum: [created_at:asc, created_at:desc] + default: created_at:desc +``` + +### Headers + +```yaml +components: + parameters: + TenantId: + name: X-Tenant-ID + in: header + required: true + schema: + type: string + +paths: + /orders: + post: + parameters: + - $ref: '#/components/parameters/TenantId' +``` + +## Response Headers + +```yaml +responses: + '200': + headers: + X-RateLimit-Limit: + schema: + type: integer + X-RateLimit-Remaining: + schema: + type: integer + X-Request-ID: + schema: + type: string + format: uuid +``` + +## Multi-Language Examples + +```yaml +x-codeSamples: + - lang: TypeScript + source: | + const response = await fetch("https://api.greyhaven.com/users", { + method: "POST", + headers: { "Authorization": `Bearer ${token}` }, + body: JSON.stringify({ email, password }) + }); + + - lang: Python + source: | + async with httpx.AsyncClient() as client: + response = await client.post( + "https://api.greyhaven.com/users", + headers={"Authorization": f"Bearer {token}"}, + json={"email": email, "password": password} + ) + + - lang: Shell + source: | + curl -X POST https://api.greyhaven.com/users \ + -H "Authorization: Bearer $TOKEN" \ + -d '{"email": "user@example.com"}' +``` + +## Webhooks (OpenAPI 3.1) + +```yaml +webhooks: + orderCreated: + post: + requestBody: + content: + application/json: + schema: + type: object + required: [event, data] + properties: + event: + type: string + const: order.created + data: + $ref: '#/components/schemas/Order' + responses: + '200': + description: Received +``` + +## Best Practices + +1. **Use $ref**: Define schemas once, reference everywhere +2. **Examples**: Realistic examples for all schemas +3. **Error Schemas**: Consistent error format +4. **Validation**: Use pattern, minLength, minimum +5. **Descriptions**: Document every field +6. **operationId**: Unique for SDK generation +7. **Tags**: Group related endpoints +8. **Deprecation**: Mark with `deprecated: true` +9. **Security**: Define at global or operation level +10. **Versioning**: Include in URL (/api/v1/) + +## Common Patterns + +### Multi-Tenant + +```yaml +components: + parameters: + TenantId: + name: X-Tenant-ID + in: header + required: true + schema: + type: string +``` + +### Idempotency + +```yaml +components: + parameters: + IdempotencyKey: + name: Idempotency-Key + in: header + schema: + type: string + format: uuid +``` + +### Rate Limiting + +```yaml +responses: + '429': + description: Rate limit exceeded + headers: + X-RateLimit-Reset: + schema: + type: integer +``` + +## FastAPI Integration + +```python +from fastapi import FastAPI + +app = FastAPI( + title="Grey Haven API", + version="1.0.0", + openapi_version="3.1.0" +) + +# Customize OpenAPI +def custom_openapi(): + if app.openapi_schema: + return app.openapi_schema + + openapi_schema = get_openapi( + title=app.title, + version=app.version, + routes=app.routes + ) + + # Add security schemes + openapi_schema["components"]["securitySchemes"] = { + "BearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + } + + app.openapi_schema = openapi_schema + return app.openapi_schema + +app.openapi = custom_openapi +``` + +## Validation + +Use @redocly/cli for validation: + +```bash +npx @redocly/cli lint openapi.yaml +``` + +Common issues: +- Missing operationId +- Missing response descriptions +- Inconsistent naming +- Missing examples +- Invalid $ref paths + +--- + +Related: [mermaid-diagrams.md](mermaid-diagrams.md) | [documentation-standards.md](documentation-standards.md) | [Return to INDEX](INDEX.md) diff --git a/skills/documentation-architecture/templates/INDEX.md b/skills/documentation-architecture/templates/INDEX.md new file mode 100644 index 0000000..d8f0e41 --- /dev/null +++ b/skills/documentation-architecture/templates/INDEX.md @@ -0,0 +1,59 @@ +# Documentation Templates + +Copy-paste ready templates for API documentation, architecture docs, and OpenAPI specifications. + +## Available Templates + +### [API Endpoint Documentation](api-endpoint.md) + +Complete template for documenting a single API endpoint with all required sections. + +**Includes**: Method/path, description, authentication, request/response formats, error codes, rate limits, code examples + +**Use when**: Documenting REST API endpoints, creating API reference pages + +--- + +### [Architecture Document](architecture-doc.md) + +Comprehensive template for system architecture documentation with Mermaid diagrams. + +**Includes**: Executive summary, system overview, component descriptions, data flow, ADRs, security model + +**Use when**: Documenting new systems, onboarding materials, architecture reviews + +--- + +### [OpenAPI Specification](openapi-spec.yaml) + +Starter OpenAPI 3.1 specification with common patterns and best practices. + +**Includes**: Info object, servers, authentication, common schemas (errors, pagination), example endpoint + +**Use when**: Starting new API documentation, generating from scratch + +--- + +## Quick Start + +1. Copy template file to your documentation directory +2. Replace all `[FILL IN]` placeholders +3. Remove optional sections if not needed +4. Validate and test + +## Template Customization + +**For your project**: +- Update company/project names +- Adjust authentication schemes +- Add project-specific error codes +- Include relevant examples + +**For your team**: +- Add team-specific sections +- Include internal links +- Reference team tools/dashboards + +--- + +Related: [Examples](../examples/INDEX.md) | [Reference](../reference/INDEX.md) | [Return to Agent](../docs-architect.md) diff --git a/skills/documentation-architecture/templates/api-endpoint.md b/skills/documentation-architecture/templates/api-endpoint.md new file mode 100644 index 0000000..e7ea6d1 --- /dev/null +++ b/skills/documentation-architecture/templates/api-endpoint.md @@ -0,0 +1,191 @@ +# [METHOD] /api/v1/[resource] + +[One sentence description of what this endpoint does] + +## Authentication + +**Required**: [Yes/No] +**Roles**: [Admin, Member, Guest] _(if applicable)_ +**Scopes**: [read:resource, write:resource] _(if applicable)_ + +## Request + +### Headers + +| Header | Required | Description | +|--------|----------|-------------| +| `Authorization` | Yes | Bearer token: `Bearer ` | +| `Content-Type` | Yes | `application/json` | +| `X-Tenant-ID` | Yes | Tenant identifier _(if multi-tenant)_ | + +### Path Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `[param_name]` | string | [Description] | + +### Query Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `[param_name]` | string | No | [default] | [Description] | +| `page` | integer | No | 1 | Page number (min: 1) | +| `per_page` | integer | No | 20 | Items per page (min: 1, max: 100) | + +### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `[field_name]` | string | Yes | [Description, validation rules] | +| `[field_name]` | integer | No | [Description, validation rules] | + +**Example**: +```json +{ + "[field_name]": "value", + "[field_name]": 123 +} +``` + +## Response + +### Success (200 OK) + +```json +{ + "id": "res_1234567890abcdef", + "[field_name]": "value", + "created_at": "2024-01-15T10:30:00Z", + "updated_at": "2024-01-15T10:30:00Z" +} +``` + +### Success (201 Created) + +**Headers**: +- `Location: /api/v1/[resource]/res_1234567890abcdef` + +```json +{ + "id": "res_1234567890abcdef", + "[field_name]": "value", + "created_at": "2024-01-15T10:30:00Z" +} +``` + +### Error Responses + +| Code | Description | Example | +|------|-------------|---------| +| 400 | Bad Request - Validation failed | `{"error": "VALIDATION_ERROR", "message": "Field 'email' is invalid"}` | +| 401 | Unauthorized - Missing or invalid token | `{"error": "UNAUTHORIZED", "message": "Invalid or missing authentication token"}` | +| 403 | Forbidden - Insufficient permissions | `{"error": "FORBIDDEN", "message": "User lacks required role"}` | +| 404 | Not Found - Resource doesn't exist | `{"error": "NOT_FOUND", "message": "Resource with id 'xyz' not found"}` | +| 409 | Conflict - Resource already exists | `{"error": "CONFLICT", "message": "Resource with this identifier already exists"}` | +| 429 | Too Many Requests - Rate limit exceeded | `{"error": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded, retry after 60 seconds"}` | +| 500 | Internal Server Error | `{"error": "INTERNAL_ERROR", "message": "An unexpected error occurred"}` | + +**Error Response Schema**: +```json +{ + "error": "ERROR_CODE", + "message": "Human-readable error message", + "details": { + "field": "field_name", + "reason": "specific reason" + } +} +``` + +## Rate Limiting + +- **Authenticated**: [1000] requests per hour +- **Unauthenticated**: [100] requests per hour + +**Response Headers**: +- `X-RateLimit-Limit`: Maximum requests per hour +- `X-RateLimit-Remaining`: Remaining requests in current window +- `X-RateLimit-Reset`: Unix timestamp when limit resets + +## Pagination + +_(If endpoint returns paginated results)_ + +**Request**: Use `page` and `per_page` query parameters +**Response**: Includes `pagination` object + +```json +{ + "data": [...], + "pagination": { + "page": 1, + "per_page": 20, + "total": 145, + "total_pages": 8, + "next_page": 2, + "prev_page": null + } +} +``` + +## Code Examples + +### TypeScript + +```typescript +const response = await fetch('https://api.greyhaven.com/[resource]', { + method: '[METHOD]', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + [field_name]: 'value' + }) +}); + +if (!response.ok) { + const error = await response.json(); + throw new Error(error.message); +} + +const data = await response.json(); +console.log('[Resource] created:', data.id); +``` + +### Python + +```python +import httpx + +async with httpx.AsyncClient() as client: + response = await client.[method]( + 'https://api.greyhaven.com/[resource]', + headers={'Authorization': f'Bearer {token}'}, + json={'[field_name]': 'value'} + ) + response.raise_for_status() + data = response.json() + print(f'[Resource] created: {data["id"]}') +``` + +### cURL + +```bash +curl -X [METHOD] https://api.greyhaven.com/[resource] \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "[field_name]": "value" + }' +``` + +## Changelog + +| Version | Date | Changes | +|---------|------|---------| +| v1.0.0 | 2024-01-15 | Initial release | + +--- + +[Return to API Reference](../README.md) diff --git a/skills/documentation-architecture/templates/architecture-doc.md b/skills/documentation-architecture/templates/architecture-doc.md new file mode 100644 index 0000000..a69f4d0 --- /dev/null +++ b/skills/documentation-architecture/templates/architecture-doc.md @@ -0,0 +1,307 @@ +# [System Name] Architecture + +**Version**: [1.0.0] +**Last Updated**: [YYYY-MM-DD] +**Status**: [Draft / In Review / Approved] +**Authors**: [Names] + +## Executive Summary + +[2-3 paragraph high-level overview suitable for non-technical stakeholders. Include:] +- What the system does +- Key business value +- Major technical decisions +- Scale/performance characteristics + +## Table of Contents + +- [System Overview](#system-overview) +- [Architecture Overview](#architecture-overview) +- [Core Components](#core-components) +- [Data Flow](#data-flow) +- [Integration Points](#integration-points) +- [Security Model](#security-model) +- [Deployment Architecture](#deployment-architecture) +- [Architecture Decision Records](#architecture-decision-records) +- [Appendix](#appendix) + +## System Overview + +### Purpose + +[What problem does this system solve?] + +### Key Features + +- **Feature 1**: [Description] +- **Feature 2**: [Description] +- **Feature 3**: [Description] + +### Non-Functional Requirements + +| Requirement | Target | Current | +|-------------|--------|---------| +| Availability | 99.9% | 99.95% | +| Response Time (p95) | <500ms | 320ms | +| Throughput | 1000 req/s | 850 req/s | +| Data Retention | 7 years | 7 years | + +## Architecture Overview + +### High-Level Architecture + +```mermaid +graph TB + subgraph "Client Layer" + Browser[Web Browser] + Mobile[Mobile App] + end + + subgraph "Edge Layer" + Gateway[API Gateway] + CDN[CDN] + end + + subgraph "Application Layer" + Frontend[Frontend Service] + Backend[Backend Service] + end + + subgraph "Data Layer" + DB[(Database)] + Cache[(Cache)] + Storage[(Object Storage)] + end + + Browser --> Gateway + Mobile --> Gateway + Gateway --> Frontend + Gateway --> Backend + Frontend --> DB + Backend --> DB + Backend --> Cache + Backend --> Storage +``` + +### Technology Stack + +| Layer | Technology | Version | Rationale | +|-------|------------|---------|-----------| +| Frontend | [Framework] | [x.y.z] | [Why chosen] | +| Backend | [Framework] | [x.y.z] | [Why chosen] | +| Database | [Database] | [x.y.z] | [Why chosen] | +| Cache | [Cache] | [x.y.z] | [Why chosen] | +| Deployment | [Platform] | [x.y.z] | [Why chosen] | + +## Core Components + +### [Component 1]: [Name] + +**Purpose**: [What this component does] + +**Responsibilities**: +- [Responsibility 1] +- [Responsibility 2] +- [Responsibility 3] + +**Technology**: [Framework/Language] +**Repository**: [Link to repo] + +**Key Interfaces**: +- REST API: `POST /api/v1/[endpoint]` +- WebSocket: `wss://[domain]/[path]` +- Message Queue: `[queue-name]` + +### [Component 2]: [Name] + +[Same structure as Component 1] + +## Data Flow + +### [Flow 1]: [Name] + +```mermaid +sequenceDiagram + actor User + participant Frontend + participant Backend + participant DB + + User->>Frontend: [Action] + Frontend->>Backend: POST /api/[endpoint] + Backend->>DB: Query/Update + DB-->>Backend: Result + Backend-->>Frontend: Response + Frontend-->>User: Display result +``` + +**Steps**: +1. [Step 1 description] +2. [Step 2 description] +3. [Step 3 description] + +**Error Handling**: +- [Error scenario 1]: [How handled] +- [Error scenario 2]: [How handled] + +## Integration Points + +### External Service 1: [Service Name] + +**Purpose**: [Why we integrate with this service] +**Protocol**: [REST API / GraphQL / gRPC] +**Authentication**: [Method] +**Rate Limits**: [X requests per Y] +**SLA**: [Uptime guarantee] + +**Endpoints Used**: +- `[METHOD] /[path]` - [Purpose] +- `[METHOD] /[path]` - [Purpose] + +**Fallback Strategy**: [What happens if service unavailable] + +### External Service 2: [Service Name] + +[Same structure as External Service 1] + +## Security Model + +### Authentication + +**Method**: [JWT / OAuth2 / API Key] + +```mermaid +sequenceDiagram + participant User + participant Auth + participant Service + + User->>Auth: Login with credentials + Auth-->>User: JWT token + User->>Service: Request + JWT + Service->>Service: Validate token + Service-->>User: Response +``` + +### Authorization + +**Model**: [RBAC / ABAC / ACL] + +**Roles**: +- `admin`: [Permissions] +- `member`: [Permissions] +- `guest`: [Permissions] + +### Data Protection + +**Encryption**: +- At rest: [Method, algorithm] +- In transit: TLS 1.3 +- Database: [Encryption method] + +**Multi-Tenancy**: +- Isolation method: [Row-Level Security / Separate DBs / Separate Schemas] +- Tenant identification: [Header / Subdomain / Path] + +## Deployment Architecture + +### Environments + +| Environment | Purpose | URL | Auto-Deploy | +|-------------|---------|-----|-------------| +| Development | Feature development | https://dev.[domain] | On PR | +| Staging | Pre-production testing | https://staging.[domain] | On merge to main | +| Production | Live system | https://[domain] | On release tag | + +### Infrastructure + +```mermaid +graph TB + subgraph "Production" + LB[Load Balancer] + App1[App Server 1] + App2[App Server 2] + App3[App Server 3] + DB[(Primary DB)] + Replica[(Read Replica)] + end + + LB --> App1 + LB --> App2 + LB --> App3 + App1 --> DB + App2 --> DB + App3 --> DB + App1 -.read.-> Replica + App2 -.read.-> Replica + App3 -.read.-> Replica + DB -.replication.-> Replica +``` + +**Resources**: +- App Servers: [X instances, Y CPU, Z GB RAM] +- Database: [Specifications] +- Cache: [Specifications] + +### CI/CD Pipeline + +```mermaid +graph LR + Code[Code Push] --> Tests[Run Tests] + Tests --> Build[Build] + Build --> Deploy[Deploy to Staging] + Deploy --> Smoke[Smoke Tests] + Smoke --> Approve{Manual Approve?} + Approve -->|Yes| Prod[Deploy to Production] + Approve -->|No| Wait[Wait] +``` + +## Architecture Decision Records + +### ADR-001: [Decision Title] + +**Date**: [YYYY-MM-DD] +**Status**: [Accepted / Superseded / Deprecated] +**Decision Makers**: [Names] + +**Context**: [What circumstances led to this decision?] + +**Decision**: [What was decided?] + +**Alternatives Considered**: +1. **[Option 1]**: [Pros/Cons] +2. **[Option 2]**: [Pros/Cons] + +**Consequences**: +- **Positive**: [Benefit 1], [Benefit 2] +- **Negative**: [Trade-off 1], [Trade-off 2] + +**Implementation**: [How was this implemented?] + +### ADR-002: [Decision Title] + +[Same structure as ADR-001] + +## Appendix + +### Glossary + +| Term | Definition | +|------|------------| +| [Term] | [Definition] | + +### References + +- Architecture Diagrams: [Link] +- API Documentation: [Link] +- Runbooks: [Link] +- Monitoring Dashboard: [Link] + +### Related Documentation + +- [Link to related doc 1] +- [Link to related doc 2] + +--- + +[Return to Documentation Index](../README.md) diff --git a/skills/documentation-architecture/templates/openapi-spec.yaml b/skills/documentation-architecture/templates/openapi-spec.yaml new file mode 100644 index 0000000..3babb5c --- /dev/null +++ b/skills/documentation-architecture/templates/openapi-spec.yaml @@ -0,0 +1,429 @@ +openapi: 3.1.0 + +info: + title: [Your API Name] + version: 1.0.0 + description: | + [Brief description of your API] + + ## Authentication + All endpoints require JWT authentication via Bearer token in Authorization header. + + ## Rate Limiting + - Authenticated: 1000 requests/hour + - Unauthenticated: 100 requests/hour + + ## Base URL + Production: https://api.yourdomain.com + Staging: https://api-staging.yourdomain.com + + contact: + name: [Your Team Name] + email: support@yourdomain.com + url: https://docs.yourdomain.com + + license: + name: MIT + url: https://opensource.org/licenses/MIT + +servers: + - url: https://api.yourdomain.com + description: Production + - url: https://api-staging.yourdomain.com + description: Staging + - url: http://localhost:3000 + description: Local development + +# Global security requirement (can be overridden per endpoint) +security: + - BearerAuth: [] + +tags: + - name: users + description: User management operations + - name: authentication + description: Authentication and authorization + +paths: + /auth/login: + post: + summary: User login + description: Authenticate user with email and password + operationId: login + tags: + - authentication + security: [] # No auth required for login + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/LoginRequest' + responses: + '200': + description: Login successful + content: + application/json: + schema: + $ref: '#/components/schemas/LoginResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/TooManyRequests' + + /users: + get: + summary: List users + description: Retrieve a paginated list of users + operationId: listUsers + tags: + - users + parameters: + - $ref: '#/components/parameters/PageParam' + - $ref: '#/components/parameters/PerPageParam' + - name: role + in: query + description: Filter by user role + schema: + type: string + enum: [admin, member, guest] + responses: + '200': + description: Users retrieved successfully + headers: + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedUsers' + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/TooManyRequests' + + post: + summary: Create user + description: Create a new user account + operationId: createUser + tags: + - users + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UserCreate' + responses: + '201': + description: User created successfully + headers: + Location: + description: URL of created user + schema: + type: string + example: /users/usr_1234567890abcdef + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '409': + $ref: '#/components/responses/Conflict' + + /users/{user_id}: + get: + summary: Get user by ID + description: Retrieve a single user by their unique identifier + operationId: getUser + tags: + - users + parameters: + - $ref: '#/components/parameters/UserIdParam' + responses: + '200': + description: User retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '404': + $ref: '#/components/responses/NotFound' + +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token obtained from /auth/login endpoint + + parameters: + UserIdParam: + name: user_id + in: path + required: true + description: User identifier + schema: + type: string + pattern: ^usr_[a-z0-9]{16}$ + example: usr_1234567890abcdef + + PageParam: + name: page + in: query + description: Page number (1-indexed) + schema: + type: integer + minimum: 1 + default: 1 + example: 1 + + PerPageParam: + name: per_page + in: query + description: Items per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + example: 20 + + headers: + X-RateLimit-Limit: + description: Maximum requests per hour + schema: + type: integer + example: 1000 + + X-RateLimit-Remaining: + description: Remaining requests in current window + schema: + type: integer + example: 847 + + X-RateLimit-Reset: + description: Unix timestamp when limit resets + schema: + type: integer + example: 1699564800 + + schemas: + LoginRequest: + type: object + required: [email, password] + properties: + email: + type: string + format: email + examples: [user@example.com] + password: + type: string + minLength: 8 + examples: [secure123] + + LoginResponse: + type: object + required: [token, user] + properties: + token: + type: string + description: JWT authentication token + user: + $ref: '#/components/schemas/User' + + UserCreate: + type: object + required: [email, password] + properties: + email: + type: string + format: email + examples: [user@example.com] + password: + type: string + minLength: 8 + examples: [secure123] + name: + type: string + examples: [John Doe] + role: + type: string + enum: [admin, member, guest] + default: member + + User: + type: object + required: [id, email, role, created_at] + properties: + id: + type: string + pattern: ^usr_[a-z0-9]{16}$ + examples: [usr_1234567890abcdef] + email: + type: string + format: email + examples: [user@example.com] + name: + type: [string, 'null'] + examples: [John Doe] + role: + type: string + enum: [admin, member, guest] + examples: [member] + created_at: + type: string + format: date-time + examples: ["2024-01-15T10:30:00Z"] + updated_at: + type: string + format: date-time + examples: ["2024-01-15T10:30:00Z"] + + PaginatedUsers: + type: object + required: [data, pagination] + properties: + data: + type: array + items: + $ref: '#/components/schemas/User' + pagination: + $ref: '#/components/schemas/PaginationMeta' + + PaginationMeta: + type: object + required: [page, per_page, total, total_pages] + properties: + page: + type: integer + minimum: 1 + examples: [1] + per_page: + type: integer + minimum: 1 + maximum: 100 + examples: [20] + total: + type: integer + examples: [145] + total_pages: + type: integer + examples: [8] + next_page: + type: [integer, 'null'] + examples: [2] + prev_page: + type: [integer, 'null'] + examples: [null] + + ErrorResponse: + type: object + required: [error, message] + properties: + error: + type: string + description: Error code (UPPERCASE_SNAKE_CASE) + examples: [VALIDATION_ERROR, UNAUTHORIZED, NOT_FOUND] + message: + type: string + description: Human-readable error message + examples: [Validation failed for field 'email'] + details: + type: object + description: Additional error context + additionalProperties: true + examples: + - field: email + reason: Invalid format + + responses: + BadRequest: + description: Bad request (validation error) + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + validation_error: + value: + error: VALIDATION_ERROR + message: Validation failed for field 'email' + details: + field: email + reason: Invalid email format + + Unauthorized: + description: Unauthorized (invalid or missing authentication) + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + missing_token: + value: + error: UNAUTHORIZED + message: Authentication token required + invalid_token: + value: + error: UNAUTHORIZED + message: Invalid or expired authentication token + + Forbidden: + description: Forbidden (insufficient permissions) + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + insufficient_role: + value: + error: FORBIDDEN + message: User lacks required role + + NotFound: + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + not_found: + value: + error: NOT_FOUND + message: Resource with id 'xyz' not found + + Conflict: + description: Conflict (resource already exists) + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + already_exists: + value: + error: CONFLICT + message: Resource with this identifier already exists + + TooManyRequests: + description: Rate limit exceeded + headers: + X-RateLimit-Reset: + $ref: '#/components/headers/X-RateLimit-Reset' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + rate_limit: + value: + error: RATE_LIMIT_EXCEEDED + message: Rate limit exceeded, retry after 60 seconds diff --git a/skills/onboarding-coordination/SKILL.md b/skills/onboarding-coordination/SKILL.md new file mode 100644 index 0000000..f5ca01e --- /dev/null +++ b/skills/onboarding-coordination/SKILL.md @@ -0,0 +1,25 @@ +# Onboarding Coordination Skill + +Automate developer onboarding with personalized plans, Linear integration, knowledge base setup, and milestone tracking. + +## Description + +Streamlined onboarding workflows with pre-boarding, Day 1 setup, weekly milestones, and 30/60/90 day tracking. + +## What's Included + +- **Examples**: Onboarding plans, Linear automation workflows +- **Reference**: Onboarding best practices, milestone templates +- **Templates**: Onboarding checklists, setup scripts + +## Use When + +- New developer onboarding +- Team expansion +- Role transitions + +## Related Agents + +- `onboarding-coordinator` + +**Skill Version**: 1.0 diff --git a/skills/onboarding-coordination/examples/INDEX.md b/skills/onboarding-coordination/examples/INDEX.md new file mode 100644 index 0000000..4f5b0ba --- /dev/null +++ b/skills/onboarding-coordination/examples/INDEX.md @@ -0,0 +1,67 @@ +# Onboarding Automation Examples + +Real-world examples of automated developer onboarding with personalized plans, Linear integration, and milestone tracking. + +## Available Examples + +### [Junior Engineer Onboarding](junior-engineer-onboarding.md) + +Complete onboarding workflow for a junior frontend engineer from day 1 to 90 days. + +**Scenario**: First full-time engineering hire needs comprehensive onboarding with extra mentorship + +**Solution**: Structured learning path, buddy system, good-first-issues, daily check-ins + +**Results**: First PR in 2 days (target: 3), 90-day satisfaction 4.8/5, promoted to mid-level at 18 months + +**Key Techniques**: Graduated complexity, buddy pairing, structured milestones, feedback loops + +--- + +### [Senior Engineer Onboarding](senior-engineer-onboarding.md) + +Fast-track onboarding for experienced engineer focused on architecture and technical leadership. + +**Scenario**: Senior hire needs to understand architecture quickly and start contributing to technical decisions + +**Solution**: Architecture deep-dives, ADR reviews, design doc creation, early leadership opportunities + +**Results**: First architectural proposal in week 2, leading design review in week 3, mentoring in week 4 + +**Key Techniques**: Architecture-first, design doc workflow, early technical leadership, stakeholder mapping + +--- + +### [Linear Integration Automation](linear-automation-workflow.md) + +Complete Linear automation for tracking onboarding progress from pre-boarding through 90 days. + +**Scenario**: Manual onboarding tracking in spreadsheets, inconsistent milestone completion, no visibility + +**Solution**: Automated Linear issue creation, milestone tracking, dashboard views, notification automation + +**Results**: 100% milestone completion visibility, 0 missed check-ins, manager time saved 5hrs/new hire + +**Key Techniques**: Linear API automation, milestone issues, custom views, webhook notifications + +--- + +## Common Patterns + +1. **Personalization**: Adapt onboarding to role (junior vs senior) and specialty (frontend vs backend vs fullstack) +2. **Automation**: Environment setup, account provisioning, Linear issue creation all automated +3. **Human Touch**: Reserve mentorship, code review, and team integration for humans +4. **Milestone Tracking**: 30/60/90 day check-ins with manager feedback loops +5. **Feedback Integration**: Continuous improvement from new hire retrospectives + +## Quick Reference + +| Need | Example | Key Focus | +|------|---------|-----------| +| Junior onboarding | [junior-engineer-onboarding.md](junior-engineer-onboarding.md) | Learning path, mentorship | +| Senior onboarding | [senior-engineer-onboarding.md](senior-engineer-onboarding.md) | Architecture, leadership | +| Linear automation | [linear-automation-workflow.md](linear-automation-workflow.md) | Task tracking, visibility | + +--- + +Related: [Reference Guides](../reference/INDEX.md) | [Templates](../templates/) | [Return to Agent](../onboarding-coordinator.md) diff --git a/skills/onboarding-coordination/reference/INDEX.md b/skills/onboarding-coordination/reference/INDEX.md new file mode 100644 index 0000000..635428e --- /dev/null +++ b/skills/onboarding-coordination/reference/INDEX.md @@ -0,0 +1,59 @@ +# Onboarding Coordinator Reference Guide + +Quick-lookup reference for onboarding best practices, Linear API patterns, buddy system guidelines, and milestone tracking. + +## Files in This Directory + +### [onboarding-best-practices.md](onboarding-best-practices.md) +Research-backed best practices for developer onboarding - what works, what doesn't, and why. Covers psychological safety, graduated complexity, buddy systems, and feedback loops. + +**When to use**: Designing new onboarding programs, improving existing processes, training managers/buddies + +**Key topics**: +- First 90 days framework (pre-boarding, Day 1, Week 1, milestones) +- Junior vs mid vs senior onboarding differences +- Buddy system best practices +- Common onboarding mistakes and how to avoid them + +### [linear-api-patterns.md](linear-api-patterns.md) +Complete Linear API reference for onboarding automation - issue creation, custom views, webhooks, and integrations. + +**When to use**: Building onboarding automation, creating custom Linear integrations, troubleshooting API issues + +**Key topics**: +- Linear CLI commands for issue management +- GraphQL API examples +- Webhook configuration for milestone notifications +- Custom dashboard views for managers and buddies + +### [buddy-system-guide.md](buddy-system-guide.md) +Comprehensive guide to effective buddy systems - selection, training, responsibilities, and success metrics. + +**When to use**: Launching buddy program, training buddies, improving buddy effectiveness + +**Key topics**: +- Buddy selection criteria +- Week-by-week buddy responsibilities +- Check-in templates and conversation guides +- Measuring buddy program effectiveness + +### [milestone-tracking.md](milestone-tracking.md) +Framework for 30/60/90 day milestones - what to measure, how to assess, and when to intervene. + +**When to use**: Preparing for milestone check-ins, assessing onboarding progress, identifying at-risk new hires + +**Key topics**: +- Success criteria for each milestone +- Red flags and intervention strategies +- Performance rating frameworks +- Feedback templates for managers + +## Navigation + +**Parent**: [Onboarding Coordinator Agent](../onboarding-coordinator.md) +**Examples**: [Examples Index](../examples/INDEX.md) +**Templates**: [Templates Index](../templates/INDEX.md) + +--- + +Return to [agent documentation](../onboarding-coordinator.md) diff --git a/skills/onboarding-coordination/templates/INDEX.md b/skills/onboarding-coordination/templates/INDEX.md new file mode 100644 index 0000000..8fb726d --- /dev/null +++ b/skills/onboarding-coordination/templates/INDEX.md @@ -0,0 +1,41 @@ +# Onboarding Coordinator Templates + +Copy-paste ready templates for onboarding plans, buddy checklists, and milestone check-ins. + +## Files in This Directory + +### [onboarding-plan-template.md](onboarding-plan-template.md) +Complete onboarding plan template adaptable for junior, mid-level, and senior engineers. Copy, customize with new hire details, and assign Linear issues. + +**When to use**: Creating onboarding plan for new hire +**Customization**: Replace [placeholders] with actual details + +### [buddy-checklist-template.md](buddy-checklist-template.md) +Week-by-week checklist for buddies showing responsibilities from Day 1 through Week 12. Print and follow throughout onboarding period. + +**When to use**: Assigning buddy to new hire +**Customization**: Adjust check-in frequencies based on experience level + +### [milestone-check-in-templates.md](milestone-check-in-templates.md) +Meeting agendas and assessment templates for 30/60/90 day milestone check-ins with rating frameworks and feedback prompts. + +**When to use**: Preparing for formal milestone reviews +**Customization**: Add role-specific success criteria + +## Usage + +All templates are markdown files designed for: +- Copy-paste into Linear issues +- Email to new hires or buddies +- Manager preparation documents +- Print and follow checklists + +## Navigation + +**Parent**: [Onboarding Coordinator Agent](../onboarding-coordinator.md) +**Examples**: [Examples Index](../examples/INDEX.md) +**Reference**: [Reference Index](../reference/INDEX.md) + +--- + +Return to [agent documentation](../onboarding-coordinator.md) diff --git a/skills/ontological-documentation/README.md b/skills/ontological-documentation/README.md new file mode 100644 index 0000000..016bd6e --- /dev/null +++ b/skills/ontological-documentation/README.md @@ -0,0 +1,68 @@ +# Ontological Documentation Skill + +This skill provides comprehensive tools and templates for creating ontological documentation of software systems. + +## Directory Structure + +``` +ontological-documentation/ +├── SKILL.md # Main skill definition +├── README.md # This file +├── references/ # Reference guides +│ ├── concept_extraction_guide.md +│ ├── documentation_templates.md +│ └── ontology_patterns.md +├── scripts/ # Utility scripts +│ ├── extract_concepts.py +│ └── generate_ontology_diagram.py +└── assets/ # Examples and templates + ├── examples/ + │ └── ecommerce-ontology.md + └── ontology-templates/ + └── domain-ontology.md +``` + +## What's Included + +### Reference Guides + +- **concept_extraction_guide.md**: Methodologies for extracting domain concepts from codebases +- **documentation_templates.md**: Standardized templates for documenting concepts and relationships +- **ontology_patterns.md**: Common patterns and best practices for ontological documentation + +### Scripts + +- **extract_concepts.py**: Automated concept extraction from Python and JavaScript/TypeScript code +- **generate_ontology_diagram.py**: Generate Mermaid, PlantUML, GraphViz, and JSON-LD diagrams + +### Examples & Templates + +- **ecommerce-ontology.md**: Complete example of e-commerce domain ontology +- **domain-ontology.md**: Template for documenting new domain ontologies + +## Usage + +This skill activates automatically when working on: +- Domain modeling and architecture documentation +- Creating conceptual frameworks +- Extracting and documenting business concepts from code +- Building knowledge graphs and semantic models + +## Quick Start + +1. Read [SKILL.md](SKILL.md) for the full skill definition +2. Review the [concept extraction guide](references/concept_extraction_guide.md) +3. Use the [templates](references/documentation_templates.md) for your documentation +4. Check the [e-commerce example](assets/examples/ecommerce-ontology.md) for inspiration + +## Scripts Usage + +### Extract Concepts +```bash +python scripts/extract_concepts.py /path/to/codebase +``` + +### Generate Diagrams +```bash +python scripts/generate_ontology_diagram.py ontology.json --format mermaid +``` diff --git a/skills/ontological-documentation/SKILL.md b/skills/ontological-documentation/SKILL.md new file mode 100644 index 0000000..2760181 --- /dev/null +++ b/skills/ontological-documentation/SKILL.md @@ -0,0 +1,399 @@ +--- +name: grey-haven-ontological-documentation +description: Create comprehensive ontological documentation for Grey Haven systems - extract domain concepts from TanStack Start and FastAPI codebases, model semantic relationships, generate visual representations of system architecture, and document business domains. Use when onboarding, documenting architecture, or analyzing legacy systems. +--- + +# Grey Haven Ontological Documentation + +Create comprehensive ontological documentation that captures fundamental concepts, relationships, and classification systems within Grey Haven codebases and systems. + +## When to Use This Skill + +Use this skill when you need to: +- Document the conceptual structure and domain model of Grey Haven applications +- Extract and organize business concepts from TanStack Start or FastAPI codebases +- Create visual representations of multi-tenant system architectures +- Build semantic maps of entities, services, and their tenant-isolated interactions +- Design or document domain models for new Grey Haven features +- Analyze and communicate complex architectures to stakeholders +- Create knowledge graphs for Grey Haven development teams +- Onboard new developers to Grey Haven project structure + +## Core Capabilities + +### 1. Concept Extraction from Grey Haven Codebases + +**TanStack Start (Frontend) Extraction:** +- Drizzle schema tables and relationships +- React components and their hierarchies +- TanStack Router route structure +- Better-auth session and user models +- Server functions and their dependencies +- Multi-tenant data patterns (tenant_id isolation) + +**FastAPI (Backend) Extraction:** +- SQLModel entities and relationships +- Repository pattern implementations +- Service layer business logic +- API endpoint hierarchies +- Multi-tenant repository filters +- Pydantic schemas and validation models + +### 2. Grey Haven Architecture Patterns + +**Identify and Document:** +- **Multi-Tenant Patterns**: tenant_id isolation, RLS roles (admin/authenticated/anon) +- **Repository Pattern**: BaseRepository with automatic tenant filtering +- **Service Layer**: Business logic separation from endpoints +- **Database Conventions**: snake_case fields, UUID primary keys, timestamps +- **Authentication**: Better-auth integration with session management +- **Deployment**: Cloudflare Workers architecture + +### 3. Visual Documentation Formats + +**Mermaid Diagrams** (for README files): +```mermaid +erDiagram + USER ||--o{ ORGANIZATION : belongs_to + USER { + uuid id PK + string email_address UK + uuid tenant_id FK + timestamp created_at + } + ORGANIZATION ||--o{ TEAM : contains + ORGANIZATION { + uuid id PK + string name + uuid tenant_id FK + timestamp created_at + } +``` + +**System Architecture**: +```mermaid +graph TB + Client[TanStack Start Client] + Server[Server Functions] + Auth[Better-auth] + DB[(PostgreSQL + RLS)] + + Client -->|Authenticated Requests| Server + Client -->|Auth Flow| Auth + Server -->|Query with tenant_id| DB + Auth -->|Session Validation| DB +``` + +### 4. Domain Model Documentation Template + +```markdown +## Entity: User + +### Definition +Represents an authenticated user in the Grey Haven system with multi-tenant isolation. + +### Database Schema +- **Table**: users (snake_case) +- **Primary Key**: id (UUID) +- **Tenant Isolation**: tenant_id (UUID, indexed) +- **Unique Constraints**: email_address per tenant +- **Timestamps**: created_at, updated_at (automatic) + +### Relationships +- **Belongs To**: Organization (via tenant_id) +- **Has Many**: Sessions (Better-auth) +- **Has Many**: TeamMemberships + +### Business Rules +- Email must be unique within tenant +- Cannot access data from other tenants +- Session expires after 30 days of inactivity +- RLS enforces tenant_id filtering at database level + +### TypeScript Type +```typescript +interface User { + id: string; + emailAddress: string; + tenantId: string; + createdAt: Date; + updatedAt: Date; +} +``` + +### Python Model +```python +class User(SQLModel, table=True): + __tablename__ = "users" + id: UUID = Field(default_factory=uuid4, primary_key=True) + email_address: str = Field(unique=True, index=True) + tenant_id: UUID = Field(foreign_key="organizations.id", index=True) + created_at: datetime = Field(default_factory=datetime.utcnow) + updated_at: datetime = Field(default_factory=datetime.utcnow) +``` +``` + +## Workflow + +### Step 1: Discovery and Extraction + +**For TanStack Start Projects:** +1. Analyze Drizzle schema files in `src/lib/server/schema/` +2. Map React component structure in `src/lib/components/` +3. Document TanStack Router routes in `src/routes/` +4. Extract server functions from `src/lib/server/functions/` +5. Identify tenant isolation patterns + +**For FastAPI Projects:** +1. Analyze SQLModel models in `app/db/models/` +2. Map repository pattern in `app/db/repositories/` +3. Document service layer in `app/services/` +4. Extract API routes from `app/routers/` +5. Identify BaseRepository tenant filtering + +### Step 2: Ontology Construction + +**Categorize by Grey Haven Patterns:** + +1. **Core Entities** (tables with tenant_id) + - User, Organization, Team, etc. + - Always include tenant_id + - UUID primary keys + - snake_case field names + +2. **Service Boundaries** + - Repository layer (data access) + - Service layer (business logic) + - Router layer (API endpoints) + - Clear separation of concerns + +3. **Relationships and Dependencies** + - Foreign key relationships + - Repository dependencies + - Service composition + - API endpoint groupings + +4. **Multi-Tenant Patterns** + - RLS role usage (admin/authenticated/anon) + - tenant_id filtering in repositories + - Session-based tenant resolution + - Cross-tenant access prevention + +### Step 3: Documentation Creation + +**Use Grey Haven Documentation Standards:** + +1. **Entity Documentation** + - Definition and purpose + - Database schema with exact field names + - Relationships to other entities + - Business rules and constraints + - TypeScript and Python representations + +2. **Service Documentation** + - Service responsibilities + - Repository dependencies + - Business logic patterns + - Multi-tenant considerations + +3. **API Documentation** + - Endpoint hierarchies + - Request/response schemas + - Authentication requirements + - Tenant isolation verification + +### Step 4: Visualization + +**Create Diagrams For:** + +1. **Database ERD** - All tables with relationships and tenant_id fields +2. **Service Dependencies** - Repository → Service → Router layers +3. **Authentication Flow** - Better-auth integration with multi-tenant context +4. **Deployment Architecture** - Cloudflare Workers, Neon PostgreSQL, Redis +5. **Data Flow** - Client → Server Functions → Repository → Database (with RLS) + +## Common Use Cases + +### Use Case 1: New Developer Onboarding +*"I need to understand how Grey Haven's multi-tenant architecture works."* + +**Approach:** +1. Extract all entities with tenant_id fields +2. Document BaseRepository tenant filtering pattern +3. Create ERD showing tenant_id relationships +4. Explain RLS roles and session-based tenant resolution +5. Show data flow with tenant isolation + +### Use Case 2: Feature Design Documentation +*"Document the domain model for the new billing feature before implementation."* + +**Approach:** +1. Design entity schema following Grey Haven conventions +2. Plan repository and service layer structure +3. Document API endpoints with tenant isolation +4. Create Mermaid diagrams for the feature +5. Validate multi-tenant patterns + +### Use Case 3: Architecture Review +*"Analyze the current codebase to identify inconsistencies in multi-tenant patterns."* + +**Approach:** +1. Extract all repositories and check tenant_id filtering +2. Review entities for proper tenant_id indexing +3. Audit RLS role usage across the application +4. Identify missing tenant isolation +5. Generate compliance report + +### Use Case 4: Legacy Code Analysis +*"Understand the original domain model before refactoring the user management system."* + +**Approach:** +1. Extract current User entity and relationships +2. Map all services depending on User +3. Document authentication flow with Better-auth +4. Identify refactoring boundaries +5. Create before/after architecture diagrams + +## Grey Haven Specific Patterns + +### Multi-Tenant Entity Pattern +```typescript +// Drizzle Schema (TanStack Start) +export const usersTable = pgTable("users", { + id: uuid("id").primaryKey().defaultRandom(), + emailAddress: text("email_address").unique().notNull(), + tenantId: uuid("tenant_id").references(() => organizationsTable.id).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), +}); +``` + +```python +# SQLModel (FastAPI) +class User(SQLModel, table=True): + __tablename__ = "users" + + id: UUID = Field(default_factory=uuid4, primary_key=True) + email_address: str = Field(unique=True, index=True) + tenant_id: UUID = Field(foreign_key="organizations.id", index=True) + created_at: datetime = Field(default_factory=datetime.utcnow) + updated_at: datetime = Field(default_factory=datetime.utcnow) +``` + +### Repository Pattern with Tenant Isolation +```python +# BaseRepository with automatic tenant filtering +class BaseRepository(Generic[T]): + def __init__(self, session: AsyncSession, model: type[T]): + self.session = session + self.model = model + + async def get_by_id(self, id: UUID, tenant_id: UUID) -> Optional[T]: + """Automatic tenant isolation.""" + result = await self.session.execute( + select(self.model) + .where(self.model.id == id) + .where(self.model.tenant_id == tenant_id) # Always filter + ) + return result.scalar_one_or_none() +``` + +### RLS Role Pattern +```typescript +// Database connections with RLS roles +const adminDb = drizzle(process.env.DATABASE_URL_ADMIN); // Full access +const authenticatedDb = drizzle(process.env.DATABASE_URL_AUTHENTICATED); // Tenant-scoped +const anonDb = drizzle(process.env.DATABASE_URL_ANON); // Public only +``` + +## Documentation Output Structure + +### Directory Organization +``` +documentation/ +├── architecture/ +│ ├── system-overview.md +│ ├── multi-tenant-architecture.md +│ └── deployment-architecture.md +├── domain-model/ +│ ├── entities/ +│ │ ├── user.md +│ │ ├── organization.md +│ │ └── team.md +│ ├── relationships.md +│ └── business-rules.md +├── diagrams/ +│ ├── database-erd.mmd +│ ├── service-dependencies.mmd +│ ├── auth-flow.mmd +│ └── deployment.mmd +└── ontology.json +``` + +### Ontology JSON Structure +```json +{ + "version": "1.0.0", + "system": "Grey Haven Application", + "architecture": "Multi-tenant TanStack Start + FastAPI", + "entities": [ + { + "name": "User", + "table": "users", + "primaryKey": "id", + "tenantKey": "tenant_id", + "fields": [...], + "relationships": [...], + "businessRules": [...] + } + ], + "services": [...], + "patterns": { + "multiTenant": true, + "rls": true, + "repositoryPattern": true + } +} +``` + +## When to Apply This Skill + +Use ontological documentation when: +- Onboarding new developers to Grey Haven projects +- Designing new features with domain modeling +- Documenting multi-tenant architecture +- Analyzing legacy code before refactoring +- Creating architecture presentations for stakeholders +- Building knowledge bases for Grey Haven teams +- Ensuring consistency across TanStack Start and FastAPI implementations +- Auditing multi-tenant isolation patterns +- Planning database migrations or schema changes + +## Integration with Other Grey Haven Skills + +**Works Best With:** +- `grey-haven-database-conventions` - Ensure proper schema design +- `grey-haven-project-structure` - Understand codebase organization +- `grey-haven-authentication-patterns` - Document Better-auth integration +- `grey-haven-data-modeling` - Design Drizzle and SQLModel schemas +- `grey-haven-api-design-standards` - Document API hierarchies + +## Critical Reminders + +1. **Always document tenant_id** - Every entity must show tenant isolation +2. **Follow naming conventions** - snake_case for database, camelCase for TypeScript +3. **Include both TypeScript and Python** - Grey Haven uses both stacks +4. **Show RLS roles** - Document admin/authenticated/anon usage +5. **Repository pattern is required** - All data access goes through repositories +6. **UUID primary keys** - Never use auto-increment integers +7. **Timestamps are automatic** - created_at and updated_at +8. **Multi-tenant first** - Every design considers tenant isolation +9. **Visual diagrams required** - Mermaid for all architecture documentation +10. **Cross-reference skills** - Link to relevant Grey Haven skills + +## Template References + +These patterns are from Grey Haven's actual templates: +- **Frontend**: `cvi-template` (TanStack Start + React 19 + Drizzle) +- **Backend**: `cvi-backend-template` (FastAPI + SQLModel + Repository Pattern) +- **Multi-tenant**: Neon PostgreSQL with RLS diff --git a/skills/ontological-documentation/checklists/ontology-documentation-checklist.md b/skills/ontological-documentation/checklists/ontology-documentation-checklist.md new file mode 100644 index 0000000..f92fca2 --- /dev/null +++ b/skills/ontological-documentation/checklists/ontology-documentation-checklist.md @@ -0,0 +1,236 @@ +# Ontological Documentation Checklist + +Systematic checklist for creating comprehensive ontological documentation. + +## Pre-Documentation + +- [ ] **Identify codebase scope** (frontend, backend, or full-stack) +- [ ] **Understand domain** (business concepts, terminology) +- [ ] **Set up documentation tools** (Mermaid, diagramming tools) +- [ ] **Review existing documentation** (READMEs, architecture docs) +- [ ] **Identify stakeholders** (who will use this documentation) + +## Concept Extraction + +### Frontend (TanStack Start) +- [ ] **Database schema extracted** (Drizzle tables, relationships) +- [ ] **Component hierarchy mapped** (React component tree) +- [ ] **Routes documented** (TanStack Router structure) +- [ ] **State management identified** (Context, queries, mutations) +- [ ] **Server functions cataloged** (API surface) + +### Backend (FastAPI) +- [ ] **SQLModel entities documented** (all models) +- [ ] **Relationships mapped** (foreign keys, associations) +- [ ] **Repository pattern documented** (all repositories) +- [ ] **Service layer mapped** (business logic) +- [ ] **API endpoints cataloged** (all routes) +- [ ] **Pydantic schemas listed** (request/response models) + +### Multi-Tenant Patterns +- [ ] **tenant_id fields identified** on all tables +- [ ] **RLS policies documented** (row level security) +- [ ] **Tenant isolation verified** in queries +- [ ] **Repository filters documented** (automatic tenant filtering) +- [ ] **Admin vs user access documented** + +## Entity Documentation + +### For Each Entity +- [ ] **Name and purpose** clearly stated +- [ ] **Attributes documented** (all fields with types) +- [ ] **Relationships documented** (to other entities) +- [ ] **Constraints documented** (unique, required, validation) +- [ ] **Business rules noted** (validation, lifecycle) + +### Database Entities +- [ ] **Table name** documented +- [ ] **Primary key** identified +- [ ] **Foreign keys** documented +- [ ] **Indexes** listed +- [ ] **Timestamps** (created_at, updated_at) +- [ ] **Tenant isolation** (tenant_id field) + +## Relationship Mapping + +### Types of Relationships +- [ ] **One-to-One** relationships documented +- [ ] **One-to-Many** relationships documented +- [ ] **Many-to-Many** relationships documented +- [ ] **Join tables** identified (for many-to-many) +- [ ] **Cascade behavior** documented (delete, update) + +### Relationship Documentation +- [ ] **Source entity** identified +- [ ] **Target entity** identified +- [ ] **Relationship name** clear and descriptive +- [ ] **Cardinality** specified +- [ ] **Business meaning** explained + +## Architecture Documentation + +### System Components +- [ ] **Frontend components** listed and categorized +- [ ] **Backend services** documented +- [ ] **Database** structure documented +- [ ] **External services** identified (Stripe, Resend, etc.) +- [ ] **Authentication system** documented (Better-auth) + +### Data Flow +- [ ] **User actions** → **Frontend** flow documented +- [ ] **Frontend** → **Backend** API calls documented +- [ ] **Backend** → **Database** queries documented +- [ ] **Backend** → **External services** documented +- [ ] **Response flow** back to user documented + +## Visualization + +### Diagrams Created +- [ ] **Entity-Relationship Diagram** (ERD) for database +- [ ] **Component Hierarchy** for React components +- [ ] **Architecture Overview** showing all systems +- [ ] **Data Flow Diagrams** for critical paths +- [ ] **Multi-Tenant Isolation** diagram + +### Diagram Quality +- [ ] **Clear labels** on all elements +- [ ] **Legend provided** (symbols explained) +- [ ] **Color coding** used effectively +- [ ] **Readable font size** and layout +- [ ] **Diagrams source-controlled** (Mermaid or PlantUML) + +## Domain Model + +### Business Concepts +- [ ] **Core domain entities** identified +- [ ] **Business processes** documented +- [ ] **Business rules** captured +- [ ] **Domain terminology** defined +- [ ] **Invariants** documented + +### Semantic Relationships +- [ ] **"Is-a" relationships** (inheritance) +- [ ] **"Has-a" relationships** (composition) +- [ ] **"Uses" relationships** (dependencies) +- [ ] **Aggregation** relationships +- [ ] **Association** relationships + +## Grey Haven Specific + +### Multi-Tenant Architecture +- [ ] **Tenant model** documented +- [ ] **Organization model** documented +- [ ] **User-Tenant relationship** explained +- [ ] **Team structure** documented (if applicable) +- [ ] **RLS roles** explained (admin, authenticated, anon) + +### Authentication & Authorization +- [ ] **Better-auth integration** documented +- [ ] **Session management** explained +- [ ] **User roles** documented +- [ ] **Permission model** explained +- [ ] **OAuth providers** listed + +### Database Conventions +- [ ] **Naming conventions** documented (snake_case) +- [ ] **UUID usage** explained (primary keys) +- [ ] **Timestamp fields** standardized +- [ ] **Soft deletes** documented (if used) +- [ ] **Audit fields** documented (if used) + +## Documentation Quality + +### Completeness +- [ ] **All entities** documented +- [ ] **All relationships** documented +- [ ] **All business rules** captured +- [ ] **All external integrations** noted +- [ ] **All deployment architecture** documented + +### Clarity +- [ ] **Technical jargon** explained +- [ ] **Domain terminology** consistent +- [ ] **Examples provided** where helpful +- [ ] **Diagrams clear** and readable +- [ ] **Navigation easy** (links, TOC) + +### Maintainability +- [ ] **Documentation source-controlled** (with code) +- [ ] **Update process** defined +- [ ] **Ownership** assigned (who maintains) +- [ ] **Review schedule** established +- [ ] **Feedback mechanism** in place + +## Automation + +### Scripts Used +- [ ] **extract_concepts.py** run successfully +- [ ] **generate_ontology_diagram.py** produced diagrams +- [ ] **Output reviewed** and verified +- [ ] **Customizations documented** +- [ ] **Scripts committed** to repository + +### Continuous Documentation +- [ ] **Documentation updates** in PR checklist +- [ ] **Schema changes** trigger doc updates +- [ ] **API changes** trigger doc updates +- [ ] **CI checks** for documentation completeness + +## Stakeholder Review + +### Technical Review +- [ ] **Developers reviewed** documentation +- [ ] **Technical accuracy** verified +- [ ] **Missing information** identified +- [ ] **Feedback incorporated** + +### Business Review +- [ ] **Domain experts reviewed** business concepts +- [ ] **Business terminology** verified +- [ ] **Business rules** confirmed +- [ ] **Use cases validated** + +## Deployment + +### Documentation Delivery +- [ ] **Documentation committed** to repository +- [ ] **README updated** with links +- [ ] **Wiki/Confluence** updated (if used) +- [ ] **Team notified** of new documentation +- [ ] **Onboarding materials** updated + +### Accessibility +- [ ] **Documentation discoverable** (easy to find) +- [ ] **Navigation clear** (links, search) +- [ ] **Formats appropriate** (markdown, diagrams) +- [ ] **Mobile-friendly** (if applicable) + +## Scoring + +- **90+ items checked**: Excellent - Comprehensive documentation ✅ +- **75-89 items**: Good - Most areas covered ⚠️ +- **60-74 items**: Fair - Significant gaps exist 🔴 +- **<60 items**: Poor - Inadequate documentation ❌ + +## Priority Items + +Address these first: +1. **Entity documentation** - Core to understanding +2. **Relationship mapping** - Critical for navigation +3. **Multi-tenant patterns** - Security-critical +4. **Data flow diagrams** - Helps debugging +5. **Automation setup** - Saves time + +## Related Resources + +- [Concept Extraction Guide](../reference/concept_extraction_guide.md) +- [Ontology Patterns](../reference/ontology_patterns.md) +- [Examples](../examples/INDEX.md) +- [Templates](../templates/) +- [Scripts](../scripts/) + +--- + +**Total Items**: 100+ documentation checks +**Critical Items**: Entity docs, Relationships, Multi-tenant, Data flow +**Last Updated**: 2025-11-09 diff --git a/skills/ontological-documentation/examples/INDEX.md b/skills/ontological-documentation/examples/INDEX.md new file mode 100644 index 0000000..8004a92 --- /dev/null +++ b/skills/ontological-documentation/examples/INDEX.md @@ -0,0 +1,69 @@ +# Ontological Documentation Examples + +Real-world examples of creating ontological documentation for Grey Haven systems. + +## Available Examples + +1. **[TanStack Start Ontology](tanstack-start-example.md)** - Frontend codebase analysis + - Extracting concepts from Drizzle schemas + - Mapping React component hierarchies + - Documenting multi-tenant patterns + - Visualizing route structure + +2. **[FastAPI Ontology](fastapi-example.md)** - Backend codebase analysis + - SQLModel entity relationships + - Repository pattern documentation + - Service layer mapping + - API endpoint hierarchy + +3. **[Multi-Tenant System Architecture](multi-tenant-ontology.md)** - Complete system documentation + - Tenant isolation patterns + - RLS policies visualization + - Database schema relationships + - Authentication flow + +4. **[Domain Model Extraction](domain-model-extraction.md)** - Business concept mapping + - Identifying domain entities + - Relationship mapping + - Business rule documentation + - Semantic relationships + +## Recommended Path + +**For new projects:** +1. Start with [domain-model-extraction.md](domain-model-extraction.md) +2. Document frontend with [tanstack-start-example.md](tanstack-start-example.md) +3. Document backend with [fastapi-example.md](fastapi-example.md) +4. Complete system view with [multi-tenant-ontology.md](multi-tenant-ontology.md) + +**For existing systems:** +1. Run extraction scripts on codebase +2. Follow [domain-model-extraction.md](domain-model-extraction.md) to identify concepts +3. Use templates to document findings + +## Quick Reference + +### Frontend Ontology +- See [tanstack-start-example.md](tanstack-start-example.md) +- Use `scripts/extract_concepts.py` for automation + +### Backend Ontology +- See [fastapi-example.md](fastapi-example.md) +- Focus on repository and service patterns + +### Visualization +- See [multi-tenant-ontology.md](multi-tenant-ontology.md) +- Use `scripts/generate_ontology_diagram.py` + +## Related Materials + +- **[Concept Extraction Guide](../reference/concept_extraction_guide.md)** - How to extract concepts +- **[Ontology Patterns](../reference/ontology_patterns.md)** - Common patterns +- **[Templates](../templates/)** - Ready-to-use ontology templates +- **[Scripts](../scripts/)** - Automation scripts + +--- + +**Total Examples**: 4 comprehensive guides +**Coverage**: TanStack Start, FastAPI, Multi-tenant, Domain modeling +**Last Updated**: 2025-11-09 diff --git a/skills/ontological-documentation/examples/ecommerce-ontology.md b/skills/ontological-documentation/examples/ecommerce-ontology.md new file mode 100644 index 0000000..b673435 --- /dev/null +++ b/skills/ontological-documentation/examples/ecommerce-ontology.md @@ -0,0 +1,98 @@ +# E-Commerce Domain Ontology Example + +## Overview +This example demonstrates an ontological documentation approach for a typical e-commerce system. + +## Core Concepts + +### Primary Entities +``` +E-Commerce Domain +├── Customer Management +│ ├── Customer +│ ├── CustomerProfile +│ └── Address +├── Product Management +│ ├── Product +│ ├── Category +│ └── ProductVariant +├── Order Management +│ ├── Order +│ ├── OrderLine +│ └── OrderStatus +└── Payment Management + ├── Payment + ├── PaymentMethod + └── PaymentStatus +``` + +### Key Relationships + +#### Customer Relationships +- **Customer** has-a **CustomerProfile** +- **Customer** has-many **Address** +- **Customer** places-many **Order** +- **Customer** has-many **Payment** + +#### Product Relationships +- **Product** belongs-to **Category** +- **Product** has-many **ProductVariant** +- **ProductVariant** appears-in **OrderLine** + +#### Order Relationships +- **Order** contains-many **OrderLine** +- **Order** has-a **OrderStatus** +- **Order** has-a **Payment** +- **OrderLine** references-a **ProductVariant** + +## Business Rules + +### Customer Rules +- Customer must have at least one address +- Customer profile must include valid email +- Customer can have multiple shipping addresses + +### Order Rules +- Order must have at least one order line +- Order total must equal sum of order line totals +- Order status progression is immutable + +### Payment Rules +- Payment amount must match order total +- Payment method must be valid for customer +- Payment status affects order fulfillment + +## Implementation Examples + +### Order Entity Example +```python +class Order: + def __init__(self, customer: Customer): + self.customer = customer + self.order_lines: List[OrderLine] = [] + self.status = OrderStatus.PENDING + self.created_at = datetime.now() + + def add_product(self, product: ProductVariant, quantity: int): + # Add business logic for adding products + pass + + def calculate_total(self) -> Money: + # Calculate order total + pass +``` + +### Relationship Example +```python +# Order -> Customer relationship +class Order: + def __init__(self, customer: Customer): + self.customer_id = customer.id # Reference relationship + self.customer = customer # Object relationship +``` + +## Documentation Links +- [Customer Documentation](customer-concept.md) +- [Product Documentation](product-concept.md) +- [Order Documentation](order-concept.md) +- [Payment Documentation](payment-concept.md) diff --git a/skills/ontological-documentation/reference/INDEX.md b/skills/ontological-documentation/reference/INDEX.md new file mode 100644 index 0000000..04030ea --- /dev/null +++ b/skills/ontological-documentation/reference/INDEX.md @@ -0,0 +1,43 @@ +# Ontological Documentation Reference + +Technical reference for creating ontological documentation of Grey Haven systems. + +## Reference Materials + +1. **[Concept Extraction Guide](concept_extraction_guide.md)** - How to extract concepts from code + - TanStack Start extraction patterns + - FastAPI extraction patterns + - Identifying entities and relationships + - Semantic analysis techniques + +2. **[Ontology Patterns](ontology_patterns.md)** - Common Grey Haven patterns + - Multi-tenant patterns + - Repository pattern + - Service layer patterns + - Authentication patterns + - Database conventions + +3. **[Documentation Templates](documentation_templates.md)** - Template formats + - Entity documentation + - Relationship diagrams + - Architecture overviews + - Domain model templates + +4. **[Visualization Techniques](visualization-techniques.md)** - Diagram creation + - Mermaid diagrams + - Entity-relationship diagrams + - Component hierarchies + - Data flow diagrams + +## Quick Links + +- For examples: See [examples/](../examples/INDEX.md) +- For templates: See [templates/](../templates/) +- For scripts: See [scripts/](../scripts/) +- For checklists: See [checklists/](../checklists/) + +--- + +**Coverage**: Concept extraction, Patterns, Templates, Visualization +**Platforms**: TanStack Start, FastAPI +**Last Updated**: 2025-11-09 diff --git a/skills/ontological-documentation/reference/README.md b/skills/ontological-documentation/reference/README.md new file mode 100644 index 0000000..9c4d5af --- /dev/null +++ b/skills/ontological-documentation/reference/README.md @@ -0,0 +1,55 @@ +# Reference Guides + +This directory contains comprehensive reference guides for ontological documentation. + +## Files + +### concept_extraction_guide.md +Methodologies and techniques for identifying and extracting domain concepts, entities, and relationships from software codebases. + +**Topics covered:** +- Static code analysis techniques +- Naming convention analysis +- Data structure analysis +- Configuration and metadata analysis +- Language-specific extraction patterns (Python, JavaScript/TypeScript, Java) +- Concept categorization framework +- Relationship identification +- Extraction workflow +- Quality assurance + +### documentation_templates.md +Standardized templates for creating comprehensive ontological documentation. + +**Templates included:** +- Concept Definition Template +- Relationship Documentation Template +- Domain Model Overview Template +- Ontology Change Log Template +- API Ontology Template +- Database Schema Ontology Template +- Event-Driven Architecture Ontology Template + +### ontology_patterns.md +Common ontological patterns and taxonomies found in software systems. + +**Patterns covered:** +- Fundamental relationship types (Is-A, Part-Of, Instance-Of, Depends-On, Associates-With) +- Layered Architecture Pattern +- Domain-Driven Design Pattern +- MVC Pattern Ontology +- Microservices Pattern Ontology +- Taxonomy classification systems +- Ontology validation rules + +## Usage + +These guides are designed to be used together: + +1. Start with **ontology_patterns.md** to understand common patterns +2. Use **concept_extraction_guide.md** to extract concepts from your codebase +3. Apply **documentation_templates.md** to document your findings + +## Integration with Main Skill + +These references support the main [SKILL.md](../SKILL.md) and are automatically available when the ontological documentation skill is activated. diff --git a/skills/ontological-documentation/reference/concept_extraction_guide.md b/skills/ontological-documentation/reference/concept_extraction_guide.md new file mode 100644 index 0000000..a442f1b --- /dev/null +++ b/skills/ontological-documentation/reference/concept_extraction_guide.md @@ -0,0 +1,271 @@ +# Concept Extraction Guide for Software Systems + +This guide provides methodologies and techniques for identifying and extracting domain concepts, entities, and relationships from software codebases to build ontological documentation. + +## Extraction Methodologies + +### 1. Static Code Analysis + +#### Class and Interface Analysis +- **Objective**: Identify conceptual entities and their hierarchies +- **Sources**: Class definitions, interface declarations, type annotations +- **Techniques**: + - Parse AST (Abstract Syntax Trees) to find type definitions + - Extract inheritance relationships (extends, implements) + - Identify composition patterns through member variables + - Analyze method signatures for behavioral concepts + +#### Function and Method Analysis +- **Objective**: Discover actions, processes, and behavioral concepts +- **Sources**: Function definitions, method declarations +- **Techniques**: + - Group related functions into conceptual categories + - Identify command/query patterns (CQRS) + - Extract business process flows from method call chains + - Map function parameters to conceptual relationships + +#### Import and Dependency Analysis +- **Objective**: Understand system boundaries and external dependencies +- **Sources**: Import statements, package dependencies, service calls +- **Techniques**: + - Map module dependencies to conceptual relationships + - Identify external system boundaries + - Categorize dependencies (internal, external, third-party) + - Analyze dependency graphs for architectural insights + +### 2. Naming Convention Analysis + +#### Semantic Naming Patterns +- **Entity Nouns**: User, Order, Product, Account (domain objects) +- **Process Verbs**: ProcessPayment, ValidateInput, SendEmail (actions) +- **State Adjectives**: Active, Pending, Completed, Expired (states) +- **Role-based Names**: AdminService, UserGateway, PaymentProcessor (roles) + +#### Naming Pattern Recognition +``` +Entity + Pattern = Concept Type +- User + Repository = Data Access Concept +- Order + Service = Business Logic Concept +- Payment + Gateway = Integration Concept +- Notification + Event = Event Concept +``` + +### 3. Data Structure Analysis + +#### Database Schema Analysis +- **Tables as Entities**: Each table represents a domain concept +- **Foreign Keys as Relationships**: FKs define relationships between concepts +- **Indexes as Properties**: Important attributes for concept identification +- **Constraints as Rules**: Business rules and validation logic + +#### API Contract Analysis +- **REST Resources**: URL paths often map to domain concepts +- **GraphQL Types**: Schema types define conceptual models +- **Message Schemas**: Event/message structures reveal concepts +- **OpenAPI Specifications**: Complete conceptual model of external interface + +### 4. Configuration and Metadata Analysis + +#### Configuration Files +- **Application Settings**: System behavior concepts +- **Feature Flags**: Feature-based concept organization +- **Environment Variables**: Deployment and environment concepts +- **Routing Tables**: Navigation and flow concepts + +#### Documentation and Comments +- **README Files**: High-level conceptual overview +- **Code Comments**: Designer intent and conceptual explanations +- **API Documentation**: External conceptual contracts +- **Architecture Diagrams**: Visual conceptual relationships + +## Extraction Techniques by Language + +### Python +```python +# Key patterns to identify: +class UserService: # Service concept + def __init__(self, user_repo): # Dependency relationship + self.user_repo = user_repo + + def create_user(self, user_dto): # Action concept + # Domain logic here + pass + +# Look for: +# - Class definitions (entities, services, repositories) +# - Method names (actions, processes) +# - Parameter types (relationships) +# - Decorators (cross-cutting concerns) +``` + +### JavaScript/TypeScript +```typescript +// Key patterns to identify: +interface User { # Entity concept + id: string; + name: string; +} + +class UserService { # Service concept + constructor(private userRepo: UserRepository) {} # Dependency + + async createUser(userData: CreateUserDto): Promise { # Action + types + // Implementation + } +} + +// Look for: +# - Interface definitions (contracts, entities) +# - Class definitions (services, controllers) +# - Type annotations (concept properties) +# - Decorators (metadata, concerns) +``` + +### Java +```java +// Key patterns to identify: +@Entity # Entity annotation +public class User { # Entity concept + @Id + private Long id; + + @OneToMany # Relationship annotation + private List orders; +} + +@Service # Service annotation +public class UserService { # Service concept + @Autowired # Dependency injection + private UserRepository userRepo; + + public User createUser(UserDto userDto) { // Action + type + // Implementation + } +} + +// Look for: +# - Annotations (component types, relationships) +# - Class definitions (entities, services) +# - Interface definitions (contracts) +# - Method signatures (actions, processes) +``` + +## Concept Categorization Framework + +### Primary Categories + +1. **Domain Entities** (Nouns) + - Core business objects: User, Order, Product, Account + - Usually persistent, have identity + - Contain business logic and state + +2. **Value Objects** (Nouns) + - Immutable concepts without identity: Address, Money, DateRange + - Defined by their attributes + - Often embedded in entities + +3. **Services** (Verb + Noun) + - Business logic coordinators: UserService, PaymentService + - Stateless operations + - Orchestrate domain objects + +4. **Repositories** (Noun + Repository/Store) + - Data access abstractions: UserRepository, OrderRepository + - Collection-like interfaces + - Hide storage details + +5. **Controllers/Handlers** (Noun + Controller/Handler) + - Request/response coordination: UserController, OrderController + - Interface between external world and domain + - Thin layer, delegate to services + +### Secondary Categories + +6. **Events/Notifications** (Past Tense Verbs + Noun) + - State changes: OrderCreated, PaymentProcessed, UserRegistered + - Asynchronous communication + - Decouple system components + +7. **DTOs/Models** (Noun + Dto/Model) + - Data transfer objects: UserDto, OrderModel + - External contract representations + - No business logic + +8. **Utilities/Helpers** (Adjective/Noun + Utility/Helper) + - Cross-cutting functionality: ValidationHelper, EmailUtility + - Reusable operations + - No domain concepts + +## Relationship Identification + +### Direct Relationships +- **Inheritance**: `class Admin extends User` (Is-A) +- **Composition**: `class Order { private List lines; }` (Part-Of) +- **Dependency**: `UserService(UserRepository repo)` (Depends-On) + +### Indirect Relationships +- **Shared Interfaces**: Implement same interface (Associates-With) +- **Common Patterns**: Similar naming or structure (Similar-To) +- **Event Connections**: Producer-consumer patterns (Communicates-With) + +### Semantic Relationships +- **Temporal**: CreatedBefore, UpdatedAfter +- **Spatial**: Contains, LocatedWithin +- **Causal**: Triggers, Enables, Prevents +- **Logical**: Implies, Contradicts, Equivalent + +## Extraction Workflow + +### Phase 1: Automated Extraction +1. Run static analysis tools to identify: + - Class/interface definitions + - Inheritance hierarchies + - Import dependencies + - Method signatures + +### Phase 2: Manual Analysis +1. Review automated results for semantic accuracy +2. Identify implicit concepts not captured by code +3. Map business terminology to technical concepts +4. Validate relationships with domain experts + +### Phase 3: Ontology Construction +1. Organize concepts into hierarchies +2. Define relationships between concepts +3. Add semantic metadata and descriptions +4. Validate completeness and consistency + +### Phase 4: Documentation Generation +1. Create visual representations +2. Generate textual documentation +3. Create interactive navigation +4. Establish maintenance processes + +## Quality Assurance + +### Validation Checks +- [ ] All identified concepts have clear definitions +- [ ] Relationships are correctly classified +- [ ] No circular inheritance exists +- [ ] Domain terminology is consistent +- [ ] Technical and business concepts are aligned + +### Review Process +1. **Developer Review**: Technical accuracy and completeness +2. **Domain Expert Review**: Business concept validation +3. **Architecture Review**: Consistency with system design +4. **Documentation Review**: Clarity and usability + +## Maintenance Strategies + +### Continuous Updates +- Monitor code changes for new concepts +- Update ontology when requirements evolve +- Regular reviews with stakeholders +- Automated validation checks + +### Version Management +- Tag ontology versions with releases +- Track concept evolution over time +- Maintain change logs +- Backward compatibility considerations diff --git a/skills/ontological-documentation/reference/documentation_templates.md b/skills/ontological-documentation/reference/documentation_templates.md new file mode 100644 index 0000000..9961d74 --- /dev/null +++ b/skills/ontological-documentation/reference/documentation_templates.md @@ -0,0 +1,447 @@ +# Ontological Documentation Templates + +This document provides standardized templates for creating comprehensive ontological documentation for software systems. + +## Core Documentation Templates + +### 1. Concept Definition Template + +```markdown +# [Concept Name] + +## Quick Reference +- **Type**: [Entity/Value Object/Service/Repository/etc.] +- **Category**: [Domain/Business/Infrastructure/etc.] +- **Status**: [Active/Deprecated/Experimental] +- **Owner**: [Team/Person responsible] + +## Definition +[Clear, concise definition of the concept. What is it? What purpose does it serve?] + +## Purpose and Scope +**Why this concept exists:** +- [Problem it solves] +- [Business requirement it addresses] +- [Technical necessity] + +**Scope and Boundaries:** +- [What's included] +- [What's excluded] +- [Related but separate concepts] + +## Characteristics +### Essential Properties +- **Property 1**: [Description] - [Type] - [Constraints] +- **Property 2**: [Description] - [Type] - [Constraints] + +### Behavioral Aspects +- **Action 1**: [Description] - [Preconditions] - [Postconditions] +- **Action 2**: [Description] - [Preconditions] - [Postconditions] + +### Constraints and Rules +- [Business rule 1] +- [Validation rule 2] +- [Integrity constraint 3] + +## Relationships + +### Hierarchical Relationships +- **Is-A**: [Parent Concept] - [Rationale] +- **Has-A**: [Child Components] - [Composition details] + +### Dependency Relationships +- **Depends-On**: [Required Concept] - [Dependency type] +- **Required-By**: [Dependent Concept] - [Usage context] + +### Association Relationships +- **Associates-With**: [Related Concept] - [Nature of association] +- **Similar-To**: [Analogous Concept] - [Comparison points] + +## Implementation + +### Code Representation +```python +# Example implementation +class [ConceptName]: + def __init__(self): + self.property1 = None + self.property2 = None +``` + +### Data Structure +- **Storage Format**: [Database table, JSON, etc.] +- **Serialization**: [How it's represented in API/transport] +- **Persistence**: [Where and how it's stored] + +### Lifecycle +- **Creation**: [How instances are created] +- **Evolution**: [How instances change over time] +- **Deletion**: [How instances are removed] + +## Examples + +### Concrete Examples +1. **Example 1**: [Specific instance with explanation] + - [Context] + - [Properties] + - [Behavior] + +2. **Example 2**: [Another specific instance] + - [Context] + - [Properties] + - [Behavior] + +### Usage Patterns +- **Pattern 1**: [Common usage scenario] +- **Pattern 2**: [Another usage scenario] + +## Evolution and History +- **Created**: [Date] - [Initial reason] +- **Major Changes**: [Change history] +- **Future Roadmap**: [Planned modifications] + +## Related Documentation +- [Link to related concepts] +- [Link to implementation details] +- [Link to API documentation] +- [Link to user documentation] +``` + +### 2. Relationship Documentation Template + +```markdown +# [Relationship Type]: [Source Concept] → [Target Concept] + +## Relationship Overview +- **Type**: [Is-A/Part-Of/Depends-On/Associates-With/etc.] +- **Source**: [Source Concept Name] +- **Target**: [Target Concept Name] +- **Strength**: [Strong/Medium/Weak] +- **Direction**: [Unidirectional/Bidirectional] + +## Definition +[Clear explanation of what this relationship means in the domain context] + +## Rationale +**Why this relationship exists:** +- [Business reason] +- [Technical necessity] +- [Domain modeling decision] + +## Characteristics + +### Cardinality +- **Source → Target**: [One-to-One/One-to-Many/Many-to-Many] +- **Minimum**: [Required/Optional - specify minimum] +- **Maximum**: [Unbounded/Specific limit] + +### Constraints +- **Existence Constraint**: [Rules about when relationship can exist] +- **Deletion Constraint**: [What happens when one end is deleted] +- **Update Constraint**: [How relationship changes are handled] + +### Semantic Properties +- **Transitivity**: [Whether relationship is transitive] +- **Symmetry**: [Whether relationship is symmetric] +- **Reflexivity**: [Whether relationship is reflexive] + +## Implementation + +### Code Representation +```python +# Example implementation +class SourceConcept: + def __init__(self): + self.target_concepts = [] # Relationship implementation +``` + +### Data Modeling +- **Foreign Keys**: [How relationship is stored in database] +- **Join Tables**: [If applicable, for many-to-many relationships] +- **Indexing**: [Performance considerations] + +### API Representation +- **REST Endpoints**: [How relationship is exposed in API] +- **GraphQL Schema**: [How relationship appears in GraphQL] +- **Serialization**: [How relationship is represented in JSON/XML] + +## Examples + +### Example Instances +1. **Example 1**: [Specific relationship instance] + - **Source Instance**: [Details] + - **Target Instance**: [Details] + - **Context**: [When and why this exists] + +2. **Example 2**: [Another specific instance] + - **Source Instance**: [Details] + - **Target Instance**: [Details] + - **Context**: [When and why this exists] + +### Usage Patterns +- **Creation Pattern**: [How relationships are established] +- **Query Pattern**: [How relationships are accessed] +- **Modification Pattern**: [How relationships are changed] + +## Validation Rules + +### Business Rules +- [Rule 1]: [Description and validation logic] +- [Rule 2]: [Description and validation logic] + +### Technical Constraints +- [Constraint 1]: [Technical limitation or requirement] +- [Constraint 2]: [Performance or scalability consideration] + +## Related Documentation +- [Source Concept Documentation] +- [Target Concept Documentation] +- [Related Relationships] +- [Implementation Details] +``` + +### 3. Domain Model Overview Template + +```markdown +# [Domain Name] Domain Model + +## Executive Summary +[Brief overview of the domain and its core concepts] + +## Core Concepts Map +[Visual representation or hierarchical list of main concepts] + +### Primary Entities +- **[Entity 1]**: [Brief description] +- **[Entity 2]**: [Brief description] +- **[Entity 3]**: [Brief description] + +### Supporting Concepts +- **[Value Object 1]**: [Brief description] +- **[Service 1]**: [Brief description] +- **[Repository 1]**: [Brief description] + +## Concept Hierarchy + +``` +[Top-level concepts] +├── [Category 1] +│ ├── [Sub-concept 1.1] +│ ├── [Sub-concept 1.2] +│ └── [Sub-concept 1.3] +├── [Category 2] +│ ├── [Sub-concept 2.1] +│ └── [Sub-concept 2.2] +└── [Category 3] + ├── [Sub-concept 3.1] + └── [Sub-concept 3.2] +``` + +## Key Relationships + +### Critical Relationships +1. **[Relationship 1]**: [Source] → [Target] + - [Importance and impact] + - [Business significance] + +2. **[Relationship 2]**: [Source] → [Target] + - [Importance and impact] + - [Business significance] + +### Relationship Patterns +- **Composition Pattern**: [Description] +- **Dependency Pattern**: [Description] +- **Association Pattern**: [Description] + +## Business Rules and Constraints + +### Domain Rules +1. **[Rule 1]**: [Description] - [Impact] +2. **[Rule 2]**: [Description] - [Impact] + +### Invariants +- **Invariant 1**: [What must always be true] +- **Invariant 2**: [What must always be true] + +## Processes and Workflows + +### Core Business Processes +1. **[Process 1]** + - **Trigger**: [What starts the process] + - **Steps**: [Sequence of actions] + - **Actors**: [Who/what participates] + - **Outcomes**: [Results and side effects] + +2. **[Process 2]** + - **Trigger**: [What starts the process] + - **Steps**: [Sequence of actions] + - **Actors**: [Who/what participates] + - **Outcomes**: [Results and side effects] + +### State Machines +- **[Entity 1] States**: [State transitions and conditions] +- **[Entity 2] States**: [State transitions and conditions] + +## Integration Points + +### External Systems +- **[System 1]**: [Integration type and purpose] +- **[System 2]**: [Integration type and purpose] + +### Data Flows +- **Inbound Data**: [What data comes from where] +- **Outbound Data**: [What data goes to where] + +## Evolution Strategy + +### Current State +- [Description of current domain model state] + +### Planned Changes +- **[Change 1]**: [Description and timeline] +- **[Change 2]**: [Description and timeline] + +### Migration Strategy +- [How to transition from current to future state] + +## Quality Metrics + +### Model Health +- **Complexity**: [Assessment of model complexity] +- **Consistency**: [How consistent the model is] +- **Completeness**: [Gaps and coverage] + +### Usage Metrics +- **Most Used Concepts**: [Statistics on concept usage] +- **Relationship Density**: [How interconnected concepts are] +- **Change Frequency**: [How often concepts change] + +## Related Documentation +- [Link to detailed concept documentation] +- [Link to API documentation] +- [Link to business requirements] +- [Link to technical architecture] +``` + +### 4. Ontology Change Log Template + +```markdown +# Ontology Change Log + +## Version [Version Number] - [Date] + +### Summary +[Brief overview of changes in this version] + +### Added Concepts +- **[Concept Name]**: [Reason for addition] - [Impact] +- **[Concept Name]**: [Reason for addition] - [Impact] + +### Modified Concepts +- **[Concept Name]**: [Changes made] - [Reason for change] - [Impact] +- **[Concept Name]**: [Changes made] - [Reason for change] - [Impact] + +### Removed Concepts +- **[Concept Name]**: [Reason for removal] - [Migration strategy] + +### Added Relationships +- **[Relationship]**: [Source] → [Target] - [Reason] + +### Modified Relationships +- **[Relationship]**: [Source] → [Target] - [Changes] - [Reason] + +### Removed Relationships +- **[Relationship]**: [Source] → [Target] - [Reason] - [Impact] + +### Breaking Changes +- **[Change Description]**: [Impact on dependent systems] - [Migration required] + +### Migration Guide +[Step-by-step guide for adapting to these changes] + +## Previous Versions +[Link to previous change logs] +``` + +## Specialized Templates + +### API Ontology Template +```markdown +# [API Name] Ontology + +## Resource Model +[List of all resources and their relationships] + +## Semantic Operations +[CRUD operations and their domain meanings] + +## Media Types +[Content types and their semantic significance] + +## Hypermedia Controls +[HATEOAS relationships and link semantics] +``` + +### Database Schema Ontology Template +```markdown +# [Database Name] Schema Ontology + +## Table Concepts +[Tables as domain concepts] + +## Column Semantics +[Columns as concept properties] + +## Constraint Logic +[Constraints as business rules] + +## Trigger Semantics +[Triggers as automated processes] +``` + +### Event-Driven Architecture Ontology Template +```markdown +# [System Name] Event Ontology + +## Event Taxonomy +[Categorization of all events] + +## Event Relationships +[Causal and temporal relationships] + +## Event Semantics +[Meaning and significance of events] + +## Process Integration +[How events drive business processes] +``` + +## Template Usage Guidelines + +### When to Use Each Template + +1. **Concept Definition**: Use for every significant domain concept +2. **Relationship Documentation**: Use for critical or complex relationships +3. **Domain Model Overview**: Use for bounded contexts or major domains +4. **Change Log**: Use for every ontology modification +5. **Specialized Templates**: Use for specific architectural patterns + +### Quality Checklist + +For each documentation entry: +- [ ] Definition is clear and unambiguous +- [ ] Purpose and scope are well-defined +- [ ] Relationships are accurately described +- [ ] Examples are relevant and illustrative +- [ ] Implementation details are correct +- [ ] Related documentation is linked +- [ ] Review date is recorded +- [ ] Owner is identified + +### Review Process + +1. **Self-Review**: Check completeness and accuracy +2. **Peer Review**: Get feedback from other developers +3. **Domain Expert Review**: Validate with domain experts +4. **Architecture Review**: Ensure alignment with system architecture +5. **Documentation Review**: Check for clarity and usability diff --git a/skills/ontological-documentation/reference/ontology_patterns.md b/skills/ontological-documentation/reference/ontology_patterns.md new file mode 100644 index 0000000..c1242f1 --- /dev/null +++ b/skills/ontological-documentation/reference/ontology_patterns.md @@ -0,0 +1,230 @@ +# Ontological Patterns in Software Documentation + +This document describes common ontological patterns and taxonomies found in software systems for creating effective conceptual documentation. + +## Core Ontological Concepts + +### Fundamental Relationship Types + +**Is-A (Inheritance/Hyponymy)** +- Description: A concept is a subtype or specialization of another concept +- Example: `User` is-a `Person`, `Manager` is-a `Employee` +- Code pattern: Class inheritance, interface implementation +- Documentation: Use "extends," "inherits from," "is a type of" + +**Part-Of (Mereology/Composition)** +- Description: A concept is a component or constituent of another concept +- Example: `Wheel` is-part-of `Car`, `Method` is-part-of `Class` +- Code pattern: Object composition, nested classes, containment relationships +- Documentation: Use "contains," "comprises," "consists of" + +**Instance-Of (Instantiation)** +- Description: An object is an instance of a concept/class +- Example: `john_doe` is-instance-of `User`, `order_123` is-instance-of `Order` +- Code pattern: Object creation, variable assignment +- Documentation: Use "instance of," "example of," "specific case of" + +**Depends-On (Dependency)** +- Description: A concept requires or relies on another concept +- Example: `OrderService` depends-on `PaymentGateway`, `Controller` depends-on `Service` +- Code pattern: Import statements, dependency injection, method calls +- Documentation: Use "requires," "uses," "relies on" + +**Associates-With (Association)** +- Description: A concept has a loose semantic connection to another concept +- Example: `User` associates-with `Order`, `Product` associates-with `Category` +- Code pattern: Foreign keys, bidirectional references, event subscriptions +- Documentation: Use "related to," "connected with," "associated with" + +## Common Software Ontology Patterns + +### Layered Architecture Pattern + +``` +Presentation Layer +├── Controllers +├── Views +└── ViewModels + +Business Logic Layer +├── Services +├── Domain Models +└── Business Rules + +Data Access Layer +├── Repositories +├── Data Mappers +└── Database Models + +Infrastructure Layer +├── External APIs +├── Message Queues +└── File Storage +``` + +**Relationships:** +- Controllers depend-on Services +- Services depend-on Repositories +- Repositories depend-on Database Models +- ViewModels part-of Presentation Layer + +### Domain-Driven Design Pattern + +**Entities**: Objects with distinct identity +- `Customer`, `Order`, `Product` + +**Value Objects**: Objects defined by their attributes +- `Address`, `Money`, `DateRange` + +**Aggregates**: Clusters of domain objects +- `OrderAggregate` contains `Order`, `OrderLine`, `OrderStatus` + +**Repositories**: Collections of aggregate roots +- `CustomerRepository`, `OrderRepository` + +**Domain Services**: Business logic that doesn't fit in entities +- `PaymentService`, `ShippingService` + +### MVC Pattern Ontology + +``` +Model +├── Entity Models +├── View Models +└── Data Transfer Objects + +View +├── Templates +├── Components +└── Layouts + +Controller +├── Action Methods +├── Route Handlers +└── API Endpoints +``` + +**Relationships:** +- Controllers manipulate Models +- Controllers select Views +- Views display Models +- Models notify Views of changes + +### Microservices Pattern Ontology + +**Service Categories:** +- **API Gateway**: Entry point for external requests +- **Core Services**: Business logic services +- **Supporting Services**: Shared functionality (auth, logging) +- **Data Services**: Database operations + +**Service Relationships:** +- `API Gateway` routes-to `Core Services` +- `Core Services` call `Supporting Services` +- `Services` publish-to `Message Broker` +- `Services` read-from `Configuration Service` + +## Taxonomy Classification Systems + +### Functional Classification + +**By Purpose:** +- Business Logic Components +- Data Management Components +- Presentation Components +- Integration Components +- Infrastructure Components + +**By Lifecycle:** +- Singleton Components +- Scoped Components +- Transient Components +- Request-scoped Components + +### Structural Classification + +**By Abstraction Level:** +- Interface Layer (highest abstraction) +- Service Layer +- Repository Layer +- Data Layer (lowest abstraction) + +**By Coupling:** +- Tightly Coupled Components +- Loosely Coupled Components +- Decoupled Components + +## Documentation Templates + +### Concept Documentation Template + +```markdown +# [Concept Name] + +## Definition +Brief, clear definition of the concept + +## Purpose +Why this concept exists and what problem it solves + +## Characteristics +Key attributes and properties of the concept + +## Relationships +- **Is-A**: [Parent concepts] +- **Part-Of**: [Containing concepts] +- **Depends-On**: [Required concepts] +- **Associates-With**: [Related concepts] + +## Examples +Concrete examples of the concept in use + +## Implementation Notes +Technical implementation details and considerations +``` + +### Relationship Documentation Template + +```markdown +# [Relationship Type]: [Source] → [Target] + +## Relationship Description +Description of the semantic relationship + +## Cardinality +- One-to-One, One-to-Many, Many-to-Many +- Required vs Optional + +## Constraints +Rules and limitations on the relationship + +## Implementation +How the relationship is implemented in code + +## Examples +Specific examples of the relationship +``` + +## Ontology Validation Rules + +### Consistency Rules + +1. **No Circular Inheritance**: A class cannot inherit from itself (directly or indirectly) +2. **Complete Relationships**: All referenced concepts must be defined +3. **Consistent Naming**: Use consistent terminology throughout the ontology +4. **Proper Abstraction Levels**: Related concepts should be at similar abstraction levels + +### Completeness Rules + +1. **Defined Concepts**: All concepts must have clear definitions +2. **Documented Relationships**: All relationships should be explicitly documented +3. **Coverage**: Important domain concepts should be represented +4. **Hierarchy**: Concepts should be organized in logical hierarchies + +### Best Practices + +1. **Use Standard Terminology**: Prefer established domain vocabulary +2. **Avoid Ambiguity**: Define terms clearly and consistently +3. **Maintain Separation**: Separate conceptual models from implementation details +4. **Document Rationale**: Explain why certain relationships exist +5. **Regular Review**: Update ontology as system evolves diff --git a/skills/ontological-documentation/scripts/README.md b/skills/ontological-documentation/scripts/README.md new file mode 100644 index 0000000..49023f0 --- /dev/null +++ b/skills/ontological-documentation/scripts/README.md @@ -0,0 +1,114 @@ +# Ontological Documentation Scripts + +This directory contains utility scripts for automated concept extraction and diagram generation. + +## Scripts + +### extract_concepts.py + +Analyzes codebases to extract domain concepts, entities, and relationships for building ontological documentation. + +**Features:** +- Supports Python and JavaScript/TypeScript +- Extracts classes, functions, inheritance relationships, and imports +- Builds ontological structure with relationships (is_a, part_of, depends_on, associates_with) +- Generates JSON ontology representation +- Creates Mermaid diagrams + +**Usage:** +```bash +# Analyze a single file +python extract_concepts.py /path/to/file.py + +# Analyze a directory +python extract_concepts.py /path/to/codebase + +# Save output to file +python extract_concepts.py /path/to/codebase > ontology.json +``` + +**Output:** +- JSON ontology structure +- Mermaid diagram representation + +### generate_ontology_diagram.py + +Generates visual representations of ontologies in various formats. + +**Features:** +- Supports multiple diagram formats (Mermaid, PlantUML, GraphViz DOT, JSON-LD) +- Customizable relationship symbols +- Semantic web compatibility (JSON-LD output) +- Styled diagrams with concept type differentiation + +**Usage:** +```bash +# Generate all formats +python generate_ontology_diagram.py ontology.json + +# Generate specific format +python generate_ontology_diagram.py ontology.json --format mermaid + +# Specify output directory +python generate_ontology_diagram.py ontology.json --output ./diagrams + +# Available formats: mermaid, plantuml, dot, json-ld, all +``` + +**Output Examples:** + +Mermaid: +```bash +python generate_ontology_diagram.py ontology.json --format mermaid +# Creates: ontology_mermaid.md +``` + +PlantUML: +```bash +python generate_ontology_diagram.py ontology.json --format plantuml +# Creates: ontology_plantuml.puml +``` + +GraphViz DOT: +```bash +python generate_ontology_diagram.py ontology.json --format dot +# Creates: ontology.dot +``` + +JSON-LD: +```bash +python generate_ontology_diagram.py ontology.json --format json-ld +# Creates: ontology_jsonld.json +``` + +## Requirements + +Both scripts use only Python standard library modules: +- `ast` - Python AST parsing +- `re` - Regular expressions +- `json` - JSON processing +- `pathlib` - Path handling +- `argparse` - Command-line argument parsing + +No additional dependencies required! + +## Example Workflow + +```bash +# 1. Extract concepts from codebase +python extract_concepts.py /path/to/codebase > ontology.json + +# 2. Generate diagrams +python generate_ontology_diagram.py ontology.json --output ./docs/diagrams + +# 3. Review generated documentation +ls ./docs/diagrams/ +# ontology_mermaid.md +# ontology_plantuml.puml +# ontology.dot +# ontology_jsonld.json +``` + +## Integration with Skill + +These scripts support the main [SKILL.md](../SKILL.md) by providing automated tools for concept extraction and visualization. Use them to bootstrap your ontological documentation process. diff --git a/skills/ontological-documentation/scripts/extract_concepts.py b/skills/ontological-documentation/scripts/extract_concepts.py new file mode 100755 index 0000000..a8c5387 --- /dev/null +++ b/skills/ontological-documentation/scripts/extract_concepts.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 +""" +Concept Extraction Script for Ontological Documentation + +This script analyzes codebases to extract domain concepts, entities, and relationships +for building ontological documentation. It supports multiple programming languages +and can identify inheritance hierarchies, composition patterns, and semantic relationships. +""" + +import ast +import re +import json +import sys +from pathlib import Path +from typing import Dict, List, Set, Tuple, Any +from collections import defaultdict + +class ConceptExtractor: + """Extracts ontological concepts from source code.""" + + def __init__(self): + self.concepts = defaultdict(dict) + self.relationships = defaultdict(list) + self.taxonomies = defaultdict(list) + + def extract_from_python(self, file_path: Path) -> Dict[str, Any]: + """Extract concepts from Python source code.""" + try: + with open(file_path, 'r', encoding='utf-8') as f: + tree = ast.parse(f.read()) + + visitor = ClassVisitor() + visitor.visit(tree) + + return { + 'classes': visitor.classes, + 'inheritance': visitor.inheritance, + 'imports': visitor.imports, + 'functions': visitor.functions + } + except Exception as e: + return {'error': str(e)} + + def extract_from_javascript(self, file_path: Path) -> Dict[str, Any]: + """Extract concepts from JavaScript/TypeScript source code.""" + concepts = { + 'classes': [], + 'interfaces': [], + 'functions': [], + 'imports': [] + } + + try: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Extract class declarations + class_pattern = r'(?:class|interface)\s+(\w+)(?:\s+extends\s+(\w+))?' + for match in re.finditer(class_pattern, content): + class_name = match.group(1) + parent_class = match.group(2) + concepts['classes'].append({ + 'name': class_name, + 'parent': parent_class, + 'type': 'class' if 'class' in match.group(0) else 'interface' + }) + + # Extract function declarations + func_pattern = r'(?:function\s+(\w+)|const\s+(\w+)\s*=\s*(?:\([^)]*\)\s*)?=>|(\w+)\s*:\s*\([^)]*\)\s*=>)' + for match in re.finditer(func_pattern, content): + func_name = match.group(1) or match.group(2) or match.group(3) + if func_name: + concepts['functions'].append({'name': func_name}) + + # Extract imports + import_pattern = r'import\s+.*?from\s+["\']([^"\']+)["\']' + for match in re.finditer(import_pattern, content): + concepts['imports'].append({'source': match.group(1)}) + + except Exception as e: + concepts['error'] = str(e) + + return concepts + + def build_ontology(self, extracted_data: List[Dict[str, Any]]) -> Dict[str, Any]: + """Build ontological structure from extracted data.""" + ontology = { + 'concepts': {}, + 'relationships': { + 'is_a': [], # inheritance + 'part_of': [], # composition + 'depends_on': [], # dependencies + 'associates_with': [] # loose associations + }, + 'taxonomies': {} + } + + all_classes = [] + all_functions = [] + all_imports = [] + + # Collect all entities + for data in extracted_data: + if 'classes' in data: + all_classes.extend(data['classes']) + if 'functions' in data: + all_functions.extend(data['functions']) + if 'imports' in data: + all_imports.extend(data['imports']) + + # Build concepts + for cls in all_classes: + if isinstance(cls, dict): + concept_name = cls.get('name', str(cls)) + ontology['concepts'][concept_name] = { + 'type': 'class', + 'properties': cls + } + + # Build inheritance relationships + parent = cls.get('parent') + if parent: + ontology['relationships']['is_a'].append({ + 'subject': concept_name, + 'object': parent + }) + + for func in all_functions: + if isinstance(func, dict): + func_name = func.get('name', str(func)) + ontology['concepts'][func_name] = { + 'type': 'function', + 'properties': func + } + + return ontology + + def generate_mermaid_diagram(self, ontology: Dict[str, Any]) -> str: + """Generate Mermaid diagram from ontology.""" + lines = ["graph TD"] + + # Add concepts as nodes + for concept_name, concept_data in ontology['concepts'].items(): + concept_type = concept_data.get('type', 'concept') + if concept_type == 'class': + lines.append(f" {concept_name}[({concept_name})]") + else: + lines.append(f" {concept_name}[{concept_name}]") + + # Add relationships + for rel_type, relationships in ontology['relationships'].items(): + for rel in relationships: + subject = rel['subject'] + obj = rel['object'] + + if rel_type == 'is_a': + lines.append(f" {subject} --|> {obj}") + elif rel_type == 'part_of': + lines.append(f" {subject} --* {obj}") + elif rel_type == 'depends_on': + lines.append(f" {subject} -.-> {obj}") + else: + lines.append(f" {subject} --- {obj}") + + return "\n".join(lines) + +class ClassVisitor(ast.NodeVisitor): + """AST visitor for Python class analysis.""" + + def __init__(self): + self.classes = [] + self.inheritance = [] + self.imports = [] + self.functions = [] + + def visit_ClassDef(self, node): + class_info = { + 'name': node.name, + 'bases': [base.id for base in node.bases if hasattr(base, 'id')], + 'methods': [], + 'line_number': node.lineno + } + + for item in node.body: + if isinstance(item, ast.FunctionDef): + class_info['methods'].append({ + 'name': item.name, + 'args': [arg.arg for arg in item.args.args], + 'line_number': item.lineno + }) + + self.classes.append(class_info) + + # Track inheritance + for base in node.bases: + if hasattr(base, 'id'): + self.inheritance.append({ + 'child': node.name, + 'parent': base.id + }) + + self.generic_visit(node) + + def visit_Import(self, node): + for alias in node.names: + self.imports.append({ + 'module': alias.name, + 'alias': alias.asname + }) + self.generic_visit(node) + + def visit_FunctionDef(self, node): + func_info = { + 'name': node.name, + 'args': [arg.arg for arg in node.args.args], + 'line_number': node.lineno + } + self.functions.append(func_info) + self.generic_visit(node) + +def main(): + """Main function to run concept extraction.""" + if len(sys.argv) < 2: + print("Usage: python extract_concepts.py ") + sys.exit(1) + + path = Path(sys.argv[1]) + extractor = ConceptExtractor() + extracted_data = [] + + if path.is_file(): + if path.suffix == '.py': + data = extractor.extract_from_python(path) + extracted_data.append(data) + elif path.suffix in ['.js', '.ts', '.jsx', '.tsx']: + data = extractor.extract_from_javascript(path) + extracted_data.append(data) + elif path.is_dir(): + for file_path in path.rglob('*'): + if file_path.is_file(): + if file_path.suffix == '.py': + data = extractor.extract_from_python(file_path) + extracted_data.append(data) + elif file_path.suffix in ['.js', '.ts', '.jsx', '.tsx']: + data = extractor.extract_from_javascript(file_path) + extracted_data.append(data) + + ontology = extractor.build_ontology(extracted_data) + + # Output as JSON + print(json.dumps(ontology, indent=2)) + + # Also generate Mermaid diagram + diagram = extractor.generate_mermaid_diagram(ontology) + print("\n--- Mermaid Diagram ---") + print(diagram) + +if __name__ == "__main__": + main() diff --git a/skills/ontological-documentation/scripts/generate_ontology_diagram.py b/skills/ontological-documentation/scripts/generate_ontology_diagram.py new file mode 100755 index 0000000..7cfc766 --- /dev/null +++ b/skills/ontological-documentation/scripts/generate_ontology_diagram.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python3 +""" +Ontology Diagram Generator + +This script generates visual representations of ontologies in various formats +including Mermaid, PlantUML, GraphViz DOT, and JSON-LD for semantic web applications. +""" + +import json +import sys +import argparse +import re +from pathlib import Path +from typing import Dict, List, Any, Optional + +class OntologyDiagramGenerator: + """Generates diagrams for ontological documentation.""" + + def __init__(self): + self.relationship_symbols = { + 'is_a': '--|>', # Inheritance + 'part_of': '--*', # Composition + 'depends_on': '-.->', # Dependency + 'associates_with': '---', # Association + 'instance_of': '..>' # Instantiation + } + + def generate_mermaid(self, ontology: Dict[str, Any]) -> str: + """Generate Mermaid diagram from ontology.""" + lines = ["graph TD"] + lines.append(" %% Ontology Diagram") + lines.append(" %% Generated from ontological documentation") + + # Add styling + lines.extend([ + " classDef concept fill:#e1f5fe,stroke:#01579b,stroke-width:2px", + " classDef class fill:#f3e5f5,stroke:#4a148c,stroke-width:2px", + " classDef interface fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px", + " classDef function fill:#fff3e0,stroke:#e65100,stroke-width:2px" + ]) + + # Add concepts as nodes + concept_classes = {} + for concept_name, concept_data in ontology['concepts'].items(): + concept_type = concept_data.get('type', 'concept') + + if concept_type == 'class': + lines.append(f" {self._safe_name(concept_name)}[({concept_name})]") + concept_classes[concept_name] = 'class' + elif concept_type == 'interface': + lines.append(f" {self._safe_name(concept_name)}[{{interface}}{concept_name}]") + concept_classes[concept_name] = 'interface' + elif concept_type == 'function': + lines.append(f" {self._safe_name(concept_name)}[{concept_name}()]") + concept_classes[concept_name] = 'function' + else: + lines.append(f" {self._safe_name(concept_name)}[{concept_name}]") + concept_classes[concept_name] = 'concept' + + # Add relationships + for rel_type, relationships in ontology['relationships'].items(): + symbol = self.relationship_symbols.get(rel_type, '---') + for rel in relationships: + subject = self._safe_name(rel['subject']) + obj = self._safe_name(rel['object']) + label = rel.get('label', rel_type.replace('_', ' ').title()) + lines.append(f" {subject} {symbol} {obj}") + + # Apply classes + for concept_name, css_class in concept_classes.items(): + lines.append(f" class {self._safe_name(concept_name)} {css_class}") + + return "\n".join(lines) + + def generate_plantuml(self, ontology: Dict[str, Any]) -> str: + """Generate PlantUML diagram from ontology.""" + lines = ["@startuml Ontology"] + lines.append("!theme plain") + lines.append("skinparam classAttributeIconSize 0") + + # Add concepts as classes + for concept_name, concept_data in ontology['concepts'].items(): + concept_type = concept_data.get('type', 'concept') + + if concept_type == 'class': + lines.append(f"class {concept_name} {{}}") + elif concept_type == 'interface': + lines.append(f"interface {concept_name} {{}}") + elif concept_type == 'function': + lines.append(f"object {concept_name} {{}}") + else: + lines.append(f"abstract {concept_name} {{}}") + + lines.append("") # Empty line for separation + + # Add relationships + for rel_type, relationships in ontology['relationships'].items(): + for rel in relationships: + subject = rel['subject'] + obj = rel['object'] + + if rel_type == 'is_a': + lines.append(f"{subject} <|-- {obj}") + elif rel_type == 'part_of': + lines.append(f"{subject} *-- {obj}") + elif rel_type == 'depends_on': + lines.append(f"{subject} ..> {obj}") + else: + lines.append(f"{subject} -- {obj}") + + lines.append("@enduml") + return "\n".join(lines) + + def generate_dot(self, ontology: Dict[str, Any]) -> str: + """Generate GraphViz DOT diagram from ontology.""" + lines = ["digraph Ontology {"] + lines.append(' rankdir=TB;') + lines.append(' node [shape=box, style=filled];') + lines.append(' edge [fontsize=10];') + + # Add concepts as nodes + for concept_name, concept_data in ontology['concepts'].items(): + concept_type = concept_data.get('type', 'concept') + + # Set colors based on type + if concept_type == 'class': + color = "lightpurple" + elif concept_type == 'interface': + color = "lightgreen" + elif concept_type == 'function': + color = "lightorange" + else: + color = "lightblue" + + lines.append(f' "{concept_name}" [label="{concept_name}", fillcolor="{color}"];') + + lines.append("") # Empty line for separation + + # Add relationships + for rel_type, relationships in ontology['relationships'].items(): + for rel in relationships: + subject = rel['subject'] + obj = rel['object'] + label = rel.get('label', rel_type.replace('_', ' ').title()) + + # Set arrow styles based on relationship type + if rel_type == 'is_a': + arrow = 'empty' + elif rel_type == 'part_of': + arrow = 'diamond' + elif rel_type == 'depends_on': + arrow = 'dashed' + else: + arrow = 'normal' + + lines.append(f' "{subject}" -> "{obj}" [label="{label}", arrowhead={arrow}];') + + lines.append("}") + return "\n".join(lines) + + def generate_json_ld(self, ontology: Dict[str, Any]) -> Dict[str, Any]: + """Generate JSON-LD representation of ontology.""" + context = { + "@context": { + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "owl": "http://www.w3.org/2002/07/owl#", + "Concept": "rdfs:Class", + "subClassOf": { + "@id": "rdfs:subClassOf", + "@type": "@id" + }, + "partOf": { + "@id": "http://example.org/ontology#partOf", + "@type": "@id" + }, + "dependsOn": { + "@id": "http://example.org/ontology#dependsOn", + "@type": "@id" + } + }, + "@graph": [] + } + + # Add concepts + for concept_name, concept_data in ontology['concepts'].items(): + concept_entry = { + "@id": f"http://example.org/ontology#{concept_name}", + "@type": ["Concept"] + } + + concept_type = concept_data.get('type', 'concept') + if concept_type == 'class': + concept_entry["@type"].append("owl:Class") + elif concept_type == 'interface': + concept_entry["@type"].append("owl:Class") + + context["@graph"].append(concept_entry) + + # Add relationships + for rel_type, relationships in ontology['relationships'].items(): + for rel in relationships: + subject = rel['subject'] + obj = rel['object'] + + if rel_type == 'is_a': + context["@graph"].append({ + "@id": f"http://example.org/ontology#{subject}", + "subClassOf": f"http://example.org/ontology#{obj}" + }) + elif rel_type == 'part_of': + context["@graph"].append({ + "@id": f"http://example.org/ontology#{subject}", + "partOf": f"http://example.org/ontology#{obj}" + }) + elif rel_type == 'depends_on': + context["@graph"].append({ + "@id": f"http://example.org/ontology#{subject}", + "dependsOn": f"http://example.org/ontology#{obj}" + }) + + return context + + def _safe_name(self, name: str) -> str: + """Convert name to safe identifier for diagram formats.""" + # Replace special characters and spaces with underscores + return re.sub(r'[^a-zA-Z0-9_]', '_', name) + +def load_ontology(file_path: Path) -> Dict[str, Any]: + """Load ontology from JSON file.""" + with open(file_path, 'r', encoding='utf-8') as f: + return json.load(f) + +def main(): + """Main function to generate diagrams.""" + parser = argparse.ArgumentParser(description='Generate ontology diagrams') + parser.add_argument('ontology_file', help='Path to ontology JSON file') + parser.add_argument('--format', choices=['mermaid', 'plantuml', 'dot', 'json-ld', 'all'], + default='all', help='Diagram format to generate') + parser.add_argument('--output', help='Output directory (default: current directory)') + args = parser.parse_args() + + ontology_path = Path(args.ontology_file) + if not ontology_path.exists(): + print(f"Error: Ontology file '{ontology_path}' not found") + sys.exit(1) + + ontology = load_ontology(ontology_path) + generator = OntologyDiagramGenerator() + + output_dir = Path(args.output) if args.output else Path('.') + output_dir.mkdir(exist_ok=True) + + formats_to_generate = ['mermaid', 'plantuml', 'dot', 'json-ld'] if args.format == 'all' else [args.format] + + for format_type in formats_to_generate: + if format_type == 'mermaid': + diagram = generator.generate_mermaid(ontology) + output_file = output_dir / f"{ontology_path.stem}_mermaid.md" + with open(output_file, 'w', encoding='utf-8') as f: + f.write(f"# {ontology_path.stem} Ontology Diagram\n\n") + f.write("```mermaid\n") + f.write(diagram) + f.write("\n```") + print(f"Generated Mermaid diagram: {output_file}") + + elif format_type == 'plantuml': + diagram = generator.generate_plantuml(ontology) + output_file = output_dir / f"{ontology_path.stem}_plantuml.puml" + with open(output_file, 'w', encoding='utf-8') as f: + f.write(diagram) + print(f"Generated PlantUML diagram: {output_file}") + + elif format_type == 'dot': + diagram = generator.generate_dot(ontology) + output_file = output_dir / f"{ontology_path.stem}.dot" + with open(output_file, 'w', encoding='utf-8') as f: + f.write(diagram) + print(f"Generated GraphViz DOT diagram: {output_file}") + + elif format_type == 'json-ld': + json_ld = generator.generate_json_ld(ontology) + output_file = output_dir / f"{ontology_path.stem}_jsonld.json" + with open(output_file, 'w', encoding='utf-8') as f: + json.dump(json_ld, f, indent=2) + print(f"Generated JSON-LD: {output_file}") + +if __name__ == "__main__": + main() diff --git a/skills/ontological-documentation/templates/assets-README.md b/skills/ontological-documentation/templates/assets-README.md new file mode 100644 index 0000000..e59758c --- /dev/null +++ b/skills/ontological-documentation/templates/assets-README.md @@ -0,0 +1,79 @@ +# Assets + +This directory contains examples and templates for ontological documentation. + +## Directory Structure + +``` +assets/ +├── examples/ # Real-world examples +│ └── ecommerce-ontology.md +└── ontology-templates/ # Reusable templates + └── domain-ontology.md +``` + +## Examples + +### ecommerce-ontology.md + +A complete example demonstrating ontological documentation for an e-commerce system. + +**Includes:** +- Core concept hierarchy +- Entity relationships +- Business rules +- Implementation examples +- Documentation structure + +**Use this example to:** +- Understand how to structure domain ontologies +- Learn relationship documentation patterns +- See practical implementation examples +- Get inspiration for your own domain models + +## Templates + +### domain-ontology.md + +A ready-to-use template for documenting domain ontologies. + +**Features:** +- Pre-structured sections +- Placeholder content +- Usage instructions +- Hierarchical concept organization + +**How to use:** +1. Copy the template to your project +2. Replace placeholders with your domain information +3. Fill in the concept hierarchy +4. Document relationships +5. Add implementation details + +## Creating Your Own Ontology + +1. **Start with the template**: Copy `ontology-templates/domain-ontology.md` +2. **Review the example**: Study `examples/ecommerce-ontology.md` for structure +3. **Extract concepts**: Use `scripts/extract_concepts.py` on your codebase +4. **Document relationships**: Apply patterns from `references/ontology_patterns.md` +5. **Use standard templates**: Reference `references/documentation_templates.md` +6. **Generate diagrams**: Use `scripts/generate_ontology_diagram.py` + +## Adding New Examples + +When adding new examples: +1. Follow the structure shown in `ecommerce-ontology.md` +2. Include concept hierarchies +3. Document key relationships +4. Provide business rules +5. Add implementation examples +6. Link to related documentation + +## Adding New Templates + +When creating new templates: +1. Use placeholder syntax: `[Placeholder Name]` +2. Include usage instructions +3. Provide inline comments +4. Keep structure clear and minimal +5. Add to this README with description diff --git a/skills/ontological-documentation/templates/domain-ontology.md b/skills/ontological-documentation/templates/domain-ontology.md new file mode 100644 index 0000000..13103b4 --- /dev/null +++ b/skills/ontological-documentation/templates/domain-ontology.md @@ -0,0 +1,35 @@ +# Domain Ontology Template + +## Overview +This template provides a structured approach to documenting domain ontologies for software systems. + +## Domain: [Domain Name] + +### Core Concepts + + +``` +[Domain Name] +├── Primary Entities +│ ├── Entity 1 +│ ├── Entity 2 +│ └── Entity 3 +├── Value Objects +│ ├── Value Object 1 +│ └── Value Object 2 +├── Services +│ ├── Service 1 +│ └── Service 2 +└── Repositories + ├── Repository 1 + └── Repository 2 +``` + +### Relationships + + +## Usage Instructions +1. Replace [Domain Name] with your actual domain +2. Fill in the core concepts hierarchy +3. Document the relationships +4. Add specific concept details using the concept template diff --git a/skills/pr-template/SKILL.md b/skills/pr-template/SKILL.md new file mode 100644 index 0000000..dc4510b --- /dev/null +++ b/skills/pr-template/SKILL.md @@ -0,0 +1,242 @@ +--- +name: grey-haven-pr-template +description: Generate pull request descriptions following Grey Haven Studio standards with clear summary, motivation, implementation details, testing strategy, and comprehensive checklist. Use when creating or reviewing pull requests. +--- + +# Grey Haven Pull Request Template + +Create comprehensive, informative pull request descriptions that help reviewers understand the changes, context, and testing approach. + +## PR Structure (Standard Template) + +Every pull request should follow this structure: + +```markdown +## Summary +[Concise 2-4 sentence description of changes] + +## Motivation +[Why these changes are needed - business value, user impact, problem solved] + +## Implementation Details +[Technical approach, key decisions, trade-offs considered] + +## Testing +[Test strategy: unit/integration/e2e/benchmark, manual testing steps] + +## Checklist +- [ ] Code follows Grey Haven style guidelines (90 char TS, 130 char Python) +- [ ] Type hints added (Python) or types maintained (TypeScript) +- [ ] Tests added/updated (unit, integration, e2e, benchmark) +- [ ] Database migrations tested (up and down) +- [ ] Multi-tenant isolation verified (tenant_id/RLS) +- [ ] Pre-commit hooks passing +- [ ] Documentation updated +- [ ] No breaking changes (or documented with migration guide) +``` + +## Section Guidelines + +### Summary +**Purpose**: Provide a brief, clear overview of what changed. + +**Guidelines**: +- 2-4 sentences maximum +- Focus on **what** changed from user/system perspective +- Reference Linear issues: `GREY-123` +- Mention multi-tenant or RLS changes +- Use present tense + +**Example**: +```markdown +## Summary + +This PR adds magic link authentication using better-auth with email verification. +Users can now sign in via emailed links instead of passwords. Addresses Linear +issue GREY-234 and integrates with multi-tenant RLS policies. +``` + +### Motivation +**Purpose**: Explain **why** these changes are necessary. + +**Guidelines**: +- Describe the problem being solved +- Explain business value or user impact +- Reference Linear issues: `GREY-123` +- Include context not obvious from code +- Mention technical debt or performance concerns + +**Example**: +```markdown +## Motivation + +Users reported frustration with password-based auth (GREY-234). Analytics show +35% abandon signup at password creation. Magic links offer passwordless auth, +better security, reduced support burden, and built-in email verification. + +Aligns with Q1 goal of improving onboarding conversion by 20%. +``` + +### Implementation Details +**Purpose**: Explain **how** the changes work at technical level. + +**Guidelines**: +- Mention Grey Haven tech stack: TanStack Start/Router/Query, FastAPI, Drizzle, SQLModel +- Reference Grey Haven patterns: repository pattern, multi-tenant RLS, server functions +- Note database schema changes (snake_case fields, tenant_id, indexes) +- Explain testing markers: unit, integration, e2e, benchmark +- Include code references with [file:line](file#Lline) format + +**Example**: +```markdown +## Implementation Details + +### Authentication Flow +1. Added magic link server function in [lib/server/functions/auth.ts:45](lib/server/functions/auth.ts#L45) +2. Created email template with Resend integration +3. Implemented token verification route at `/auth/verify` + +### Key Changes +- **Server Functions**: New `sendMagicLink` and `verifyMagicLink` with tenant context +- **Database Schema**: Added `magic_link_tokens` table (snake_case, tenant_id, RLS) +- **Routes**: Magic link verification page with TanStack Router navigation + +### Design Decisions +- Token expiry: 15 minutes (security vs UX balance) +- Single-use tokens with unique constraint +- Tenant isolation via RLS policies +- Email via Resend (better deliverability) +``` + +### Testing +**Purpose**: Describe testing strategy and manual testing steps. + +**Guidelines**: +- List testing markers used: unit, integration, e2e, benchmark +- Include manual testing steps +- Mention test coverage +- Note any edge cases tested + +**Example**: +```markdown +## Testing + +### Automated Tests +- **Unit tests** (18 new): Token generation, validation, expiry logic +- **Integration tests** (6 new): Email sending, database operations +- **E2e tests** (3 new): Full magic link flow with Playwright + +### Manual Testing +1. Request magic link from login page +2. Verify email received within 30 seconds +3. Click link in email +4. Verify successful auth and redirect +5. Test expired token (wait 16 minutes) +6. Test single-use token (use link twice) + +### Coverage +- New code: 94% coverage +- Critical paths: 100% coverage +``` + +## Checklist + +Use this checklist before requesting review: + +- [ ] Code follows Grey Haven style guidelines (90 char TS, 130 char Python) +- [ ] Type hints added (Python) or types maintained (TypeScript) +- [ ] Tests added/updated (unit, integration, e2e, benchmark) +- [ ] Test coverage meets 80% threshold +- [ ] Database migrations tested (up and down) +- [ ] Multi-tenant isolation verified (tenant_id/RLS) +- [ ] Pre-commit hooks passing (Prettier, Ruff, mypy) +- [ ] Documentation updated (README, API docs) +- [ ] No breaking changes (or documented with migration guide) +- [ ] Linear issue referenced in description +- [ ] Commit messages follow commitlint format +- [ ] Virtual environment activated (Python projects) + +## Supporting Documentation + +All supporting files are under 500 lines per Anthropic best practices: + +- **[examples/](examples/)** - Complete PR examples + - [feature-pr.md](examples/feature-pr.md) - Feature PR example + - [bugfix-pr.md](examples/bugfix-pr.md) - Bug fix PR example + - [refactor-pr.md](examples/refactor-pr.md) - Refactoring PR example + - [INDEX.md](examples/INDEX.md) - Examples navigation + +- **[templates/](templates/)** - Copy-paste ready templates + - [feature-template.md](templates/feature-template.md) - Feature PR template + - [bugfix-template.md](templates/bugfix-template.md) - Bug fix PR template + - [database-template.md](templates/database-template.md) - Database PR template + +- **[checklists/](checklists/)** - Pre-PR validation + - [pr-checklist.md](checklists/pr-checklist.md) - Comprehensive PR checklist + +## Special Cases + +### Breaking Changes +Always include migration guide: + +```markdown +## Breaking Changes + +**BREAKING:** User IDs are now UUIDs instead of sequential integers. + +### Migration Guide +1. Update API clients to handle UUID format +2. Run database migration: `bun run db:migrate` +3. Update any hardcoded user IDs in tests +4. Verify multi-tenant isolation still works + +### Timeline +- Migration available: 2025-01-15 +- Required by: 2025-02-01 +``` + +### Database Migrations +Include up and down testing: + +```markdown +## Database Migration + +### Changes +- Add `tenant_id` column to `organizations` table +- Create RLS policies for tenant isolation +- Add index on `tenant_id` + +### Testing +- ✅ Migration up: Successful +- ✅ Migration down: Successful +- ✅ Data backfill: Verified for 1000+ records +- ✅ RLS policies: Tested with different tenants +``` + +## When to Apply This Skill + +Use this skill when: +- Creating pull requests in Grey Haven projects +- Reviewing pull requests for completeness +- Onboarding new developers to PR standards +- Creating PR templates for new repos +- Documenting complex features or refactorings + +## Template Reference + +These standards come from Grey Haven's actual templates: +- **cvi-template**: TanStack Start + React 19 +- **cvi-backend-template**: FastAPI + SQLModel + +## Critical Reminders + +1. **Summary: 2-4 sentences** - concise overview +2. **Motivation: explain why** - business value, user impact +3. **Implementation: explain how** - technical approach with file references +4. **Testing: comprehensive** - automated + manual steps +5. **Checklist: complete** - all items checked before review +6. **Linear issues: reference** - GREY-123 format +7. **Multi-tenant: verify** - tenant_id and RLS mentioned +8. **Breaking changes: document** - migration guide required +9. **Database: test migrations** - up and down +10. **Code references: clickable** - use [file:line](file#Lline) format diff --git a/skills/pr-template/checklists/pr-checklist.md b/skills/pr-template/checklists/pr-checklist.md new file mode 100644 index 0000000..d7ea13e --- /dev/null +++ b/skills/pr-template/checklists/pr-checklist.md @@ -0,0 +1,73 @@ +# Pull Request Checklist + +**Use before requesting PR review.** + +## Code Quality + +- [ ] Code follows Grey Haven style guidelines (90 char TS, 130 char Python) +- [ ] Type hints added (Python) or types maintained (TypeScript) +- [ ] No console.log or print statements (except intentional logging) +- [ ] Variable and function names are descriptive +- [ ] Comments explain **why**, not **what** + +## Testing + +- [ ] Unit tests added/updated +- [ ] Integration tests added/updated (if applicable) +- [ ] E2e tests added/updated (if applicable) +- [ ] Test coverage meets 80% threshold +- [ ] Manual testing completed +- [ ] Edge cases tested + +## Database (if applicable) + +- [ ] Migrations tested (up and down) +- [ ] snake_case field names used +- [ ] tenant_id included with RLS policies +- [ ] Indexes added for foreign keys +- [ ] Migration includes rollback strategy + +## Multi-Tenant (if applicable) + +- [ ] tenant_id filtering in all queries +- [ ] RLS policies created/updated +- [ ] Tenant isolation verified with tests +- [ ] JWT claims include tenant_id + +## Documentation + +- [ ] README updated (if needed) +- [ ] API documentation updated (if applicable) +- [ ] Inline code comments added for complex logic +- [ ] Migration guide provided (if breaking changes) + +## Pre-Commit + +- [ ] Pre-commit hooks passing +- [ ] Prettier/Ruff formatting applied +- [ ] ESLint/mypy checks passing +- [ ] Virtual environment activated (Python projects) + +## Pull Request Description + +- [ ] Summary: 2-4 sentences describing changes +- [ ] Motivation: Why changes are needed +- [ ] Implementation: Technical approach with file references +- [ ] Testing: Automated and manual testing steps +- [ ] Linear issue referenced (GREY-123) +- [ ] Breaking changes documented (if any) + +## Security + +- [ ] No sensitive data in code (passwords, keys, tokens) +- [ ] Input validation implemented +- [ ] SQL injection prevented (using ORM) +- [ ] XSS prevented (proper escaping) +- [ ] Rate limiting considered (if API changes) + +## Final Checks + +- [ ] Commit messages follow commitlint format +- [ ] No merge conflicts +- [ ] Branch is up to date with main +- [ ] All checklist items completed diff --git a/skills/pr-template/templates/feature-template.md b/skills/pr-template/templates/feature-template.md new file mode 100644 index 0000000..154587c --- /dev/null +++ b/skills/pr-template/templates/feature-template.md @@ -0,0 +1,53 @@ +## Summary + +[2-4 sentences describing what was added and why] + +## Motivation + +[Explain the problem being solved and business value] + +- **Problem**: [Describe user pain point or business need] +- **Impact**: [Expected user/business benefit] +- **Linear Issue**: GREY-XXX + +## Implementation Details + +### Key Changes +- **[Component/Module]**: [What changed] +- **[Database/Schema]**: [Schema changes with snake_case, tenant_id] +- **[Routes/API]**: [New endpoints or routes] + +### Design Decisions +- **Decision 1**: [Rationale] +- **Decision 2**: [Rationale] + +### Multi-Tenant Considerations +- tenant_id filtering in [location] +- RLS policies on [tables] +- Tested with multiple tenants + +## Testing + +### Automated Tests +- **Unit tests** (X new): [What's tested] +- **Integration tests** (X new): [What's tested] +- **E2e tests** (X new): [What's tested] + +### Manual Testing +1. [Step 1] +2. [Step 2] +3. [Step 3] + +### Coverage +- New code: X% coverage +- Critical paths: 100% coverage + +## Checklist +- [ ] Code follows Grey Haven style guidelines +- [ ] Type hints added/maintained +- [ ] Tests added/updated (80%+ coverage) +- [ ] Database migrations tested +- [ ] Multi-tenant isolation verified +- [ ] Pre-commit hooks passing +- [ ] Documentation updated +- [ ] No breaking changes diff --git a/skills/project-structure/SKILL.md b/skills/project-structure/SKILL.md new file mode 100644 index 0000000..459d999 --- /dev/null +++ b/skills/project-structure/SKILL.md @@ -0,0 +1,327 @@ +--- +name: grey-haven-project-structure +description: Organize Grey Haven projects following standard structures for TanStack Start (frontend) and FastAPI (backend). Use when creating new projects, organizing files, or refactoring project layout. +--- + +# Grey Haven Project Structure + +Follow Grey Haven Studio's standardized project structures for **TypeScript/React** (TanStack Start) and **Python/FastAPI** projects. + +## Frontend Structure (TanStack Start + React 19) + +Based on `cvi-template` - TanStack Start, React 19, Drizzle, Better-auth. + +### Directory Layout + +``` +project-root/ +├── .claude/ # Claude Code configuration +├── .github/workflows/ # GitHub Actions (CI/CD) +├── public/ # Static assets +├── src/ +│ ├── routes/ # TanStack Router file-based routes +│ │ ├── __root.tsx # Root layout +│ │ ├── index.tsx # Homepage +│ │ ├── _authenticated/ # Protected routes group +│ │ │ ├── _layout.tsx # Auth layout wrapper +│ │ │ ├── dashboard.tsx # /dashboard +│ │ │ └── profile.tsx # /profile +│ │ └── auth/ # Auth routes +│ │ ├── login.tsx # /auth/login +│ │ └── signup.tsx # /auth/signup +│ ├── lib/ +│ │ ├── components/ # React components +│ │ │ ├── ui/ # Shadcn UI (PascalCase) +│ │ │ ├── auth/ # Auth components +│ │ │ ├── layout/ # Layout components +│ │ │ └── shared/ # Shared components +│ │ ├── server/ # Server-side code +│ │ │ ├── schema/ # Drizzle schema (snake_case) +│ │ │ ├── functions/ # Server functions +│ │ │ ├── auth.ts # Better-auth config +│ │ │ └── db.ts # Database connections +│ │ ├── config/ # Configuration +│ │ │ ├── env.ts # Environment validation +│ │ │ └── auth.ts # Auth configuration +│ │ ├── middleware/ # Route middleware +│ │ ├── hooks/ # Custom React hooks (use-*) +│ │ ├── utils/ # Utility functions +│ │ └── types/ # TypeScript types +│ └── tests/ +│ ├── unit/ # Vitest unit tests +│ ├── integration/ # Vitest integration tests +│ └── e2e/ # Playwright E2E tests +├── migrations/ # Drizzle migrations +├── .env.example # Example environment variables +├── .prettierrc # Prettier (90 chars, double quotes) +├── .eslintrc # ESLint (any allowed, strict off) +├── tsconfig.json # TypeScript (~/* path alias) +├── commitlint.config.cjs # Commitlint (100 char header) +├── drizzle.config.ts # Drizzle ORM config +├── vite.config.ts # Vite configuration +├── vitest.config.ts # Vitest test configuration +├── package.json # Dependencies (use bun!) +└── README.md # Project documentation +``` + +### Key Frontend Patterns + +#### Path Imports - Always Use ~/* Alias + +```typescript +// ✅ Correct - Use ~/* path alias +import { Button } from "~/lib/components/ui/button"; +import { getUserById } from "~/lib/server/functions/users"; +import { env } from "~/lib/config/env"; +import { useAuth } from "~/lib/hooks/use-auth"; + +// ❌ Wrong - Relative paths +import { Button } from "../../lib/components/ui/button"; +``` + +#### File Naming Conventions + +- **Components**: PascalCase (`UserProfile.tsx`, `LoginForm.tsx`) +- **Routes**: lowercase with hyphens (`user-profile.tsx`, `auth/login.tsx`) +- **Utilities**: camelCase or kebab-case (`formatDate.ts`, `use-auth.ts`) +- **Server functions**: camelCase (`auth.ts`, `users.ts`) +- **Schema files**: plural lowercase (`users.ts`, `organizations.ts`) + +#### Component Structure Order + +```typescript +import { useState } from "react"; // 1. External imports +import { useQuery } from "@tanstack/react-query"; +import { Button } from "~/lib/components/ui/button"; // 2. Internal imports +import { getUserById } from "~/lib/server/functions/users"; + +interface UserProfileProps { // 3. Types/Interfaces + userId: string; +} + +export default function UserProfile({ userId }: UserProfileProps) { // 4. Component + const [editing, setEditing] = useState(false); // 5. State hooks + + const { data: user } = useQuery({ // 6. Queries/Mutations + queryKey: ["user", userId], + queryFn: () => getUserById(userId), + staleTime: 60000, + }); + + const handleSave = () => { // 7. Event handlers + // ... + }; + + return ( // 8. JSX +
+

{user?.full_name}

+ +
+ ); +} +``` + +## Backend Structure (FastAPI + Python) + +Based on `cvi-backend-template` - FastAPI, SQLModel, Alembic. + +### Directory Layout + +``` +project-root/ +├── .github/workflows/ # GitHub Actions +├── app/ +│ ├── __init__.py +│ ├── main.py # FastAPI app entry point +│ ├── routers/ # API routers (endpoints) +│ │ ├── __init__.py +│ │ ├── auth.py # /auth routes +│ │ ├── users.py # /users routes +│ │ └── health.py # /health routes +│ ├── services/ # Business logic layer +│ │ ├── __init__.py +│ │ ├── user_service.py +│ │ ├── auth_service.py +│ │ └── billing_service.py +│ ├── db/ # Database layer +│ │ ├── __init__.py +│ │ ├── models/ # SQLModel models (snake_case) +│ │ │ ├── __init__.py +│ │ │ ├── user.py +│ │ │ ├── tenant.py +│ │ │ └── organization.py +│ │ ├── repositories/ # Data access layer +│ │ │ ├── __init__.py +│ │ │ ├── base.py # Base repository +│ │ │ └── user_repository.py +│ │ └── session.py # Database sessions +│ ├── schemas/ # Pydantic schemas +│ │ ├── __init__.py +│ │ ├── user.py # UserCreate, UserResponse +│ │ ├── auth.py # AuthRequest, AuthResponse +│ │ └── common.py # Shared schemas +│ ├── middleware/ # FastAPI middleware +│ │ ├── __init__.py +│ │ ├── auth.py # JWT verification +│ │ └── tenant.py # Tenant context +│ ├── core/ # Core configuration +│ │ ├── __init__.py +│ │ ├── config.py # Settings (Doppler) +│ │ ├── security.py # Password hashing, JWT +│ │ └── deps.py # FastAPI dependencies +│ └── utils/ # Utility functions +│ ├── __init__.py +│ └── format.py +├── tests/ # Pytest tests +│ ├── __init__.py +│ ├── unit/ # @pytest.mark.unit +│ ├── integration/ # @pytest.mark.integration +│ └── e2e/ # @pytest.mark.e2e +├── alembic/ # Alembic migrations +│ ├── versions/ +│ └── env.py +├── .env.example # Example environment variables +├── .env # Local environment (gitignored) +├── pyproject.toml # Ruff, mypy, pytest config +├── alembic.ini # Alembic configuration +├── Taskfile.yml # Task runner commands +├── requirements.txt # Production dependencies +├── requirements-dev.txt # Development dependencies +└── README.md # Project documentation +``` + +### Key Backend Patterns + +#### Import Organization (Automatic with Ruff) + +```python +"""Module docstring describing purpose.""" + +# 1. Standard library imports +import os +from datetime import datetime +from typing import Optional +from uuid import UUID + +# 2. Third-party imports +from fastapi import APIRouter, Depends, HTTPException +from sqlmodel import select + +# 3. Local imports +from app.db.models.user import User +from app.db.repositories.user_repository import UserRepository +from app.schemas.user import UserCreate, UserResponse +``` + +#### File Naming Conventions + +- **Modules**: snake_case (`user_service.py`, `auth_middleware.py`) +- **Models**: PascalCase class, snake_case file (`User` in `user.py`) +- **Tests**: `test_` prefix (`test_user_service.py`) + +#### Repository Pattern (with Tenant Isolation) + +```python +# app/db/repositories/base.py +from typing import Generic, TypeVar, Optional +from uuid import UUID +from sqlmodel import select +from sqlalchemy.ext.asyncio import AsyncSession + +T = TypeVar("T") + +class BaseRepository(Generic[T]): + """Base repository with CRUD and tenant isolation.""" + + def __init__(self, session: AsyncSession, model: type[T]): + self.session = session + self.model = model + + async def get_by_id(self, id: UUID, tenant_id: UUID) -> Optional[T]: + """Get by ID with automatic tenant filtering.""" + result = await self.session.execute( + select(self.model) + .where(self.model.id == id) + .where(self.model.tenant_id == tenant_id) + ) + return result.scalar_one_or_none() +``` + +#### Service Layer Pattern + +```python +# app/services/user_service.py +from uuid import UUID +from app.db.repositories.user_repository import UserRepository +from app.schemas.user import UserCreate, UserResponse + +class UserService: + """User business logic.""" + + def __init__(self, user_repo: UserRepository): + self.user_repo = user_repo + + async def create_user(self, data: UserCreate, tenant_id: UUID) -> UserResponse: + """Create new user with validation.""" + existing = await self.user_repo.get_by_email(data.email_address, tenant_id) + if existing: + raise ValueError("Email already registered") + + user = await self.user_repo.create(data, tenant_id) + return UserResponse.model_validate(user) +``` + +## Supporting Documentation + +All supporting files are under 500 lines per Anthropic best practices: + +- **[examples/](examples/)** - Complete project examples + - [frontend-directory-structure.md](examples/frontend-directory-structure.md) - Full TanStack Start structure + - [backend-directory-structure.md](examples/backend-directory-structure.md) - Full FastAPI structure + - [component-structure.md](examples/component-structure.md) - React component patterns + - [repository-pattern.md](examples/repository-pattern.md) - Repository with tenant isolation + - [service-pattern.md](examples/service-pattern.md) - Service layer examples + - [INDEX.md](examples/INDEX.md) - Examples navigation + +- **[reference/](reference/)** - Structure references + - [file-naming.md](reference/file-naming.md) - Naming conventions + - [import-organization.md](reference/import-organization.md) - Import ordering rules + - [path-aliases.md](reference/path-aliases.md) - Path alias configuration + - [INDEX.md](reference/INDEX.md) - Reference navigation + +- **[templates/](templates/)** - Copy-paste ready templates + - [tanstack-start-project/](templates/tanstack-start-project/) - Frontend scaffold + - [fastapi-project/](templates/fastapi-project/) - Backend scaffold + +- **[checklists/](checklists/)** - Structure checklists + - [project-setup-checklist.md](checklists/project-setup-checklist.md) - New project setup + - [refactoring-checklist.md](checklists/refactoring-checklist.md) - Structure refactoring + +## When to Apply This Skill + +Use this skill when: +- Creating new Grey Haven projects +- Organizing files in existing projects +- Refactoring project layout +- Setting up directory structure +- Deciding where to place new files +- Onboarding new team members to project structure + +## Template Reference + +These patterns are from Grey Haven's production templates: +- **Frontend**: cvi-template (TanStack Start + React 19) +- **Backend**: cvi-backend-template (FastAPI + Python) + +## Critical Reminders + +1. **Path aliases**: Always use ~/* for TypeScript imports +2. **File naming**: Components PascalCase, modules snake_case +3. **Import order**: External → Internal → Local +4. **Component structure**: Imports → Types → Component → Hooks → Handlers → JSX +5. **Tenant isolation**: Repository pattern includes tenant_id filtering +6. **Database fields**: snake_case in schema files (NOT camelCase) +7. **Test organization**: unit/, integration/, e2e/ directories +8. **Configuration**: Doppler for all environment variables +9. **Routes**: TanStack file-based routing in src/routes/ +10. **Backend layers**: Routers → Services → Repositories → Models diff --git a/skills/project-structure/checklists/project-setup-checklist.md b/skills/project-structure/checklists/project-setup-checklist.md new file mode 100644 index 0000000..1194800 --- /dev/null +++ b/skills/project-structure/checklists/project-setup-checklist.md @@ -0,0 +1,165 @@ +# Project Setup Checklist + +**Use when creating new Grey Haven projects.** + +## Initial Setup + +### Choose Template +- [ ] Frontend: Use cvi-template (TanStack Start + React 19) +- [ ] Backend: Use cvi-backend-template (FastAPI + Python) +- [ ] Clone from Grey Haven GitHub organization +- [ ] Remove .git directory and re-initialize + +### Environment Configuration +- [ ] Copy .env.example to .env +- [ ] Set up Doppler project (dev, test, staging, production) +- [ ] Add required secrets to Doppler +- [ ] Test Doppler access: `doppler run --config dev -- echo "Working"` +- [ ] Add .env to .gitignore (verify) + +## Frontend Setup (TanStack Start) + +### Directory Structure +- [ ] Verify src/routes/ structure (file-based routing) +- [ ] Verify src/lib/ organization (components, server, config, etc.) +- [ ] Verify public/ for static assets +- [ ] Verify migrations/ for Drizzle migrations + +### Configuration Files +- [ ] Update tsconfig.json (verify ~/* path alias) +- [ ] Update package.json (project name, description) +- [ ] Verify .prettierrc (90 char line length, double quotes) +- [ ] Verify .eslintrc (any allowed, strict off) +- [ ] Update commitlint.config.cjs (100 char header) +- [ ] Update vite.config.ts (project-specific settings) +- [ ] Update vitest.config.ts (coverage thresholds >80%) + +### Dependencies +- [ ] Run `bun install` (NOT npm!) +- [ ] Verify Drizzle ORM installed +- [ ] Verify better-auth installed +- [ ] Verify TanStack Start/Query/Router installed +- [ ] Verify Shadcn UI components + +### Database Setup +- [ ] Create PostgreSQL database (PlanetScale) +- [ ] Add DATABASE_URL to Doppler +- [ ] Add DATABASE_URL_ADMIN to Doppler +- [ ] Generate initial migration: `bun run db:generate` +- [ ] Apply migration: `doppler run --config dev -- bun run db:migrate` + +### Authentication +- [ ] Configure better-auth in src/lib/server/auth.ts +- [ ] Add BETTER_AUTH_SECRET to Doppler +- [ ] Add BETTER_AUTH_URL to Doppler +- [ ] Set up OAuth providers (if needed) +- [ ] Test auth flow locally + +## Backend Setup (FastAPI) + +### Directory Structure +- [ ] Verify app/ structure (routers, services, db, etc.) +- [ ] Verify tests/ organization (unit, integration, e2e) +- [ ] Verify alembic/ for migrations + +### Configuration Files +- [ ] Update pyproject.toml (project name, dependencies) +- [ ] Verify Ruff configuration (130 char line length) +- [ ] Verify mypy configuration (strict type checking) +- [ ] Verify pytest configuration (markers, coverage >80%) +- [ ] Update alembic.ini (database URL from Doppler) +- [ ] Create Taskfile.yml (common commands) + +### Dependencies +- [ ] Create virtual environment: `python -m venv venv` +- [ ] Activate venv: `source venv/bin/activate` +- [ ] Install deps: `pip install -r requirements.txt` +- [ ] Install dev deps: `pip install -r requirements-dev.txt` +- [ ] Verify SQLModel installed +- [ ] Verify FastAPI installed +- [ ] Verify Alembic installed + +### Database Setup +- [ ] Create PostgreSQL database (PlanetScale) +- [ ] Add DATABASE_URL to Doppler +- [ ] Add DATABASE_URL_ADMIN to Doppler +- [ ] Generate initial migration: `alembic revision --autogenerate -m "Initial"` +- [ ] Apply migration: `doppler run --config dev -- alembic upgrade head` + +## GitHub Setup + +### Repository +- [ ] Create GitHub repository +- [ ] Push initial commit +- [ ] Set up branch protection rules (main branch) +- [ ] Require PR before merging +- [ ] Require 1 approval +- [ ] Require status checks to pass + +### GitHub Actions +- [ ] Add DOPPLER_TOKEN to repository secrets +- [ ] Add CLOUDFLARE_API_TOKEN to repository secrets (if deploying) +- [ ] Verify .github/workflows/ci.yml +- [ ] Verify .github/workflows/deploy.yml +- [ ] Test CI/CD pipeline + +## Testing + +### Frontend Testing +- [ ] Run unit tests: `bun test` +- [ ] Run integration tests: `bun test:integration` +- [ ] Run E2E tests: `bun test:e2e` +- [ ] Verify coverage >80% + +### Backend Testing +- [ ] Run unit tests: `pytest -m unit` +- [ ] Run integration tests: `pytest -m integration` +- [ ] Run E2E tests: `pytest -m e2e` +- [ ] Verify coverage >80% + +## Documentation + +### README.md +- [ ] Update project name and description +- [ ] Document getting started steps +- [ ] Document Doppler setup +- [ ] Document database setup +- [ ] Document testing commands +- [ ] Document deployment process + +### Additional Docs +- [ ] Create API documentation (if applicable) +- [ ] Document architecture decisions +- [ ] Document multi-tenant setup +- [ ] Create onboarding guide + +## Deployment + +### Cloudflare Workers (Frontend) +- [ ] Create wrangler.toml files (dev, staging, production) +- [ ] Create KV namespaces +- [ ] Create R2 buckets (if needed) +- [ ] Configure custom domains +- [ ] Test deployment to staging + +### Production Readiness +- [ ] Set up monitoring (Sentry, Axiom) +- [ ] Configure error tracking +- [ ] Set up performance monitoring +- [ ] Configure rate limiting +- [ ] Set up backup strategy +- [ ] Document rollback procedure + +## Post-Setup + +### Team Onboarding +- [ ] Share Doppler access with team +- [ ] Share repository access +- [ ] Document local setup process +- [ ] Schedule team walkthrough + +### Maintenance +- [ ] Set up dependency update schedule +- [ ] Set up security scanning +- [ ] Schedule regular code reviews +- [ ] Document support procedures diff --git a/skills/project-structure/examples/INDEX.md b/skills/project-structure/examples/INDEX.md new file mode 100644 index 0000000..035c3ef --- /dev/null +++ b/skills/project-structure/examples/INDEX.md @@ -0,0 +1,48 @@ +# Project Structure Examples + +Complete project structure examples for Grey Haven applications. + +## Available Examples + +### [frontend-directory-structure.md](frontend-directory-structure.md) +Full TanStack Start project structure. +- Complete directory tree with annotations +- File-based routing examples +- Component organization patterns +- Server functions structure + +### [backend-directory-structure.md](backend-directory-structure.md) +Full FastAPI project structure. +- Complete directory tree with annotations +- Routers, services, repositories layers +- Database models and migrations +- Testing organization + +### [component-structure.md](component-structure.md) +React component organization patterns. +- Import ordering +- Hook usage patterns +- Event handler organization +- JSX structure + +### [repository-pattern.md](repository-pattern.md) +Repository pattern with tenant isolation. +- Base repository implementation +- Specific repository examples +- Tenant filtering patterns +- Async/await patterns + +### [service-pattern.md](service-pattern.md) +Service layer examples. +- Business logic organization +- Service dependencies +- Error handling +- Validation patterns + +## Quick Reference + +**Need frontend structure?** → [frontend-directory-structure.md](frontend-directory-structure.md) +**Need backend structure?** → [backend-directory-structure.md](backend-directory-structure.md) +**Need component patterns?** → [component-structure.md](component-structure.md) +**Need repository patterns?** → [repository-pattern.md](repository-pattern.md) +**Need service patterns?** → [service-pattern.md](service-pattern.md) diff --git a/skills/project-structure/reference/INDEX.md b/skills/project-structure/reference/INDEX.md new file mode 100644 index 0000000..dafd85f --- /dev/null +++ b/skills/project-structure/reference/INDEX.md @@ -0,0 +1,32 @@ +# Project Structure Reference + +Configuration references and naming conventions for Grey Haven projects. + +## Available References + +### [file-naming.md](file-naming.md) +File naming conventions for both stacks. +- TypeScript/React conventions +- Python/FastAPI conventions +- Test file naming +- Configuration file naming + +### [import-organization.md](import-organization.md) +Import ordering and organization rules. +- TypeScript import order +- Python import order (PEP 8) +- Ruff automatic sorting +- ESLint import rules + +### [path-aliases.md](path-aliases.md) +Path alias configuration and usage. +- tsconfig.json setup (~/* alias) +- Vite configuration +- Import examples +- Common pitfalls + +## Quick Reference + +**Need naming rules?** → [file-naming.md](file-naming.md) +**Need import order?** → [import-organization.md](import-organization.md) +**Need path aliases?** → [path-aliases.md](path-aliases.md)