@qijenchen/design-system 0.1.0-beta.92 → 0.1.0-beta.95
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/AGENTS.md +6 -208
- package/CLAUDE.md +2 -23
- package/dist/components/DataTable/data-table-filter-value-picker.js +4 -0
- package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -1
- package/dist/components/DataTable/filter-tree.js +6 -0
- package/dist/components/DataTable/filter-tree.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js +1 -1
- package/ds-canonical/adapters/claude/agents/README.md +58 -0
- package/ds-canonical/adapters/claude/agents/canonical-reviewer.md +14 -0
- package/ds-canonical/adapters/claude-root-instructions.md +24 -0
- package/ds-canonical/adapters/claude-settings-base.json +172 -0
- package/ds-canonical/commands/README.md +8 -13
- package/ds-canonical/commands/gov-status.md +5 -75
- package/ds-canonical/fork/AGENTS.md +116 -213
- package/ds-canonical/fork/CLAUDE.md +54 -0
- package/ds-canonical/fork/codex/agents/skills/bug-fix-rhythm/SKILL.md +207 -0
- package/ds-canonical/fork/codex/agents/skills/code-quality-audit/SKILL.md +71 -0
- package/ds-canonical/fork/codex/agents/skills/delivery-handoff/SKILL.md +244 -0
- package/ds-canonical/fork/codex/agents/skills/delivery-handoff/references/flow-diagram.md +180 -0
- package/ds-canonical/fork/codex/agents/skills/delivery-handoff/references/handoff-template.md +177 -0
- package/ds-canonical/fork/codex/agents/skills/delivery-handoff/references/inventory-checklist.md +196 -0
- package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +29 -19
- package/ds-canonical/fork/codex/agents/skills/independent-review/agents/openai.yaml +4 -0
- package/ds-canonical/fork/codex/agents/skills/independent-review/references/evidence-contract.md +24 -0
- package/ds-canonical/fork/codex/agents/skills/performance-audit/SKILL.md +114 -0
- package/ds-canonical/fork/codex/agents/skills/product-ui-audit/SKILL.md +244 -0
- package/ds-canonical/fork/codex/agents/skills/product-ui-audit/references/audit-checks.md +250 -0
- package/ds-canonical/fork/codex/agents/skills/product-ui-audit/references/common-misuses.md +329 -0
- package/ds-canonical/fork/codex/agents/skills/product-ui-audit/references/report-template.md +163 -0
- package/ds-canonical/fork/codex/agents/skills/propose-options/SKILL.md +190 -0
- package/ds-canonical/fork/codex/agents/skills/prototype/SKILL.md +246 -0
- package/ds-canonical/fork/codex/agents/skills/prototype/references/audit-checks.md +39 -0
- package/ds-canonical/fork/codex/agents/skills/prototype/references/benchmark-sources.md +94 -0
- package/ds-canonical/fork/codex/agents/skills/prototype/references/checkpoints.md +200 -0
- package/ds-canonical/fork/codex/agents/skills/prototype/references/evaluation-matrix.md +142 -0
- package/ds-canonical/fork/codex/agents/skills/prototype/references/ooux-template.md +198 -0
- package/ds-canonical/fork/codex/agents/skills/prototype/references/proposal-template.md +230 -0
- package/ds-canonical/fork/codex/agents/skills/scan-similar-bugs/SKILL.md +212 -0
- package/ds-canonical/fork/codex/agents/skills/ux-audit/SKILL.md +135 -0
- package/ds-canonical/fork/codex/agents/skills/visual-audit/SKILL.md +259 -0
- package/ds-canonical/fork/codex/agents/skills/visual-audit/output/.gitkeep +0 -0
- package/ds-canonical/fork/codex/agents/skills/visual-audit/references/audit-architecture.md +101 -0
- package/ds-canonical/fork/codex/agents/skills/visual-audit/references/visual-checklist.md +297 -0
- package/ds-canonical/fork/codex/agents/skills/visual-audit/references/world-class-benchmarks.md +198 -0
- package/ds-canonical/fork/codex/hooks.json +44 -3
- package/ds-canonical/fork/common/closed-tool-execution.mjs +1001 -0
- package/ds-canonical/fork/common/instruction.md +116 -0
- package/ds-canonical/fork/consumer/governance-check.mjs +3059 -0
- package/ds-canonical/fork/consumer/lib/closed-tool-execution.mjs +1001 -0
- package/ds-canonical/fork/consumer/lock.json +189 -0
- package/ds-canonical/fork/consumer/lock.schema.json +338 -0
- package/ds-canonical/fork/consumer/managed-files/0235cf6b3fd402a3717881dc205b5bf77aed7632e7d7d6f1811a40e3033bd816.blob +145 -0
- package/ds-canonical/fork/consumer/managed-files/145aab4b4fc470fc9ba7679b4a1d87d4f6a6d604940bd40f8d675c8af696498f.blob +47 -0
- package/ds-canonical/fork/consumer/managed-files/189e551431181e4a88783c02130d2787b801ef731c86e35bcde89a7313aa020e.blob +149 -0
- package/ds-canonical/fork/consumer/managed-files/24ad71c8613ddcf6fd23818cb3bb477a1fb6d83af4550b0bad43099813088686.blob +28 -0
- package/ds-canonical/fork/consumer/managed-files/2d1ef5617a5ce3c39ad9aae1c42af7d199f9270c0a0ccb490b63d62dbc6913db.blob +1001 -0
- package/ds-canonical/fork/consumer/managed-files/3625e02235cf166a50649a4d704cfb68fead69d0e168c481df013ebae9f6be25.blob +1412 -0
- package/ds-canonical/fork/consumer/managed-files/3ddedc67b464bdfe3dde9803a2d917920c96d633678b802eee638a44cd47366a.blob +8 -0
- package/ds-canonical/fork/consumer/managed-files/423729840acdd43c812f17c245b871716c6b60f76e4d3e94864d0e34a815bf8d.blob +348 -0
- package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +464 -0
- package/ds-canonical/fork/consumer/managed-files/475fba657eaeb8abd88f60635f3eadab27a9d0d3114ba4fffc65f43a2a0fe6e2.blob +124 -0
- package/ds-canonical/fork/consumer/managed-files/61c6aca6d7f04bedbf556eaa9d3043681fe48cbe0e93a8ccb1d69a7c5e23fdcf.blob +113 -0
- package/ds-canonical/fork/consumer/managed-files/63061e05969667005020166ea2ae7df3c96663f7a928e7dd3eebc1471022df56.blob +2398 -0
- package/ds-canonical/fork/consumer/managed-files/65410b66e8799a560e07b71779c186fed2a0ef50575ac44745df7a0938a9c2b4.blob +317 -0
- package/ds-canonical/fork/consumer/managed-files/6929a3c8bb4d69c507720b4754aeb339c6cbb8ff655e0a381d76606d64695e38.blob +393 -0
- package/ds-canonical/fork/consumer/managed-files/6b746628ac11fcccbe277ef6fea94cc2a7b90ea2822e83536eae80d5ee4b3fa5.blob +167 -0
- package/ds-canonical/fork/consumer/managed-files/76d485470d30ff014afacf7ce643763de9f4f956d9fc8128dbc1814774cf2e63.blob +671 -0
- package/ds-canonical/fork/consumer/managed-files/77e06f62670dfee01e9b5e8b0f8dad085ce711d75538c1fb0679d9818acaca82.blob +424 -0
- package/ds-canonical/fork/consumer/managed-files/7937321877e5c34c27dae36c0d507d21cd42fa3f46a8f11a284b8b4a52b6faa4.blob +84 -0
- package/ds-canonical/fork/consumer/managed-files/794bb911ba8ebef58ab0d44a55b9968a19d2c62bcfb310b1ee3a84166f087f21.blob +880 -0
- package/ds-canonical/fork/consumer/managed-files/7b17835d0ceb17cdcdcaf930540a3e5d0943a34a747ac43c12ca8cd485c11581.blob +44 -0
- package/ds-canonical/fork/consumer/managed-files/7eb6cc60abfdabaf6617c336058fad1454ccde08889295767bfd9b7fd153cc6e.blob +778 -0
- package/ds-canonical/fork/consumer/managed-files/81037efa5b3b9f4a875704005fb268acdc9233bb901f44902e4e37e1da92b8e3.blob +46 -0
- package/ds-canonical/fork/consumer/managed-files/884721550b56308812b20917adac7a12f36fdb4ecda6b02460a7f5a219c0c966.blob +78 -0
- package/ds-canonical/fork/consumer/managed-files/91f414b9462ddc1e4be1d4c6ca4ac7a7340a6eec9e98ee109cbc049d3b354b6a.blob +36 -0
- package/ds-canonical/fork/consumer/managed-files/92e4828cdcb96ff0d01daa16f6885f24045a8e72820665650f2a1890dc3a446c.blob +121 -0
- package/ds-canonical/fork/consumer/managed-files/93a89f16f6717d1e4e4e27ec20ef916e3355c7aa890ca74cd9b12db30d8a899f.blob +43 -0
- package/ds-canonical/fork/consumer/managed-files/974521d6b8fe796ea4c58b3c78b0c2b8a1be262b97100d77aaf4cc9c7724bcab.blob +45 -0
- package/ds-canonical/fork/consumer/managed-files/9791c85388d2e2e2bd564e2550fadc3351c4c960a47ce838098afcba7544eb90.blob +959 -0
- package/ds-canonical/fork/consumer/managed-files/a448d2222133335c30e6fda5116e26a40db3283a19d62ccc0663c30d272262a7.blob +174 -0
- package/ds-canonical/fork/consumer/managed-files/ab072e7f2eca51f0e18a5e18c4b333260ee5d219a12cc6df5bb2d683f0b2734d.blob +2636 -0
- package/ds-canonical/fork/consumer/managed-files/bc44cabad181f766611dab6a8f52d81100afadd50ac59799015e8ba7bf46d9eb.blob +596 -0
- package/ds-canonical/fork/consumer/managed-files/c4aa767f11714467de0038a2149cf78074baddc17fc6a9e074b6aed7544c6f3c.blob +80 -0
- package/ds-canonical/fork/consumer/managed-files/c893e9b734b162f4c631fef871128c16a9ab565f796a0e76d7f320f821f220f5.blob +41 -0
- package/ds-canonical/fork/consumer/managed-files/cd9c61f8ff24fb9bcc3663e346e2cfa419cfb84e02aae18d3dc66dd192681696.blob +9 -0
- package/ds-canonical/fork/consumer/managed-files/da2ad2737315d25a5300905ddefd62f1695056a8a1af155c5b34170ad9923bce.blob +38 -0
- package/ds-canonical/fork/consumer/managed-files/e1b11160d6c4043002d2cbff6d9a4ae551698a96137773c4244dadfac152fcb0.blob +222 -0
- package/ds-canonical/fork/consumer/managed-files/e26057408661597aac4a57cdf7e30e2f9d0afb1b9a2dbbb692c3805cd9028ca5.blob +250 -0
- package/ds-canonical/fork/consumer/managed-files/e68ae1915965d8e17d4b2441c00c2b97fe062ea308ae8535c9eebb2fc16fa245.blob +20 -0
- package/ds-canonical/fork/consumer/managed-files/e71c65f45c6af7ce12b6b3c0d8028f185da5b719e2050c9b2a6b243d41ae4878.blob +182 -0
- package/ds-canonical/fork/consumer/managed-files/e813096d69c49812e40e00be9ac8fa14d77f0ec1f97ab4c45cb096b3bedd35de.blob +3 -0
- package/ds-canonical/fork/consumer/managed-files/e930ebca6fd8eeb30802aac0930efae7bcc44cc0996f24de07c11ca6a081fedf.blob +277 -0
- package/ds-canonical/fork/consumer/managed-files/ea94e914d5c8acb84ad7660e5bda0f01b6ccc8cf96b0f6b1ce048f8938d0f5b5.blob +47 -0
- package/ds-canonical/fork/consumer/managed-files/f4db6e616d5229aa7e700c6a3ace022cb9d90baae74e38ca65fce617f18e0d9b.blob +110 -0
- package/ds-canonical/fork/consumer/managed-files/faa128c26102415dc5345e68698efbbd28a395c35ee278dceae044cf430692b2.blob +11 -0
- package/ds-canonical/fork/governance.lock +1552 -103
- package/ds-canonical/fork/hooks/_log-fire.sh +109 -12
- package/ds-canonical/fork/hooks/block_prototype_imports.py +148 -29
- package/ds-canonical/fork/hooks/check_chrome_header_avatar_canonical.sh +23 -12
- package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +116 -65
- package/ds-canonical/fork/hooks/check_consumer_code_quality.sh +140 -0
- package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +84 -30
- package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +16 -6
- package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +65 -21
- package/ds-canonical/fork/hooks/check_field_family_invariants.sh +90 -24
- package/ds-canonical/fork/hooks/check_fork_product_quality.sh +8 -6
- package/ds-canonical/fork/hooks/check_item_list_gap.sh +4 -2
- package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +31 -13
- package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +220 -95
- package/ds-canonical/fork/hooks/check_pattern_invariants.sh +73 -14
- package/ds-canonical/fork/hooks/check_sidebar_menu_button_implicit_wrap.sh +23 -12
- package/ds-canonical/fork/hooks/check_story_determinism.sh +1 -1
- package/ds-canonical/fork/hooks/check_story_invariants.sh +342 -61
- package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +52 -23
- package/ds-canonical/fork/hooks/check_tailwind_wildcard_in_docs.sh +20 -13
- package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +105 -22
- package/ds-canonical/fork/hooks/lib/_hook_integrity.sh +158 -0
- package/ds-canonical/fork/hooks/lib/_provider_paths.sh +162 -0
- package/ds-canonical/fork/launchers/approval-evidence.mjs +1131 -0
- package/ds-canonical/fork/launchers/authority-decision-evidence.mjs +413 -0
- package/ds-canonical/fork/launchers/canonical-order.mjs +8 -0
- package/ds-canonical/fork/launchers/fork-governance-dispatcher.sh +139 -29
- package/ds-canonical/fork/launchers/format-provider-hook-output.mjs +25 -0
- package/ds-canonical/fork/launchers/independent-review.mjs +490 -0
- package/ds-canonical/fork/launchers/inject_fork_governance_preamble.sh +88 -43
- package/ds-canonical/fork/launchers/normalize-provider-event.mjs +355 -0
- package/ds-canonical/fork/launchers/product-hook-dispatch-runtime.mjs +1067 -0
- package/ds-canonical/fork/launchers/provider-hook-normalization.mjs +1646 -0
- package/ds-canonical/fork/launchers/provider-hook-output-transport.mjs +525 -0
- package/ds-canonical/fork/launchers/settings-hooks.json +30 -53
- package/ds-canonical/fork/manifest.json +1881 -23
- package/ds-canonical/fork/providers/claude/hook-config.json +333 -0
- package/ds-canonical/fork/providers/claude/instruction.md +54 -0
- package/ds-canonical/fork/providers/claude/skills/bug-fix-rhythm/SKILL.md +207 -0
- package/ds-canonical/fork/providers/claude/skills/code-quality-audit/SKILL.md +71 -0
- package/ds-canonical/fork/providers/claude/skills/delivery-handoff/SKILL.md +244 -0
- package/ds-canonical/fork/providers/claude/skills/delivery-handoff/references/flow-diagram.md +180 -0
- package/ds-canonical/fork/providers/claude/skills/delivery-handoff/references/handoff-template.md +177 -0
- package/ds-canonical/fork/providers/claude/skills/delivery-handoff/references/inventory-checklist.md +196 -0
- package/ds-canonical/fork/providers/claude/skills/independent-review/SKILL.md +43 -0
- package/ds-canonical/fork/providers/claude/skills/independent-review/agents/openai.yaml +4 -0
- package/ds-canonical/fork/providers/claude/skills/independent-review/references/evidence-contract.md +24 -0
- package/ds-canonical/fork/providers/claude/skills/performance-audit/SKILL.md +114 -0
- package/ds-canonical/fork/providers/claude/skills/product-ui-audit/SKILL.md +244 -0
- package/ds-canonical/fork/providers/claude/skills/product-ui-audit/references/audit-checks.md +250 -0
- package/ds-canonical/fork/providers/claude/skills/product-ui-audit/references/common-misuses.md +329 -0
- package/ds-canonical/fork/providers/claude/skills/product-ui-audit/references/report-template.md +163 -0
- package/ds-canonical/fork/providers/claude/skills/propose-options/SKILL.md +190 -0
- package/ds-canonical/fork/providers/claude/skills/prototype/SKILL.md +246 -0
- package/ds-canonical/fork/providers/claude/skills/prototype/references/audit-checks.md +39 -0
- package/ds-canonical/fork/providers/claude/skills/prototype/references/benchmark-sources.md +94 -0
- package/ds-canonical/fork/providers/claude/skills/prototype/references/checkpoints.md +200 -0
- package/ds-canonical/fork/providers/claude/skills/prototype/references/evaluation-matrix.md +142 -0
- package/ds-canonical/fork/providers/claude/skills/prototype/references/ooux-template.md +198 -0
- package/ds-canonical/fork/providers/claude/skills/prototype/references/proposal-template.md +230 -0
- package/ds-canonical/fork/providers/claude/skills/scan-similar-bugs/SKILL.md +212 -0
- package/ds-canonical/fork/providers/claude/skills/ux-audit/SKILL.md +135 -0
- package/ds-canonical/fork/providers/claude/skills/visual-audit/SKILL.md +259 -0
- package/ds-canonical/fork/providers/claude/skills/visual-audit/output/.gitkeep +0 -0
- package/ds-canonical/fork/providers/claude/skills/visual-audit/references/audit-architecture.md +101 -0
- package/ds-canonical/fork/providers/claude/skills/visual-audit/references/visual-checklist.md +297 -0
- package/ds-canonical/fork/providers/claude/skills/visual-audit/references/world-class-benchmarks.md +198 -0
- package/ds-canonical/fork/providers/codex/hook-config.json +57 -0
- package/ds-canonical/fork/providers/codex/skills/bug-fix-rhythm/SKILL.md +207 -0
- package/ds-canonical/fork/providers/codex/skills/code-quality-audit/SKILL.md +71 -0
- package/ds-canonical/fork/providers/codex/skills/delivery-handoff/SKILL.md +244 -0
- package/ds-canonical/fork/providers/codex/skills/delivery-handoff/references/flow-diagram.md +180 -0
- package/ds-canonical/fork/providers/codex/skills/delivery-handoff/references/handoff-template.md +177 -0
- package/ds-canonical/fork/providers/codex/skills/delivery-handoff/references/inventory-checklist.md +196 -0
- package/ds-canonical/fork/providers/codex/skills/independent-review/SKILL.md +43 -0
- package/ds-canonical/fork/providers/codex/skills/independent-review/agents/openai.yaml +4 -0
- package/ds-canonical/fork/providers/codex/skills/independent-review/references/evidence-contract.md +24 -0
- package/ds-canonical/fork/providers/codex/skills/performance-audit/SKILL.md +114 -0
- package/ds-canonical/fork/providers/codex/skills/product-ui-audit/SKILL.md +244 -0
- package/ds-canonical/fork/providers/codex/skills/product-ui-audit/references/audit-checks.md +250 -0
- package/ds-canonical/fork/providers/codex/skills/product-ui-audit/references/common-misuses.md +329 -0
- package/ds-canonical/fork/providers/codex/skills/product-ui-audit/references/report-template.md +163 -0
- package/ds-canonical/fork/providers/codex/skills/propose-options/SKILL.md +190 -0
- package/ds-canonical/fork/providers/codex/skills/prototype/SKILL.md +246 -0
- package/ds-canonical/fork/providers/codex/skills/prototype/references/audit-checks.md +39 -0
- package/ds-canonical/fork/providers/codex/skills/prototype/references/benchmark-sources.md +94 -0
- package/ds-canonical/fork/providers/codex/skills/prototype/references/checkpoints.md +200 -0
- package/ds-canonical/fork/providers/codex/skills/prototype/references/evaluation-matrix.md +142 -0
- package/ds-canonical/fork/providers/codex/skills/prototype/references/ooux-template.md +198 -0
- package/ds-canonical/fork/providers/codex/skills/prototype/references/proposal-template.md +230 -0
- package/ds-canonical/fork/providers/codex/skills/scan-similar-bugs/SKILL.md +212 -0
- package/ds-canonical/fork/providers/codex/skills/ux-audit/SKILL.md +135 -0
- package/ds-canonical/fork/providers/codex/skills/visual-audit/SKILL.md +259 -0
- package/ds-canonical/fork/providers/codex/skills/visual-audit/output/.gitkeep +0 -0
- package/ds-canonical/fork/providers/codex/skills/visual-audit/references/audit-architecture.md +101 -0
- package/ds-canonical/fork/providers/codex/skills/visual-audit/references/visual-checklist.md +297 -0
- package/ds-canonical/fork/providers/codex/skills/visual-audit/references/world-class-benchmarks.md +198 -0
- package/ds-canonical/fork/references/story-baseline-registry.json +117 -0
- package/ds-canonical/fork/references/story-baseline-registry.schema.json +145 -0
- package/ds-canonical/fork/references/utility-registry.json +377 -0
- package/ds-canonical/fork/references/utility-registry.schema.json +373 -0
- package/ds-canonical/fork/review/canonical-order.mjs +8 -0
- package/ds-canonical/fork/review/provider-review-binding.mjs +2377 -0
- package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +19 -12
- package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +19 -15
- package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +17 -11
- package/ds-canonical/fork/skills/independent-review/SKILL.md +37 -0
- package/ds-canonical/fork/skills/independent-review/agents/openai.yaml +4 -0
- package/ds-canonical/fork/skills/independent-review/references/evidence-contract.md +24 -0
- package/ds-canonical/fork/skills/performance-audit/SKILL.md +11 -8
- package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +31 -21
- package/ds-canonical/fork/skills/product-ui-audit/references/audit-checks.md +9 -5
- package/ds-canonical/fork/skills/product-ui-audit/references/report-template.md +30 -26
- package/ds-canonical/fork/skills/propose-options/SKILL.md +6 -4
- package/ds-canonical/fork/skills/prototype/SKILL.md +21 -21
- package/ds-canonical/fork/skills/prototype/references/audit-checks.md +7 -6
- package/ds-canonical/fork/skills/prototype/references/checkpoints.md +26 -17
- package/ds-canonical/fork/skills/prototype/references/evaluation-matrix.md +2 -1
- package/ds-canonical/fork/skills/prototype/references/ooux-template.md +1 -1
- package/ds-canonical/fork/skills/prototype/references/proposal-template.md +6 -5
- package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +28 -27
- package/ds-canonical/fork/skills/ux-audit/SKILL.md +8 -5
- package/ds-canonical/fork/skills/visual-audit/SKILL.md +37 -43
- package/ds-canonical/fork/skills/visual-audit/references/audit-architecture.md +15 -15
- package/ds-canonical/fork/skills/visual-audit/references/visual-checklist.md +6 -6
- package/ds-canonical/fork/skills/visual-audit/references/world-class-benchmarks.md +2 -2
- package/ds-canonical/hooks/README.md +37 -23
- package/ds-canonical/hooks/_log-fire.sh +109 -12
- package/ds-canonical/hooks/auto_regen_ds_barrel.sh +52 -20
- package/ds-canonical/hooks/block_prototype_imports.py +148 -29
- package/ds-canonical/hooks/check_audit_post_report_validator.sh +76 -66
- package/ds-canonical/hooks/check_audit_sample_escape.sh +4 -4
- package/ds-canonical/hooks/check_benchmark_citation.sh +3 -3
- package/ds-canonical/hooks/check_canonical_propagation.sh +81 -22
- package/ds-canonical/hooks/check_chrome_header_avatar_canonical.sh +22 -11
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +100 -56
- package/ds-canonical/hooks/check_codex_collab_5step.sh +31 -25
- package/ds-canonical/hooks/check_consumer_app_invariants.sh +116 -65
- package/ds-canonical/hooks/check_consumer_code_quality.sh +140 -0
- package/ds-canonical/hooks/check_datatable_invariants.sh +103 -25
- package/ds-canonical/hooks/check_dim_count_drift.sh +5 -4
- package/ds-canonical/hooks/check_ds_anchor_preflight.sh +83 -29
- package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +16 -6
- package/ds-canonical/hooks/check_escape_marker_abuse.sh +65 -21
- package/ds-canonical/hooks/check_field_controls_contracts.sh +10 -8
- package/ds-canonical/hooks/check_field_family_invariants.sh +90 -24
- package/ds-canonical/hooks/check_file_size_budget.sh +32 -14
- package/ds-canonical/hooks/check_full_story_visual_interaction_sweep.sh +60 -21
- package/ds-canonical/hooks/check_item_list_gap.sh +4 -2
- package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +31 -13
- package/ds-canonical/hooks/check_main_branch_workbench.sh +27 -68
- package/ds-canonical/hooks/check_naming_and_abstraction.sh +20 -9
- package/ds-canonical/hooks/check_opacity_token_usage.sh +220 -95
- package/ds-canonical/hooks/check_orphan_ds_css.sh +48 -18
- package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +29 -11
- package/ds-canonical/hooks/check_pattern_invariants.sh +73 -14
- package/ds-canonical/hooks/check_peoplepicker_ssot_drift.sh +4 -4
- package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
- package/ds-canonical/hooks/check_plugin_fork_health.sh +151 -41
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +5 -109
- package/ds-canonical/hooks/check_propose_discipline.sh +116 -29
- package/ds-canonical/hooks/check_propose_pre_grep_verify.sh +6 -4
- package/ds-canonical/hooks/check_propose_without_benchmark.sh +10 -6
- package/ds-canonical/hooks/check_select_all_canonical.sh +1 -1
- package/ds-canonical/hooks/check_sidebar_menu_button_implicit_wrap.sh +22 -11
- package/ds-canonical/hooks/check_solo_workflow.sh +117 -186
- package/ds-canonical/hooks/check_spec_class_drift.sh +33 -19
- package/ds-canonical/hooks/check_ssot_header_declaration.sh +6 -5
- package/ds-canonical/hooks/check_story_determinism.sh +1 -1
- package/ds-canonical/hooks/check_story_invariants.sh +526 -213
- package/ds-canonical/hooks/check_storybook_addon_packaging.sh +65 -15
- package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +137 -67
- package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +52 -23
- package/ds-canonical/hooks/check_tailwind_wildcard_in_docs.sh +20 -13
- package/ds-canonical/hooks/check_wrapper_primitive_schema_drift.sh +15 -12
- package/ds-canonical/hooks/chrome_header_dispatcher.sh +75 -26
- package/ds-canonical/hooks/enforce_home_charter.sh +29 -8
- package/ds-canonical/hooks/governance_control_plane.sh +15 -0
- package/ds-canonical/hooks/inject_deploy_url_after_push.sh +105 -22
- package/ds-canonical/hooks/inject_pending_self_audit.sh +27 -24
- package/ds-canonical/hooks/lib/_app_shell_primary_header_consistency.sh +77 -29
- package/ds-canonical/hooks/lib/_approval_re.sh +5 -6
- package/ds-canonical/hooks/lib/_chrome_header_handcraft.sh +66 -20
- package/ds-canonical/hooks/lib/_code_quality.sh +9 -9
- package/ds-canonical/hooks/lib/_cva_default_sync.sh +11 -13
- package/ds-canonical/hooks/lib/_governance_coverage_check.sh +48 -21
- package/ds-canonical/hooks/lib/_hardcoded_strings.sh +13 -10
- package/ds-canonical/hooks/lib/_header_with_tabs_border.sh +68 -48
- package/ds-canonical/hooks/lib/_hook_integrity.sh +158 -0
- package/ds-canonical/hooks/lib/_layout_space_canonical.sh +5 -5
- package/ds-canonical/hooks/lib/_overlay_handcraft.sh +21 -23
- package/ds-canonical/hooks/lib/_person_data_richness.sh +5 -5
- package/ds-canonical/hooks/lib/_provider_paths.sh +162 -0
- package/ds-canonical/hooks/lib/_story_compile_drift.sh +5 -5
- package/ds-canonical/hooks/lib/_tab_lg_chrome_header_equal.sh +247 -27
- package/ds-canonical/hooks/lib/_token_hygiene.sh +39 -22
- package/ds-canonical/hooks/lib/approval-evidence.mjs +1131 -0
- package/ds-canonical/hooks/lib/audit-report-validator.mjs +118 -0
- package/ds-canonical/hooks/lib/provider-marker-regex.mjs +23 -0
- package/ds-canonical/hooks/lib/tsx-governance-analysis.mjs +410 -0
- package/ds-canonical/hooks/log_governance_fires.sh +45 -13
- package/ds-canonical/hooks/log_skill_invokes.sh +8 -7
- package/ds-canonical/hooks/post_edit_dispatcher.sh +106 -28
- package/ds-canonical/hooks/registrations.json +488 -0
- package/ds-canonical/hooks/registrations.schema.json +58 -0
- package/ds-canonical/hooks/retired/2026-06-11-prune-merge/check_propose_plain_chinese.sh +1 -1
- package/ds-canonical/hooks/retired/check_governance_compliance.sh +1 -1
- package/ds-canonical/hooks/session_start_governance_check.sh +109 -78
- package/ds-canonical/hooks/stop_audit_content_quality.sh +20 -0
- package/ds-canonical/hooks/stop_extract_canonical_rules.sh +20 -0
- package/ds-canonical/hooks/stop_passive_logging.sh +133 -59
- package/ds-canonical/hooks/stop_self_audit.sh +349 -210
- package/ds-canonical/hooks/tests/fixtures/transcripts/claude-transcript-jsonl-v1.jsonl +3 -0
- package/ds-canonical/hooks/tests/fixtures/transcripts/claude-transcript-jsonl-v2.jsonl +3 -0
- package/ds-canonical/hooks/tests/registration-output-contracts.mjs +151 -0
- package/ds-canonical/hooks/tests/run-all.sh +35 -5
- package/ds-canonical/hooks/tests/test_auto_regen_ds_barrel.sh +48 -4
- package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +157 -9
- package/ds-canonical/hooks/tests/test_check_app_shell_primary_header_consistency.sh +265 -127
- package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +119 -12
- package/ds-canonical/hooks/tests/test_check_audit_sample_escape.sh +0 -0
- package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +0 -0
- package/ds-canonical/hooks/tests/test_check_canonical_propagation.sh +56 -5
- package/ds-canonical/hooks/tests/test_check_chrome_header_avatar_canonical.sh +6 -6
- package/ds-canonical/hooks/tests/test_check_chrome_header_handcraft.sh +193 -100
- package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +143 -17
- package/ds-canonical/hooks/tests/test_check_codex_collab_5step.sh +49 -5
- package/ds-canonical/hooks/tests/test_check_consumer_app_invariants.sh +128 -0
- package/ds-canonical/hooks/tests/test_check_consumer_app_story_title.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_consumer_code_quality.sh +64 -0
- package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_consumer_no_ds_catalog.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_data_table_size_num_to_meta_width.sh +26 -8
- package/ds-canonical/hooks/tests/test_check_datatable_invariants.sh +126 -12
- package/ds-canonical/hooks/tests/test_check_dim_count_drift.sh +13 -8
- package/ds-canonical/hooks/tests/test_check_ds_anchor_preflight.sh +53 -7
- package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +3 -3
- package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +17 -7
- package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +72 -16
- package/ds-canonical/hooks/tests/test_check_file_size_budget.sh +67 -5
- package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +107 -4
- package/ds-canonical/hooks/tests/test_check_full_story_visual_interaction_sweep.sh +25 -15
- package/ds-canonical/hooks/tests/test_check_header_with_tabs_border.sh +177 -67
- package/ds-canonical/hooks/tests/test_check_item_list_gap.sh +7 -7
- package/ds-canonical/hooks/tests/test_check_layout_space_magic_numbers.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_main_branch_workbench.sh +45 -110
- package/ds-canonical/hooks/tests/test_check_naming_and_abstraction.sh +19 -2
- package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +114 -8
- package/ds-canonical/hooks/tests/test_check_orphan_ds_css.sh +38 -22
- package/ds-canonical/hooks/tests/test_check_overlay_open_focus_escape_probe.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +68 -6
- package/ds-canonical/hooks/tests/test_check_plugin_fork_health.sh +53 -0
- package/ds-canonical/hooks/tests/test_check_post_main_ssot_propagate.sh +37 -0
- package/ds-canonical/hooks/tests/test_check_propose_cite_required.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_propose_discipline.sh +59 -0
- package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +7 -5
- package/ds-canonical/hooks/tests/test_check_propose_pre_grep_verify.sh +5 -5
- package/ds-canonical/hooks/tests/test_check_propose_without_benchmark.sh +45 -0
- package/ds-canonical/hooks/tests/test_check_sidebar_menu_button_implicit_wrap.sh +5 -5
- package/ds-canonical/hooks/tests/test_check_solo_workflow.sh +183 -105
- package/ds-canonical/hooks/tests/test_check_spec_class_drift.sh +35 -7
- package/ds-canonical/hooks/tests/{test_check_story_anatomy.sh.broken → test_check_story_anatomy.sh} +73 -4
- package/ds-canonical/hooks/tests/{test_check_story_category.sh.broken → test_check_story_category.sh} +82 -2
- package/ds-canonical/hooks/tests/test_check_story_determinism.sh +39 -0
- package/ds-canonical/hooks/tests/test_check_story_invariants.sh +559 -43
- package/ds-canonical/hooks/tests/{test_check_story_name_jargon.sh.broken → test_check_story_name_jargon.sh} +22 -1
- package/ds-canonical/hooks/tests/test_check_storybook_addon_packaging.sh +79 -0
- package/ds-canonical/hooks/tests/test_check_storybook_addon_preset_cjs.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_substantive_edit_approval_preflight.sh +546 -28
- package/ds-canonical/hooks/tests/test_check_tab_lg_chrome_header_equal.sh +180 -98
- package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +3 -3
- package/ds-canonical/hooks/tests/test_check_tailwind_wildcard_in_docs.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_token_hygiene.sh +63 -15
- package/ds-canonical/hooks/tests/test_check_wrapper_primitive_schema_drift.sh +12 -6
- package/ds-canonical/hooks/tests/test_chrome_header_dispatcher.sh +125 -0
- package/ds-canonical/hooks/tests/test_enforce_home_charter.sh +57 -3
- package/ds-canonical/hooks/tests/test_governance_control_plane.sh +66 -0
- package/ds-canonical/hooks/tests/test_governance_coverage_check.sh +88 -0
- package/ds-canonical/hooks/tests/test_hook_integrity_classification.sh +217 -0
- package/ds-canonical/hooks/tests/test_inject_deploy_url_after_push.sh +85 -2
- package/ds-canonical/hooks/tests/test_inject_pending_self_audit.sh +116 -11
- package/ds-canonical/hooks/tests/test_log_governance_fires.sh +117 -1
- package/ds-canonical/hooks/tests/test_log_skill_invokes.sh +74 -5
- package/ds-canonical/hooks/tests/test_post_edit_dispatcher.sh +234 -77
- package/ds-canonical/hooks/tests/test_provider_peer_symmetry.sh +76 -0
- package/ds-canonical/hooks/tests/test_registration_output_contracts.sh +20 -0
- package/ds-canonical/hooks/tests/test_session_start_governance_check.sh +130 -18
- package/ds-canonical/hooks/tests/test_stop_audit_content_quality.sh +75 -0
- package/ds-canonical/hooks/tests/test_stop_capture_metrics.sh +101 -23
- package/ds-canonical/hooks/tests/test_stop_extract_canonical_rules.sh +75 -0
- package/ds-canonical/hooks/tests/{test_stop_governance_drift_check.sh.broken → test_stop_governance_drift_check.sh} +22 -12
- package/ds-canonical/hooks/tests/test_stop_passive_logging.sh +102 -1
- package/ds-canonical/hooks/tests/test_stop_repository_command_context.sh +38 -0
- package/ds-canonical/hooks/tests/test_stop_self_audit.sh +363 -9
- package/ds-canonical/references/README.md +8 -8
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +4 -4
- package/ds-canonical/references/build-ui-canonicals.md +3 -3
- package/ds-canonical/references/certified-surfaces.md +46 -15
- package/ds-canonical/references/cva-patterns.md +2 -2
- package/ds-canonical/references/failure-class-registry.json +3 -2
- package/ds-canonical/references/governance-audit-coverage.md +8 -8
- package/ds-canonical/references/naming-conventions.md +1 -1
- package/ds-canonical/references/preflight-gate-baseline.json +210 -3
- package/ds-canonical/references/principle-dim-map.json +15 -9
- package/ds-canonical/references/props-naming.md +1 -1
- package/ds-canonical/references/scenario-definition.md +33 -30
- package/ds-canonical/references/spec-rules.md +1 -1
- package/ds-canonical/references/ssot-consultation.md +3 -3
- package/ds-canonical/references/ssot-index.md +1 -1
- package/ds-canonical/references/story-baseline-registry.json +9 -6
- package/ds-canonical/references/story-baseline-registry.schema.json +145 -0
- package/ds-canonical/references/structural-token-retention.md +1 -1
- package/ds-canonical/references/tailwind-gotchas.md +2 -2
- package/ds-canonical/references/ui-dev-rules.md +1 -1
- package/ds-canonical/rules/README.md +14 -9
- package/ds-canonical/rules/meta-patterns.md +13 -13
- package/ds-canonical/rules/self-verify.md +12 -7
- package/ds-canonical/rules/spec-rules.md +1 -1
- package/ds-canonical/rules/ui-development.md +5 -5
- package/ds-canonical/skills/README.md +27 -15
- package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +14 -7
- package/ds-canonical/skills/canonical-reviewer/SKILL.md +29 -0
- package/ds-canonical/skills/code-quality-audit/SKILL.md +11 -7
- package/ds-canonical/skills/codex-collab/SKILL.md +19 -238
- package/ds-canonical/skills/codex-collab/references/brief-template.md +37 -47
- package/ds-canonical/skills/codex-collab/references/transport.md +38 -57
- package/ds-canonical/skills/codify-corrections/SKILL.md +32 -31
- package/ds-canonical/skills/codify-principle/SKILL.md +33 -32
- package/ds-canonical/skills/component-quality-gate/SKILL.md +13 -13
- package/ds-canonical/skills/component-quality-gate/references/checklist.md +7 -7
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +28 -229
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +36 -131
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +33 -187
- package/ds-canonical/skills/deep-audit-cross-codex/references/skill-relationships-antipatterns-benchmarks.md +22 -24
- package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +15 -79
- package/ds-canonical/skills/deep-audit-cross-codex/references/upstream-directives-r-mapping.md +24 -23
- package/ds-canonical/skills/delivery-handoff/SKILL.md +14 -8
- package/ds-canonical/skills/design-system-audit/SKILL.md +63 -62
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +86 -77
- package/ds-canonical/skills/design-system-audit/references/checkpoints.md +59 -50
- package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +8 -8
- package/ds-canonical/skills/design-system-audit/references/principle-audit-protocol.md +37 -32
- package/ds-canonical/skills/design-system-audit/references/rule-placement.md +39 -38
- package/ds-canonical/skills/ensure-canonical/SKILL.md +36 -34
- package/ds-canonical/skills/governance-health/SKILL.md +26 -22
- package/ds-canonical/skills/governance-status/SKILL.md +30 -0
- package/ds-canonical/skills/independent-review/SKILL.md +37 -0
- package/ds-canonical/skills/independent-review/agents/openai.yaml +4 -0
- package/ds-canonical/skills/independent-review/references/evidence-contract.md +24 -0
- package/ds-canonical/skills/knowledge-prune/SKILL.md +45 -52
- package/ds-canonical/skills/knowledge-prune/references/phase-z-cross-repo-ssot-propagation.md +10 -7
- package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +6 -6
- package/ds-canonical/skills/new-component/SKILL.md +18 -12
- package/ds-canonical/skills/new-component/references/new-component-checklist.md +7 -6
- package/ds-canonical/skills/performance-audit/SKILL.md +13 -10
- package/ds-canonical/skills/product-ui-audit/SKILL.md +27 -17
- package/ds-canonical/skills/product-ui-audit/references/audit-checks.md +7 -4
- package/ds-canonical/skills/product-ui-audit/references/report-template.md +30 -26
- package/ds-canonical/skills/propose-options/SKILL.md +1 -1
- package/ds-canonical/skills/prototype/SKILL.md +19 -21
- package/ds-canonical/skills/prototype/references/audit-checks.md +6 -5
- package/ds-canonical/skills/prototype/references/checkpoints.md +26 -17
- package/ds-canonical/skills/prototype/references/ooux-template.md +1 -1
- package/ds-canonical/skills/prototype/references/proposal-template.md +4 -4
- package/ds-canonical/skills/scan-similar-bugs/SKILL.md +22 -21
- package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +21 -20
- package/ds-canonical/skills/story-writing/SKILL.md +12 -12
- package/ds-canonical/skills/story-writing/references/anatomy-standard.md +1 -1
- package/ds-canonical/skills/story-writing/references/category-templates.md +5 -5
- package/ds-canonical/skills/ux-audit/SKILL.md +12 -9
- package/ds-canonical/skills/visual-audit/SKILL.md +41 -47
- package/ds-canonical/skills/visual-audit/references/audit-architecture.md +7 -7
- package/ds-canonical/skills/visual-audit/references/visual-checklist.md +12 -12
- package/ds-canonical/skills/visual-audit/references/world-class-benchmarks.md +3 -3
- package/ds-canonical/templates/_README.md +6 -7
- package/ds-canonical/templates/consumer-governance.md +78 -0
- package/ds-canonical/templates/dashboard-app.tsx +3 -1
- package/ds-canonical/templates/product-launchers/fork-governance-dispatcher.sh +154 -0
- package/ds-canonical/templates/product-launchers/format-provider-hook-output.mjs +25 -0
- package/ds-canonical/templates/product-launchers/independent-review.mjs +490 -0
- package/ds-canonical/templates/product-launchers/inject_fork_governance_preamble.sh +99 -0
- package/ds-canonical/templates/product-launchers/normalize-provider-event.mjs +355 -0
- package/ds-canonical/templates/product-launchers/product-hook-dispatch-runtime.mjs +1067 -0
- package/ds-canonical/templates/product-provider-wiring.json +80 -0
- package/ds-canonical/templates/product-provider-wiring.schema.json +151 -0
- package/llms-full.txt +6 -6
- package/llms.txt +2 -2
- package/package.json +15 -4
- package/src/tokens/utility-registry.json +119 -4
- package/src/tokens/utility-registry.schema.json +373 -0
- package/tools/README.md +9 -0
- package/tools/codemods/cli.mjs +93 -0
- package/tools/codemods/index.mjs +582 -0
- package/tools/shared/safe-filesystem.mjs +371 -0
- package/tools/visual-baseline/cli.mjs +97 -0
- package/tools/visual-baseline/index.mjs +522 -0
- /package/dist/{react-day-picker.css → index.css} +0 -0
package/AGENTS.md
CHANGED
|
@@ -1,210 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@qijenchen/design-system` package scope
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
**最終 authority 不是本檔**:本檔引導行為;不可繞過的強制層 = `npm run release:preflight`(~30 deterministic gates)+ CI(release.yml / ci.yml)。**任何 provider 產出的不合規變更都無法通過 preflight/CI 發佈**。Claude 另有 write-time hooks 加速回饋(`.claude/hooks/`,provider 專屬加速器,非信任邊界)。
|
|
3
|
+
This file is a **scoped augmentation**, not a second repository or product-governance SSOT.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
1. **對標世界級 + 不取巧省工**——每個設計決策都要能回答「Polaris / Material / Atlassian / Ant / Carbon / Apple HIG 怎麼做?」沒對齊又說不出理由 = 設計 bug。視覺整齊度不輸原版 + 符合 DS 語言**同時成立**。**禁止以「選較簡單」「省 N edits」為由選 shortcut**——一律最世界級做法。說「快速修」「省工程」是 yellow flag,停下重想。
|
|
11
|
-
2. **不憑直覺發明 / 優先消費既有**——新增任何值 / 名 / pattern / variant / layout primitive 前先 `grep` 既有。**強制 `# SSOT 消費 canonical` 清單**——寫視覺 code 前列消費的 components/patterns/tokens/spec。提建議也算定 pattern,給 option 必對照 DS canonical + ≥3 家世界級。**禁止憑印象列部分家**。
|
|
12
|
-
3. **改一處必看三處**——code / spec / story 三方聯動。改 cva `defaultVariants` / variant / token 前先 grep 該元件所有檔案,一次改完。
|
|
13
|
-
4. **範例必真實業務場景**——Jira / Stripe / Notion / Figma 可辨識情境;禁 `Option A/B/C`、「按鈕一」、極端不現實、ASCII art。
|
|
14
|
-
5. **猶豫就問**——無前例的決策:grep 既有 → 讀近親 spec → 仍不確定停下問。**禁止憑直覺造新 pattern**。
|
|
15
|
-
6. **大原則吸收瑣碎**——同類 bug 反覆糾正 = meta 層沒抓住。見 `.claude/rules/meta-patterns.md` 31 active M-rules(M1-M32,M27/M33/M34/M35 retired,折入 M20/M7/M23(c)(d))。**AI 不需 user 提醒才找 root invariant**——rule 震盪 → AI 自跑 M12 benchmark + invariant test。User 第 2 次問 → 必截圖 verify(M13)。對話結論 → AUTO 5-layer pipeline(M14)。Visual / behavior decision 前必先 WebFetch ≥ 3 source(M26)。Solo-work git ops 必先 grep canonical(M28)。**視覺/結構 propose 前必 grep DS spec.md 找 owner SSOT(M29)— 出 3-column 表;否則提案不被接受**。使用者 tell me once 不該要 tell me twice。
|
|
16
|
-
|
|
17
|
-
完整 M-rules 詳 `.claude/rules/meta-patterns.md`(必讀)。
|
|
18
|
-
|
|
19
|
-
# Rule Index(progressive disclosure — 編對應檔案前必先讀)
|
|
20
|
-
|
|
21
|
-
| 檔案 | 何時必讀 |
|
|
22
|
-
|---|---|
|
|
23
|
-
| `.claude/rules/meta-patterns.md` | 每個任務(31 條 M-rules,fundamental)|
|
|
24
|
-
| `.claude/rules/spec-rules.md` | 編任何 `*.spec.md` 或 DS 內容 |
|
|
25
|
-
| `.claude/rules/ui-development.md` | 編任何 `.tsx`/`.ts`(Tailwind 5 條 / Token 4 條 / Props 命名 / shadcn / public-vs-internal)|
|
|
26
|
-
| `.claude/rules/story-rules.md` | 編任何 `*.stories.tsx`(三層定位 / Title / 範例準則)|
|
|
27
|
-
| `.claude/rules/self-verify.md` | 編任何檔案(Pre/Mid/Post/Pre-commit 4 階段自驗)|
|
|
28
|
-
| `.claude/references/ssot-consultation.md` | 寫視覺 code 前(9 項決策對應 SSOT + tsx 檔頭宣告)|
|
|
29
|
-
| `.claude/references/ssot-index.md` | 視覺/結構 propose 前(owner SSOT 對照)|
|
|
30
|
-
| `.claude/references/naming-conventions.md` | 命名新檔/變數/prop |
|
|
31
|
-
| `.claude/skills/design-system-audit/references/audit-prompts.md` | 稽核判準 SSOT(91 dim rubric,Claude 與 Codex 同判準)|
|
|
32
|
-
|
|
33
|
-
(Claude Code 對 `.claude/rules/` 有 path-scoped 自動載入;其他 agent 依本表在編對應檔案前主動讀取——**同一份檔案,零複製**。)
|
|
34
|
-
|
|
35
|
-
# 治理 canonical(home 分層 + anti-bloat)
|
|
36
|
-
|
|
37
|
-
## 規則放哪 home(8-home 分層)
|
|
38
|
-
|
|
39
|
-
| Level | Home | 收什麼 |
|
|
40
|
-
|-------|------|--------|
|
|
41
|
-
| 1 | `AGENTS.md`(本檔;Claude 經 CLAUDE.md import) | 每 session signal 的 mindset + 6 條 + 任務導航 |
|
|
42
|
-
| 1.5 | `.claude/rules/*.md` | path-scoped rules(Claude 自動載入;其他 agent 按 Rule Index 讀) |
|
|
43
|
-
| 2 | `{name}.spec.md` | 單元件「何時用 / 為什麼」 |
|
|
44
|
-
| 3 | Pattern `spec.md` | runtime 跨元件 primitive |
|
|
45
|
-
| 4 | Code(`.tsx` / `.css`) | cva / 型別等機械強制 |
|
|
46
|
-
| 5 | Skill(`.claude/skills/`) | invoke 情境的多步驟 workflow + checkpoint |
|
|
47
|
-
| 6 | Memory(`~/.claude/.../memory/` SSOT + repo `.claude/memory/` mirror)| 跨 session 狀態。本機編完跑 `npm run sync-memory` 推回 repo |
|
|
48
|
-
| 7 | Hook(`.claude/hooks/`) | 機械化 pre/post tool 檢查(Claude write-time;最終兜底 = preflight/CI) |
|
|
49
|
-
| 8 | Slash Command(`.claude/commands/`) | 一次性單步 action |
|
|
50
|
-
| 9 | Plan doc(`.claude/planning/`)| 完整 plan / RFC / spec 草稿 SSOT;memory file 是短 index pointer |
|
|
51
|
-
|
|
52
|
-
**Q1 設計規則 → Level 1-4 / Q2 invoke 情境 → Skill or Command / Q3 隨時間變化 → Memory(short index)+ Plan doc / Q4 機械化 → Hook(+ preflight gate)**。完整 flowchart → `.claude/skills/design-system-audit/references/rule-placement.md`。
|
|
53
|
-
|
|
54
|
-
## 行數預算(Anthropic 對齊)
|
|
55
|
-
|
|
56
|
-
Bootstrap(AGENTS.md + CLAUDE.md 合計)target ≤ 250 / transition ≤ 400 / hard cap 800。SKILL ≤ 250 / spec ≤ 300(foundational SSOT 例外 ≤ 800-1200)/ memory **per-file ≤ 100 lines** + **MEMORY.md index ≤ 20 entries**(soft 18 / hard 20)。Hooks **26 soft / 60 hard**(SSOT = `session_start_governance_check.sh` Check 7)。動態值以 `scripts/sync-governance-counters.mjs` 跑出為準。
|
|
57
|
-
|
|
58
|
-
## Anti-bloat L1-L3
|
|
59
|
-
|
|
60
|
-
- **L1 Pre-write**:`check_file_size_budget.sh`(+ governance hooks — dynamic SSOT 在 `.claude/hooks/`)
|
|
61
|
-
- **L2 Per-edit**:`log_governance_fires.sh` → `.claude/logs/hook-fires.jsonl`
|
|
62
|
-
- **L3 Periodic**(季度 / `--deep`):`/knowledge-prune` skill,retire ≥ 5%
|
|
63
|
-
|
|
64
|
-
## 加規則前必過 3 題
|
|
65
|
-
|
|
66
|
-
1. 既有 Meta-Pattern / 近親 spec / canonical chapter 命中 → append pointer 不新寫
|
|
67
|
-
2. **Rule-of-3**:同概念 ≥ 3 處 → 選 SSOT 其他 pointer
|
|
68
|
-
3. 7 天後還會 fire 嗎?不確定 → 不寫
|
|
69
|
-
|
|
70
|
-
# 稽核 canonical
|
|
71
|
-
|
|
72
|
-
3 層級 × 6 維度。**Stakeholder-visible artifact**(prototype / 元件 merge / 產品 demo)**必過 code + visual 雙層 audit**(搭配 M6+M10)。
|
|
73
|
-
|
|
74
|
-
| Tier | 時機 | Scope | Skill |
|
|
75
|
-
|------|------|-------|------|
|
|
76
|
-
| 1 Stakeholder-gate(強制)| 新元件 merge / prototype / demo | artifact-scoped | `/component-quality-gate` / `/prototype` P3.5 / `/product-ui-audit` P5 |
|
|
77
|
-
| 2 Daily dev | bug / refactor / 文字改 | git diff + direct consumer | `visual-audit --scope=changed`(default) |
|
|
78
|
-
| 3 Periodic deep | release / token 大改 / 季度 | full DS | `/design-system-audit --deep` |
|
|
79
|
-
|
|
80
|
-
| 維度 | 對應 skill |
|
|
81
|
-
|------|-----------|
|
|
82
|
-
| D1 設計語言 | `/design-system-audit` |
|
|
83
|
-
| D2 程式語言 | tsc + lint + `/design-system-audit` |
|
|
84
|
-
| D3 元件效能 | `/performance-audit` |
|
|
85
|
-
| D4 UX 行為 | `/ux-audit` |
|
|
86
|
-
| D5 視覺品質 | `/visual-audit`(Layer A mechanical + B AI) |
|
|
87
|
-
| D6 原則自檢 | `design-system-audit/references/principle-audit-protocol.md` |
|
|
88
|
-
|
|
89
|
-
**Consistency 類稽核必 Phase 0 全掃再判**。**Deep audit 全掃優先 + 決策 batch-at-end 鐵律**(2026-07-11 user directive):先全 DS 掃完(NO-SAMPLE)→ 跨元件去重 → **最後一次**列「真問題 + 只影響 SSOT-UI/UX」決策清單給 user 拍板;非-SSOT autonomous 做完美;**禁**途中逐元件問 user。
|
|
90
|
-
|
|
91
|
-
**Audit-vs-execute 分權**:動 canonical substantive meaning → STOP 提議;對齊 / 表達統一 / 清 duplicate / 補 pointer → AUTO。
|
|
92
|
-
|
|
93
|
-
**Scope classifier — Surgical visual bug**:user 列 N 個 visual defects + 無新 canonical / 無新 API contract / 無 cross-component semantic 改動 → **Surgical scope**:no collab / no new M-rule / no audit report,batch fix + final pixel-quantified verify only。Substantive 改動走 audit-vs-execute STOP 提議流程。
|
|
94
|
-
|
|
95
|
-
# Independent second opinion(跨 provider 對抗審查)
|
|
96
|
-
|
|
97
|
-
**Canonical 概念**:重大變更(governance / Critical DS change / deep audit)需 **author provider ≠ reviewer provider** 的獨立第二意見。判準 = 同一份 rubric(`audit-prompts.md`),reviewer 只提 findings(rule 對照 + severity + evidence),**不得**自建規範。記錄雙方 provider/model/version。
|
|
98
|
-
- Claude authored → Codex review(現行 driver = `/deep-audit-cross-codex` Phase B,transport = `scripts/codex-run-guarded.mjs`)
|
|
99
|
-
- Codex authored → Claude review(對稱)
|
|
100
|
-
- **Backend 不可用 → fail-closed**:標 review pending;禁同一 agent 假扮另一 provider;Critical change 不得取得 compliant 宣稱。
|
|
101
|
-
|
|
102
|
-
# SSOT 消費 canonical
|
|
103
|
-
|
|
104
|
-
寫視覺 code 前必查對照 — 沒列 = 自創。**完整對照表 + 強制 checklist** → `.claude/references/ssot-consultation.md`(SSOT owner;含 9 項決策對應 SSOT + 新元件 tsx 開頭「── 消費的 SSOT ──」段強制要求)。
|
|
105
|
-
|
|
106
|
-
# 任務導航表
|
|
107
|
-
|
|
108
|
-
| 任務 | 必讀 |
|
|
109
|
-
|------|------|
|
|
110
|
-
| **新增元件** | `.claude/rules/ui-development.md` 「建立 UI 前必讀 / shadcn 元件規範」 + `.claude/rules/spec-rules.md` → `/component-quality-gate` |
|
|
111
|
-
| **修 variant / size / state** | 該元件 `spec.md` → `/story-writing` |
|
|
112
|
-
| **新增 token** | `tokens/README.md` → `.claude/rules/ui-development.md`「Token 命名 4 條硬規則」→ `tokens/xxx.spec.md` |
|
|
113
|
-
| **寫 story / 視覺 code** | `/story-writing` + `# SSOT 消費 canonical` |
|
|
114
|
-
| **命名新檔 / 變數 / prop** | `# 命名與語言一致性` + `.claude/rules/ui-development.md`「元件 Props 命名」 |
|
|
115
|
-
| **新元件 layout** | `# 4-Family Layout Model` |
|
|
116
|
-
| **建產品 / 開新 product app** | `npm run create-app <name>` → `apps/<name>/`;**2-scenario architecture SSOT** → `.claude/references/scenario-definition.md` |
|
|
117
|
-
| **新 skill / hook / command** | `.claude/{home}/README.md` charter |
|
|
118
|
-
| **無前例設計決策** | `# 遇不確定時的協議` |
|
|
119
|
-
| **Tailwind 出怪事** | `.claude/rules/ui-development.md`「Tailwind 5 條核心」+ `# 失敗記憶索引` |
|
|
120
|
-
| **Stakeholder 產出 / 稽核** | `# 稽核 canonical` |
|
|
121
|
-
| **User 糾正後** | `# 治理 canonical`(home 判斷) |
|
|
122
|
-
| **跟 codex 討論 / 多輪震盪 / 任何 codex 輸出** | `.claude/skills/codex-collab/SKILL.md`(M31 5-step canonical SSOT)+ M31 anchor in meta-patterns.md |
|
|
123
|
-
| **PR merge 後 / session start branch 健檢** | `# Git solo-work canonical` |
|
|
124
|
-
|
|
125
|
-
**找不到** → 進 `# 遇不確定時的協議`,不自決定。
|
|
126
|
-
|
|
127
|
-
# Git solo-work canonical(SSOT → `.claude/memory/feedback_solo_dev_workflow.md`)
|
|
128
|
-
|
|
129
|
-
**1 chat = 1 working branch**;**Netlify preview 是 user gate**;**「push」/「OK」trigger 才 merge main**。**Session-start 必先 grep canonical + 開 working branch,禁直接在 main 上 edit production code**(M28 sub-rule)。
|
|
130
|
-
|
|
131
|
-
| 步驟 | 動作 |
|
|
132
|
-
|------|------|
|
|
133
|
-
| 1 Edit | AI 改 code |
|
|
134
|
-
| 2 Commit + push working branch | 自動觸發 Netlify per-branch preview |
|
|
135
|
-
| 3 告訴 user 主要 change(or preview URL)| 讓 user 知道看什麼 |
|
|
136
|
-
| 4 等 user trigger | **「push / OK / 好 / 合 main」** → step 5;**「改 X / 不對 / 等等」** → 繼續 step 1 |
|
|
137
|
-
| 5 Squash merge to main | 不開 PR(可 GitHub API squash-merge OR fast-forward)|
|
|
138
|
-
| 5.5 **SSOT propagation gate** | 動到 skill/hook/spec/token/AGENTS/CLAUDE 等 SSOT-affecting paths + user 已給 merge trigger → AI 自動 bump → **`npm run release:preflight`**(唯一發版前 gate 指令)→ tag → publish → `npm view` 驗證,全程不停下問;user 隨時可喊停。bump npm `0.1.0-beta.<N+1>` |
|
|
139
|
-
| 6 砍 remote branch | `git push origin --delete <branch>` |
|
|
140
|
-
| 7 Local 對齊 | `git checkout main && git fetch && git reset --hard origin/main && git branch -d <branch>` |
|
|
141
|
-
|
|
142
|
-
**禁止**:開 PR / AI 自決 push main / 同 chat 開多 branch / 留 stale 不刪 / 「下個 session 處理」deferred 措辭。
|
|
143
|
-
|
|
144
|
-
# 命名與語言一致性
|
|
145
|
-
|
|
146
|
-
**3 重 test**(governance):
|
|
147
|
-
1. **既有 DS 詞彙**:對齊 `compact/rich / sm/md/lg / action/indicator / scanning/reading`?
|
|
148
|
-
2. **世界級 idiom**:≥ 2 家 world-class DS 用此詞?
|
|
149
|
-
3. **跨元件認知衝突**:同字串在其他元件已有不同語義?
|
|
150
|
-
|
|
151
|
-
3 test 全過才採納。詳細 → `.claude/references/naming-conventions.md`。
|
|
152
|
-
**語言一致性**:spec.md 繁中(技術術語保留英) / code identifier 英 / 單一檔案不中英夾雜。
|
|
153
|
-
|
|
154
|
-
# 4-Family Layout Model
|
|
155
|
-
|
|
156
|
-
**每元件 spec 第一段必聲明 Layout Family**(1/2/3/4 或「self-contained」)。
|
|
157
|
-
|
|
158
|
-
| Family | 用途 | SSOT |
|
|
159
|
-
|--------|------|------|
|
|
160
|
-
| 1 Menu item / 2 List item | scanning / reading | `patterns/element-anatomy/item-anatomy.spec.md` |
|
|
161
|
-
| 3 Pill | 單行互動 pill | `components/Button/button.spec.md`「Pill Layout」|
|
|
162
|
-
| 4 Field control | 可編輯資料輸入 | `components/Field/field-controls.spec.md` |
|
|
163
|
-
|
|
164
|
-
# 自主執行 canonical(Autonomy Default)
|
|
165
|
-
|
|
166
|
-
**Default = autonomous + complete + verify-to-perfection;省工 = anti-pattern(違 mindset #1)**。
|
|
167
|
-
|
|
168
|
-
| 動作類別 | 預設 |
|
|
169
|
-
|---------|-----|
|
|
170
|
-
| **SSOT-affecting UI/UX**(增刪改 component / token / spec 視覺結構 / 跨元件 design language) | **ASK** — 中文具體人話講選項 + tradeoff,user 拍板才動 |
|
|
171
|
-
| Bug fix / clean / refactor / 命名一致 / test / audit / verify | **AUTO** — 整批做完 + 完整驗證 + 撤回機制 |
|
|
172
|
-
| Governance / hook / skill / spec **內部**(typo / pointer / 結構 — 不動 canonical meaning)| **AUTO** |
|
|
173
|
-
| Perf / a11y / 漸進遷移(不動 SSOT)| **AUTO** — 整批 + verify |
|
|
174
|
-
|
|
175
|
-
**自主執行同時優化 7 軸**:言簡意賅 / 效率+效能 / SSOT 鐵律 / 易懂維護擴充 / 世界級+一致設計語言 / 完整 self-verify / 自動 self-improve。
|
|
176
|
-
**反 pattern**(禁):「省工」/「下次再做」/「下個 session」/「OK 嗎?」過度 ASK / shortcut 避 verify。
|
|
177
|
-
**Trigger phrase auto-pipeline**:「依原則自主」/「不需問」/「馬不停蹄」/「全部做完」/「自動」→ autonomous mode,僅 SSOT-affecting UI/UX 停下 ASK。
|
|
178
|
-
**Triple-verify before propose**:propose / 列 option / 發現「問題」前必 inline 跑 (1) grep DS-wide (2) Read spec.md / tsx (3) 對照 canonical exception。三題全過才 propose。
|
|
179
|
-
**SSOT auto-sync invariant**:跨 file 數字禁 hardcode 多處;以 `scripts/sync-governance-counters.mjs` 機械對齊。
|
|
180
|
-
|
|
181
|
-
# 遇不確定時的協議
|
|
182
|
-
|
|
183
|
-
**無前例且影響 SSOT UI/UX 設計判斷**時 3 步,禁跳:**grep 既有**(30 秒)→ **讀近親 spec.md** → **仍不確定停下問** user。
|
|
184
|
-
**非 SSOT-UI/UX**(refactor / test / perf / a11y / hook / skill / typo / 對齊既有 canonical)→ autonomous,無需問。
|
|
185
|
-
禁:跳 grep 憑記憶 / 隨便挑 / 留 TODO。可跳:bug 修 / 機械勞動 / user 明確指示。
|
|
186
|
-
|
|
187
|
-
# 失敗記憶索引(技術沉默陷阱 only)
|
|
188
|
-
|
|
189
|
-
| 技術陷阱 | 一行 anchor |
|
|
190
|
-
|--------|-----------|
|
|
191
|
-
| Tailwind v4 `[--foo]` 必 `var()` | silent 失效 |
|
|
192
|
-
| tailwind-merge 自訂 utility 必註冊 group | 否則 strip |
|
|
193
|
-
| 元件自包 Provider | 劫持全站 |
|
|
194
|
-
| 清 unused imports 後 runtime | tsc 不充分,需 storybook |
|
|
195
|
-
| shadcn compat alias 回流 | dark mode 不聯動 |
|
|
196
|
-
| `asChild ? Slot : Native` 內部 JSX 仍渲染多 children | React.Children.only runtime fail;asChild 分支 render 只傳 consumer child |
|
|
197
|
-
| `tsc -b` 不 emit declaration | TS4023 漏抓;型別 surface 改動必 `npm run build:lib` |
|
|
198
|
-
| 工具 flag 沉默陷阱:`rsync -a` 等長同秒靜默跳過(必 `--checksum`)/ `rg` 黏寫 `-rn` 的 `-r`=replace 輸出假字串 | 寫後斷言 + flag 分開寫 |
|
|
199
|
-
| DS css 不在 tokens.css aggregator 也沒被 tsx import = orphan | consumer 靜默拿不到 |
|
|
200
|
-
| storybook-smoke 驗舊 build = 假綠 | smoke script 已加 stale-build guard |
|
|
201
|
-
|
|
202
|
-
新 bug → 歸 Meta-Pattern OR 本表 1 行;> 10 條 = 漏寫,評估 meta-merge 既有 M-rule。
|
|
203
|
-
|
|
204
|
-
# 專案 Stack
|
|
205
|
-
|
|
206
|
-
Vite + React + TypeScript + Tailwind v4 + shadcn/ui + Storybook + 自訂 Design Token;完整路徑 + Token 系統 → `packages/design-system/src/tokens/README.md`(charter)— DS 內化在 npm workspace。
|
|
207
|
-
|
|
208
|
-
# 元件完成 + Exploration
|
|
209
|
-
|
|
210
|
-
merge 前 invoke `/component-quality-gate`(35 項 + visual + clean-code 三層)。正式 `packages/design-system/src/` vs 比稿 `src/explorations/`;比稿 `*.v1.stories.tsx` + `notes.md`,定案升級 patterns/ 或 components/。
|
|
5
|
+
- Inside the design-system monorepo, inherit the repository-root `AGENTS.md`; do not restate or override it here. Author governance only in the canonical owners classified by `infra/governance/protected-root-classification.json`, then regenerate the declared build graph.
|
|
6
|
+
- Inside an installed npm package, treat every file under `node_modules/@qijenchen/design-system` as immutable release material. Never patch it in place; fix the upstream DS source, publish an immutable exact version/BOM, and upgrade the consumer through its governed PR flow.
|
|
7
|
+
- Consumer product governance comes from the authenticated `ds-canonical/fork/manifest.json` snapshot materialized into the consumer repository. This package-scoped file is navigation only and must never substitute for that snapshot, its lock, provider adapters, verifier, or protected CI.
|
|
8
|
+
- Rules, hooks, skills, commands, and provider views under this package follow the same owner/output boundary: `ds-canonical/` is the shipped canonical corpus; generated `.claude`, `.codex`, and `.agents` views are rebuildable adapters and are not edit targets.
|
package/CLAUDE.md
CHANGED
|
@@ -1,26 +1,5 @@
|
|
|
1
1
|
@AGENTS.md
|
|
2
2
|
|
|
3
|
-
# Claude Code
|
|
3
|
+
# Claude Code package-scope adapter
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Path-scoped rules(Claude 自動載入;2026 Anthropic 推薦)
|
|
8
|
-
|
|
9
|
-
僅在編對應 path 檔案時載入,降低每 session context 成本(其他 agent 依 AGENTS.md「Rule Index」在編對應檔案前主動讀**同一份檔案**):
|
|
10
|
-
|
|
11
|
-
- `.claude/rules/meta-patterns.md` — 31 active M-rules(always loads,fundamental)
|
|
12
|
-
- `.claude/rules/spec-rules.md` — paths: `**/*.spec.md` + `packages/design-system/**`
|
|
13
|
-
- `.claude/rules/ui-development.md` — paths: `**/*.tsx` + `**/*.ts`
|
|
14
|
-
- `.claude/rules/story-rules.md` — paths: `**/*.stories.tsx`
|
|
15
|
-
- `.claude/rules/self-verify.md` — paths: `src/**` + `.claude/**` + `*.spec.md` + `*.tsx` + `*.css`
|
|
16
|
-
|
|
17
|
-
## Hooks(write-time 加速器,非信任邊界)
|
|
18
|
-
|
|
19
|
-
`.claude/hooks/` 於 Claude Code lifecycle(PreToolUse / PostToolUse / Stop / SessionStart / UserPromptSubmit)機械強制——提供**最早的回饋**(寫入當下攔截)。它是 Claude 專屬加速器;**最終不可繞過層 = `npm run release:preflight` + CI**(AGENTS.md 檔頭宣告),任何 provider 的產出都被同一套 deterministic gates 把關。Hook 計數/預算 SSOT = `session_start_governance_check.sh` Check 7。
|
|
20
|
-
|
|
21
|
-
## Skills / Commands / Memory(Claude 專屬 discovery)
|
|
22
|
-
|
|
23
|
-
- **Skills**:`.claude/skills/`(invoke 情境的多步驟 workflow + checkpoint;`/skill-name` 觸發)
|
|
24
|
-
- **Commands**:`.claude/commands/`(一次性單步 action)
|
|
25
|
-
- **Memory**:`~/.claude/projects/<project>/memory/` 為 SSOT + repo `.claude/memory/` mirror(本機編完跑 `npm run sync-memory` 推回 repo,讓 cloud sandbox / 其他 agent 看得到)
|
|
26
|
-
- **Plugin 邊界**:plugin 只能是 optional adapter / distribution convenience;任何 Critical 治理不得 plugin-only(cloud plugin 不可靠實證 → `.claude/memory/reference_cloud_governance_loading.md`)
|
|
5
|
+
The import above loads only the concise package augmentation. In the author monorepo, repository-wide Claude instructions are already inherited from the root `CLAUDE.md`; in an installed package, consumer governance still comes from the authenticated `ds-canonical/fork` snapshot rather than this file.
|
|
@@ -149,6 +149,10 @@ function FilterValuePicker({
|
|
|
149
149
|
className
|
|
150
150
|
}
|
|
151
151
|
);
|
|
152
|
+
// person_single / person_multi — 走 PeoplePicker(2026-05-07 升級,對齊 cell-registry SSOT)。
|
|
153
|
+
// colInfo.people 來自 column meta.people。Filter value:
|
|
154
|
+
// - person_single:存 PersonValue | null(picker emit array,我們取 [0])
|
|
155
|
+
// - person_multi:存 PersonValue[]
|
|
152
156
|
case "person_single": {
|
|
153
157
|
const v = value;
|
|
154
158
|
return /* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-table-filter-value-picker.js","sources":["../../../src/components/DataTable/data-table-filter-value-picker.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved(2026-07-14 自 data-table-filter-panel.tsx 拆檔搬移,原檔級 escape 隨段落帶入)。\n// @internal — DS-internal 單元(FilterValuePicker value-picker switcher,只被\n// data-table-filter-group.tsx FilterRow 消費);不隨 index.ts re-export 進 npm public surface。\n// ────────────────────────────── 消費的 SSOT ──────────────────────────────\n// - Field controls(Family 4,統一 size=\"sm\"):Input / NumberInput / Select / Combobox /\n// DatePicker + DatePickerRange(showTime)/ PeoplePicker — 按 ValueShape dispatch\n// (ValueShape ↔ picker 對照 canonical:./data-table.spec.md「三、Operator × ValueShape SSOT」)\n// - className 直接 forward 給 inner Field control(不另開 wrapper div)—\n// field-control-group.spec.md「forward className」canonical(2026-05-04 #2 fix)\n// - Operator / ValueShape SSOT:./filter-operators.ts(DATE_RELATIVE_OPTIONS / DATE_RELATIVE_GROUPS)\n// - Person pool:PeoplePicker PersonValue(對齊 cell-registry meta.people SSOT)\n// 設計規則:./filter-operators.spec.md「ValueShape canonical」+ ./data-table.spec.md「進階篩選」段\n\nimport { Select, type SelectOption } from '@/design-system/components/Select/select'\nimport { Combobox } from '@/design-system/components/Combobox/combobox'\nimport { Input } from '@/design-system/components/Input/input'\nimport { NumberInput } from '@/design-system/components/NumberInput/number-input'\nimport { DatePicker, DatePickerRange } from '@/design-system/components/DatePicker/date-picker'\nimport { PeoplePicker } from '@/design-system/components/PeoplePicker/people-picker'\nimport type { PersonValue } from '@/design-system/components/PeoplePicker/person-display'\nimport {\n DATE_RELATIVE_OPTIONS,\n DATE_RELATIVE_GROUPS,\n type ValueShape,\n} from './filter-operators'\n\n// ── FilterValuePicker(value-picker switcher per ValueShape)────────────────\n//\n// 2026-05-03 M21 retract:本 helper 原獨立檔 filter-value-picker.tsx(187 行 / 1 consumer),\n// claim「未來 inline filter UI 共用」= premature abstraction → inline 回 panel。\n// 2026-07-14 拆檔 refactor:panel 822 行過 800 hard cap(file-size escalation)→ 重新抽出為\n// @internal 私有模組(非 public API 抽象,與 M21 不衝突;consumer 仍只見 DataTableFilterPanel)。\n\nexport interface FilterValuePickerColInfo {\n id: string\n label: string\n options?: Array<{ value: string; label: string }>\n /** Person pool — person/multiPerson filter picker 用(2026-05-07 升級,SSOT 對齊 cell-registry) */\n people?: Array<{ name: string; avatarUrl?: string; description?: string }>\n}\n\nexport interface FilterValuePickerProps {\n shape: ValueShape | null\n value: unknown\n onChange: (v: unknown) => void\n colInfo?: FilterValuePickerColInfo\n disabled?: boolean\n /** 用 column.label 組「{label} 篩選值」(panel 每 row 不顯式 label,a11y 必填) */\n ariaLabel?: string\n /** Forward 給內部 Field control 的 className(2026-05-04 #2 fix)\n * 避免外層包 wrapper div 破壞 FieldControlGroup CSS variants(rounded radii / margin overlap) */\n className?: string\n}\n\nexport function FilterValuePicker({\n shape,\n value,\n onChange,\n colInfo,\n disabled,\n ariaLabel,\n className,\n}: FilterValuePickerProps) {\n if (!shape || disabled) {\n return <Input size=\"sm\" value=\"\" onChange={() => {}} placeholder=\"輸入值…\" disabled aria-label={ariaLabel} className={className} />\n }\n\n switch (shape) {\n case 'none':\n return null\n\n case 'text':\n return (\n <Input\n size=\"sm\"\n value={String(value ?? '')}\n onChange={(e) => onChange(e.target.value)}\n placeholder=\"輸入值…\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'number':\n return (\n <NumberInput\n size=\"sm\"\n value={typeof value === 'number' ? value : null}\n onChange={(v) => onChange(v ?? '')}\n placeholder=\"輸入數字…\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'date_single':\n return (\n <DatePicker\n size=\"sm\"\n value={typeof value === 'string' ? value : null}\n onChange={(v) => onChange(v ?? '')}\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'date_range':\n return (\n <DatePickerRange\n size=\"sm\"\n value={Array.isArray(value) && value.length === 2\n ? (value as [string | null, string | null])\n : null}\n onChange={(v) => onChange(v)}\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'date_relative': {\n // 群組分類:Past / Current / Future(對齊 Linear / Notion idiom),走 Select.groups → SelectMenu\n const opts: SelectOption[] = DATE_RELATIVE_OPTIONS.map((o) => ({\n value: o.value,\n label: o.label,\n group: o.group,\n }))\n return (\n <Select\n size=\"sm\"\n options={opts}\n groups={DATE_RELATIVE_GROUPS as unknown as Array<{ key: string; label: string }>}\n value={String(value ?? '')}\n onChange={(v) => onChange(v)}\n placeholder=\"選擇相對日期\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n\n case 'select_single': {\n const opts: SelectOption[] = (colInfo?.options ?? []).map((o) => ({\n value: o.value,\n label: o.label,\n }))\n return (\n <Select\n size=\"sm\"\n options={opts}\n value={String(value ?? '')}\n onChange={(v) => onChange(v)}\n placeholder=\"選擇值\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n\n case 'select_multi': {\n const opts = (colInfo?.options ?? []).map((o) => ({\n value: o.value,\n label: o.label,\n }))\n const arr = Array.isArray(value) ? (value as string[]) : []\n return (\n <Combobox\n size=\"sm\"\n options={opts}\n value={arr}\n onChange={(v) => onChange(v)}\n placeholder=\"選擇值…\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n\n case 'datetime_single':\n return (\n <DatePicker\n size=\"sm\"\n showTime\n value={typeof value === 'string' ? value : null}\n onChange={(v) => onChange(v ?? '')}\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'datetime_range':\n return (\n <DatePickerRange\n size=\"sm\"\n showTime\n value={Array.isArray(value) && value.length === 2\n ? (value as [string | null, string | null])\n : null}\n onChange={(v) => onChange(v)}\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n // person_single / person_multi — 走 PeoplePicker(2026-05-07 升級,對齊 cell-registry SSOT)。\n // colInfo.people 來自 column meta.people。Filter value:\n // - person_single:存 PersonValue | null(picker emit array,我們取 [0])\n // - person_multi:存 PersonValue[]\n case 'person_single': {\n const v = value as PersonValue | null | undefined\n return (\n <PeoplePicker\n size=\"sm\"\n value={v ?? null}\n people={colInfo?.people ?? []}\n onChange={(next) => onChange(next[0] ?? null)}\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n case 'person_multi': {\n const v = Array.isArray(value) ? (value as PersonValue[]) : []\n return (\n <PeoplePicker\n size=\"sm\"\n value={v}\n people={colInfo?.people ?? []}\n onChange={(next) => onChange(next)}\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n default:\n return null\n }\n}\n"],"names":["DatePicker"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-table-filter-value-picker.js","sources":["../../../src/components/DataTable/data-table-filter-value-picker.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved(2026-07-14 自 data-table-filter-panel.tsx 拆檔搬移,原檔級 escape 隨段落帶入)。\n// @internal — DS-internal 單元(FilterValuePicker value-picker switcher,只被\n// data-table-filter-group.tsx FilterRow 消費);不隨 index.ts re-export 進 npm public surface。\n// ────────────────────────────── 消費的 SSOT ──────────────────────────────\n// - Field controls(Family 4,統一 size=\"sm\"):Input / NumberInput / Select / Combobox /\n// DatePicker + DatePickerRange(showTime)/ PeoplePicker — 按 ValueShape dispatch\n// (ValueShape ↔ picker 對照 canonical:./data-table.spec.md「三、Operator × ValueShape SSOT」)\n// - className 直接 forward 給 inner Field control(不另開 wrapper div)—\n// field-control-group.spec.md「forward className」canonical(2026-05-04 #2 fix)\n// - Operator / ValueShape SSOT:./filter-operators.ts(DATE_RELATIVE_OPTIONS / DATE_RELATIVE_GROUPS)\n// - Person pool:PeoplePicker PersonValue(對齊 cell-registry meta.people SSOT)\n// 設計規則:./filter-operators.spec.md「ValueShape canonical」+ ./data-table.spec.md「進階篩選」段\n\nimport { Select, type SelectOption } from '@/design-system/components/Select/select'\nimport { Combobox } from '@/design-system/components/Combobox/combobox'\nimport { Input } from '@/design-system/components/Input/input'\nimport { NumberInput } from '@/design-system/components/NumberInput/number-input'\nimport { DatePicker, DatePickerRange } from '@/design-system/components/DatePicker/date-picker'\nimport { PeoplePicker } from '@/design-system/components/PeoplePicker/people-picker'\nimport type { PersonValue } from '@/design-system/components/PeoplePicker/person-display'\nimport {\n DATE_RELATIVE_OPTIONS,\n DATE_RELATIVE_GROUPS,\n type ValueShape,\n} from './filter-operators'\n\n// ── FilterValuePicker(value-picker switcher per ValueShape)────────────────\n//\n// 2026-05-03 M21 retract:本 helper 原獨立檔 filter-value-picker.tsx(187 行 / 1 consumer),\n// claim「未來 inline filter UI 共用」= premature abstraction → inline 回 panel。\n// 2026-07-14 拆檔 refactor:panel 822 行過 800 hard cap(file-size escalation)→ 重新抽出為\n// @internal 私有模組(非 public API 抽象,與 M21 不衝突;consumer 仍只見 DataTableFilterPanel)。\n\nexport interface FilterValuePickerColInfo {\n id: string\n label: string\n options?: Array<{ value: string; label: string }>\n /** Person pool — person/multiPerson filter picker 用(2026-05-07 升級,SSOT 對齊 cell-registry) */\n people?: Array<{ name: string; avatarUrl?: string; description?: string }>\n}\n\nexport interface FilterValuePickerProps {\n shape: ValueShape | null\n value: unknown\n onChange: (v: unknown) => void\n colInfo?: FilterValuePickerColInfo\n disabled?: boolean\n /** 用 column.label 組「{label} 篩選值」(panel 每 row 不顯式 label,a11y 必填) */\n ariaLabel?: string\n /** Forward 給內部 Field control 的 className(2026-05-04 #2 fix)\n * 避免外層包 wrapper div 破壞 FieldControlGroup CSS variants(rounded radii / margin overlap) */\n className?: string\n}\n\nexport function FilterValuePicker({\n shape,\n value,\n onChange,\n colInfo,\n disabled,\n ariaLabel,\n className,\n}: FilterValuePickerProps) {\n if (!shape || disabled) {\n return <Input size=\"sm\" value=\"\" onChange={() => {}} placeholder=\"輸入值…\" disabled aria-label={ariaLabel} className={className} />\n }\n\n switch (shape) {\n case 'none':\n return null\n\n case 'text':\n return (\n <Input\n size=\"sm\"\n value={String(value ?? '')}\n onChange={(e) => onChange(e.target.value)}\n placeholder=\"輸入值…\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'number':\n return (\n <NumberInput\n size=\"sm\"\n value={typeof value === 'number' ? value : null}\n onChange={(v) => onChange(v ?? '')}\n placeholder=\"輸入數字…\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'date_single':\n return (\n <DatePicker\n size=\"sm\"\n value={typeof value === 'string' ? value : null}\n onChange={(v) => onChange(v ?? '')}\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'date_range':\n return (\n <DatePickerRange\n size=\"sm\"\n value={Array.isArray(value) && value.length === 2\n ? (value as [string | null, string | null])\n : null}\n onChange={(v) => onChange(v)}\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'date_relative': {\n // 群組分類:Past / Current / Future(對齊 Linear / Notion idiom),走 Select.groups → SelectMenu\n const opts: SelectOption[] = DATE_RELATIVE_OPTIONS.map((o) => ({\n value: o.value,\n label: o.label,\n group: o.group,\n }))\n return (\n <Select\n size=\"sm\"\n options={opts}\n groups={DATE_RELATIVE_GROUPS as unknown as Array<{ key: string; label: string }>}\n value={String(value ?? '')}\n onChange={(v) => onChange(v)}\n placeholder=\"選擇相對日期\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n\n case 'select_single': {\n const opts: SelectOption[] = (colInfo?.options ?? []).map((o) => ({\n value: o.value,\n label: o.label,\n }))\n return (\n <Select\n size=\"sm\"\n options={opts}\n value={String(value ?? '')}\n onChange={(v) => onChange(v)}\n placeholder=\"選擇值\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n\n case 'select_multi': {\n const opts = (colInfo?.options ?? []).map((o) => ({\n value: o.value,\n label: o.label,\n }))\n const arr = Array.isArray(value) ? (value as string[]) : []\n return (\n <Combobox\n size=\"sm\"\n options={opts}\n value={arr}\n onChange={(v) => onChange(v)}\n placeholder=\"選擇值…\"\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n\n case 'datetime_single':\n return (\n <DatePicker\n size=\"sm\"\n showTime\n value={typeof value === 'string' ? value : null}\n onChange={(v) => onChange(v ?? '')}\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n case 'datetime_range':\n return (\n <DatePickerRange\n size=\"sm\"\n showTime\n value={Array.isArray(value) && value.length === 2\n ? (value as [string | null, string | null])\n : null}\n onChange={(v) => onChange(v)}\n aria-label={ariaLabel}\n className={className}\n />\n )\n\n // person_single / person_multi — 走 PeoplePicker(2026-05-07 升級,對齊 cell-registry SSOT)。\n // colInfo.people 來自 column meta.people。Filter value:\n // - person_single:存 PersonValue | null(picker emit array,我們取 [0])\n // - person_multi:存 PersonValue[]\n case 'person_single': {\n const v = value as PersonValue | null | undefined\n return (\n <PeoplePicker\n size=\"sm\"\n value={v ?? null}\n people={colInfo?.people ?? []}\n onChange={(next) => onChange(next[0] ?? null)}\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n case 'person_multi': {\n const v = Array.isArray(value) ? (value as PersonValue[]) : []\n return (\n <PeoplePicker\n size=\"sm\"\n value={v}\n people={colInfo?.people ?? []}\n onChange={(next) => onChange(next)}\n aria-label={ariaLabel}\n className={className}\n />\n )\n }\n default:\n return null\n }\n}\n"],"names":["DatePicker"],"mappings":"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"}
|
|
@@ -202,6 +202,12 @@ function matchOperator(op, cellValue, filterValue, datePrecision, prepared) {
|
|
|
202
202
|
return String(cellValue ?? "").toLowerCase().includes((prepared == null ? void 0 : prepared.filterTextLower) ?? String(filterValue).toLowerCase());
|
|
203
203
|
case "does_not_contain":
|
|
204
204
|
return !String(cellValue ?? "").toLowerCase().includes((prepared == null ? void 0 : prepared.filterTextLower) ?? String(filterValue).toLowerCase());
|
|
205
|
+
// 2026-05-12 Round 6 fix(user 抓 Roadmap 進階篩選「全部」結果空)— impl-vs-spec drift。
|
|
206
|
+
// Per `filter-operators.spec.md` L116「is 直接接受多值,不另設 is_any_of」+ L103-116
|
|
207
|
+
// select(is/is_not + select_multi ValueShape OR 語意)+ L31-32(select.is / person.is 都走
|
|
208
|
+
// select_multi / person_multi ValueShape)。原 impl 只做 single-value 比對,filterValue 是
|
|
209
|
+
// array(全選 options)→ String(array)= "v1,v2" → 永遠 != single cellValue → 結果空。
|
|
210
|
+
// Fix:加 array handling,走 OR 語意:any → match;is_not → every-not-match。
|
|
205
211
|
case "is":
|
|
206
212
|
if (datePrecision) {
|
|
207
213
|
const a = toDateMs(cellValue, datePrecision);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-tree.js","sources":["../../../src/components/DataTable/filter-tree.ts"],"sourcesContent":["/**\n * filter-tree.ts — FilterTree types + helpers + evaluator\n *\n * 抽自 data-table-filter-panel.tsx 為了 file-size budget(panel 拆分)。\n * 純資料結構 + pure functions,無 UI dep。\n *\n * 詳:./filter-operators.spec.md\n */\n\nimport {\n startOfDay, endOfDay,\n startOfWeek, endOfWeek,\n startOfMonth, endOfMonth,\n addDays, addWeeks, addMonths, subDays,\n} from 'date-fns'\nimport { OPERATOR_REGISTRY, type OperatorSpec } from './filter-operators'\n\n/**\n * 把 relative key 轉成 [start, end] 時間區間(local time,inclusive)。\n * 對齊 Notion / ClickUp idiom — 以「今天」為錨點計算。\n */\nfunction relativeKeyToRange(key: string, now: Date = new Date()): [number, number] | null {\n const today = startOfDay(now)\n switch (key) {\n case 'today': return [today.getTime(), endOfDay(now).getTime()]\n case 'yesterday': return [startOfDay(subDays(now, 1)).getTime(), endOfDay(subDays(now, 1)).getTime()]\n case 'tomorrow': return [startOfDay(addDays(now, 1)).getTime(), endOfDay(addDays(now, 1)).getTime()]\n case 'this_week': return [startOfWeek(now, { weekStartsOn: 1 }).getTime(), endOfWeek(now, { weekStartsOn: 1 }).getTime()]\n case 'last_week': return [startOfWeek(addWeeks(now, -1), { weekStartsOn: 1 }).getTime(), endOfWeek(addWeeks(now, -1), { weekStartsOn: 1 }).getTime()]\n case 'next_week': return [startOfWeek(addWeeks(now, 1), { weekStartsOn: 1 }).getTime(), endOfWeek(addWeeks(now, 1), { weekStartsOn: 1 }).getTime()]\n case 'this_month': return [startOfMonth(now).getTime(), endOfMonth(now).getTime()]\n case 'last_month': return [startOfMonth(addMonths(now, -1)).getTime(), endOfMonth(addMonths(now, -1)).getTime()]\n case 'next_month': return [startOfMonth(addMonths(now, 1)).getTime(), endOfMonth(addMonths(now, 1)).getTime()]\n case 'past_7_days': return [startOfDay(subDays(now, 7)).getTime(), endOfDay(now).getTime()]\n case 'past_30_days': return [startOfDay(subDays(now, 30)).getTime(), endOfDay(now).getTime()]\n case 'next_7_days': return [startOfDay(now).getTime(), endOfDay(addDays(now, 7)).getTime()]\n case 'next_30_days': return [startOfDay(now).getTime(), endOfDay(addDays(now, 30)).getTime()]\n default: return null\n }\n}\n\n// ── Types ────────────────────────────────────────────────────────────────\n\nexport type Conjunction = 'and' | 'or'\n\nexport interface FilterCondition {\n kind: 'cond'\n /** 唯一 row id(stable across renders) */\n id: string\n /** 對應 column id;空字串 = 尚未選 field(operator/value picker disabled) */\n field: string\n /** OperatorSpec.op key — 對齊 OPERATOR_REGISTRY */\n op: string\n /** 依 ValueShape 解讀的值 */\n value: unknown\n /** Date 欄位比對精度(2026-07-04 Q6 拍板,兌現 spec「includeTime」雙精度契約):\n * panel 建 condition 時依 column meta 寫入 — includeTime=true → 'ms',否則 'day'\n * (AG Grid midnight-truncate / MUI X setHours(0,0,0,0) 共識;避 Airtable 邊界漏抓地雷)。\n * 非 date 欄位 / 舊 tree 無此欄 → undefined(date ops fallback 'day' 預設)。 */\n datePrecision?: 'day' | 'ms'\n}\n\nexport interface FilterGroup {\n kind: 'group'\n id: string\n /** group 內 children 共用的 join(全 AND 或全 OR;不允許混) */\n conjunction: Conjunction\n /** ⚠️ 型別鎖死:children 只能 condition,不可再 nested group(1-level cap) */\n children: FilterCondition[]\n}\n\nexport type FilterTreeFlat = {\n mode: 'flat'\n conjunction: Conjunction\n children: FilterCondition[]\n}\n\nexport type FilterTreeNested = {\n mode: 'nested'\n conjunction: Conjunction\n children: FilterGroup[]\n}\n\nexport type FilterTree = FilterTreeFlat | FilterTreeNested\n\n// ── Helpers — public API ────────────────────────────────────────────────\n\n/** Empty FilterTree — consumer 用來 init useState */\nexport function createEmptyFilterTree(mode: 'flat'): FilterTreeFlat\nexport function createEmptyFilterTree(mode: 'nested'): FilterTreeNested\nexport function createEmptyFilterTree(mode: 'flat' | 'nested'): FilterTree {\n if (mode === 'flat') return { mode: 'flat', conjunction: 'and', children: [] }\n return { mode: 'nested', conjunction: 'and', children: [] }\n}\n\n/** 是否有任何 active filter — DataTable trigger button checked state 用 */\nexport function isFilterTreeActive(tree: FilterTree): boolean {\n if (tree.mode === 'flat') {\n return tree.children.some((c) => isConditionComplete(c))\n }\n return tree.children.some((g) => g.children.some((c) => isConditionComplete(c)))\n}\n\n/** Condition 是否「已填齊」可參與 filter 求值 */\nfunction isConditionComplete(c: FilterCondition): boolean {\n if (!c.field || !c.op) return false\n const spec = getOperatorSpecLoose(c.op)\n if (!spec) return false\n if (spec.valueShape === 'none') return true\n return c.value !== '' && c.value !== null && c.value !== undefined\n}\n\n/** loose lookup — 我們不知 columnType,試所有 type。\n * D3 perf(2026-07-06):module-level Map cache — OPERATOR_REGISTRY 是 module 常數,\n * 同一 op key 查詢結果永不變;原每 call 線性掃全 registry,而 evaluateTree 熱路徑\n * per-row 呼叫時(10k rows × N conditions)放大成數十萬次比較,cache 後 O(1)。 */\nconst opSpecLooseCache = new Map<string, OperatorSpec | null>()\nfunction getOperatorSpecLoose(op: string): OperatorSpec | null {\n const cached = opSpecLooseCache.get(op)\n if (cached !== undefined) return cached\n let found: OperatorSpec | null = null\n for (const list of Object.values(OPERATOR_REGISTRY)) {\n const hit = list.find((o) => o.op === op)\n if (hit) { found = hit; break }\n }\n opSpecLooseCache.set(op, found)\n return found\n}\n\n// ── Deep-equal compare(refresh icon 顯示判定用) ─────────────────────────\n\n/** Compare two FilterTrees structurally — 忽略內部 row id(refresh-detect 不該被內部 id 干擾)*/\nexport function isFilterTreeEqual(a: FilterTree, b: FilterTree): boolean {\n if (a.mode !== b.mode || a.conjunction !== b.conjunction) return false\n if (a.children.length !== b.children.length) return false\n if (a.mode === 'flat' && b.mode === 'flat') {\n return a.children.every((c, i) => isConditionEqual(c, b.children[i]))\n }\n if (a.mode === 'nested' && b.mode === 'nested') {\n return a.children.every((g, i) => isGroupEqual(g, b.children[i]))\n }\n return false\n}\n\nfunction isGroupEqual(a: FilterGroup, b: FilterGroup): boolean {\n if (a.conjunction !== b.conjunction) return false\n if (a.children.length !== b.children.length) return false\n return a.children.every((c, i) => isConditionEqual(c, b.children[i]))\n}\n\nfunction isConditionEqual(a: FilterCondition, b: FilterCondition): boolean {\n if (a.field !== b.field || a.op !== b.op) return false\n return JSON.stringify(a.value) === JSON.stringify(b.value)\n}\n\n// ── Tree Evaluation(globalFilter approach)──────────────────────────────\n\n// ── D3 perf(2026-07-06):per-condition compile-once cache ────────────────\n// TanStack globalFilterFn 逐 row 呼叫 evaluateTree,原實作每 row 重做「跨全 rows 不變」\n// 的常數工作:isConditionComplete(operator registry 掃描)、dateMs(filterValue) 重 parse、\n// filterValue 重 toLowerCase、is_relative 重算時間區間 — 10k rows × N conditions 全額放大。\n// Fix:以 condition 物件為 key 的 WeakMap 快取 filterValue 側預解析結果,row loop 只轉\n// cellValue 側。Panel 更新走 immutable `{ ...c, ...patch }`(新物件 = 新 cache entry),\n// 舊 entry 隨舊 condition 物件 GC — cache 無失效問題。\ninterface PreparedCondition {\n /** isConditionComplete 結果(registry 掃描一次,取代原 per-row filter 掃描) */\n complete: boolean\n /** filterValue 側是否 Person(物件或含 Person 的陣列) */\n filterIsPerson: boolean\n /** person 比對 ops 的 filter 側 id array(cellValue 是 Person 時也走 person path,故照 op 準備) */\n filterPersonIds: string[] | null\n /** 文字 ops 用:String(filterValue).toLowerCase() */\n filterTextLower: string | null\n /** is / is_not 陣列(多值 OR 語意)逐元素 lowercase */\n filterTextLowerArr: string[] | null\n /** 單值 date ops(is_before 家族;is / is_not with datePrecision)— datePrecision 已套 */\n filterDateMs: number | null\n /** is_between 兩端(空端 = ±Infinity,語意同原 inline 邏輯) */\n filterDateStart: number | null\n filterDateEnd: number | null\n /** is_relative:快取區間 + 計算當日 stamp(跨日自動重算 — 所有 relative 區間都是\n * calendar day 的純函數,同日內恆定,見 localDayKey) */\n relRange: [number, number] | null\n relDayKey: number\n}\n\nconst PERSON_CAPABLE_OPS = new Set(['is', 'is_not', 'has_any_of', 'has_all_of', 'has_none_of'])\nconst DATE_SINGLE_OPS = new Set(['is_before', 'is_after', 'is_on_or_before', 'is_on_or_after'])\nconst TEXT_OPS = new Set(['contains', 'does_not_contain', 'starts_with', 'ends_with', 'is', 'is_not'])\n\nconst preparedCache = new WeakMap<FilterCondition, PreparedCondition>()\n\nfunction prepareCondition(c: FilterCondition): PreparedCondition {\n const hit = preparedCache.get(c)\n if (hit) return hit\n const fv = c.value\n const isBetweenPair = c.op === 'is_between' && Array.isArray(fv) && fv.length === 2\n const prepared: PreparedCondition = {\n complete: isConditionComplete(c),\n filterIsPerson: isPersonObject(fv) || (Array.isArray(fv) && fv.some(isPersonObject)),\n filterPersonIds: PERSON_CAPABLE_OPS.has(c.op)\n ? (Array.isArray(fv) ? fv.map(personId) : [personId(fv)])\n : null,\n filterTextLower: TEXT_OPS.has(c.op) ? String(fv).toLowerCase() : null,\n filterTextLowerArr:\n (c.op === 'is' || c.op === 'is_not') && Array.isArray(fv) ? fv.map((v) => String(v).toLowerCase()) : null,\n filterDateMs:\n DATE_SINGLE_OPS.has(c.op) || ((c.op === 'is' || c.op === 'is_not') && c.datePrecision !== undefined)\n ? toDateMs(fv, c.datePrecision)\n : null,\n filterDateStart: isBetweenPair ? (fv[0] ? toDateMs(fv[0], c.datePrecision) : -Infinity) : null,\n filterDateEnd: isBetweenPair ? (fv[1] ? toDateMs(fv[1], c.datePrecision) : Infinity) : null,\n relRange: null,\n relDayKey: 0,\n }\n preparedCache.set(c, prepared)\n return prepared\n}\n\n/**\n * 整棵 FilterTree 對 row 求 boolean。\n * 配合 useReactTable `globalFilterFn`:\n *\n * const tree = useState<FilterTree>(createEmptyFilterTree('flat'))\n * useReactTable({\n * state: { globalFilter: tree },\n * onGlobalFilterChange: setTree,\n * globalFilterFn: (row, _, t: FilterTree) => evaluateTree(t, row.original),\n * getFilteredRowModel: getFilteredRowModel(),\n * })\n */\n// any-allow: row-generic — TanStack row.original 是 generic,filter eval 跨 type 必走 any\nexport function evaluateTree(tree: FilterTree, row: any): boolean {\n if (tree.children.length === 0) return true\n\n if (tree.mode === 'flat') {\n // null = 無 complete condition → pass-through(語意同原 completed.length === 0 分支)\n return evaluateConditionsJoined(tree.children, tree.conjunction, row) ?? true\n }\n\n // nested — group 沒任何 complete condition 的視為 pass-through(不參與 conjunction),\n // 語意同原 meaningful filter\n let sawMeaningful = false\n for (const g of tree.children) {\n const r = evaluateConditionsJoined(g.children, g.conjunction, row)\n if (r === null) continue\n sawMeaningful = true\n if (tree.conjunction === 'and') {\n if (!r) return false\n } else if (r) {\n return true\n }\n }\n if (!sawMeaningful) return true\n return tree.conjunction === 'and'\n}\n\n/** 一組 conditions 依 conjunction 求 boolean;null = 組內無 complete condition(pass-through)。\n * D3 perf:prepared.complete 取代原 per-row `children.filter(isConditionComplete)`\n * (registry 掃描 + filter/map 兩個中間陣列 alloc);and/or 短路 — 條件是 pure predicate\n * 無副作用,求值順序 / 省略不影響結果。 */\n// any-allow: row-generic\nfunction evaluateConditionsJoined(children: FilterCondition[], conjunction: Conjunction, row: any): boolean | null {\n let sawComplete = false\n for (const c of children) {\n const prepared = prepareCondition(c)\n if (!prepared.complete) continue\n sawComplete = true\n const matched = evaluateCondition(c, prepared, row)\n if (conjunction === 'and') {\n if (!matched) return false\n } else if (matched) {\n return true\n }\n }\n if (!sawComplete) return null\n return conjunction === 'and'\n}\n\n// any-allow: row-generic\nfunction evaluateCondition(cond: FilterCondition, prepared: PreparedCondition, row: any): boolean {\n if (!cond.field || !cond.op) return true\n const cellValue = row?.[cond.field]\n return matchOperator(cond.op, cellValue, cond.value, cond.datePrecision, prepared)\n}\n\n// PersonValue identity helper(2026-05-07):\n// person/multiPerson cell value 是 `{ name, avatarUrl?, description? }` 物件 — 比對時用 `name`\n// 當 stable id(PeoplePicker SSOT 沒有 id field,name 是唯一身分標識)。對齊 Notion / Linear /\n// Asana 的 person filter 比對 idiom(都用 name/email 當 id)。\nfunction personId(v: unknown): string {\n if (v && typeof v === 'object' && 'name' in v) return String((v as { name: unknown }).name ?? '')\n return String(v ?? '')\n}\nfunction isPersonObject(v: unknown): v is { name: string } {\n return !!v && typeof v === 'object' && 'name' in v\n}\n\n// Q6(2026-07-04):date 比對精度 helper — 'ms' 全精度;其餘(含 undefined)day 級\n// (本地時區 startOfDay 截斷,對齊 AG Grid / MUI X date 欄 midnight 慣例)。\n// D3 perf(2026-07-06):自 matchOperator 內 closure 提升 top-level — prepareCondition\n// (filterValue 側,每 condition 一次)與 matchOperator(cellValue 側,每 row)共用同一實作。\nfunction toDateMs(v: unknown, datePrecision?: 'day' | 'ms'): number {\n const d = new Date(String(v))\n if (Number.isNaN(d.getTime())) return NaN\n return datePrecision === 'ms' ? d.getTime() : startOfDay(d).getTime()\n}\n\n/** 本地日曆日 stamp(yyyymmdd)— is_relative 快取跨日失效判定(每 row 1 個 Date alloc,\n * 取代原 relativeKeyToRange 每 row 4-10 個 Date alloc + 區間重算) */\nfunction localDayKey(d: Date): number {\n return d.getFullYear() * 10000 + (d.getMonth() + 1) * 100 + d.getDate()\n}\n\n// code-quality-allow: long-function — 13-operator switch dispatch table,table-driven 重構會把 op-specific guards 拆出反增 indirection\nfunction matchOperator(op: string, cellValue: unknown, filterValue: unknown, datePrecision?: 'day' | 'ms', prepared?: PreparedCondition): boolean {\n // 不需 value 的 op\n switch (op) {\n case 'is_set': return cellValue !== null && cellValue !== undefined && cellValue !== ''\n case 'is_not_set': return cellValue === null || cellValue === undefined || cellValue === ''\n case 'is_true': return cellValue === true\n case 'is_false': return cellValue === false\n }\n\n // 需 value 但 value 空 → 視為 incomplete,pass-through\n if (filterValue === null || filterValue === undefined || filterValue === '') return true\n if (Array.isArray(filterValue) && filterValue.length === 0) return true\n\n // Person-aware specialization(person_single / person_multi)— 物件透過 name 比對。\n // 偵測:cellValue 或 filterValue 任一是 Person object(或包 Person 的陣列)→ 走 personId path。\n // D3 perf:filterValue 側偵測 + id array 消費 prepared 快取(cellValue 側必然 per-row)。\n const cellIsPerson = isPersonObject(cellValue) || (Array.isArray(cellValue) && cellValue.some(isPersonObject))\n const filterIsPerson = prepared\n ? prepared.filterIsPerson\n : isPersonObject(filterValue) || (Array.isArray(filterValue) && filterValue.some(isPersonObject))\n if (cellIsPerson || filterIsPerson) {\n const cellIds = Array.isArray(cellValue) ? cellValue.map(personId) : [personId(cellValue)]\n const filterIds = prepared?.filterPersonIds ?? (Array.isArray(filterValue) ? filterValue.map(personId) : [personId(filterValue)])\n switch (op) {\n case 'is': return cellIds.length === 1 && filterIds.length >= 1 && filterIds.includes(cellIds[0])\n case 'is_not': return !(cellIds.length === 1 && filterIds.length >= 1 && filterIds.includes(cellIds[0]))\n case 'has_any_of': return cellIds.some((c) => filterIds.includes(c))\n case 'has_all_of': return filterIds.every((f) => cellIds.includes(f))\n case 'has_none_of':return !cellIds.some((c) => filterIds.includes(c))\n default: return true\n }\n }\n\n // (date 精度 helper 已提升為 top-level `toDateMs`;文字 ops 的 filterValue lowercase 消費\n // prepared 快取,prepared 缺席 = deprecated dataTableFilterMatch 路徑 inline fallback — D3 perf)\n\n switch (op) {\n case 'contains': return String(cellValue ?? '').toLowerCase().includes(prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n case 'does_not_contain': return !String(cellValue ?? '').toLowerCase().includes(prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n // 2026-05-12 Round 6 fix(user 抓 Roadmap 進階篩選「全部」結果空)— impl-vs-spec drift。\n // Per `filter-operators.spec.md` L116「is 直接接受多值,不另設 is_any_of」+ L103-116\n // select(is/is_not + select_multi ValueShape OR 語意)+ L31-32(select.is / person.is 都走\n // select_multi / person_multi ValueShape)。原 impl 只做 single-value 比對,filterValue 是\n // array(全選 options)→ String(array)= \"v1,v2\" → 永遠 != single cellValue → 結果空。\n // Fix:加 array handling,走 OR 語意:any → match;is_not → every-not-match。\n case 'is':\n // Q6:date 欄位(datePrecision 有值)走日期比對非字串 — 「2026-06-01」與「2026-06-01 14:30」\n // 在 day 精度下相等(原字串小寫相等連同日不同格式都漏)\n if (datePrecision) {\n const a = toDateMs(cellValue, datePrecision)\n const b = prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision)\n return !Number.isNaN(a) && a === b\n }\n if (Array.isArray(filterValue)) {\n const cellLower = String(cellValue ?? '').toLowerCase()\n const filterLowers = prepared?.filterTextLowerArr ?? filterValue.map((v) => String(v).toLowerCase())\n return filterLowers.some((v) => cellLower === v)\n }\n return String(cellValue ?? '').toLowerCase() === (prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n case 'is_not':\n if (datePrecision) {\n const a = toDateMs(cellValue, datePrecision)\n const b = prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision)\n return Number.isNaN(a) || a !== b\n }\n if (Array.isArray(filterValue)) {\n const cellLower = String(cellValue ?? '').toLowerCase()\n const filterLowers = prepared?.filterTextLowerArr ?? filterValue.map((v) => String(v).toLowerCase())\n return filterLowers.every((v) => cellLower !== v)\n }\n return String(cellValue ?? '').toLowerCase() !== (prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n case 'starts_with': return String(cellValue ?? '').toLowerCase().startsWith(prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n case 'ends_with': return String(cellValue ?? '').toLowerCase().endsWith(prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n\n case 'equals': return Number(cellValue) === Number(filterValue)\n case 'not_equals': return Number(cellValue) !== Number(filterValue)\n case 'gt': return Number(cellValue) > Number(filterValue)\n case 'gte': return Number(cellValue) >= Number(filterValue)\n case 'lt': return Number(cellValue) < Number(filterValue)\n case 'lte': return Number(cellValue) <= Number(filterValue)\n\n case 'is_before': return toDateMs(cellValue, datePrecision) < (prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision))\n case 'is_after': return toDateMs(cellValue, datePrecision) > (prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision))\n case 'is_on_or_before': return toDateMs(cellValue, datePrecision) <= (prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision))\n case 'is_on_or_after': return toDateMs(cellValue, datePrecision) >= (prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision))\n case 'is_between': {\n if (!Array.isArray(filterValue) || filterValue.length !== 2) return true\n const cv = toDateMs(cellValue, datePrecision)\n const start = prepared?.filterDateStart ?? (filterValue[0] ? toDateMs(filterValue[0], datePrecision) : -Infinity)\n const end = prepared?.filterDateEnd ?? (filterValue[1] ? toDateMs(filterValue[1], datePrecision) : Infinity)\n return cv >= start && cv <= end\n }\n case 'is_relative': {\n // Phase D 完整實作 — relative key → time range → in-range test\n // D3 perf:relativeKeyToRange 所有輸出都是 calendar day 的純函數(today / this_week /\n // past_7_days… 全是日界),同日內恆定 → 以 localDayKey 判跨日,同日重用快取區間\n // (原每 row 重算 4-10 個 Date alloc)。\n let range: [number, number] | null\n if (prepared) {\n const now = new Date()\n const dayKey = localDayKey(now)\n if (prepared.relDayKey !== dayKey) {\n prepared.relRange = relativeKeyToRange(String(filterValue), now)\n prepared.relDayKey = dayKey\n }\n range = prepared.relRange\n } else {\n range = relativeKeyToRange(String(filterValue))\n }\n if (!range) return true // unknown key,pass-through\n const cv = new Date(String(cellValue)).getTime()\n if (Number.isNaN(cv)) return false // invalid cellValue → 不命中\n const [start, end] = range\n return cv >= start && cv <= end\n }\n\n case 'has_any_of': {\n if (!Array.isArray(filterValue)) return true\n if (Array.isArray(cellValue)) return cellValue.some((c) => filterValue.includes(c))\n return filterValue.includes(cellValue)\n }\n case 'has_all_of': {\n if (!Array.isArray(filterValue)) return true\n if (Array.isArray(cellValue)) return filterValue.every((v) => cellValue.includes(v))\n return false\n }\n case 'has_none_of': {\n if (!Array.isArray(filterValue)) return true\n if (Array.isArray(cellValue)) return !cellValue.some((c) => filterValue.includes(c))\n return !filterValue.includes(cellValue)\n }\n\n default: return true\n }\n}\n\n/**\n * @deprecated v0.x — old per-column filterFn integration。\n * 新版用 `evaluateTree` 配 TanStack `globalFilter`。\n * 保留為了過渡期 backward-compat,新 consumer 不要用。\n */\nexport function dataTableFilterMatch(cellValue: unknown, filterValue: unknown): boolean {\n if (typeof filterValue === 'object' && filterValue !== null && 'operator' in filterValue && 'value' in filterValue) {\n const fv = filterValue as { operator: string; value: unknown }\n return matchOperator(fv.operator, cellValue, fv.value)\n }\n return String(cellValue ?? '').toLowerCase().includes(String(filterValue ?? '').toLowerCase())\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filter-tree.js","sources":["../../../src/components/DataTable/filter-tree.ts"],"sourcesContent":["/**\n * filter-tree.ts — FilterTree types + helpers + evaluator\n *\n * 抽自 data-table-filter-panel.tsx 為了 file-size budget(panel 拆分)。\n * 純資料結構 + pure functions,無 UI dep。\n *\n * 詳:./filter-operators.spec.md\n */\n\nimport {\n startOfDay, endOfDay,\n startOfWeek, endOfWeek,\n startOfMonth, endOfMonth,\n addDays, addWeeks, addMonths, subDays,\n} from 'date-fns'\nimport { OPERATOR_REGISTRY, type OperatorSpec } from './filter-operators'\n\n/**\n * 把 relative key 轉成 [start, end] 時間區間(local time,inclusive)。\n * 對齊 Notion / ClickUp idiom — 以「今天」為錨點計算。\n */\nfunction relativeKeyToRange(key: string, now: Date = new Date()): [number, number] | null {\n const today = startOfDay(now)\n switch (key) {\n case 'today': return [today.getTime(), endOfDay(now).getTime()]\n case 'yesterday': return [startOfDay(subDays(now, 1)).getTime(), endOfDay(subDays(now, 1)).getTime()]\n case 'tomorrow': return [startOfDay(addDays(now, 1)).getTime(), endOfDay(addDays(now, 1)).getTime()]\n case 'this_week': return [startOfWeek(now, { weekStartsOn: 1 }).getTime(), endOfWeek(now, { weekStartsOn: 1 }).getTime()]\n case 'last_week': return [startOfWeek(addWeeks(now, -1), { weekStartsOn: 1 }).getTime(), endOfWeek(addWeeks(now, -1), { weekStartsOn: 1 }).getTime()]\n case 'next_week': return [startOfWeek(addWeeks(now, 1), { weekStartsOn: 1 }).getTime(), endOfWeek(addWeeks(now, 1), { weekStartsOn: 1 }).getTime()]\n case 'this_month': return [startOfMonth(now).getTime(), endOfMonth(now).getTime()]\n case 'last_month': return [startOfMonth(addMonths(now, -1)).getTime(), endOfMonth(addMonths(now, -1)).getTime()]\n case 'next_month': return [startOfMonth(addMonths(now, 1)).getTime(), endOfMonth(addMonths(now, 1)).getTime()]\n case 'past_7_days': return [startOfDay(subDays(now, 7)).getTime(), endOfDay(now).getTime()]\n case 'past_30_days': return [startOfDay(subDays(now, 30)).getTime(), endOfDay(now).getTime()]\n case 'next_7_days': return [startOfDay(now).getTime(), endOfDay(addDays(now, 7)).getTime()]\n case 'next_30_days': return [startOfDay(now).getTime(), endOfDay(addDays(now, 30)).getTime()]\n default: return null\n }\n}\n\n// ── Types ────────────────────────────────────────────────────────────────\n\nexport type Conjunction = 'and' | 'or'\n\nexport interface FilterCondition {\n kind: 'cond'\n /** 唯一 row id(stable across renders) */\n id: string\n /** 對應 column id;空字串 = 尚未選 field(operator/value picker disabled) */\n field: string\n /** OperatorSpec.op key — 對齊 OPERATOR_REGISTRY */\n op: string\n /** 依 ValueShape 解讀的值 */\n value: unknown\n /** Date 欄位比對精度(2026-07-04 Q6 拍板,兌現 spec「includeTime」雙精度契約):\n * panel 建 condition 時依 column meta 寫入 — includeTime=true → 'ms',否則 'day'\n * (AG Grid midnight-truncate / MUI X setHours(0,0,0,0) 共識;避 Airtable 邊界漏抓地雷)。\n * 非 date 欄位 / 舊 tree 無此欄 → undefined(date ops fallback 'day' 預設)。 */\n datePrecision?: 'day' | 'ms'\n}\n\nexport interface FilterGroup {\n kind: 'group'\n id: string\n /** group 內 children 共用的 join(全 AND 或全 OR;不允許混) */\n conjunction: Conjunction\n /** ⚠️ 型別鎖死:children 只能 condition,不可再 nested group(1-level cap) */\n children: FilterCondition[]\n}\n\nexport type FilterTreeFlat = {\n mode: 'flat'\n conjunction: Conjunction\n children: FilterCondition[]\n}\n\nexport type FilterTreeNested = {\n mode: 'nested'\n conjunction: Conjunction\n children: FilterGroup[]\n}\n\nexport type FilterTree = FilterTreeFlat | FilterTreeNested\n\n// ── Helpers — public API ────────────────────────────────────────────────\n\n/** Empty FilterTree — consumer 用來 init useState */\nexport function createEmptyFilterTree(mode: 'flat'): FilterTreeFlat\nexport function createEmptyFilterTree(mode: 'nested'): FilterTreeNested\nexport function createEmptyFilterTree(mode: 'flat' | 'nested'): FilterTree {\n if (mode === 'flat') return { mode: 'flat', conjunction: 'and', children: [] }\n return { mode: 'nested', conjunction: 'and', children: [] }\n}\n\n/** 是否有任何 active filter — DataTable trigger button checked state 用 */\nexport function isFilterTreeActive(tree: FilterTree): boolean {\n if (tree.mode === 'flat') {\n return tree.children.some((c) => isConditionComplete(c))\n }\n return tree.children.some((g) => g.children.some((c) => isConditionComplete(c)))\n}\n\n/** Condition 是否「已填齊」可參與 filter 求值 */\nfunction isConditionComplete(c: FilterCondition): boolean {\n if (!c.field || !c.op) return false\n const spec = getOperatorSpecLoose(c.op)\n if (!spec) return false\n if (spec.valueShape === 'none') return true\n return c.value !== '' && c.value !== null && c.value !== undefined\n}\n\n/** loose lookup — 我們不知 columnType,試所有 type。\n * D3 perf(2026-07-06):module-level Map cache — OPERATOR_REGISTRY 是 module 常數,\n * 同一 op key 查詢結果永不變;原每 call 線性掃全 registry,而 evaluateTree 熱路徑\n * per-row 呼叫時(10k rows × N conditions)放大成數十萬次比較,cache 後 O(1)。 */\nconst opSpecLooseCache = new Map<string, OperatorSpec | null>()\nfunction getOperatorSpecLoose(op: string): OperatorSpec | null {\n const cached = opSpecLooseCache.get(op)\n if (cached !== undefined) return cached\n let found: OperatorSpec | null = null\n for (const list of Object.values(OPERATOR_REGISTRY)) {\n const hit = list.find((o) => o.op === op)\n if (hit) { found = hit; break }\n }\n opSpecLooseCache.set(op, found)\n return found\n}\n\n// ── Deep-equal compare(refresh icon 顯示判定用) ─────────────────────────\n\n/** Compare two FilterTrees structurally — 忽略內部 row id(refresh-detect 不該被內部 id 干擾)*/\nexport function isFilterTreeEqual(a: FilterTree, b: FilterTree): boolean {\n if (a.mode !== b.mode || a.conjunction !== b.conjunction) return false\n if (a.children.length !== b.children.length) return false\n if (a.mode === 'flat' && b.mode === 'flat') {\n return a.children.every((c, i) => isConditionEqual(c, b.children[i]))\n }\n if (a.mode === 'nested' && b.mode === 'nested') {\n return a.children.every((g, i) => isGroupEqual(g, b.children[i]))\n }\n return false\n}\n\nfunction isGroupEqual(a: FilterGroup, b: FilterGroup): boolean {\n if (a.conjunction !== b.conjunction) return false\n if (a.children.length !== b.children.length) return false\n return a.children.every((c, i) => isConditionEqual(c, b.children[i]))\n}\n\nfunction isConditionEqual(a: FilterCondition, b: FilterCondition): boolean {\n if (a.field !== b.field || a.op !== b.op) return false\n return JSON.stringify(a.value) === JSON.stringify(b.value)\n}\n\n// ── Tree Evaluation(globalFilter approach)──────────────────────────────\n\n// ── D3 perf(2026-07-06):per-condition compile-once cache ────────────────\n// TanStack globalFilterFn 逐 row 呼叫 evaluateTree,原實作每 row 重做「跨全 rows 不變」\n// 的常數工作:isConditionComplete(operator registry 掃描)、dateMs(filterValue) 重 parse、\n// filterValue 重 toLowerCase、is_relative 重算時間區間 — 10k rows × N conditions 全額放大。\n// Fix:以 condition 物件為 key 的 WeakMap 快取 filterValue 側預解析結果,row loop 只轉\n// cellValue 側。Panel 更新走 immutable `{ ...c, ...patch }`(新物件 = 新 cache entry),\n// 舊 entry 隨舊 condition 物件 GC — cache 無失效問題。\ninterface PreparedCondition {\n /** isConditionComplete 結果(registry 掃描一次,取代原 per-row filter 掃描) */\n complete: boolean\n /** filterValue 側是否 Person(物件或含 Person 的陣列) */\n filterIsPerson: boolean\n /** person 比對 ops 的 filter 側 id array(cellValue 是 Person 時也走 person path,故照 op 準備) */\n filterPersonIds: string[] | null\n /** 文字 ops 用:String(filterValue).toLowerCase() */\n filterTextLower: string | null\n /** is / is_not 陣列(多值 OR 語意)逐元素 lowercase */\n filterTextLowerArr: string[] | null\n /** 單值 date ops(is_before 家族;is / is_not with datePrecision)— datePrecision 已套 */\n filterDateMs: number | null\n /** is_between 兩端(空端 = ±Infinity,語意同原 inline 邏輯) */\n filterDateStart: number | null\n filterDateEnd: number | null\n /** is_relative:快取區間 + 計算當日 stamp(跨日自動重算 — 所有 relative 區間都是\n * calendar day 的純函數,同日內恆定,見 localDayKey) */\n relRange: [number, number] | null\n relDayKey: number\n}\n\nconst PERSON_CAPABLE_OPS = new Set(['is', 'is_not', 'has_any_of', 'has_all_of', 'has_none_of'])\nconst DATE_SINGLE_OPS = new Set(['is_before', 'is_after', 'is_on_or_before', 'is_on_or_after'])\nconst TEXT_OPS = new Set(['contains', 'does_not_contain', 'starts_with', 'ends_with', 'is', 'is_not'])\n\nconst preparedCache = new WeakMap<FilterCondition, PreparedCondition>()\n\nfunction prepareCondition(c: FilterCondition): PreparedCondition {\n const hit = preparedCache.get(c)\n if (hit) return hit\n const fv = c.value\n const isBetweenPair = c.op === 'is_between' && Array.isArray(fv) && fv.length === 2\n const prepared: PreparedCondition = {\n complete: isConditionComplete(c),\n filterIsPerson: isPersonObject(fv) || (Array.isArray(fv) && fv.some(isPersonObject)),\n filterPersonIds: PERSON_CAPABLE_OPS.has(c.op)\n ? (Array.isArray(fv) ? fv.map(personId) : [personId(fv)])\n : null,\n filterTextLower: TEXT_OPS.has(c.op) ? String(fv).toLowerCase() : null,\n filterTextLowerArr:\n (c.op === 'is' || c.op === 'is_not') && Array.isArray(fv) ? fv.map((v) => String(v).toLowerCase()) : null,\n filterDateMs:\n DATE_SINGLE_OPS.has(c.op) || ((c.op === 'is' || c.op === 'is_not') && c.datePrecision !== undefined)\n ? toDateMs(fv, c.datePrecision)\n : null,\n filterDateStart: isBetweenPair ? (fv[0] ? toDateMs(fv[0], c.datePrecision) : -Infinity) : null,\n filterDateEnd: isBetweenPair ? (fv[1] ? toDateMs(fv[1], c.datePrecision) : Infinity) : null,\n relRange: null,\n relDayKey: 0,\n }\n preparedCache.set(c, prepared)\n return prepared\n}\n\n/**\n * 整棵 FilterTree 對 row 求 boolean。\n * 配合 useReactTable `globalFilterFn`:\n *\n * const tree = useState<FilterTree>(createEmptyFilterTree('flat'))\n * useReactTable({\n * state: { globalFilter: tree },\n * onGlobalFilterChange: setTree,\n * globalFilterFn: (row, _, t: FilterTree) => evaluateTree(t, row.original),\n * getFilteredRowModel: getFilteredRowModel(),\n * })\n */\n// any-allow: row-generic — TanStack row.original 是 generic,filter eval 跨 type 必走 any\nexport function evaluateTree(tree: FilterTree, row: any): boolean {\n if (tree.children.length === 0) return true\n\n if (tree.mode === 'flat') {\n // null = 無 complete condition → pass-through(語意同原 completed.length === 0 分支)\n return evaluateConditionsJoined(tree.children, tree.conjunction, row) ?? true\n }\n\n // nested — group 沒任何 complete condition 的視為 pass-through(不參與 conjunction),\n // 語意同原 meaningful filter\n let sawMeaningful = false\n for (const g of tree.children) {\n const r = evaluateConditionsJoined(g.children, g.conjunction, row)\n if (r === null) continue\n sawMeaningful = true\n if (tree.conjunction === 'and') {\n if (!r) return false\n } else if (r) {\n return true\n }\n }\n if (!sawMeaningful) return true\n return tree.conjunction === 'and'\n}\n\n/** 一組 conditions 依 conjunction 求 boolean;null = 組內無 complete condition(pass-through)。\n * D3 perf:prepared.complete 取代原 per-row `children.filter(isConditionComplete)`\n * (registry 掃描 + filter/map 兩個中間陣列 alloc);and/or 短路 — 條件是 pure predicate\n * 無副作用,求值順序 / 省略不影響結果。 */\n// any-allow: row-generic\nfunction evaluateConditionsJoined(children: FilterCondition[], conjunction: Conjunction, row: any): boolean | null {\n let sawComplete = false\n for (const c of children) {\n const prepared = prepareCondition(c)\n if (!prepared.complete) continue\n sawComplete = true\n const matched = evaluateCondition(c, prepared, row)\n if (conjunction === 'and') {\n if (!matched) return false\n } else if (matched) {\n return true\n }\n }\n if (!sawComplete) return null\n return conjunction === 'and'\n}\n\n// any-allow: row-generic\nfunction evaluateCondition(cond: FilterCondition, prepared: PreparedCondition, row: any): boolean {\n if (!cond.field || !cond.op) return true\n const cellValue = row?.[cond.field]\n return matchOperator(cond.op, cellValue, cond.value, cond.datePrecision, prepared)\n}\n\n// PersonValue identity helper(2026-05-07):\n// person/multiPerson cell value 是 `{ name, avatarUrl?, description? }` 物件 — 比對時用 `name`\n// 當 stable id(PeoplePicker SSOT 沒有 id field,name 是唯一身分標識)。對齊 Notion / Linear /\n// Asana 的 person filter 比對 idiom(都用 name/email 當 id)。\nfunction personId(v: unknown): string {\n if (v && typeof v === 'object' && 'name' in v) return String((v as { name: unknown }).name ?? '')\n return String(v ?? '')\n}\nfunction isPersonObject(v: unknown): v is { name: string } {\n return !!v && typeof v === 'object' && 'name' in v\n}\n\n// Q6(2026-07-04):date 比對精度 helper — 'ms' 全精度;其餘(含 undefined)day 級\n// (本地時區 startOfDay 截斷,對齊 AG Grid / MUI X date 欄 midnight 慣例)。\n// D3 perf(2026-07-06):自 matchOperator 內 closure 提升 top-level — prepareCondition\n// (filterValue 側,每 condition 一次)與 matchOperator(cellValue 側,每 row)共用同一實作。\nfunction toDateMs(v: unknown, datePrecision?: 'day' | 'ms'): number {\n const d = new Date(String(v))\n if (Number.isNaN(d.getTime())) return NaN\n return datePrecision === 'ms' ? d.getTime() : startOfDay(d).getTime()\n}\n\n/** 本地日曆日 stamp(yyyymmdd)— is_relative 快取跨日失效判定(每 row 1 個 Date alloc,\n * 取代原 relativeKeyToRange 每 row 4-10 個 Date alloc + 區間重算) */\nfunction localDayKey(d: Date): number {\n return d.getFullYear() * 10000 + (d.getMonth() + 1) * 100 + d.getDate()\n}\n\n// code-quality-allow: long-function — 13-operator switch dispatch table,table-driven 重構會把 op-specific guards 拆出反增 indirection\nfunction matchOperator(op: string, cellValue: unknown, filterValue: unknown, datePrecision?: 'day' | 'ms', prepared?: PreparedCondition): boolean {\n // 不需 value 的 op\n switch (op) {\n case 'is_set': return cellValue !== null && cellValue !== undefined && cellValue !== ''\n case 'is_not_set': return cellValue === null || cellValue === undefined || cellValue === ''\n case 'is_true': return cellValue === true\n case 'is_false': return cellValue === false\n }\n\n // 需 value 但 value 空 → 視為 incomplete,pass-through\n if (filterValue === null || filterValue === undefined || filterValue === '') return true\n if (Array.isArray(filterValue) && filterValue.length === 0) return true\n\n // Person-aware specialization(person_single / person_multi)— 物件透過 name 比對。\n // 偵測:cellValue 或 filterValue 任一是 Person object(或包 Person 的陣列)→ 走 personId path。\n // D3 perf:filterValue 側偵測 + id array 消費 prepared 快取(cellValue 側必然 per-row)。\n const cellIsPerson = isPersonObject(cellValue) || (Array.isArray(cellValue) && cellValue.some(isPersonObject))\n const filterIsPerson = prepared\n ? prepared.filterIsPerson\n : isPersonObject(filterValue) || (Array.isArray(filterValue) && filterValue.some(isPersonObject))\n if (cellIsPerson || filterIsPerson) {\n const cellIds = Array.isArray(cellValue) ? cellValue.map(personId) : [personId(cellValue)]\n const filterIds = prepared?.filterPersonIds ?? (Array.isArray(filterValue) ? filterValue.map(personId) : [personId(filterValue)])\n switch (op) {\n case 'is': return cellIds.length === 1 && filterIds.length >= 1 && filterIds.includes(cellIds[0])\n case 'is_not': return !(cellIds.length === 1 && filterIds.length >= 1 && filterIds.includes(cellIds[0]))\n case 'has_any_of': return cellIds.some((c) => filterIds.includes(c))\n case 'has_all_of': return filterIds.every((f) => cellIds.includes(f))\n case 'has_none_of':return !cellIds.some((c) => filterIds.includes(c))\n default: return true\n }\n }\n\n // (date 精度 helper 已提升為 top-level `toDateMs`;文字 ops 的 filterValue lowercase 消費\n // prepared 快取,prepared 缺席 = deprecated dataTableFilterMatch 路徑 inline fallback — D3 perf)\n\n switch (op) {\n case 'contains': return String(cellValue ?? '').toLowerCase().includes(prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n case 'does_not_contain': return !String(cellValue ?? '').toLowerCase().includes(prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n // 2026-05-12 Round 6 fix(user 抓 Roadmap 進階篩選「全部」結果空)— impl-vs-spec drift。\n // Per `filter-operators.spec.md` L116「is 直接接受多值,不另設 is_any_of」+ L103-116\n // select(is/is_not + select_multi ValueShape OR 語意)+ L31-32(select.is / person.is 都走\n // select_multi / person_multi ValueShape)。原 impl 只做 single-value 比對,filterValue 是\n // array(全選 options)→ String(array)= \"v1,v2\" → 永遠 != single cellValue → 結果空。\n // Fix:加 array handling,走 OR 語意:any → match;is_not → every-not-match。\n case 'is':\n // Q6:date 欄位(datePrecision 有值)走日期比對非字串 — 「2026-06-01」與「2026-06-01 14:30」\n // 在 day 精度下相等(原字串小寫相等連同日不同格式都漏)\n if (datePrecision) {\n const a = toDateMs(cellValue, datePrecision)\n const b = prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision)\n return !Number.isNaN(a) && a === b\n }\n if (Array.isArray(filterValue)) {\n const cellLower = String(cellValue ?? '').toLowerCase()\n const filterLowers = prepared?.filterTextLowerArr ?? filterValue.map((v) => String(v).toLowerCase())\n return filterLowers.some((v) => cellLower === v)\n }\n return String(cellValue ?? '').toLowerCase() === (prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n case 'is_not':\n if (datePrecision) {\n const a = toDateMs(cellValue, datePrecision)\n const b = prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision)\n return Number.isNaN(a) || a !== b\n }\n if (Array.isArray(filterValue)) {\n const cellLower = String(cellValue ?? '').toLowerCase()\n const filterLowers = prepared?.filterTextLowerArr ?? filterValue.map((v) => String(v).toLowerCase())\n return filterLowers.every((v) => cellLower !== v)\n }\n return String(cellValue ?? '').toLowerCase() !== (prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n case 'starts_with': return String(cellValue ?? '').toLowerCase().startsWith(prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n case 'ends_with': return String(cellValue ?? '').toLowerCase().endsWith(prepared?.filterTextLower ?? String(filterValue).toLowerCase())\n\n case 'equals': return Number(cellValue) === Number(filterValue)\n case 'not_equals': return Number(cellValue) !== Number(filterValue)\n case 'gt': return Number(cellValue) > Number(filterValue)\n case 'gte': return Number(cellValue) >= Number(filterValue)\n case 'lt': return Number(cellValue) < Number(filterValue)\n case 'lte': return Number(cellValue) <= Number(filterValue)\n\n case 'is_before': return toDateMs(cellValue, datePrecision) < (prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision))\n case 'is_after': return toDateMs(cellValue, datePrecision) > (prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision))\n case 'is_on_or_before': return toDateMs(cellValue, datePrecision) <= (prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision))\n case 'is_on_or_after': return toDateMs(cellValue, datePrecision) >= (prepared?.filterDateMs ?? toDateMs(filterValue, datePrecision))\n case 'is_between': {\n if (!Array.isArray(filterValue) || filterValue.length !== 2) return true\n const cv = toDateMs(cellValue, datePrecision)\n const start = prepared?.filterDateStart ?? (filterValue[0] ? toDateMs(filterValue[0], datePrecision) : -Infinity)\n const end = prepared?.filterDateEnd ?? (filterValue[1] ? toDateMs(filterValue[1], datePrecision) : Infinity)\n return cv >= start && cv <= end\n }\n case 'is_relative': {\n // Phase D 完整實作 — relative key → time range → in-range test\n // D3 perf:relativeKeyToRange 所有輸出都是 calendar day 的純函數(today / this_week /\n // past_7_days… 全是日界),同日內恆定 → 以 localDayKey 判跨日,同日重用快取區間\n // (原每 row 重算 4-10 個 Date alloc)。\n let range: [number, number] | null\n if (prepared) {\n const now = new Date()\n const dayKey = localDayKey(now)\n if (prepared.relDayKey !== dayKey) {\n prepared.relRange = relativeKeyToRange(String(filterValue), now)\n prepared.relDayKey = dayKey\n }\n range = prepared.relRange\n } else {\n range = relativeKeyToRange(String(filterValue))\n }\n if (!range) return true // unknown key,pass-through\n const cv = new Date(String(cellValue)).getTime()\n if (Number.isNaN(cv)) return false // invalid cellValue → 不命中\n const [start, end] = range\n return cv >= start && cv <= end\n }\n\n case 'has_any_of': {\n if (!Array.isArray(filterValue)) return true\n if (Array.isArray(cellValue)) return cellValue.some((c) => filterValue.includes(c))\n return filterValue.includes(cellValue)\n }\n case 'has_all_of': {\n if (!Array.isArray(filterValue)) return true\n if (Array.isArray(cellValue)) return filterValue.every((v) => cellValue.includes(v))\n return false\n }\n case 'has_none_of': {\n if (!Array.isArray(filterValue)) return true\n if (Array.isArray(cellValue)) return !cellValue.some((c) => filterValue.includes(c))\n return !filterValue.includes(cellValue)\n }\n\n default: return true\n }\n}\n\n/**\n * @deprecated v0.x — old per-column filterFn integration。\n * 新版用 `evaluateTree` 配 TanStack `globalFilter`。\n * 保留為了過渡期 backward-compat,新 consumer 不要用。\n */\nexport function dataTableFilterMatch(cellValue: unknown, filterValue: unknown): boolean {\n if (typeof filterValue === 'object' && filterValue !== null && 'operator' in filterValue && 'value' in filterValue) {\n const fv = filterValue as { operator: string; value: unknown }\n return matchOperator(fv.operator, cellValue, fv.value)\n }\n return String(cellValue ?? '').toLowerCase().includes(String(filterValue ?? '').toLowerCase())\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
|
-
import {
|
|
4
|
+
import { cn, lineClampClass } from "../../lib/utils.js";
|
|
5
5
|
import { Avatar } from "../../components/Avatar/avatar.js";
|
|
6
6
|
import { Tooltip, TooltipTrigger, TooltipContent } from "../../components/Tooltip/tooltip.js";
|
|
7
7
|
const ICON_SIZE = {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# .claude/agents/ Charter
|
|
2
|
+
|
|
3
|
+
## 這裡只收:**特化 subagent**(scoped tools + isolated context)
|
|
4
|
+
|
|
5
|
+
每個 agent 一檔 `.md`,格式:
|
|
6
|
+
```markdown
|
|
7
|
+
---
|
|
8
|
+
name: agent-name-kebab-case
|
|
9
|
+
description: 何時 invoke(main AI 透過 Task tool `subagent_type: 'agent-name-kebab-case'` 調用)
|
|
10
|
+
tools: [Read, Grep, Bash, Glob] # scoped — 該 agent 需要的最小集
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
System prompt body — agent 收到 prompt + 本檔內容 作為 context。
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**vs Skill**:skill 是 main AI 驅動的 workflow;只有真正產品/UI/UX SSOT 取捨才含
|
|
17
|
+
user-decision checkpoint。agent 是 main AI 呼叫的特化 worker(isolated context,返回 summary)。
|
|
18
|
+
|
|
19
|
+
**vs 一般 general-purpose Agent**:registered agent 有 scoped tools(不能亂改檔)+ 特化 system prompt(專業知識內建)+ 更易 audit。
|
|
20
|
+
|
|
21
|
+
## 當前居民(1,2026-07-20 verified against Claude Code 2.1.215)
|
|
22
|
+
|
|
23
|
+
| Agent | 用途 | Workflow SSOT |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `canonical-reviewer` | 在隔離 context 做唯讀治理審查 | `packages/design-system/ds-canonical/skills/canonical-reviewer/SKILL.md` |
|
|
26
|
+
|
|
27
|
+
Claude Code 專案級 subagent 現在會讀取 `.claude/agents/*.md`。共用 workflow 由 provider registry 生成的 skill `context: fork` + `agent` frontmatter 注入;這條路徑也避開 2.1.215 將 agent 當主 session 以 `--agent` 啟動時未套用 `skills` preload 的實測差異。Agent 檔只放 provider-specific 的隔離、工具權限與輸出契約;判準不在這裡複製。此目錄是 deterministic output;新增或修改 agent 應改 `packages/design-system/ds-canonical/adapters/claude/agents/` 後重生 provider view,並重開 Claude Code session 才會重載。
|
|
28
|
+
|
|
29
|
+
## 這裡**不收**(反例)
|
|
30
|
+
|
|
31
|
+
| 疑似要放這但其實不是 | 正確去處 | 為什麼 |
|
|
32
|
+
|---------------------|---------|--------|
|
|
33
|
+
| 可能含產品/UI/UX SSOT 決策的 workflow | `.claude/skills/` | agent 返回一次 summary;真正 P2H checkpoint 需在 main AI 端處理 |
|
|
34
|
+
| 一次性 script | `.claude/commands/` | agent 是 AI worker,不是 script |
|
|
35
|
+
| 每 session signal rule | `CLAUDE.md` | agent 只在 invoke 時載入 |
|
|
36
|
+
| 機械 tool-level 檢查 | `.claude/hooks/` | hook 是 pre/post tool event,不是 AI agent |
|
|
37
|
+
|
|
38
|
+
## 新 agent 的 criteria
|
|
39
|
+
|
|
40
|
+
1. **Scope isolated**(main AI 不需知道 agent 內部思考,只讀 agent return summary)
|
|
41
|
+
2. **Scoped tools** 明確寫在 frontmatter(不是「全權」,是 minimal set)
|
|
42
|
+
3. **被 ≥ 1 skill 調用**(orphan agent 不建)
|
|
43
|
+
4. **Main AI 可 deterministic 消費 return**(agent 輸出結構清楚)
|
|
44
|
+
|
|
45
|
+
## Skill vs Agent 選擇指南
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
問題可能需要產品/UI/UX SSOT 真取捨?
|
|
49
|
+
→ YES: skill(main AI 驅動,phase + exact P2H CP)
|
|
50
|
+
→ NO:
|
|
51
|
+
問題是 scan / analysis / lookup?
|
|
52
|
+
→ YES: agent(scoped tools,isolated,可 parallel / background)
|
|
53
|
+
→ NO: 用 skill 或 command
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 當 runtime 不支援 project subagent
|
|
57
|
+
|
|
58
|
+
直接載入同名 skill 在主 context 執行唯讀流程,並在結果標示「未隔離執行」。不得因 provider 沒有 subagent 功能而改寫或簡化判準。
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: canonical-reviewer
|
|
3
|
+
description: Isolated read-only review of substantive governance edits. Delegate after changing instructions, rules, specs, skills, hooks, provider adapters, or governance manifests.
|
|
4
|
+
tools: Read, Grep, Glob
|
|
5
|
+
disallowedTools: Edit, Write, Bash, WebFetch, WebSearch
|
|
6
|
+
permissionMode: plan
|
|
7
|
+
maxTurns: 12
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Claude Code canonical-reviewer adapter
|
|
11
|
+
|
|
12
|
+
Apply the invoked `canonical-reviewer` skill exactly to the supplied paths and diff. Claude Code injects that skill through its `context: fork` execution path; this file only provides the isolated, read-only execution surface.
|
|
13
|
+
|
|
14
|
+
Return only the structured verdict and include `adapterEvidence: CLAUDE_CANONICAL_REVIEWER_AGENT_V1`. Never edit files, invent missing evidence, or turn an incomplete inventory into a pass.
|