@yss123/cage-cli 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/approval-tree.d.ts +88 -0
- package/dist/approval-tree.js +18 -0
- package/dist/approval-tree.js.map +1 -0
- package/dist/capabilities.js +46 -31
- package/dist/capabilities.js.map +1 -1
- package/dist/graph-approvals.d.ts +11 -1
- package/dist/graph-approvals.js +18 -5
- package/dist/graph-approvals.js.map +1 -1
- package/dist/graph-relations.js +10 -8
- package/dist/graph-relations.js.map +1 -1
- package/dist/graph.js +2 -0
- package/dist/graph.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/ontology.js +8 -5
- package/dist/ontology.js.map +1 -1
- package/dist/project-structure.js +2 -2
- package/dist/project-structure.js.map +1 -1
- package/dist/sync.d.ts +1 -1
- package/dist/sync.js +25 -108
- package/dist/sync.js.map +1 -1
- package/graph-ui/approval-flow.js +28 -8
- package/graph-ui/approval-parallel.js +110 -0
- package/graph-ui/approval-tree.js +17 -0
- package/graph-ui/business.css +26 -0
- package/graph-ui/business.js +30 -13
- package/graph-ui/graph-approvals.js +18 -5
- package/graph-ui/graph-relations.js +10 -8
- package/package.json +6 -6
- package/templates/app/.agents/skills/cage-development/SKILL.md +10 -0
- package/templates/app/AGENTS.md +39 -122
- package/templates/app/CLAUDE.md +6 -0
- package/templates/app/README.md +9 -8
- package/templates/app/apps/api/package.json +2 -2
- package/templates/app/apps/web/package.json +1 -1
- package/templates/app/apps/web/src/modules/approval/components/task-operation-dialog.tsx +61 -0
- package/templates/app/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +6 -9
- package/templates/app/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +81 -49
- package/templates/app/apps/web/src/modules/approval/pages/approval-tasks.css +18 -0
- package/templates/app/apps/web/src/modules/purchase/components/approval-history.tsx +39 -4
- package/templates/app/apps/web/src/modules/purchase/components/order-reviewers.tsx +38 -0
- package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.tsx +9 -2
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-intelligence.css +2 -0
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +2 -0
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +2 -0
- package/templates/app/apps/web/src/shared/lib/error-message.ts +6 -1
- package/templates/app/apps/web/src/shared/ui/approval-member-picker.css +10 -0
- package/templates/app/apps/web/src/shared/ui/approval-member-picker.tsx +51 -0
- package/templates/app/cage.config.json +14 -0
- package/templates/app/db/migrations/0004_draft.sql +27 -0
- package/templates/app/docs/README.md +11 -0
- package/templates/app/docs/development/README.md +64 -0
- package/templates/app/docs/development/examples/note/note-detail-page.tsx +106 -0
- package/templates/app/docs/development/examples/note/note.ts +67 -0
- package/templates/app/docs/development/examples/note/notes-page.tsx +87 -0
- package/templates/{blank/docs/ai → app/docs/development/examples}/purchase-example.md +24 -10
- package/templates/{blank/docs/ai → app/docs/development/getting-started}/architecture.md +4 -4
- package/templates/app/docs/development/getting-started/first-feature.md +66 -0
- package/templates/{blank/docs/ai → app/docs/development/getting-started}/workflow.md +13 -12
- package/templates/app/docs/development/guides/approvals.md +270 -0
- package/templates/app/docs/development/guides/business-collaboration.md +63 -0
- package/templates/{blank/docs/ai → app/docs/development/guides}/files.md +5 -1
- package/templates/app/docs/{ai → development/guides}/intelligence.md +4 -4
- package/templates/{blank/docs/ai → app/docs/development/guides}/recipes.md +31 -11
- package/templates/app/docs/{ai → development/guides}/relations.md +2 -2
- package/templates/app/docs/{ai → development/guides}/routing.md +1 -1
- package/templates/{blank/docs/ai → app/docs/development/guides}/transfers.md +1 -1
- package/templates/{blank/docs/ai → app/docs/development/reference}/commenting.md +1 -1
- package/templates/app/docs/{ai → development/reference}/primitives.md +8 -62
- package/templates/app/docs/{ai → development/reference}/project-structure.md +2 -2
- package/templates/app/docs/development/skills/cage-development/SKILL.md +134 -0
- package/templates/{blank/docs/ai → app/docs/development/tools}/diagnostics.md +3 -3
- package/templates/{blank/docs/ai → app/docs/development/tools}/ontology-context.md +3 -3
- package/templates/app/docs/{ai → development/tools}/troubleshooting.md +7 -1
- package/templates/app/docs/errors/CAGE-AI-001.md +3 -3
- package/templates/app/docs/errors/CAGE-APPR-001.md +13 -10
- package/templates/app/docs/errors/CAGE-APPR-002.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-003.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-004.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-005.md +5 -5
- package/templates/app/docs/errors/CAGE-APPR-006.md +25 -10
- package/templates/app/docs/errors/CAGE-APPR-007.md +9 -7
- package/templates/app/docs/errors/CAGE-APPR-010.md +1 -1
- package/templates/app/docs/errors/CAGE-AUDIT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-AUTH-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BIZ-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BIZ-002.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-002.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CMD-002.md +1 -1
- package/templates/app/docs/errors/CAGE-CMD-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-001.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-002.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-004.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-005.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-006.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-007.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-008.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-009.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-010.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-011.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-012.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-013.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-014.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-015.md +1 -1
- package/templates/app/docs/errors/CAGE-CREATE-001.md +2 -2
- package/templates/app/docs/errors/CAGE-DECL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-002.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-003.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-004.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-001.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-002.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-003.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-004.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-005.md +1 -1
- package/templates/app/docs/errors/CAGE-DIAG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-DLQ-001.md +1 -1
- package/templates/app/docs/errors/CAGE-ENV-001.md +1 -1
- package/templates/app/docs/errors/CAGE-ENV-002.md +1 -1
- package/templates/app/docs/errors/CAGE-ENV-003.md +1 -1
- package/templates/app/docs/errors/CAGE-EVENT-001.md +2 -2
- package/templates/app/docs/errors/CAGE-EVENT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-001.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-002.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-003.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-004.md +1 -1
- package/templates/app/docs/errors/CAGE-FILE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-001.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-002.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-003.md +1 -1
- package/templates/app/docs/errors/CAGE-GRANT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-GRANT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-GRANT-003.md +1 -1
- package/templates/app/docs/errors/CAGE-IMPACT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-IMPACT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-003.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-004.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-005.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-001.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-002.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-003.md +1 -1
- package/templates/app/docs/errors/CAGE-LINT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-002.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-005.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-001.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-002.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-003.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-004.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-005.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-006.md +1 -1
- package/templates/app/docs/errors/CAGE-ORG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-PAIR-002.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-001.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-002.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-003.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-003.md +1 -1
- package/templates/app/docs/errors/CAGE-READS-001.md +1 -1
- package/templates/app/docs/errors/CAGE-REG-001.md +2 -2
- package/templates/app/docs/errors/CAGE-REG-002.md +1 -1
- package/templates/app/docs/errors/CAGE-REG-003.md +1 -1
- package/templates/app/docs/errors/CAGE-REL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-ROUTE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SCOPE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SCOPE-002.md +1 -1
- package/templates/app/docs/errors/CAGE-SECRET-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SECRET-002.md +1 -1
- package/templates/app/docs/errors/CAGE-SENS-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SQL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SQL-002.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-002.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-003.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-004.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-005.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-010.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-011.md +1 -1
- package/templates/app/docs/errors/CAGE-STRUCT-001.md +4 -4
- package/templates/app/docs/errors/CAGE-STRUCT-002.md +3 -3
- package/templates/app/docs/errors/CAGE-SYNC-001.md +4 -4
- package/templates/app/docs/errors/CAGE-TEST-001.md +1 -1
- package/templates/app/docs/errors/CAGE-TEST-002.md +1 -1
- package/templates/app/docs/errors/CAGE-TEST-003.md +1 -1
- package/templates/app/docs/errors/CAGE-UNSAFE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-UPGRADE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-WORKER-001.md +1 -1
- package/templates/app/docs/errors/CAGE-WORKER-002.md +1 -1
- package/templates/app/docs/errors/CAGE-WORKER-003.md +1 -1
- package/templates/app/docs/errors/README.md +3 -3
- package/templates/app/docs/errors/runtime.md +1 -1
- package/templates/app/package.json +3 -3
- package/templates/app/packages/contracts/package.json +1 -1
- package/templates/app/packages/contracts/src/aggregates/approval-member/index.ts +70 -0
- package/templates/app/packages/contracts/src/aggregates/purchase-order/index.ts +10 -2
- package/templates/app/packages/contracts/src/index.ts +9 -0
- package/templates/app/packages/domain/package.json +5 -5
- package/templates/app/packages/domain/src/modules/purchase/approval-members.ts +54 -0
- package/templates/app/packages/domain/src/modules/purchase/approvals.ts +19 -8
- package/templates/app/packages/domain/src/modules/purchase/index.ts +2 -1
- package/templates/app/packages/domain/src/modules/purchase/intelligence.test.ts +169 -7
- package/templates/app/packages/domain/src/modules/purchase/purchase-order.ts +56 -5
- package/templates/blank/.agents/skills/cage-development/SKILL.md +10 -0
- package/templates/blank/AGENTS.md +39 -122
- package/templates/blank/CLAUDE.md +6 -0
- package/templates/blank/README.md +3 -3
- package/templates/blank/apps/api/package.json +1 -1
- package/templates/blank/apps/web/package.json +1 -1
- package/templates/blank/docs/README.md +11 -0
- package/templates/blank/docs/development/README.md +64 -0
- package/templates/blank/docs/development/examples/note/note-detail-page.tsx +106 -0
- package/templates/blank/docs/development/examples/note/note.ts +67 -0
- package/templates/blank/docs/development/examples/note/notes-page.tsx +87 -0
- package/templates/{app/docs/ai → blank/docs/development/examples}/purchase-example.md +24 -10
- package/templates/{app/docs/ai → blank/docs/development/getting-started}/architecture.md +4 -4
- package/templates/blank/docs/development/getting-started/first-feature.md +66 -0
- package/templates/{app/docs/ai → blank/docs/development/getting-started}/workflow.md +13 -12
- package/templates/blank/docs/development/guides/approvals.md +270 -0
- package/templates/blank/docs/development/guides/business-collaboration.md +63 -0
- package/templates/{app/docs/ai → blank/docs/development/guides}/files.md +5 -1
- package/templates/blank/docs/{ai → development/guides}/intelligence.md +4 -4
- package/templates/{app/docs/ai → blank/docs/development/guides}/recipes.md +31 -11
- package/templates/blank/docs/{ai → development/guides}/relations.md +2 -2
- package/templates/blank/docs/{ai → development/guides}/routing.md +1 -1
- package/templates/{app/docs/ai → blank/docs/development/guides}/transfers.md +1 -1
- package/templates/{app/docs/ai → blank/docs/development/reference}/commenting.md +1 -1
- package/templates/blank/docs/{ai → development/reference}/primitives.md +8 -62
- package/templates/blank/docs/{ai → development/reference}/project-structure.md +2 -2
- package/templates/blank/docs/development/skills/cage-development/SKILL.md +134 -0
- package/templates/{app/docs/ai → blank/docs/development/tools}/diagnostics.md +3 -3
- package/templates/{app/docs/ai → blank/docs/development/tools}/ontology-context.md +3 -3
- package/templates/blank/docs/{ai → development/tools}/troubleshooting.md +7 -1
- package/templates/blank/docs/errors/CAGE-AI-001.md +3 -3
- package/templates/blank/docs/errors/CAGE-APPR-001.md +13 -10
- package/templates/blank/docs/errors/CAGE-APPR-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-005.md +5 -5
- package/templates/blank/docs/errors/CAGE-APPR-006.md +25 -10
- package/templates/blank/docs/errors/CAGE-APPR-007.md +9 -7
- package/templates/blank/docs/errors/CAGE-APPR-010.md +1 -1
- package/templates/blank/docs/errors/CAGE-AUDIT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-AUTH-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BIZ-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BIZ-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CMD-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-CMD-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-006.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-007.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-008.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-009.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-010.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-011.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-012.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-013.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-014.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-015.md +1 -1
- package/templates/blank/docs/errors/CAGE-CREATE-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-DECL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-DIAG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-DLQ-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-ENV-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-ENV-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-ENV-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-EVENT-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-EVENT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-FILE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-GRANT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-GRANT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-GRANT-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-IMPACT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-IMPACT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-LINT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-006.md +1 -1
- package/templates/blank/docs/errors/CAGE-ORG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-PAIR-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-READS-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-REG-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-REG-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-REG-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-REL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-ROUTE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SCOPE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SCOPE-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-SECRET-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SECRET-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-SENS-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SQL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SQL-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-010.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-011.md +1 -1
- package/templates/blank/docs/errors/CAGE-STRUCT-001.md +4 -4
- package/templates/blank/docs/errors/CAGE-STRUCT-002.md +3 -3
- package/templates/blank/docs/errors/CAGE-SYNC-001.md +4 -4
- package/templates/blank/docs/errors/CAGE-TEST-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-TEST-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-TEST-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-UNSAFE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-UPGRADE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-WORKER-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-WORKER-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-WORKER-003.md +1 -1
- package/templates/blank/docs/errors/README.md +3 -3
- package/templates/blank/docs/errors/runtime.md +1 -1
- package/templates/blank/docs/examples/purchase-admin/README.md +1 -1
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/components/task-operation-dialog.tsx +61 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +6 -9
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +81 -49
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks.css +18 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/approval-history.tsx +39 -4
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/order-reviewers.tsx +38 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.tsx +9 -2
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-intelligence.css +2 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +2 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +2 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +6 -1
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/approval-member-picker.css +10 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/approval-member-picker.tsx +51 -0
- package/templates/blank/docs/examples/purchase-admin/cage.config.json +14 -0
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/approval-member/index.ts +70 -0
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/purchase-order/index.ts +10 -2
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/index.ts +9 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approval-members.ts +54 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +19 -8
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts +2 -1
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/intelligence.test.ts +169 -7
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +56 -5
- package/templates/blank/package.json +3 -3
- package/templates/blank/packages/contracts/package.json +1 -1
- package/templates/blank/packages/domain/package.json +5 -5
- package/templates/app/docs/ai/README.md +0 -98
- package/templates/blank/docs/ai/README.md +0 -98
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { wrapApprovalText } from './approval-flow.js';
|
|
2
|
+
/** 创建空容器;空条件分支仍有一条可连接直线,不假造审批节点或已经运行的票。 */
|
|
3
|
+
function fragment(width = 348, height = 0) {
|
|
4
|
+
return { width, height, axis: width / 2, boxes: [], paths: [], merges: [] };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 把已测量子图复制平移到父图,路径坐标只由本模块产生的 M/L/H/V 绝对指令转换。
|
|
8
|
+
* 不改源片段,递归兄弟分支共享文本对象也不会互相污染;标题与输入不拼入 SVG path。
|
|
9
|
+
*/
|
|
10
|
+
function append(parent, child, dx, dy) {
|
|
11
|
+
parent.boxes.push(...child.boxes.map(box => ({ ...box, x: box.x + dx, y: box.y + dy })));
|
|
12
|
+
for (const path of child.paths)
|
|
13
|
+
parent.paths.push({ ...path,
|
|
14
|
+
d: path.d.replace(/([ML])(-?[\d.]+),(-?[\d.]+)|([HV])(-?[\d.]+)/g, (_match, point, x, y, axis, value) => point ? `${point}${Number(x) + dx},${Number(y) + dy}` : `${axis}${Number(value) + (axis === 'H' ? dx : dy)}`),
|
|
15
|
+
...(path.x === undefined ? {} : { x: path.x + dx }), ...(path.y === undefined ? {} : { y: path.y + dy }) });
|
|
16
|
+
parent.merges.push(...child.merges.map(point => ({ x: point.x + dx, y: point.y + dy })));
|
|
17
|
+
}
|
|
18
|
+
/** 把一个审批叶节点测量成普通任务框;编号只是声明定位,不决定并行分支的实际运行顺序。 */
|
|
19
|
+
function taskFragment(task) {
|
|
20
|
+
const title = wrapApprovalText(task.title, 14), actor = wrapApprovalText(`审批人:${task.actor}`, 19), rule = wrapApprovalText(task.rule, 18);
|
|
21
|
+
const height = 18 + title.length * 18 + actor.length * 16 + rule.length * 16, result = fragment(348, height);
|
|
22
|
+
result.boxes.push({ kind: 'task', x: 50, y: 0, width: 248, height, title, actor, rule, task });
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
/** 各顺序子片段在最大宽度中心对齐,连接只经过相邻片段之间的空隙。 */
|
|
26
|
+
function sequence(parts) {
|
|
27
|
+
// 条件旁路使子图左右不对称,按中心轴两侧的最大占用测量,不能只取原始 width 后居中导致右侧越界。
|
|
28
|
+
const result = fragment(Math.max(348, ...parts.map(part => 2 * Math.max(part.axis, part.width - part.axis))));
|
|
29
|
+
if (!parts.length) {
|
|
30
|
+
result.height = 20;
|
|
31
|
+
result.paths.push({ d: `M${result.axis},0 V20` });
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
for (const [index, part] of parts.entries()) {
|
|
35
|
+
if (index) {
|
|
36
|
+
result.paths.push({ d: `M${result.axis},${result.height} V${result.height + 28}` });
|
|
37
|
+
result.height += 28;
|
|
38
|
+
}
|
|
39
|
+
append(result, part, result.axis - part.axis, result.height);
|
|
40
|
+
result.height += part.height;
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 条件未满足的路径在整个子树外侧绕行并汇合,包含并行网关时也不能跳过一半后进入内部支路。
|
|
46
|
+
* 中心轴保持子树入口,额外宽度只用于右侧旁路;祖先会按 axis 平移,避免嵌套时斜穿节点。
|
|
47
|
+
*/
|
|
48
|
+
function conditional(body, condition) {
|
|
49
|
+
const result = fragment(body.width + 76), title = wrapApprovalText(`${condition}?`, 13), height = Math.max(64, title.length * 36);
|
|
50
|
+
result.axis = body.axis;
|
|
51
|
+
result.boxes.push({ kind: 'condition', x: result.axis - 124, y: 0, width: 248, height, title, actor: [], rule: [] });
|
|
52
|
+
const top = height + 30, merge = top + body.height + 26;
|
|
53
|
+
append(result, body, 0, top);
|
|
54
|
+
result.paths.push({ d: `M${result.axis},${height} V${top}`, label: '是', x: result.axis + 16, y: height + 18 }, { d: `M${result.axis},${top + body.height} V${merge}` }, { d: `M${result.axis + 124},${height / 2} H${body.width + 38} V${merge} H${result.axis}`,
|
|
55
|
+
label: '否 · 跳过', x: body.width + 38, y: (height / 2 + merge) / 2, skip: true });
|
|
56
|
+
result.merges.push({ x: result.axis, y: merge });
|
|
57
|
+
result.height = merge;
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 并行分支分别测量,入口共享同一 fork,出口统一接 join;短分支等待线停留在自己通道内。
|
|
62
|
+
* 分支内部可以有条件或嵌套并行,所有子树完成后才绘制后续出口,不把任一分支画成可独自通过。
|
|
63
|
+
*/
|
|
64
|
+
function parallelFragment(parallel) {
|
|
65
|
+
const branches = parallel.branches.map(branch => ({ title: wrapApprovalText(branch.title, 14), body: groupsFragment(branch.groups) }));
|
|
66
|
+
const gap = 30, width = branches.reduce((sum, branch) => sum + branch.body.width, 0) + gap * (branches.length - 1);
|
|
67
|
+
const result = fragment(Math.max(348, width)), title = wrapApprovalText(`${parallel.title} · AND 分叉`, 24), forkHeight = 20 + title.length * 17;
|
|
68
|
+
result.boxes.push({ kind: 'fork', x: result.axis - 150, y: 0, width: 300, height: forkHeight, title, actor: [], rule: [] });
|
|
69
|
+
const splitY = forkHeight + 20, branchTop = splitY + 20;
|
|
70
|
+
let dx = 0, bottom = branchTop;
|
|
71
|
+
const ends = [];
|
|
72
|
+
for (const branch of branches) {
|
|
73
|
+
const axis = dx + branch.body.axis, labelHeight = 16 + branch.title.length * 17;
|
|
74
|
+
result.boxes.push({ kind: 'branch', x: axis - 124, y: branchTop, width: 248, height: labelHeight,
|
|
75
|
+
title: branch.title, actor: [], rule: [] });
|
|
76
|
+
const bodyTop = branchTop + labelHeight + 24;
|
|
77
|
+
result.paths.push({ d: `M${result.axis},${forkHeight} V${splitY} H${axis} V${branchTop}` }, { d: `M${axis},${branchTop + labelHeight} V${bodyTop}` });
|
|
78
|
+
append(result, branch.body, dx, bodyTop);
|
|
79
|
+
const end = bodyTop + branch.body.height;
|
|
80
|
+
ends.push({ x: axis, y: end });
|
|
81
|
+
bottom = Math.max(bottom, end);
|
|
82
|
+
dx += branch.body.width + gap;
|
|
83
|
+
}
|
|
84
|
+
const mergeY = bottom + 24, joinY = mergeY + 20, joinHeight = 40;
|
|
85
|
+
for (const end of ends)
|
|
86
|
+
result.paths.push({ d: `M${end.x},${end.y} V${mergeY} H${result.axis} V${joinY}` });
|
|
87
|
+
result.boxes.push({ kind: 'join', x: result.axis - 150, y: joinY, width: 300, height: joinHeight,
|
|
88
|
+
title: ['AND 汇合 · 等待全部活动分支'], actor: [], rule: [] });
|
|
89
|
+
result.height = joinY + joinHeight;
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
/** 将保留的声明组递归转换为几何,条件包裹整个子树,parallel 和普通 tasks 不混为串行。 */
|
|
93
|
+
function groupsFragment(groups) {
|
|
94
|
+
return sequence(groups.map(group => {
|
|
95
|
+
const body = group.parallel ? parallelFragment(group.parallel) : group.groups ? groupsFragment(group.groups) : sequence(group.tasks.map(taskFragment));
|
|
96
|
+
return group.condition ? conditional(body, group.condition) : body;
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 为带嵌套容器的审批投影生成完整布局;外层主流程使用返回宽高避免与邻接业务节点重叠。
|
|
101
|
+
* 结构输入由已校验声明派生;此处不重新执行条件,也不猜测当前实例哪些分支已跳过。
|
|
102
|
+
*/
|
|
103
|
+
export function layoutStructuredApproval(flow, name) {
|
|
104
|
+
const title = wrapApprovalText(name, 18), body = groupsFragment(flow.groups), top = 64 + title.length * 20;
|
|
105
|
+
const framed = fragment(body.width + 52);
|
|
106
|
+
append(framed, body, 26, top);
|
|
107
|
+
const footer = top + body.height + 42;
|
|
108
|
+
framed.paths.push({ d: `M${26 + body.axis},${top + body.height} V${footer - 16}` });
|
|
109
|
+
return { flow, title, width: framed.width, height: footer + 60, boxes: framed.boxes, paths: framed.paths, merges: framed.merges, footer };
|
|
110
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 审批声明 JSON 的只读结构与遍历工具,供本体抽取、字段关系说明和静态流程图共同消费。
|
|
3
|
+
* 仅遍历 when、parallel 和 branch 保留的声明树,不执行条件、选人、审批引擎或实际实例状态。
|
|
4
|
+
* 浏览器可引用这些类型和纯函数,不引入 approval 运行时;标题和依赖始终来自抽取快照。
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 按声明的深度优先次序遍历全部节点,保留容器供条件解释使用,分支本身不是可执行步骤。
|
|
8
|
+
* 本函数不排序或改写源树;声明深度/数量由注册器限制,序号只能作为静态定位,不能当执行先后。
|
|
9
|
+
*/
|
|
10
|
+
export function approvalRouteNodes(nodes) {
|
|
11
|
+
return nodes.flatMap(node => [node, ...(node.kind === 'when' ? approvalRouteNodes(node.steps) :
|
|
12
|
+
node.kind === 'parallel' ? node.branches.flatMap(branch => approvalRouteNodes(branch.nodes)) : [])]);
|
|
13
|
+
}
|
|
14
|
+
/** 收集全部人工/AI 叶节点,用于取证与选人依赖;并行结构仍需由调用方从原树单独保留。 */
|
|
15
|
+
export function approvalRouteSteps(nodes) {
|
|
16
|
+
return approvalRouteNodes(nodes).filter((node) => node.kind === 'step' || node.kind === 'ai');
|
|
17
|
+
}
|
package/graph-ui/business.css
CHANGED
|
@@ -553,6 +553,22 @@
|
|
|
553
553
|
font-size: 12px;
|
|
554
554
|
font-weight: 500;
|
|
555
555
|
}
|
|
556
|
+
/* AND 网关与分支标题使用独立形状,不能被误认成一个需要审批的任务;坐标由递归测量布局提供。 */
|
|
557
|
+
.approval-gateway-shape {
|
|
558
|
+
fill: #e9f0e4;
|
|
559
|
+
stroke: #72916f;
|
|
560
|
+
stroke-width: 1.5;
|
|
561
|
+
}
|
|
562
|
+
.approval-gateway-title {
|
|
563
|
+
fill: #35573b;
|
|
564
|
+
font-size: 12px;
|
|
565
|
+
font-weight: 600;
|
|
566
|
+
}
|
|
567
|
+
.approval-branch-shape {
|
|
568
|
+
fill: #f2f5ed;
|
|
569
|
+
stroke: #b5c5ab;
|
|
570
|
+
stroke-dasharray: 4 3;
|
|
571
|
+
}
|
|
556
572
|
.approval-flow-merge {
|
|
557
573
|
fill: #8ba28e;
|
|
558
574
|
}
|
|
@@ -781,6 +797,16 @@
|
|
|
781
797
|
.business-approval-steps p {
|
|
782
798
|
font-size: 10px;
|
|
783
799
|
}
|
|
800
|
+
/* 并行分支在规则详情中保留树层级,独立左边线区分分支,不靠列表编号暗示执行先后。 */
|
|
801
|
+
.business-approval-branches {
|
|
802
|
+
margin: 8px 0;
|
|
803
|
+
padding-left: 16px;
|
|
804
|
+
list-style: none;
|
|
805
|
+
border-left: 2px solid #b5c5ab;
|
|
806
|
+
}
|
|
807
|
+
.business-approval-branches > li {
|
|
808
|
+
margin: 10px 0 14px;
|
|
809
|
+
}
|
|
784
810
|
/* 源码预览按行显示并保留原始缩进,横向溢出在代码框内滚动;行号只参与显示。 */
|
|
785
811
|
.source-preview {
|
|
786
812
|
min-width: 0;
|
package/graph-ui/business.js
CHANGED
|
@@ -496,20 +496,37 @@ export function renderBusiness(host, model, snapshot, actions) {
|
|
|
496
496
|
}
|
|
497
497
|
const approvalFlow = approvals.get(selected.id);
|
|
498
498
|
if (approvalFlow) {
|
|
499
|
-
const rules = section('审批步骤与条件', '
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
row
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
499
|
+
const rules = section('审批步骤与条件', '顺序节点依次执行;并行分支同时开始,全部活动分支完成后汇合。条件在启动时判断。');
|
|
500
|
+
/** 阅读说明与图保持同一嵌套结构,编号只作定位,不把兄弟分支写成前后执行。 */
|
|
501
|
+
const describeGroups = (groups) => {
|
|
502
|
+
const list = h('ol', 'business-approval-steps');
|
|
503
|
+
for (const group of groups) {
|
|
504
|
+
const row = h('li');
|
|
505
|
+
if (group.condition)
|
|
506
|
+
row.append(h('strong', '', `当${group.condition}时`));
|
|
507
|
+
for (const task of group.tasks) {
|
|
508
|
+
row.append(h('strong', '', `${task.number}. ${task.title}`));
|
|
509
|
+
row.append(h('p', '', `审批人:${task.actor} · ${task.rule}`));
|
|
510
|
+
}
|
|
511
|
+
if (group.groups)
|
|
512
|
+
row.append(describeGroups(group.groups));
|
|
513
|
+
if (group.parallel) {
|
|
514
|
+
row.append(h('strong', '', `${group.parallel.title} · AND 并行`));
|
|
515
|
+
const branches = h('ul', 'business-approval-branches');
|
|
516
|
+
for (const branch of group.parallel.branches) {
|
|
517
|
+
const branchRow = h('li');
|
|
518
|
+
branchRow.append(h('strong', '', branch.title), describeGroups(branch.groups));
|
|
519
|
+
branches.append(branchRow);
|
|
520
|
+
}
|
|
521
|
+
row.append(branches, h('p', 'source-note', '各分支同时开始;全部活动分支完成 → AND 汇合后继续'));
|
|
522
|
+
}
|
|
523
|
+
if (group.condition)
|
|
524
|
+
row.append(h('p', 'source-note', '条件不满足 → 跳过整组,继续后续步骤'));
|
|
525
|
+
list.append(row);
|
|
508
526
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
}
|
|
527
|
+
return list;
|
|
528
|
+
};
|
|
529
|
+
const list = describeGroups(approvalFlow.groups);
|
|
513
530
|
rules.append(list, h('p', '', approvalFlow.route
|
|
514
531
|
? '所需步骤全部通过后继续业务;任一步骤驳回则结束本轮审批。'
|
|
515
532
|
: '未抽取到绑定的审批步骤,请查看审批声明或外部流程配置。'));
|
|
@@ -50,16 +50,29 @@ export function approvalFlowForStep(model, step, labels = {}) {
|
|
|
50
50
|
? `${fieldName(item.approver.field)}的负责人`
|
|
51
51
|
: item.approver.kind === 'role'
|
|
52
52
|
? (labels[`role:${item.approver.code}`] ?? '待命名审批角色')
|
|
53
|
-
: `指定用户 ${item.approver.userId}`,
|
|
54
|
-
rule: item.mode === 'all' ? '所有审批人同意后继续' : '任一审批人同意即可继续',
|
|
53
|
+
: item.approver.kind === 'field' ? `${fieldName(item.approver.field)}指定的审批人` : `指定用户 ${item.approver.userId}`,
|
|
54
|
+
rule: [item.mode === 'all' ? '所有审批人同意后继续' : item.actions?.addSign ? '任一原审批人同意' : '任一审批人同意即可继续',
|
|
55
|
+
...(item.assignment === 'candidate' ? ['候选共享一项任务,领取后处理'] : []),
|
|
56
|
+
...(item.actions?.addSign ? ['支持前签、后签和并行加签,全部加签人同意后继续'] : []),
|
|
57
|
+
...(item.actions?.transfer ? ['允许转办'] : []), ...(item.actions?.delegate ? ['允许委派,归还后由原负责人决定'] : []),
|
|
58
|
+
...(item.sla ? [`节点总时限 ${item.sla.durationMinutes} 自然分钟${item.sla.remindBeforeMinutes ? `,提前 ${item.sla.remindBeforeMinutes} 分钟提醒` : ''};超时仅提醒`] : [])].join(';'),
|
|
55
59
|
mode: item.mode,
|
|
56
60
|
});
|
|
61
|
+
/** 条件内只有普通叶步骤时保留原协议;含嵌套条件/并行时保留树,避免旧图把分支误连成一条链。 */
|
|
62
|
+
const groups = (nodes) => nodes.map(item => {
|
|
63
|
+
if (item.kind === 'parallel')
|
|
64
|
+
return { tasks: [], parallel: { title: item.title,
|
|
65
|
+
branches: item.branches.map(branch => ({ title: branch.title, groups: groups(branch.nodes) })) } };
|
|
66
|
+
if (item.kind === 'when')
|
|
67
|
+
return { condition: conditionName(item.cond),
|
|
68
|
+
...(item.steps.every(node => node.kind === 'step' || node.kind === 'ai') ?
|
|
69
|
+
{ tasks: item.steps.map(node => task(node)) } : { tasks: [], groups: groups(item.steps) }) };
|
|
70
|
+
return { tasks: [task(item)] };
|
|
71
|
+
});
|
|
57
72
|
return {
|
|
58
73
|
approval,
|
|
59
74
|
route,
|
|
60
|
-
groups: route.attrs.nodes
|
|
61
|
-
? { tasks: [task(item)] }
|
|
62
|
-
: { condition: conditionName(item.cond), tasks: item.steps.map(task) }),
|
|
75
|
+
groups: groups(route.attrs.nodes),
|
|
63
76
|
};
|
|
64
77
|
}
|
|
65
78
|
/** 从真实驳回结论绑定辨认出口,不依赖阶段标题里是否含有“驳回”二字。 */
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* 不求值 handler、映射或审批条件;存在连线只代表对应声明关系,不证明某次操作已发生。
|
|
5
5
|
*/
|
|
6
6
|
import { kindLabels, nodeLabel } from './graph-model.js';
|
|
7
|
+
import { approvalRouteNodes, approvalRouteSteps } from './approval-tree.js';
|
|
7
8
|
/**
|
|
8
9
|
* 按真实关系种类解释连线,补充声明来源和证据强度。
|
|
9
10
|
* @param edge 已投影且两端有效的边。
|
|
@@ -76,13 +77,14 @@ export function explainEdge(edge, model, labels = {}) {
|
|
|
76
77
|
const as = edge.as ?? '';
|
|
77
78
|
if (as === '可路由字段')
|
|
78
79
|
return result('允许审批读取', '审批可用字段', `“${a}”把“${b}”列入 routable,允许该字段作为审批流程变量。`, '明确审批可以拿到哪些业务数据。这里只表示允许使用;是否真的参与分支或审批选人,要查看审批路由的字段关系。', [`${from.id}.routable 包含 ${field}`]);
|
|
80
|
+
if (as === '催办申请人')
|
|
81
|
+
return result('确定催办申请人', '流程运营依赖', `“${a}”以提交时“${b}”的用户标识确定可催办申请人。`, '字段必须进入 routable;催办仍校验当前对象可见性、运行节点、业务版本及冷却,不赋予审批权限。字段也可能被选人或条件使用,完整用途见路由声明。', [`route.reminders.requesterField = ${JSON.stringify(field)}`]);
|
|
79
82
|
if (as === '路由条件') {
|
|
80
|
-
// fieldsUsed 同时包含 when
|
|
83
|
+
// fieldsUsed 同时包含 when 条件、部门负责人和单据人员字段,必须区分实际选人方式。
|
|
81
84
|
const nodes = from.attrs.nodes;
|
|
82
|
-
const conditions = nodes.filter((n) => n.kind === 'when' && n.cond.field === field);
|
|
83
|
-
const steps = nodes
|
|
84
|
-
.
|
|
85
|
-
.filter((s) => (s.approver ?? s.handoff)?.kind === 'deptLeader' && (s.approver ?? s.handoff)?.field === field);
|
|
85
|
+
const conditions = approvalRouteNodes(nodes).filter((n) => n.kind === 'when' && n.cond.field === field);
|
|
86
|
+
const steps = approvalRouteSteps(nodes).map(step => ({ ...step, selector: step.kind === 'ai' ? step.handoff : step.approver }))
|
|
87
|
+
.filter(step => (step.selector.kind === 'deptLeader' || step.selector.kind === 'field') && step.selector.field === field);
|
|
86
88
|
const operators = {
|
|
87
89
|
gt: '大于',
|
|
88
90
|
gte: '大于等于',
|
|
@@ -93,11 +95,11 @@ export function explainEdge(edge, model, labels = {}) {
|
|
|
93
95
|
in: '属于',
|
|
94
96
|
};
|
|
95
97
|
const reasons = [
|
|
96
|
-
...steps.map((s) => `“${s.title}”步骤根据“${b}”找到对应部门的负责人`),
|
|
97
|
-
...conditions.map((n) => `当“${b}”${operators[n.cond.op]} ${JSON.stringify(n.cond.value)} 时,加入${n.steps.map((s) => `“${s.title}”`).join('、')}
|
|
98
|
+
...steps.map((s) => s.selector.kind === 'field' ? `“${s.title}”步骤读取“${b}”保存的审批人员` : `“${s.title}”步骤根据“${b}”找到对应部门的负责人`),
|
|
99
|
+
...conditions.map((n) => `当“${b}”${operators[n.cond.op]} ${JSON.stringify(n.cond.value)} 时,加入${approvalRouteSteps(n.steps).map((s) => `“${s.title}”`).join('、')}步骤(内部顺序或并行关系见路由)`),
|
|
98
100
|
];
|
|
99
101
|
return result(steps.length && conditions.length ? '选人和分支依据' : steps.length ? '确定审批人' : '判断审批分支', '审批路由依赖', `${reasons.join(';')}。`, '字段值会影响审批人或需要经过的审批步骤;修改字段含义时,应一起检查这些路由配置。', [
|
|
100
|
-
...steps.map((s) => `${s.title}: approver.
|
|
102
|
+
...steps.map((s) => `${s.title}: approver.${s.selector.kind}(${JSON.stringify(field)})`),
|
|
101
103
|
...conditions.map((n) => `when(${n.cond.op}(${JSON.stringify(field)}, ${JSON.stringify(n.cond.value)}))`),
|
|
102
104
|
]);
|
|
103
105
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yss123/cage-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "cage 命令行:init、gen、check、graph、context、impact、test、verify、budget、sync、upgrade、dev、migrate",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"typescript": "~5.9.3",
|
|
32
32
|
"vitest": "^4.1.12",
|
|
33
33
|
"zod": "^4.1.5",
|
|
34
|
-
"@yss123/cage-
|
|
35
|
-
"@yss123/cage-
|
|
36
|
-
"@yss123/cage-
|
|
37
|
-
"@yss123/cage-lint": "0.3.
|
|
38
|
-
"@yss123/cage-
|
|
34
|
+
"@yss123/cage-admin": "0.3.2",
|
|
35
|
+
"@yss123/cage-testkit": "0.3.2",
|
|
36
|
+
"@yss123/cage-ontology": "0.3.2",
|
|
37
|
+
"@yss123/cage-lint": "0.3.2",
|
|
38
|
+
"@yss123/cage-kernel": "0.3.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/pg": "^8.15.5"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cage-development
|
|
3
|
+
description: 使用 Cage 框架开发、修改和排查业务应用。适用于依赖 @yss123/cage-*、具有 cage.config.json 的项目,与用户澄清需求、比较方案,再完成业务建模、框架接入与验收;维护框架内部实现时先遵守框架仓库的 AGENTS.md。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- 由 scripts/sync-agent-skill.ts 生成;教学正文只在 docs/development 维护。 -->
|
|
7
|
+
|
|
8
|
+
加载本技能后,先阅读[完整框架开发方法](../../../docs/development/skills/cage-development/SKILL.md),再根据其中的任务选择表读取相关专题。
|
|
9
|
+
正文包含 Cage 运行模型、与用户澄清业务并选择方案的方法、真实接入、排错与验收;不要只凭本入口的摘要开始编写业务。
|
|
10
|
+
当前项目的业务要求与 AGENTS.md 仍适用,框架维护任务遵守仓库的包边界。
|
package/templates/app/AGENTS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
这是 create-cage-app 生成的业务项目,当前包含采购演示,可逐步替换成客户业务。业务源码在本项目,框架通过 @yss123/cage-* 依赖提供。
|
|
4
4
|
|
|
5
|
-
先读 [业务开发指南](docs/
|
|
5
|
+
先读 [业务开发指南](docs/development/README.md),按需求查 [任务配方](docs/development/guides/recipes.md)、[完整采购范例](docs/development/examples/purchase-example.md) 和 [错误码册](docs/errors/README.md)。这些资料就在项目内,无需克隆框架仓库。
|
|
6
6
|
|
|
7
7
|
## 项目自己的业务约定
|
|
8
8
|
|
|
@@ -10,127 +10,44 @@
|
|
|
10
10
|
|
|
11
11
|
本节位于托管区之外,cage sync 会保留。下面的托管区提供框架工作方法。
|
|
12
12
|
|
|
13
|
-
<!-- cage:begin v0.3.
|
|
13
|
+
<!-- cage:begin v0.3.2 -->
|
|
14
14
|
## Cage 业务开发约定(框架托管)
|
|
15
15
|
|
|
16
|
-
###
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
| `packages/domain/src/modules/<业务>` | 同业务聚合、状态、命令、事件、审批、查询、任务;模块 index.ts 公开导出 |
|
|
54
|
-
| `packages/domain/src/workflows`、`integrations` | 跨模块反应 / 第三方协议与映射;不堆到全局 reactions.ts、queries.ts |
|
|
55
|
-
| `packages/domain/src/index.ts` | 统一导出全部业务及组织模型;没导出的声明不会注册 |
|
|
56
|
-
| `packages/contracts/src/aggregates/<聚合>`、`queries` | 生成协议;根 index.ts 提供 @app/contracts 公共入口,只用 cage gen 更新 |
|
|
57
|
-
| `apps/api/src` | API/worker 薄启动入口,普通业务不手写 HTTP 路由 |
|
|
58
|
-
| `apps/web/src/app` | 会话、URL 路由、布局、模块组合;main.tsx 只挂载和装配 Provider |
|
|
59
|
-
| `apps/web/src/modules/<业务>` | 页面、表单、专用 hooks/API/样式;从模块 index.ts 暴露页面 |
|
|
60
|
-
| `apps/web/src/shared` | 跨模块复用且不绑定业务的 UI、hooks、纯辅助函数 |
|
|
61
|
-
| `db/migrations` | 审查过的结构迁移,作用于已配置的数据库 |
|
|
62
|
-
| `cage.config.json` | 项目路径、检查配置、graph.labels 中文图谱名称 |
|
|
63
|
-
| `.env` | 本机真实连接和密钥;不提交、不输出完整内容 |
|
|
64
|
-
|
|
65
|
-
新增业务不得继续平铺在 src 根目录。前端模块只依赖自己和 shared,由 app 通过公开入口组合;shared 不反向依赖业务。领域模块间确有读取依赖时走目标模块 index.ts;跨模块反应写 workflows。小模块不创建空层级,已有独立职责再拆文件。完整命名、目录树和示例见 `docs/ai/project-structure.md`。新模板启用 `structure: modular`,check/verify 检查目录和导入边界,遇到 CAGE-STRUCT 错误按职责修正。
|
|
66
|
-
|
|
67
|
-
### 实现一项需求的顺序
|
|
68
|
-
|
|
69
|
-
1. 先运行 `pnpm exec cage context "需求关键词"`;按需 `graph search` 查候选、`graph inspect` 查完整节点、`graph path` 看关系路径。确认中文匹配入口后按源码位置阅读实现;候选和子图不是全部依赖,具体条件不能凭连线推断。完整用法见 `docs/ai/ontology-context.md`。从用户需求确定字段、状态/动作、角色/范围、约束与后续动作;能由业务声明表达的不改框架。
|
|
70
|
-
2. 已有业务含义变化时,修改前用 `pnpm exec cage snapshot save before-change` 保存不可覆盖的命名基线;或使用真实 Git 提交/独立本体图文件。
|
|
71
|
-
3. 先声明 aggregate/states/command/constraints/event,再写 handler、reaction/query/approval/connector,最后接业务页面。声明使用模块顶层 `export const`,从 domain 入口导出,跨文件导出名保持唯一。
|
|
72
|
-
4. 运行 `pnpm exec cage check` 和 `pnpm gen`,审查 contracts 和迁移草稿。按需运行 `pnpm exec cage impact --snapshot before-change`,补中文图谱名。
|
|
73
|
-
5. 页面使用生成方法并处理 code/domainCode;列表、详情等多页面注册到 React Router,Link/NavLink 导航,筛选/分页进入 URL。组件状态用于弹窗和表单等临时交互。新增能力由管理员授权。数据库结构变化审查后显式 `pnpm migrate`,dev 不代办迁移。
|
|
74
|
-
6. 按 `commenting.md` 逐文件检查详细中文注释与实现一致,再按改动运行必要验证;交付时说明体验步骤、授权配置、迁移、注释覆盖,以及通过/跳过/未执行项。
|
|
75
|
-
|
|
76
|
-
### 必守边界
|
|
77
|
-
|
|
78
|
-
- 聚合定义对象字段、状态、范围字段与不变量;命令是业务写入口。handler 只计算业务变化和读取 reads 中的依赖,不直接操作数据库、网络或文件。
|
|
79
|
-
- 对象关系统一放在 fields:ref 为外键,hasMany 为只读反查,manyToMany 自动生成中间表并随所属对象命令提交。前端用生成的 related 方法分页读取;不手写纯中间表聚合,不把单页结果当完整集合替换。完整例子见 docs/ai/relations.md。
|
|
80
|
-
- command 的 input 使用 Zod;已有记录输入必须含 id。from/to 表达状态迁移;`to: null` 只适用于已有记录并保留状态,不能靠 handler 改 status 绕过状态机。
|
|
81
|
-
- 写入、审计、版本、幂等和事务交给内核;不要在业务项目重建 repository、连接池或权限流水线。
|
|
82
|
-
- 自定义查询声明 reads,二选一使用 sql/build,只读 `safe.*`;值参数化。不得拼接 SQL、查询基表绕过范围或硬编码密钥。
|
|
83
|
-
- scope 描述范围字段,角色数据决定实际范围;`scope.none()` 不代表所有人可访问。`can/useCan` 只控制按钮,后端负责授权。
|
|
84
|
-
- 前端从 `@app/contracts` 导入生成 API/类型,按需使用 `@yss123/cage-client`;不导入 domain、内核或数据库包。
|
|
85
|
-
- 多页面业务必须有独立 URL,不用 useState/tab 模拟整页导航;详情从 URL 取 ID 并独立加载。前端路由集中在 app,页面从模块公开入口接入 Outlet。登录后保留原路径/query/hash,401 回登录,403 展示权限提示;/admin/ 使用普通链接进入独立系统管理。前端路由不替代后端授权。实现、SPA 部署回退和浏览器配方见 docs/ai/routing.md。
|
|
86
|
-
- 跨聚合写入通过 reaction 调用命令。`tx: 'same'` 共同提交/回滚,默认 outbox 异步执行。资源 acquires/releases 是图规则标签,实际占用/释放仍由命令完成;离开持有状态要检查每条释放路径。
|
|
87
|
-
- 批量调用同一命令使用 reactionEach,声明 maxItems、唯一排序字段 orderBy、items 和 map;稳定键排序后串行执行。步骤依赖用 order,同事件同事务分组不得混用有序/无序或重复顺序。保持同事务回滚边界,见 docs/ai/primitives.md。
|
|
88
|
-
- 审批通过 approval + route 配置,结论是 process 命令;选中审批人不等于已具备能力、范围和额度。第三方 IO 通过连接器,回调验签后调用 external 命令。
|
|
89
|
-
- job 的读取与调用必须列入 reads/calls。业务导入/导出放在需要的业务页面调用通用任务接口,202 只代表任务已接受;worker 必须运行。
|
|
90
|
-
- 已部署事件只做兼容扩展;新增字段设为 optional,不兼容含义用新事件。已执行/已发布迁移不能改写,结构更新新增迁移。
|
|
91
|
-
- 不手改生成物,不关闭门禁掩盖错误,不因方便自动授予生产角色新能力。unsafe 必须写明 reason/owner,不能替代普通业务声明。
|
|
92
|
-
- 按前述详细中文注释规范完成文件、API、业务声明和关键流程说明;避免无业务价值的防御分支和静默兜底。维护脚本用 TypeScript,不新增 .mjs。
|
|
93
|
-
|
|
94
|
-
### 最小命令形状
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
// 片段:Order 为已导出的聚合,OrderSubmitted 为事件,业务校验按需求添加。
|
|
98
|
-
/**
|
|
99
|
-
* 将草稿订单提交审批:只有 draft 可进入 pending_approval。
|
|
100
|
-
* 入口使用 Order.submit 能力,真实授权、范围、状态和版本由内核检查;声明本身不自动授予权限。
|
|
101
|
-
* handler 返回草稿供后续校验和持久化,OrderSubmitted 随命令事务登记,后续由已配置消费者处理。
|
|
102
|
-
* 失败不会提交这次业务变更;事件已登记也不代表后续审批已经完成。
|
|
103
|
-
*/
|
|
104
|
-
export const submitOrder = command(Order, {
|
|
105
|
-
name: 'submit',
|
|
106
|
-
from: 'draft',
|
|
107
|
-
to: 'pending_approval',
|
|
108
|
-
emits: [OrderSubmitted],
|
|
109
|
-
handler: (draft) => draft,
|
|
110
|
-
});
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
完整可添加的普通管理对象见 `docs/ai/primitives.md` 的 Note;采购模板的审批、资源、第三方示例在 `packages/domain/src`,空白模板的参考源码在 `docs/examples/purchase-admin`。先读本项目 AGENTS.md 中的模板说明,区分运行代码与参考资料。API 以当前包公开导出和生成契约为准,不发明不存在的配置和方法。
|
|
114
|
-
|
|
115
|
-
### 启动、验证与排错
|
|
116
|
-
|
|
117
|
-
在项目根目录执行:首次 `pnpm install`,复制并编辑 .env.example,仅提供已有库管理员连接后 `pnpm exec cage init`;再 `pnpm build:app`、`pnpm gen`,审查后 `pnpm migrate`;用 `pnpm exec cage bootstrap --credentials-file .cage/admin-credentials.json` 显式初始化组织,最后 `pnpm dev`。已有配置不能覆盖,凭据文件必须是新受限文件且父目录存在。
|
|
118
|
-
|
|
119
|
-
init 只准备账号/schema/授权并填写三个连接,不创建数据库;APP/READ/MIGRATE 指向同一个库。已有集群账号复用原密码,日常启动不用管理员。普通项目不新增 Docker 生命周期命令。
|
|
120
|
-
|
|
121
|
-
API 日常启动不初始化组织、不输出管理员密码;生产缺少合格 Cookie 密钥会拒绝启动。bootstrap 不自动授予业务能力。`/healthz` 只表示存活,`/readyz` 检查必要依赖。首阶段 API 与 worker 同版本部署,查看实际注册指纹和消费者心跳。
|
|
122
|
-
|
|
123
|
-
- `pnpm exec cage graph`:浏览器图谱,默认端口 15418,以终端为准。
|
|
124
|
-
- `pnpm exec cage context command:对象.命令 --format json --out .cage/context.json`:按完整 key 取精简上下文;默认 Markdown,可用 --depth/--limit 调整范围。CLI 每次重新抽取;Web“复制 AI 上下文”使用当前页面快照,改代码后先刷新图谱。
|
|
125
|
-
- `graph inspect/path` 有歧义时复制候选完整 key;path 默认沿箭头,--direction both 的逆向遍历不表示执行方向。报告中的测试/前端文件是候选,须核对;context 与图谱查询不覆盖当前图或命名快照。
|
|
126
|
-
- `pnpm exec cage capabilities 关键词 --format json`:查当前安装版本公开签名;`cage doctor`:区分注册、生成、构建、迁移、授权与实际进程证据,unknown 不能当作通过。`cage timeline/explain` 关联实际异步证据与解释授权,参数和权限见 docs/ai/diagnostics.md。
|
|
127
|
-
- 生成方法支持 signal/timeoutMs/idempotencyKey 请求选项。底层 CageResult 与 mutation 行为有区别:useCageCommand 失败抛 CageCommandError,成功才返回业务数据。订阅 client.subscribe 在退出、401、切换账号时取消查询并清除应用缓存。
|
|
128
|
-
- `pnpm exec cage gen --check`:生成一致性;`pnpm exec cage sync --check`:本托管区一致性。
|
|
129
|
-
- `pnpm exec cage test --only project`:业务测试;`pnpm verify`:类型、依赖、lint、图规则、生成、托管区、预算和测试。
|
|
130
|
-
- `pnpm exec cage verify --skip-tests` 表示不完整门禁;派生测试跳过不能当作通过。测试连接只用独立测试集群。
|
|
131
|
-
- 重要业务改动验证成功路径及有风险的反例;文档或可逆的小改动不反复跑全量数据库测试。
|
|
132
|
-
- 修改页面/导航后实际验收深链接、刷新、前进后退、登录回跳、无权限和 404;生产再查 SPA 回退。check/verify 不自动完成浏览器验收,未执行要明确报告。见 docs/ai/routing.md。
|
|
133
|
-
- CLI 报错先看 code/at/reason/fix,再查错误码册;HTTP 错误查 runtime.md。异步失败先看 worker、同步记录和死信,修复后重放,不能直接 SQL 改状态。
|
|
134
|
-
|
|
135
|
-
框架托管区由 cage sync 更新;项目独有的业务规则写在标记之外,避免升级覆盖。
|
|
16
|
+
### 学习与任务入口
|
|
17
|
+
|
|
18
|
+
本项目使用 `@yss123/cage-*`,业务写在本项目。先读项目说明指定的教材;独立项目默认入口是 `docs/development/README.md`。
|
|
19
|
+
|
|
20
|
+
开始 Cage 业务开发或排错时,加载 `docs/development/skills/cage-development/SKILL.md`。它讲清运行模型、业务澄清、能力选择、实施与验收方法;首次学习配合 `docs/development/getting-started/first-feature.md`。已熟悉时只读受影响专题,普通样式/文案改动无需重走业务建模。
|
|
21
|
+
|
|
22
|
+
支持 Agent Skills 的工具可发现 `.agents/skills/cage-development`;其他工具直接读取上述 Markdown,使用相同流程。教材随框架版本分发,均为本地文件,不需要额外服务、模型或索引。框架仓库范例使用项目说明指定的规范教材位置。
|
|
23
|
+
|
|
24
|
+
### 框架执行什么
|
|
25
|
+
|
|
26
|
+
- 业务声明从 domain 总入口导出,框架据此构建注册表、能力、HTTP 接口、本体图、契约与迁移草稿;只有文件而未导出不会生效。
|
|
27
|
+
- 命令是业务写入口;handler 描述变化与显式 reads,内核处理授权、状态、范围、版本、事务、审计及 outbox。读侧通过生成契约或 query 的安全视图,外部 IO 通过连接器。
|
|
28
|
+
- 角色、能力、数据范围、调用身份与业务约束分别建模。前端 can() 只影响显示,新增能力不自动授给生产角色;审批选人不授予能力。
|
|
29
|
+
- 页面依赖生成契约及客户端。表单、弹窗等临时状态留在前端;正式业务数据和流程结果通过服务端读写。实际记录不能用模拟仓储或固定统计替代。
|
|
30
|
+
|
|
31
|
+
### 实现与排错
|
|
32
|
+
|
|
33
|
+
1. 阅读需求、文档和现有实现后,按 `docs/development/guides/business-collaboration.md` 主动提出疑点、可行选项、影响与推荐理由,让用户选择业务规则;沿用已明确的选择与授权,等待回答时推进独立部分,可选建议不阻塞已明确任务。再把确定的规则映射到聚合、命令、归属/范围、后续行为与验收,沿已有模块修改。具体选择看 `docs/development/guides/recipes.md`,目录归属看 `docs/development/reference/project-structure.md`。
|
|
34
|
+
2. 不熟悉的 API 先按手册任务目录定位,用本地文件搜索查术语,再执行 `pnpm exec cage capabilities 符号 --format json` 核对当前版本签名;`cage context "业务关键词"` 定位当前应用。未命中不代表不支持,不发明方法或返回字段。
|
|
35
|
+
3. 涉及已有领域含义变化,先 `pnpm exec cage snapshot save before-change` 保存本体基线,或使用真实 Git 基线。修改声明与导出,运行 check/gen,审查契约和迁移草稿。生成物不手改,已执行迁移不可改写。
|
|
36
|
+
4. 页面使用真实契约,处理业务失败、版本冲突、会话与缓存;多页面使用独立 URL,列表筛选/分页进入查询参数。按 `docs/development/guides/routing.md` 验收深链接、刷新、登录回跳和无权限行为。
|
|
37
|
+
5. 出错保留 code/at/reason/fix,查 `docs/errors/README.md` 与 `docs/development/tools/troubleshooting.md`;从真实调用逐层定位。不得放宽权限、删约束或关闭门禁来掩盖问题。
|
|
38
|
+
|
|
39
|
+
遇到原生 AI、AI 审批或随页助理,先读 `docs/development/guides/intelligence.md`,核对已有能力及应用启用状态。审批、文件、关系、数据传输等专题由 Skill 按需引导,不重建框架已有运行时。
|
|
40
|
+
|
|
41
|
+
### 代码与环境边界
|
|
42
|
+
|
|
43
|
+
- 所有手写代码遵守 `docs/development/reference/commenting.md`:文件职责与调用链、导出 API/字段、复杂分支的中文说明,写清做什么、为什么、怎样完成及实际边界;随代码更新。生成物注释从生成器维护。
|
|
44
|
+
- domain 按 modules/workflows/integrations/shared 组织,Web 按 app/modules/shared 组织。模块通过 index.ts 公开入口组合,前端不导入 domain、数据库或内核运行时;不关闭 modular 目录门禁。
|
|
45
|
+
- 新项目启动步骤见 `docs/development/getting-started/workflow.md`。使用已有 PostgreSQL 和项目 .env;init 准备账号,migrate 更新结构,bootstrap 显式初始化组织,dev 启动进程。普通业务不新增连接池或管理 Docker。
|
|
46
|
+
- 不输出或提交 .env、数据库密码、Cookie 密钥及初始化凭据。API 与 worker 使用一致的 domain,修改配置后按实际需要重启。
|
|
47
|
+
|
|
48
|
+
### 完成依据
|
|
49
|
+
|
|
50
|
+
验证与改动匹配。check/gen/typecheck/build 证明各自检查范围;业务读写还需独立读取结果,权限需直接请求验证,审批需待办与结论,文件需实际内容,异步需任务终态,页面需浏览器操作。
|
|
51
|
+
|
|
52
|
+
`pnpm verify` 汇总门禁;`verify --skip-tests` 不代表完整通过。诊断用 `cage doctor`、`timeline`、`explain`,具体前提见 `docs/development/tools/diagnostics.md`。交付分别说明通过、跳过、未执行及剩余配置;文档和可逆小改动不反复执行全量数据库回归。
|
|
136
53
|
<!-- cage:end -->
|
package/templates/app/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
## 让 AI 在本项目开发业务
|
|
6
6
|
|
|
7
|
-
把本目录交给 Codex 等编程工具时,先让它读 [AGENTS.md](AGENTS.md),再按[业务开发指南](docs/
|
|
7
|
+
把本目录交给 Codex 等编程工具时,先让它读 [AGENTS.md](AGENTS.md),再按[业务开发指南](docs/development/README.md)和[任务配方](docs/development/guides/recipes.md)实现需求。普通业务不需要修改框架源码。
|
|
8
8
|
|
|
9
|
-
[采购全链路导读](docs/
|
|
9
|
+
[采购全链路导读](docs/development/examples/purchase-example.md)说明每个文件和异步步骤;[原语与最小业务](docs/development/reference/primitives.md)提供可添加的普通管理对象;[错误码册](docs/errors/README.md)用于按码修复。脚手架会将这些资料放入生成项目本地的 `docs/development` 与 `docs/errors`,无需再次克隆框架。
|
|
10
10
|
|
|
11
11
|
## 启动
|
|
12
12
|
|
|
@@ -54,15 +54,16 @@ pnpm dev
|
|
|
54
54
|
|
|
55
55
|
## 体验完整采购流程
|
|
56
56
|
|
|
57
|
-
前端使用 React Router:工作台 `/workspace`,采购列表 `/procurement/orders`,单据详情 `/procurement/orders/:id`,审批 `/approvals`,库存 `/inventory`,登录 `/login`。单号和审批待办可直接打开详情;刷新、前进后退和登录回跳保留页面地址。新增页面与部署按[路由和浏览器验收指南](docs/
|
|
57
|
+
前端使用 React Router:工作台 `/workspace`,采购列表 `/procurement/orders`,单据详情 `/procurement/orders/:id`,审批 `/approvals`,库存 `/inventory`,登录 `/login`。单号和审批待办可直接打开详情;刷新、前进后退和登录回跳保留页面地址。新增页面与部署按[路由和浏览器验收指南](docs/development/guides/routing.md)执行;Nginx 示例见 [deploy/nginx.conf](deploy/nginx.conf)。
|
|
58
58
|
|
|
59
59
|
1. 管理员在“库存台账”登记 `A-01` 等物料。
|
|
60
60
|
2. 进入 `/admin/` 创建采购员、经理,分别分配采购命令和审批能力;经理的 `PurchaseOrder.approve` 参数配置 `ceiling`。
|
|
61
61
|
3. 把经理设为采购员所在部门的负责人。提单人与审批人必须不同。
|
|
62
|
-
4.
|
|
63
|
-
5.
|
|
64
|
-
6.
|
|
65
|
-
7.
|
|
62
|
+
4. 为需要选人的采购参与者授予 `ApprovalMember.read` 与该聚合 `all` 范围,通过 `ApprovalMember.create` 登记审批协作人的公开业务姓名与专业领域;名录不授予实际审批能力。
|
|
63
|
+
5. 采购员新建采购单,可开启专业复核并选择 1–5 人;草稿使用 `PurchaseOrder.setReviewers` 调整名单,提交时重新校验。worker 先执行 AI 材料预审;未配置模型或模型失败时按声明转人工接管。
|
|
64
|
+
6. AI 预审/人工接管之后进入经理节点;启用专业复核时,经理后由所选人员全部会签。经理、专业和财务节点开放转办、委派归还和前置/后置/并行加签。
|
|
65
|
+
7. 超过 50,000 元的采购继续进入财务共享候选池,需另建 code 为 `finance` 的角色并给实际用户配置权限、范围和额度;财务必须先领取,恰好 50,000 元跳过该步骤。
|
|
66
|
+
8. 全部必签完成后,最终审批命令同事务生成库存锁;worker 调用 fake ERP 并回写单号。取消单据或收到 ERP 收货回调后异步释放锁;回调需要 `ERP_WEBHOOK_SECRET` 对原始 JSON 的 HMAC-SHA256 签名。
|
|
66
67
|
|
|
67
68
|
`Inventory.available` 是演示台账数量。本例验证库存锁生命周期,尚未实现仓库级的并发扣减、实物库存及退货核算。
|
|
68
69
|
|
|
@@ -86,6 +87,6 @@ pnpm dev
|
|
|
86
87
|
- `apps/api/src`:API 和 worker 的薄启动入口。
|
|
87
88
|
- `apps/web/src/app`、`modules`、`shared`:应用组合、业务页面、共享基础组件;`main.tsx` 只挂载。
|
|
88
89
|
|
|
89
|
-
新增业务先读[工程目录与代码组织](docs/
|
|
90
|
+
新增业务先读[工程目录与代码组织](docs/development/reference/project-structure.md),不要继续在 src 根目录平铺文件。模板已启用模块目录门禁,`cage check` 和 `verify` 会指出目录与依赖越界。
|
|
90
91
|
|
|
91
92
|
先运行 `pnpm cage gen` 再提交契约和迁移草稿;`pnpm cage verify` 检查完整工具链。派生测试遇到无法推断的业务输入会显示跳过,`purchase-order.test.ts` 另外准备真实业务数据覆盖成功审批和约束反例。
|