@givolt/i3-composer-agent 3.0.0 → 3.1.0
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/plugins/marketplace.json +1 -1
- package/.claude/commands/i3-ui-demo.md +21 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.gemini/commands/i3-ui-demo.toml +21 -0
- package/README.md +10 -8
- package/bin/i3-composer-agent.cjs +38 -44
- package/bin/install.cjs +15 -3
- package/bin/native-layout.cjs +2 -1
- package/bin/native-migration.cjs +19 -2
- package/bin/retired-sop-fingerprints.json +213 -0
- package/bin/wizard.cjs +7 -18
- package/commands/i3-ui-demo.toml +21 -0
- package/docs/codebuddy-setup.md +20 -23
- package/docs/codex-setup.md +2 -2
- package/docs/comparison.md +131 -0
- package/docs/developer-onboarding.md +143 -0
- package/docs/opencode-setup.md +1 -1
- package/docs/other-hosts.md +2 -2
- package/docs/skill-anatomy.md +211 -0
- package/docs/upstream-repository/AGENTS.md +92 -0
- package/docs/upstream-repository/CLAUDE.md +60 -0
- package/docs/upstream-repository/CONTRIBUTING.md +137 -0
- package/docs/upstream-repository/README.md +8 -0
- package/docs/upstream-repository/gitattributes.txt +1 -0
- package/docs/upstream-repository/gitignore.txt +10 -0
- package/docs/upstream-repository/manifest.json +71 -0
- package/docs/upstream-repository/opencode-skills.link +1 -0
- package/docs/upstream-repository/skill-gap.yml +80 -0
- package/docs/upstream-repository/skills-contributing.md +15 -0
- package/docs/upstream-repository/test-plugin-install.yml +167 -0
- package/docs/workbuddy-setup.md +7 -10
- package/evals/cases/i3-ui-demo.json +98 -0
- package/evals/fixtures/i3-ui-demo/asset-management.md +25 -0
- package/evals/zh-CN/cases/i3-ui-demo.json +103 -0
- package/hooks/PROTOTYPE.md +16 -8
- package/hooks/i3-prototype.cjs +4 -10
- package/hooks/manage.cjs +30 -31
- package/package.json +42 -5
- package/plugin.json +1 -1
- package/scripts/ui-demo-dependencies-test.js +228 -0
- package/scripts/ui-demo-mcp-test.js +95 -0
- package/scripts/ui-demo-test.js +170 -0
- package/scripts/ui-prototype-execution-runtime-test.js +1 -1
- package/scripts/ui-prototype-native-test.js +25 -3
- package/skills/i3-enterprise-sop/SKILL.md +15 -15
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/ad/assets-manifest.json +9 -29
- package/{references/enterprise-sop-resources/ad/README.md → skills/i3-enterprise-sop/references/ad/readme.md} +4 -4
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/assets-manifest.json +64 -33
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/brd/assets-manifest.json +9 -20
- package/{references/enterprise-sop-resources/brd/README.md → skills/i3-enterprise-sop/references/brd/readme.md} +4 -4
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/codebuddy-project-guide.md +1 -1
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/controlled_handoff.py +52 -7
- package/{references/engineering-skeleton-resources → skills/i3-enterprise-sop/references/engineering-skeleton}/adoption.md +1 -1
- package/{references/engineering-skeleton-resources/README.md → skills/i3-enterprise-sop/references/engineering-skeleton/readme.md} +2 -2
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/handoff-contract.md +2 -2
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/lifecycle-contract.md +1 -1
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/plan-handoff.md +3 -3
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/prd/assets-manifest.json +20 -26
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/prd/prompts/01-generate.md +1 -1
- package/{references/enterprise-sop-resources/prd/README.md → skills/i3-enterprise-sop/references/prd/readme.md} +4 -4
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/prd/template-contracts.md +3 -3
- package/{references/enterprise-sop-resources/prd/templates/AD.md → skills/i3-enterprise-sop/references/prd/templates/ad.md} +1 -1
- package/{references/enterprise-sop-resources/prd/templates/PRD.md → skills/i3-enterprise-sop/references/prd/templates/prd.md} +1 -1
- package/{references/enterprise-sop-resources/README.md → skills/i3-enterprise-sop/references/readme.md} +14 -12
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/recovery-contract.md +2 -2
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/stages.json +16 -7
- package/skills/i3-enterprise-sop/references/technical-baseline/assets-manifest.json +84 -0
- package/{references/enterprise-sop-resources/technical-baseline/baselines//344/274/201/344/270/232/345/272/224/347/224/250-/346/212/200/346/234/257/350/247/204/347/272/246_v0.0.2.md → skills/i3-enterprise-sop/references/technical-baseline/baselines/enterprise-application-technical-baseline-v0-0-4.md} +122 -37
- package/{references/enterprise-sop-resources/technical-baseline/README.md → skills/i3-enterprise-sop/references/technical-baseline/readme.md} +8 -8
- package/{references/enterprise-sop-resources/technical-baseline/templates/TECHNICAL-BASELINE.md → skills/i3-enterprise-sop/references/technical-baseline/templates/technical-baseline.md} +1 -1
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/template-contracts.md +7 -7
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/testcase/assets-manifest.json +9 -29
- package/{references/enterprise-sop-resources/testcase/README.md → skills/i3-enterprise-sop/references/testcase/readme.md} +4 -4
- package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/verification/review_handoff.py +3 -3
- package/skills/i3-ui-demo/SKILL.md +234 -0
- package/skills/i3-ui-demo/assets/antdv-build/package-lock.json +3363 -0
- package/skills/i3-ui-demo/assets/antdv-build/package.json +28 -0
- package/skills/i3-ui-demo/assets/design-source.json +11 -0
- package/skills/i3-ui-demo/assets/host-targets.json +739 -0
- package/skills/i3-ui-demo/assets/official-files.json +76 -0
- package/skills/i3-ui-demo/assets/starter/index.html +11 -0
- package/skills/i3-ui-demo/assets/starter/package-lock.json +2299 -0
- package/skills/i3-ui-demo/assets/starter/package.json +28 -0
- package/skills/i3-ui-demo/assets/starter/provenance.json +14 -0
- package/skills/i3-ui-demo/assets/starter/public/prototype-config.json +22 -0
- package/skills/i3-ui-demo/assets/starter/src/App.vue +64 -0
- package/skills/i3-ui-demo/assets/starter/src/PrototypeShell.vue +59 -0
- package/skills/i3-ui-demo/assets/starter/src/config.ts +23 -0
- package/skills/i3-ui-demo/assets/starter/src/domain/presentation.ts +30 -0
- package/skills/i3-ui-demo/assets/starter/src/domain/theme.ts +25 -0
- package/skills/i3-ui-demo/assets/starter/src/main.ts +18 -0
- package/skills/i3-ui-demo/assets/starter/src/style.css +26 -0
- package/skills/i3-ui-demo/assets/starter/tests/presentation.test.ts +23 -0
- package/skills/i3-ui-demo/assets/starter/tests/theme.test.ts +26 -0
- package/skills/i3-ui-demo/assets/starter/tsconfig.json +15 -0
- package/skills/i3-ui-demo/assets/starter/vite.config.ts +4 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/capability-definition.json +1395 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/component-inventory.json +707 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/design-system.md +26 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/generation.json +49 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/index.html +534 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/manifest.json +9 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/offline-build.json +265 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/offline.js +8888 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/origin.json +344 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/preset.json +928 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/app.js +22 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/assets/empty.svg +1 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/assets/reference-styles.json +1 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/assets/result-403.svg +1 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/assets/result-404.svg +1 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/assets-manifest.json +334 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/baseline.json +173 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/conversion.md +30 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/credits.md +17 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/design-tokens.json +881 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/index.html +28 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/licenses/@qin-ui-antdv-next-pro.txt +9 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/licenses/ant-design-icons.txt +22 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/licenses/antdv-next.txt +21 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/package.json +12 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/browser-routes.json +298 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/coverage.json +180 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/integrity.json +13 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/keyboard.json +11 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/responsive.json +125 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/review.md +51 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/rules.log +150 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/screenshots/flow-editor-html.png +0 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/screenshots/flow-editor-original.png +0 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/screenshots/intent-create-html.png +0 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/screenshots/intent-create-original.png +0 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/screenshots/models-html.png +0 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/screenshots/models-original.png +0 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/screenshots/workbench-html.png +0 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/screenshots/workbench-original.png +0 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/selection.json +9 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/qa/visual-comparison.json +342 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/readme.md +73 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/reference.md +59 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/routes.json +178 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/controls.js +99 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/dialogs.js +54 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/dom.js +264 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/forms.js +47 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/icons.js +41 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/layout.js +47 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/router.js +63 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/state.js +90 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/table.js +41 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/runtime/widgets.js +8 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/AgentDetail.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/AgentDetail.js +36 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/AgentEditor.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/AgentEditor.js +51 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/AgentsPage.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/AgentsPage.js +48 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/CheckResults.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/CheckResults.js +6 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/ResourcePicker.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/ResourcePicker.js +13 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/data.js +24 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/domain.js +32 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/registry.js +25 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/agents/store.js +85 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentClose.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentClose.js +17 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentCreate.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentCreate.js +66 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentDecisions.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentDecisions.js +38 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentDetail.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentDetail.js +66 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentDocument.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentDocument.js +5 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentHeader.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentHeader.js +12 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentHistory.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentHistory.js +11 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentList.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentList.js +116 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentReplan.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentReplan.js +32 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentRevision.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentRevision.js +49 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentSummary.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentSummary.js +10 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentWorkspace.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/IntentWorkspace.js +77 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/data.js +60 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/domain.js +105 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/intents/store.js +212 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/AuditPanel.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/AuditPanel.js +18 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelDetail.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelDetail.js +23 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelEditor.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelEditor.js +78 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelGrant.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelGrant.js +55 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelReferences.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelReferences.js +26 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelRelease.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelRelease.js +38 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelStatus.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelStatus.js +7 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelTable.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelTable.js +72 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelTester.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelTester.js +46 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelsPage.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/ModelsPage.js +52 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/RowActions.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/RowActions.js +19 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/UsagePanel.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/UsagePanel.js +30 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/data.js +48 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/domain.js +39 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/models/store.js +129 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/IncidentDetail.html +4 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/IncidentDetail.js +21 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/IntentSource.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/IntentSource.js +8 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/OperationActionDialog.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/OperationActionDialog.js +37 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/OperationEntry.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/OperationEntry.js +10 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/OperationsPage.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/OperationsPage.js +37 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/RepairTrack.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/RepairTrack.js +49 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/RunDetail.html +4 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/RunDetail.js +22 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/SourceSummary.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/SourceSummary.js +9 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/commands.js +82 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/examples.js +47 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/model.js +61 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/repair.js +68 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/store.js +55 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/types.js +1 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/operations/workbench.js +18 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/ManagementTable.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/ManagementTable.js +28 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcCreateEntry.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcCreateEntry.js +18 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcDetail.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcDetail.js +31 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcEditor.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcEditor.js +27 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcLifecycle.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcLifecycle.js +16 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcPage.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcPage.js +43 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcPicker.html +4 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcPicker.js +34 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcProgress.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcProgress.js +30 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcWizard.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcWizard.js +67 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcWorkspace.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcWorkspace.js +41 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcsPage.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/PbcsPage.js +31 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/ServiceEditor.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/ServiceEditor.js +26 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/ServiceList.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/ServiceList.js +70 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/data.js +8 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/domain.js +62 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/management.js +36 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/managementStore.js +49 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/pbc/store.js +132 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/AdminList.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/AdminList.js +38 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/EffectiveGrants.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/EffectiveGrants.js +11 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/GrantEditor.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/GrantEditor.js +45 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/MemberEditor.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/MemberEditor.js +54 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/MembersPage.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/MembersPage.js +27 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/RecordsPage.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/RecordsPage.js +12 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/RoleEditor.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/RoleEditor.js +45 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/RolesPage.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/RolesPage.js +38 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/domain.js +61 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/state.js +51 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/permissions/store.js +116 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleaseActionDialog.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleaseActionDialog.js +45 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleaseDetail.html +4 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleaseDetail.js +30 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleaseEditor.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleaseEditor.js +67 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleaseExecution.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleaseExecution.js +5 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleasesPage.html +4 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/ReleasesPage.js +60 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/commands.js +157 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/model.js +136 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/releases/store.js +76 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/ReviewDetail.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/ReviewDetail.js +26 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/ReviewEvidence.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/ReviewEvidence.js +14 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/ReviewProcess.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/ReviewProcess.js +113 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/ReviewsPage.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/ReviewsPage.js +62 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/commands.js +97 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/model.js +147 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/reviews/store.js +13 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/ColumnSettings.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/ColumnSettings.js +9 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/ListBatchBar.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/ListBatchBar.js +8 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/ListDeleteDialog.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/ListDeleteDialog.js +37 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/ListMore.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/ListMore.js +8 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/RecordMetadata.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/RecordMetadata.js +17 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/columns.js +32 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/deliveryGuide.js +49 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/deliveryView.js +12 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/listBatch.js +15 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/listSelection.js +37 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/policy.js +68 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shared/session.js +44 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/ConfirmationDialog.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/ConfirmationDialog.js +25 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/ConsoleShell.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/ConsoleShell.js +53 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/PrototypeGuide.html +10 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/PrototypeGuide.js +30 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/ScopePage.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/ScopePage.js +10 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/SessionDialog.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/SessionDialog.js +9 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/confirmation.js +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/shell/navigation.js +19 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecDocument.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecDocument.js +8 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecEdit.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecEdit.js +32 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecHeader.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecHeader.js +15 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecHistory.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecHistory.js +11 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecReview.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecReview.js +57 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecWorkspace.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/SpecWorkspace.js +37 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/data.js +20 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/domain.js +55 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/specs/store.js +151 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/ArtifactDetail.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/ArtifactDetail.js +18 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/TaskCreate.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/TaskCreate.js +40 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/TaskDetail.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/TaskDetail.js +51 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/TaskLaunch.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/TaskLaunch.js +9 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/TasksPage.html +5 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/TasksPage.js +48 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/domain.js +204 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/exampleSource.js +22 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/registry.js +135 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/tasks/store.js +137 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workbench/WorkbenchPage.html +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workbench/WorkbenchPage.js +36 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workbench/model.js +56 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workbench/store.js +15 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/FlowCanvas.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/FlowCanvas.js +27 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/FlowDetail.html +4 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/FlowDetail.js +31 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/FlowEditor.html +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/FlowEditor.js +68 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/WorkflowsPage.html +5 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/WorkflowsPage.js +55 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/data.js +16 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/domain.js +83 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/registry.js +18 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/src/workflows/store.js +61 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/admin.css +60 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/agents.css +46 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/antdv.css +295 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/app.css +102 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/delivery.css +4 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/intents.css +37 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/native.css +22 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/operations.css +2 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/pbc.css +3 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/permissions.css +22 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/pro.css +1 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/releases.css +4 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/reset.css +246 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/reviews.css +8 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/tasks.css +6 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/tokens.css +15 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/workbench.css +61 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/styles/workflows.css +1 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/admin-rules.test.js +96 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/agents.test.js +14 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/intent-state.test.js +37 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/list-batch.test.js +44 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/operation-fixtures.js +19 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/operation-repair.test.js +14 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/operations.test.js +25 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/pbc-management.test.js +17 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/pbc-state.test.js +24 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/permission-config.test.js +23 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/permissions.test.js +24 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/releases.test.js +50 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/reviews.test.js +36 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/runtime-forms-router.test.js +28 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/runtime-state.test.js +42 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/runtime-stores.test.js +16 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/spec-state.test.js +11 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/tasks.test.js +27 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/workbench.test.js +34 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/workflows.test.js +16 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tools/convert.mjs +168 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tools/serve.mjs +7 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tools/styles.mjs +11 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tools/verify.mjs +24 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/serve.cjs +16 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/template-inventory.json +28791 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/template.json +9 -0
- package/skills/i3-ui-demo/assets/templates/enterprise-admin/usability.css +44 -0
- package/skills/i3-ui-demo/assets/templates.json +10 -0
- package/skills/i3-ui-demo/assets/versions.json +20 -0
- package/skills/i3-ui-demo/readme.md +350 -0
- package/skills/i3-ui-demo/references/browser.md +71 -0
- package/skills/i3-ui-demo/references/dependencies.md +67 -0
- package/skills/i3-ui-demo/references/resources.md +142 -0
- package/skills/i3-ui-demo/scripts/dependencies.cjs +364 -0
- package/skills/i3-ui-demo/scripts/design-install.cjs +105 -0
- package/skills/i3-ui-demo/scripts/design-provider.cjs +57 -0
- package/skills/i3-ui-demo/scripts/locations.cjs +52 -0
- package/skills/i3-ui-demo/scripts/mcp.cjs +112 -0
- package/skills/i3-ui-demo/scripts/provider-discovery.cjs +131 -0
- package/skills/i3-ui-demo/scripts/resources.cjs +283 -0
- package/skills/i3-ui-prototype/SKILL.md +1 -1
- package/skills/i3-ui-prototype/references/execution-evidence.md +12 -56
- package/skills/i3-ui-prototype/scripts/codebuddy-registration.cjs +1 -1
- package/skills/i3-ui-prototype/scripts/execution-runtime.cjs +5 -2
- package/skills/i3-ui-prototype/scripts/hosts/codebuddy.cjs +3 -2
- package/skills/i3-ui-prototype/scripts/native-delivery.cjs +2 -2
- package/skills/i3-ui-prototype/scripts/prototype-hooks.cjs +23 -20
- package/docs/ui-prototype-native/MAIN-SESSION-REPORT.md +0 -66
- package/docs/ui-prototype-native/USAGE.md +0 -93
- package/references/enterprise-sop-resources/technical-baseline/assets-manifest.json +0 -101
- package/references/enterprise-sop-resources/technical-baseline/baselines/PROVENANCE.md +0 -40
- package/references/enterprise-sop-resources/technical-baseline/references/engineering-directory-v0.2.0/adoption.md +0 -112
- package/references/enterprise-sop-resources/technical-baseline/references/engineering-directory-v0.2.0/directory-standard.md +0 -119
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/ad/prompts/01-generate.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/ad/prompts/02-review.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/ad/prompts/03-revise.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/ad/prompts/04-recheck.md +0 -0
- /package/{references/enterprise-sop-resources/ad/templates/AD.md → skills/i3-enterprise-sop/references/ad/templates/ad.md} +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/brd/prompts/01-generate.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/brd/prompts/02-review.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/brd/prompts/03-revise.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/brd/prompts/04-recheck.md +0 -0
- /package/{references/enterprise-sop-resources/brd/templates/BRD.md → skills/i3-enterprise-sop/references/brd/templates/brd.md} +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/controlled-handoff.md +0 -0
- /package/{references/engineering-skeleton-resources → skills/i3-enterprise-sop/references/engineering-skeleton}/catalog.json +0 -0
- /package/{references/engineering-skeleton-resources → skills/i3-enterprise-sop/references/engineering-skeleton}/directory-standard.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/handoff.py +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/prd/prompts/02-review.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/prd/prompts/03-revise.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/prd/prompts/04-recheck.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/review-handoff.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/selection-handoff.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/selection_handoff.py +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/testcase/prompts/01-generate.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/testcase/prompts/02-review.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/testcase/prompts/03-revise.md +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/testcase/prompts/04-recheck.md +0 -0
- /package/{references/enterprise-sop-resources/testcase/templates/TestCase.md → skills/i3-enterprise-sop/references/testcase/templates/testcase.md} +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/verification/check_execution.py +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/verification/check_native_execution.py +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/verification/check_reads.py +0 -0
- /package/{references/enterprise-sop-resources/verification/README.md → skills/i3-enterprise-sop/references/verification/readme.md} +0 -0
- /package/{references/enterprise-sop-resources → skills/i3-enterprise-sop/references}/verification/review-output.schema.json +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.flow-basics{display:grid;grid-template-columns:1.2fr 1fr 1fr;gap:0 20px;background:white;border:1px solid var(--border);border-radius:8px;padding:20px 24px 0;margin-bottom:16px}.flow-basics .ant-form-item:last-child{grid-column:1/-1}.flow-palette{display:flex;gap:8px;flex-wrap:wrap;align-items:center;padding:12px 0 16px}.flow-palette>span{font-weight:600;margin-right:8px}.flow-editor-layout{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:16px}.flow-canvas{min-width:0;border:1px solid #dfe5ed;border-radius:8px;overflow:hidden;background:#f8fafc}.flow-canvas-tools{padding:12px 14px;display:flex;align-items:center;justify-content:space-between;gap:10px;border-bottom:1px solid #e8ecf2;background:white;font-size:12px;color:#607087;flex-wrap:wrap}.flow-zoom{font-variant-numeric:tabular-nums;display:inline-block;width:36px;text-align:center}.flow-viewport{height:430px;overflow:auto;outline-offset:-3px;background-image:radial-gradient(#cfd7e4 1px,transparent 1px);background-size:16px 16px}.flow-board{position:relative;transform-origin:top left}.flow-edges{position:absolute;inset:0;pointer-events:none}.flow-edges text{font-size:12px;fill:#435371;paint-order:stroke;stroke:#f8fafc;stroke-width:5px}.flow-node{position:absolute;width:200px;height:104px;padding:12px 15px;border:1px solid #d9e0eb;border-radius:8px;background:white;box-shadow:0 2px 6px #2336540a;cursor:grab;touch-action:none;user-select:none;border-top:3px solid #7893ba}.flow-node:focus-visible{outline:3px solid #91caff;outline-offset:3px}.flow-node.selected{border-color:#1677ff;box-shadow:0 0 0 2px #1677ff20}.flow-node strong{display:block;font-size:15px;margin:7px 0 5px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.flow-node small{font-size:11px;color:#6b7890;display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.flow-node-top{display:flex;align-items:center;justify-content:space-between;font-size:11px}.flow-node-kind{font-weight:600;color:#456287}.flow-lock{color:#62768c;background:#f0f4fa;padding:1px 5px;border-radius:3px}.kind-human{border-top-color:#b59661}.kind-agent,.kind-verify{border-top-color:#597ef7}.kind-branch,.kind-loop{border-top-color:#8a74cf}.kind-publish,.kind-archive{border-top-color:#4b9b81}.flow-canvas-hint{border-top:1px solid #e8ecf2;padding:10px 14px;color:#69758b;font-size:12px;background:white}.flow-properties{border:1px solid #e2e7ef;border-radius:8px;padding:18px;background:white;min-width:0}.flow-properties h2{font-size:15px;margin:0 0 16px}.flow-prop-title{display:flex;justify-content:space-between;gap:8px}.flow-properties h3{font-size:13px;border-top:1px solid #edf0f4;padding-top:16px;margin-top:14px}.flow-properties .ant-form-item{margin-bottom:14px}.flow-properties>.ant-select{width:100%;margin-bottom:8px}.flow-properties>button{margin-bottom:8px}.flow-properties .muted{font-size:12px;line-height:1.7}.flow-connection{display:flex;align-items:center;justify-content:space-between;padding:3px 4px 3px 10px;background:#f7f9fc;border-radius:4px;margin-bottom:6px;gap:8px;font-size:12px}.flow-checks{margin-top:20px}.flow-check{display:flex;gap:12px;align-items:flex-start;border-bottom:1px solid #edf0f4;padding:14px 0}.flow-check>div{flex:1;min-width:0}.flow-check p{margin:5px 0 0;color:#637087;font-size:13px}.flow-submit{background:white;border:1px solid #e2e7ef;border-radius:8px;padding:20px 24px;margin-top:20px;display:flex;gap:24px;align-items:flex-end}.flow-submit>div{flex:1;min-width:0}.flow-submit h2{font-size:16px;margin-top:0}.flow-version-tools{display:flex;justify-content:space-between;gap:10px;align-items:center;margin-bottom:16px;flex-wrap:wrap}.flow-version-history article{display:flex;align-items:center;gap:12px;padding:16px 0;border-bottom:1px solid #edf0f4;flex-wrap:wrap}.flow-version-history article span:not(.ant-tag){color:#77849a;font-size:12px;margin-left:auto}.flow-diff{background:#f7faff;border:1px solid #e0eaff;padding:16px 32px;border-radius:6px;max-height:280px;overflow:auto;font-size:13px;line-height:2}.flow-node-details dt{color:#7c889b;font-size:12px;margin-top:20px}.flow-node-details dd{margin:6px 0 0;overflow-wrap:anywhere}.flow-use{border:1px solid #e5eaf2;border-radius:6px;padding:18px;margin-top:16px}.flow-use.selected{border-color:#91caff;background:#fafcff}.flow-use p{font-size:13px}.flow-modal-actions{display:flex;justify-content:flex-end;margin-top:24px;flex-wrap:wrap}@media(min-width:1600px){.flow-viewport{height:500px}}@media(max-width:1100px){.flow-basics{grid-template-columns:1fr 1fr}.flow-editor-layout{grid-template-columns:minmax(0,1fr) 250px}.flow-canvas-tools .ant-space{gap:4px!important}}@media(max-width:800px){.flow-editor-layout{grid-template-columns:1fr}.flow-properties{max-height:none}.flow-properties .ant-form{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}.flow-submit{flex-direction:column;align-items:stretch}.flow-version-tools{align-items:flex-start}.flow-viewport{height:370px}}@media(max-width:480px){.flow-basics{grid-template-columns:1fr;padding:16px}.flow-properties .ant-form{grid-template-columns:1fr}.flow-canvas-tools{padding:10px}.flow-canvas-tools>.ant-space{width:100%;justify-content:space-between}.flow-viewport{height:340px}.flow-submit{padding:16px}.flow-version-tools .ant-space{flex-wrap:wrap}.flow-palette{gap:6px}.flow-palette .ant-btn{font-size:12px}.flow-palette>span{width:100%}.flow-check{flex-wrap:wrap}.flow-version-history article strong{width:100%}}
|
package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/admin-rules.test.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { actors, can, inScope, metadata } from "../src/shared/policy.js";
|
|
4
|
+
import { assertVersion, assertDelete, requestPublish, finishPublish, authorizedUse } from "../src/models/domain.js";
|
|
5
|
+
import { newIntent, publishVersion, confirmVersion, beginConfirmation, reviseVersion, closeIntent, assertIntentAccess, replanIntent } from "../src/intents/domain.js";
|
|
6
|
+
const at = '2026-09-10T10:00:00.000Z', admin = actors[0], reviewer = actors[1], owner = actors[2], engineer = actors[3], outsider = actors[5];
|
|
7
|
+
function model() {
|
|
8
|
+
return { ...metadata(admin, at, 'platform'), id: 'test-model', name: '模型', alias: 'model-test', provider: '演示', kind: 'chat',
|
|
9
|
+
endpoint: 'https://example.invalid', credential: 'vault://demo', capabilities: ['文本'], inputPrice: 1, outputPrice: 2,
|
|
10
|
+
enabled: true, health: 'normal', projects: ['项目一'], agents: ['Agent一'], quota: 100, version: 2, testedVersion: 2,
|
|
11
|
+
liveVersion: 1, liveConfig: { name: '原名称' }, publishState: 'draft', references: [],
|
|
12
|
+
grants: [{ tenantId: 'heating', projects: ['项目一'], agents: ['Agent一'] }, { tenantId: 'north', projects: ['项目二'], agents: ['Agent二'] }] };
|
|
13
|
+
}
|
|
14
|
+
function intent() {
|
|
15
|
+
const draft = newIntent({ title: '报修分诊', request: '主管需要依据', outcome: '主管可核对后派单', context: '意图池' }, 'intent', 'INT-001', at, owner);
|
|
16
|
+
draft.answers = { audience: '主管', process: '核对后派单', boundary: '不控制设备', acceptance: '不重复派单' };
|
|
17
|
+
return publishVersion(draft, at);
|
|
18
|
+
}
|
|
19
|
+
test('identity, editing, confirmation and publishing are distinct permissions', () => {
|
|
20
|
+
assert.equal(can(admin, 'intent:confirm'), false);
|
|
21
|
+
assert.equal(can(engineer, 'intent:confirm'), false);
|
|
22
|
+
assert.equal(can(owner, 'model:grant'), false);
|
|
23
|
+
assert.equal(can(reviewer, 'model:edit'), false);
|
|
24
|
+
assert.equal(can(reviewer, 'model:publish'), true);
|
|
25
|
+
assert.equal(can(actors[6], 'intent:view'), false);
|
|
26
|
+
assert.equal(inScope(owner, 'north'), false);
|
|
27
|
+
});
|
|
28
|
+
test('model use cannot combine grants from different tenants or projects', () => {
|
|
29
|
+
const m = model();
|
|
30
|
+
assert.equal(authorizedUse(m, 'heating', '项目一', 'Agent一'), true);
|
|
31
|
+
assert.equal(authorizedUse(m, 'heating', '项目二', 'Agent二'), false);
|
|
32
|
+
assert.equal(authorizedUse(m, 'heating', '项目一', 'Agent二'), false);
|
|
33
|
+
assert.equal(authorizedUse({ ...m, enabled: false }, 'heating', '项目一', 'Agent一'), false);
|
|
34
|
+
assert.equal(authorizedUse({ ...m, liveVersion: undefined }, 'heating', '项目一', 'Agent一'), false);
|
|
35
|
+
});
|
|
36
|
+
test('deletion protects references, live use and review state', () => {
|
|
37
|
+
assert.throws(() => assertDelete(model()), /停用/);
|
|
38
|
+
assert.throws(() => assertDelete({ ...model(), enabled: false, references: [{ name: '流程', kind: '模板', version: 'v1' }] }), /引用/);
|
|
39
|
+
assert.throws(() => assertDelete({ ...model(), enabled: false, publishState: 'review' }), /评审/);
|
|
40
|
+
assert.doesNotThrow(() => assertDelete({ ...model(), enabled: false }));
|
|
41
|
+
});
|
|
42
|
+
test('model stale edits and tests do not qualify for publication', () => {
|
|
43
|
+
assert.throws(() => assertVersion(model(), 2), /版本已变化/);
|
|
44
|
+
assert.throws(() => requestPublish({ ...model(), testedVersion: 1 }, admin.id, admin.name), /测试/);
|
|
45
|
+
});
|
|
46
|
+
test('publication requires another reviewer and retains the old live configuration on failure', () => {
|
|
47
|
+
const m = requestPublish(model(), admin.id, admin.name);
|
|
48
|
+
assert.throws(() => finishPublish(m, admin.id, admin.name, at, true, true, '同意'), /另一位/);
|
|
49
|
+
assert.throws(() => finishPublish(m, reviewer.id, reviewer.name, at, true, true, ''), /评审意见/);
|
|
50
|
+
const failed = finishPublish(m, reviewer.id, reviewer.name, at, true, false, '同意');
|
|
51
|
+
assert.equal(failed.publishState, 'failed');
|
|
52
|
+
assert.equal(failed.liveVersion, 1);
|
|
53
|
+
assert.equal(failed.liveConfig?.name, '原名称');
|
|
54
|
+
const approved = finishPublish(m, reviewer.id, reviewer.name, at, true, true, '同意');
|
|
55
|
+
assert.equal(approved.liveVersion, 2);
|
|
56
|
+
assert.equal(approved.liveConfig?.name, '模型');
|
|
57
|
+
assert.equal(model().liveVersion, 1);
|
|
58
|
+
});
|
|
59
|
+
test('filled clarification fields do not resolve independent open decisions', () => {
|
|
60
|
+
const m = intent();
|
|
61
|
+
m.decisions = [{ id: 'd1', question: '谁复核?', answer: '' }];
|
|
62
|
+
const candidate = reviseVersion(m, m, m.answers, at, m.decisions);
|
|
63
|
+
assert.throws(() => confirmVersion(candidate, 2, at, owner), /未决问题/);
|
|
64
|
+
const next = reviseVersion(candidate, candidate, candidate.answers, at, [{ id: 'd1', question: '谁复核?', answer: '主管', resolvedBy: owner.name, resolvedAt: at }]);
|
|
65
|
+
assert.equal(confirmVersion(next, 3, at, owner).status, 'confirmed');
|
|
66
|
+
assert.equal(candidate.versions[1]?.decisions[0]?.answer, '');
|
|
67
|
+
});
|
|
68
|
+
test('intent confirmation enforces action permission, tenant and object version', () => {
|
|
69
|
+
assert.throws(() => confirmVersion(intent(), 1, at, admin), /权限/);
|
|
70
|
+
assert.throws(() => confirmVersion(intent(), 1, at, engineer), /权限/);
|
|
71
|
+
assert.throws(() => confirmVersion(intent(), 1, at, outsider), /权限/);
|
|
72
|
+
assert.throws(() => assertIntentAccess(intent(), owner, 'intent:edit', 0), /记录已被更新/);
|
|
73
|
+
assert.equal(confirmVersion(intent(), 1, at, owner).versions[0]?.confirmedBy, owner.name);
|
|
74
|
+
});
|
|
75
|
+
test('pending and unknown confirmation requests block resubmission and edits', () => {
|
|
76
|
+
const pending = beginConfirmation(intent(), 1, at, owner, 'request-1');
|
|
77
|
+
for (const confirmation of ['pending', 'unknown']) {
|
|
78
|
+
const record = { ...pending, confirmation };
|
|
79
|
+
assert.throws(() => beginConfirmation(record, 1, at, owner, 'request-2'), /核实结果/);
|
|
80
|
+
assert.throws(() => reviseVersion(record, record, record.answers, at), /核实结果/);
|
|
81
|
+
assert.throws(() => closeIntent(record, '取消', at, owner), /核实结果/);
|
|
82
|
+
}
|
|
83
|
+
const confirmed = confirmVersion(pending, 1, at, owner);
|
|
84
|
+
assert.equal(confirmed.pendingConfirmation, undefined);
|
|
85
|
+
assert.throws(() => confirmVersion(confirmed, 1, at, owner), /版本已变化/);
|
|
86
|
+
});
|
|
87
|
+
test('closure retains the document and versions and rejects further writes', () => {
|
|
88
|
+
assert.throws(() => closeIntent(intent(), ' ', at, owner), /关闭原因/);
|
|
89
|
+
assert.throws(() => closeIntent(confirmVersion(intent(), 1, at, owner), '取消', at, owner), /确认基线/);
|
|
90
|
+
const closed = closeIntent(intent(), '需求撤回', at, owner);
|
|
91
|
+
assert.equal(closed.status, 'closed');
|
|
92
|
+
assert.equal(closed.versions.length, 1);
|
|
93
|
+
assert.equal(closed.closedBy, owner.name);
|
|
94
|
+
assert.throws(() => replanIntent(closed, ['boundary'], '调整', at), /已关闭/);
|
|
95
|
+
assert.throws(() => reviseVersion(closed, closed, closed.answers, at), /已关闭/);
|
|
96
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { checkConfiguration, assertRemovable, validateBasics } from "../src/agents/domain.js";
|
|
4
|
+
import { actors, metadata, can } from "../src/shared/policy.js";
|
|
5
|
+
const a = { ...metadata(actors[0], '2026-09-10T10:00:00Z', 'heating'), id: 'one', grantKey: 'one', name: '需求规划', code: 'demand-planner', description: '', duty: 'planner', pbcId: 'pbc-0', project: '供热工单', modelRef: 'm', promptRef: 'p', skillRefs: ['s'], tools: [{ type: 'MCP', ref: 't' }], memoryLimit: 40, toolLimit: 10, enabled: false, references: [] };
|
|
6
|
+
const resources = [{ id: 'p', kind: 'prompt', name: '预设', version: 'v1', state: 'ready', tenantIds: ['*'], description: '', content: '' }, { id: 's', kind: 'skill', name: '技能', version: 'v1', state: 'ready', tenantIds: ['heating'], description: '', content: '' }, { id: 't', kind: 'tool', type: 'MCP', name: '只读工具', version: 'v1', state: 'ready', tenantIds: ['*'], description: '', content: '', allow: ['read_file'] }];
|
|
7
|
+
const model = { id: 'm', name: '对话模型', version: 1, enabled: true, kind: 'chat', capabilities: ['工具调用'], authorized: true, deleted: false };
|
|
8
|
+
test('完整四要素可用;停用、未发布与向量模型不能启用', () => { assert.ok(checkConfiguration(a, [model], resources).every(c => c.ok)); for (const change of [{ enabled: false }, { version: undefined }, { kind: 'embedding' }, { deleted: true }])
|
|
9
|
+
assert.equal(checkConfiguration(a, [{ ...model, ...change }], resources)[0].ok, false); });
|
|
10
|
+
test('编辑权限不能代替模型资源使用授权', () => { assert.equal(can(actors[3], 'agent:edit', 'heating', 'pbc-0'), true); assert.equal(checkConfiguration(a, [{ ...model, authorized: false }], resources).find(c => c.key === 'grant').ok, false); });
|
|
11
|
+
test('跨租户、失效资源与工具类型不符阻断引用', () => { assert.equal(checkConfiguration({ ...a, tenantId: 'north' }, [model], resources).find(c => c.key === 'skills').ok, false); assert.equal(checkConfiguration(a, [model], resources.filter(r => r.id !== 'p')).find(c => c.key === 'prompt').ok, false); assert.equal(checkConfiguration({ ...a, tools: [{ type: 'HTTP', ref: 't' }] }, [model], resources).find(c => c.key === 'tools').ok, false); });
|
|
12
|
+
test('白名单过宽、能力不符及无上限配置被拒绝', () => { assert.equal(checkConfiguration(a, [model], resources.map(r => r.kind === 'tool' ? { ...r, allow: ['*'] } : r)).find(c => c.key === 'tools').ok, false); assert.equal(checkConfiguration(a, [{ ...model, capabilities: [] }], resources).find(c => c.key === 'capability').ok, false); assert.throws(() => validateBasics({ ...a, toolLimit: 0 }), /上限/); assert.throws(() => validateBasics({ ...a, memoryLimit: NaN }), /记忆/); });
|
|
13
|
+
test('技能与工具可选;未绑定工具不要求工具调用能力', () => { const r = checkConfiguration({ ...a, skillRefs: [], tools: [] }, [{ ...model, capabilities: [] }], resources); assert.ok(r.every(c => c.ok)); });
|
|
14
|
+
test('删除保护启用及引用对象,停用无引用草稿可删', () => { assert.throws(() => assertRemovable({ ...a, enabled: true }), /停用/); assert.throws(() => assertRemovable({ ...a, references: [{ name: '流程', version: 'v1' }] }), /引用/); assert.doesNotThrow(() => assertRemovable(a)); assert.throws(() => validateBasics({ ...a, code: 'Bad code' }), /标识/); });
|
package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/intent-state.test.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { newIntent, publishVersion, reviseVersion, confirmVersion, replanIntent, lastVersion } from "../src/intents/domain.js";
|
|
4
|
+
const at = '2026-09-09T10:00:00Z';
|
|
5
|
+
const input = { title: '测试意图', request: '为业务人员提供可查询的工单进度', context: '集团意图池' };
|
|
6
|
+
const answers = { audience: '业务人员', process: '查询并确认', boundary: '只读授权数据', acceptance: '结果可追溯' };
|
|
7
|
+
function ready() { return publishVersion({ ...newIntent(input, 'i1', 'INT-001', at), answers }, at); }
|
|
8
|
+
test('unresolved intent cannot generate a reviewable version', () => { assert.throws(() => publishVersion(newIntent(input, 'i1', 'INT-001', at), at), /补充/); });
|
|
9
|
+
test('confirmation binds the displayed version and rejects a stale version', () => {
|
|
10
|
+
assert.throws(() => confirmVersion(ready(), 2, at), /版本已变化/);
|
|
11
|
+
assert.equal(confirmVersion(ready(), 1, at).status, 'confirmed');
|
|
12
|
+
});
|
|
13
|
+
test('editing makes a new unconfirmed version without altering the confirmed snapshot', () => {
|
|
14
|
+
const old = confirmVersion(ready(), 1, at);
|
|
15
|
+
const revised = reviseVersion(old, input, { ...answers, process: '主管确认后派单' }, at);
|
|
16
|
+
assert.equal(revised.status, 'ready');
|
|
17
|
+
assert.equal(lastVersion(revised)?.number, 2);
|
|
18
|
+
assert.equal(revised.versions[0]?.answers.process, '查询并确认');
|
|
19
|
+
assert.ok(revised.versions[0]?.confirmedAt);
|
|
20
|
+
assert.equal(lastVersion(revised)?.confirmedAt, undefined);
|
|
21
|
+
assert.equal(old.versions.length, 1);
|
|
22
|
+
});
|
|
23
|
+
test('replanning clears only selected answers and preserves confirmation history', () => {
|
|
24
|
+
const old = confirmVersion(ready(), 1, at);
|
|
25
|
+
const reopened = replanIntent(old, ['boundary'], '补充权限边界', at);
|
|
26
|
+
assert.equal(reopened.answers.boundary, '');
|
|
27
|
+
assert.equal(reopened.answers.audience, answers.audience);
|
|
28
|
+
assert.equal(reopened.status, 'clarifying');
|
|
29
|
+
assert.ok(reopened.versions[0]?.confirmedAt);
|
|
30
|
+
assert.throws(() => confirmVersion(reopened, 1, at), /版本已变化/);
|
|
31
|
+
assert.throws(() => publishVersion(reopened, at), /补充/);
|
|
32
|
+
});
|
|
33
|
+
test('changed content cannot borrow an older confirmation candidate', () => {
|
|
34
|
+
const candidate = ready();
|
|
35
|
+
candidate.answers = { ...answers, process: '不同内容' };
|
|
36
|
+
assert.throws(() => confirmVersion(candidate, 1, at), /内容已经变化/);
|
|
37
|
+
});
|
package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/list-batch.test.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { assertBatch } from "../src/shared/listBatch.js";
|
|
4
|
+
import { actors, can } from "../src/shared/policy.js";
|
|
5
|
+
import { newIntent, assertIntentAccess, assertIntentDelete, publishVersion, confirmVersion, reviseVersion } from "../src/intents/domain.js";
|
|
6
|
+
const owner = actors[2], admin = actors[0], at = '2026-09-10T15:00:00Z';
|
|
7
|
+
const draft = (id = 'draft') => newIntent({ title: id, request: '业务描述', context: '意图池' }, id, 'INT-001', at, owner);
|
|
8
|
+
const target = (i) => ({ id: i.id, name: i.title, recordVersion: i.recordVersion });
|
|
9
|
+
const check = (i) => { assertIntentAccess(i, owner, 'intent:delete'); assertIntentDelete(i); };
|
|
10
|
+
test('batch validates every target before a caller can commit', () => {
|
|
11
|
+
const first = draft('first'), stale = draft('stale'), records = [first, stale], snapshot = JSON.stringify(records);
|
|
12
|
+
assert.throws(() => assertBatch(records, [target(first), { ...target(stale), recordVersion: 0 }], check), /已被更新/);
|
|
13
|
+
assert.equal(JSON.stringify(records), snapshot);
|
|
14
|
+
assert.deepEqual(assertBatch(records, records.map(target), check).map(i => i.id), ['first', 'stale']);
|
|
15
|
+
});
|
|
16
|
+
test('empty, duplicated, missing and already deleted targets are rejected', () => {
|
|
17
|
+
const i = draft();
|
|
18
|
+
assert.throws(() => assertBatch([i], [], check), /至少/);
|
|
19
|
+
assert.throws(() => assertBatch([i], [target(i), target(i)], check), /重复/);
|
|
20
|
+
assert.throws(() => assertBatch([], [target(i)], check), /不存在/);
|
|
21
|
+
assert.throws(() => assertBatch([{ ...i, isDeleted: true }], [target(i)], check), /不存在/);
|
|
22
|
+
});
|
|
23
|
+
test('delete permission is separate from edit and respects the tenant boundary', () => {
|
|
24
|
+
assert.equal(can(actors[3], 'intent:edit'), true);
|
|
25
|
+
assert.equal(can(actors[3], 'intent:delete'), false);
|
|
26
|
+
assert.equal(can(admin, 'intent:delete'), true);
|
|
27
|
+
assert.equal(can(owner, 'intent:delete'), true);
|
|
28
|
+
assert.throws(() => assertIntentAccess(draft(), actors[3], 'intent:delete'), /权限/);
|
|
29
|
+
assert.throws(() => assertIntentAccess(draft(), actors[5], 'intent:delete'), /权限/);
|
|
30
|
+
});
|
|
31
|
+
test('confirmed history stays protected after later revisions', () => {
|
|
32
|
+
const i = draft();
|
|
33
|
+
i.answers = { audience: '业务人员', process: '处理工单', boundary: '本租户', acceptance: '可追溯' };
|
|
34
|
+
const confirmed = confirmVersion(publishVersion(i, at), 1, at, owner);
|
|
35
|
+
const revised = reviseVersion(confirmed, confirmed, confirmed.answers, at);
|
|
36
|
+
assert.equal(revised.status, 'ready');
|
|
37
|
+
assert.throws(() => assertIntentDelete(revised), /已确认版本/);
|
|
38
|
+
});
|
|
39
|
+
test('generation and uncertain confirmation cannot be deleted', () => {
|
|
40
|
+
assert.throws(() => assertIntentDelete({ ...draft(), generating: true }), /正在整理/);
|
|
41
|
+
for (const confirmation of ['pending', 'unknown'])
|
|
42
|
+
assert.throws(() => assertIntentDelete({ ...draft(), confirmation }), /核实/);
|
|
43
|
+
assert.doesNotThrow(() => assertIntentDelete({ ...draft(), status: 'closed' }));
|
|
44
|
+
});
|
package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/operation-fixtures.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { actors } from "../src/shared/policy.js";
|
|
2
|
+
import { standardBody } from "../src/workflows/data.js";
|
|
3
|
+
import { newTask, startTask, advanceTask, approveTask, clone } from "../src/tasks/domain.js";
|
|
4
|
+
import { retainReviews } from "../src/reviews/model.js";
|
|
5
|
+
import { createRelease, sourceKey } from "../src/releases/model.js";
|
|
6
|
+
import { applyRelease } from "../src/releases/commands.js";
|
|
7
|
+
export const actor = actors[0];
|
|
8
|
+
const binding = () => ({ intentId: 'intent', spec: { number: 1, source: { intentId: 'intent', code: 'INT-1', title: '发布测试', version: 1, confirmedAt: '2026-09-14' }, content: { overview: '需求', stories: '故事', requirements: 'FR-001', acceptance: 'AC-001', success: '标准', entities: '实体', pages: '页面', assumptions: '假设' }, decisions: [], reason: '确认样例', createdBy: '演示', createdAt: '2026-09-14', confirmedBy: '演示', confirmedAt: '2026-09-14' }, specRecordVersion: 1, pbcId: 'pbc-0', project: '供热工单', tenantId: 'heating', flowId: 'flow-0', flowRecordVersion: 1, flow: { number: 1, body: standardBody(), note: '标准', by: '演示', at: '2026-09-14', submittedBy: '演示' }, agents: [] });
|
|
9
|
+
export function ready(grade = 'A') { const t = newTask(binding(), actor, '发布测试'); t.reviewPolicy = { grade, humanQuality: grade !== 'A', reason: '明确的测试策略' }; startTask(t, '测试'); advanceTask(t); approveTask(t, '测试', 1, '测试中确认原型'); for (let i = 0; i < 3; i++) {
|
|
10
|
+
const old = clone(t);
|
|
11
|
+
advanceTask(t);
|
|
12
|
+
retainReviews(old, t);
|
|
13
|
+
} return t; }
|
|
14
|
+
export const input = () => ({ name: '智能分诊首次发布', version: '1.0.0', environment: 'production', config: 'config-v1', description: '上线本次智能分诊功能', traffic: 10, errorLimit: 1, latencyLimit: 800, rollbackPlan: '异常时回退批准的稳定版本;首发停止并撤流。' });
|
|
15
|
+
export function materials(t) { return { sourceKey: sourceKey(t), commit: 'demo-commit-a1', digest: 'sha256:demo-a1', config: 'config-v1', checks: ['merge', 'build', 'functional', 'contract', 'eval', 'security', 'migration'].map(id => ({ id, name: id, status: 'pass', detail: '测试用模拟证据' })), migration: 'none', rollback: { version: '0.9.0', digest: 'sha256:demo-old', config: 'config-old', environment: 'production' }, by: '独立CI(模拟)', at: '2026-09-14' }; }
|
|
16
|
+
export function publishedTask(id = 'ops-task') { const t = ready(); t.id = id; t.code = 'DEL-' + id; t.name = '供热智能分诊'; t.releaseMaterials = materials(t); createRelease(t, actor, input(), 'release-' + id); for (const [i, action] of ['check', 'submit', 'approve', 'start', 'advance', 'advance', 'advance'].entries()) {
|
|
17
|
+
const r = t.releases[0];
|
|
18
|
+
applyRelease(t, r.id, actor, { id: id + '-' + i, action, expected: r.recordVersion, note: '测试中的模拟批准', checked: true });
|
|
19
|
+
} return t; }
|
package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/operation-repair.test.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { actor, publishedTask } from "./operation-fixtures.js";
|
|
4
|
+
import { runsForTask, incidentLabel } from "../src/operations/model.js";
|
|
5
|
+
import { applyOperation } from "../src/operations/commands.js";
|
|
6
|
+
import { linkRepairIntent, repairCandidates, closureReason } from "../src/operations/repair.js";
|
|
7
|
+
import { assertIntentDelete } from "../src/intents/domain.js";
|
|
8
|
+
const setup = () => { const t = publishedTask(); let r = runsForTask(t)[0]; let n = 0; const apply = (action, extra = {}) => { r = applyOperation(t, r.id, actor, { id: 'c' + (++n), action, expected: r.recordVersion, note: '核对证据', checked: true, ...extra }); return r; }; apply('observe', { outcome: 'fail' }); return { t, get r() { return runsForTask(t)[0]; }, apply }; };
|
|
9
|
+
test('调查失败不能编造根因;分诊需要完整调查,驳回保留历史', () => { const s = setup(), id = s.r.incidents[0].id; s.apply('diagnose', { incidentId: id, outcome: 'missing' }); assert.equal(s.r.incidents[0].reports[0].status, 'incomplete'); assert.throws(() => s.apply('triageNow', { incidentId: id }), /调查/); s.apply('diagnose', { incidentId: id }); s.apply('triageReject', { incidentId: id }); assert.equal(incidentLabel(s.r.incidents[0]), '已驳回'); assert.equal(s.r.incidents[0].reports.length, 2); assert.throws(() => s.apply('diagnose', { incidentId: id }), /结束/); });
|
|
10
|
+
test('新修复需求保持草稿,保留异常来源,同一异常禁止重复创建且禁止删除来源链', () => { const s = setup(), id = s.r.incidents[0].id; s.apply('diagnose', { incidentId: id }); s.apply('triageNow', { incidentId: id }); const i = linkRepairIntent([], s.t, s.r, id, actor, { id: 'link1', note: '修复工具超时' }); assert.equal(i.status, 'draft'); assert.equal(i.versions.length, 0); assert.equal(i.operationLinks?.[0]?.pbcId, s.r.source.pbcId); assert.throws(() => assertIntentDelete(i), /运行异常/); assert.equal(linkRepairIntent([i], s.t, s.r, id, actor, { id: 'link1', note: '修复工具超时' }).id, i.id); assert.throws(() => linkRepairIntent([i], s.t, s.r, id, actor, { id: 'link2', note: '修复工具超时' }), /已经关联/); assert.throws(() => linkRepairIntent([], s.t, s.r, id, { ...actor, grants: [] }, { id: 'x', note: 'xx' }), /权限/); });
|
|
11
|
+
test('创建需求并不关闭异常;新发布、回归、人工验收和当前版本缺一不可', () => { const s = setup(), id = s.r.incidents[0].id; s.apply('diagnose', { incidentId: id }); s.apply('triageNow', { incidentId: id }); const i = linkRepairIntent([], s.t, s.r, id, actor, { id: 'link1', note: '修复' }); assert.match(closureReason(s.r, s.r.incidents[0], [i], [s.t], actor), /修复版本/); const t = publishedTask('repair'); t.binding.intentId = i.id; t.createdAt = new Date(Date.now() + 500).toISOString(); t.releases[0].versions[0].version = '1.0.1'; t.releases[0].finishedAt = new Date(Date.now() + 1000).toISOString(); let r = runsForTask(t)[0]; const c = () => repairCandidates(s.r.incidents[0], [i], [s.t, t], actor)[0]; assert.match(closureReason(s.r, s.r.incidents[0], [i], [s.t, t], actor, c()), /验收|证据/); r = applyOperation(t, r.id, actor, { id: 'observe', action: 'observe', expected: r.recordVersion, note: '回归', outcome: 'pass' }); r = applyOperation(t, r.id, actor, { id: 'accept', action: 'accept', expected: r.recordVersion, note: '验证', checked: true }); assert.equal(closureReason(s.r, s.r.incidents[0], [i], [s.t, t], actor, c()), ''); const stale = c(); applyOperation(t, r.id, actor, { id: 'new', action: 'observe', expected: r.recordVersion, note: '新观测', outcome: 'fail' }); assert.match(closureReason(s.r, s.r.incidents[0], [i], [s.t, t], actor, stale), /变化|证据|验收/); });
|
|
12
|
+
import { operationTodos } from "../src/operations/workbench.js";
|
|
13
|
+
test('关闭绑定已验收的新版本,旧运行记录转历史且不留下旧验收待办', () => { const s = setup(), id = s.r.incidents[0].id; s.apply('diagnose', { incidentId: id }); s.apply('triageNow', { incidentId: id }); const i = linkRepairIntent([], s.t, s.r, id, actor, { id: 'close-link', note: '修复' }); const t = publishedTask('closing'); t.binding.intentId = i.id; t.createdAt = new Date(Date.now() + 1000).toISOString(); t.releases[0].finishedAt = new Date(Date.now() + 2000).toISOString(); t.releases[0].versions[0].version = '1.0.1'; let r = runsForTask(t)[0]; r = applyOperation(t, r.id, actor, { id: 'reg', action: 'observe', expected: r.recordVersion, note: '回归' }); applyOperation(t, r.id, actor, { id: 'accept', action: 'accept', expected: r.recordVersion, note: '验收', checked: true }); const target = repairCandidates(s.r.incidents[0], [i], [s.t, t], actor)[0]; const closed = applyOperation(s.t, s.r.id, actor, { id: 'close', action: 'closeIncident', incidentId: id, expected: s.r.recordVersion, note: '完成', checked: true, target }, { tasks: [s.t, t], intents: [i] }); assert.equal(closed.supersededBy?.runId, target.runId); assert.equal(operationTodos([s.t, t], actor, true).length, 0); assert.equal(closed.observations[0].health, 'abnormal'); });
|
|
14
|
+
test('已有需求关联校验租户和结束状态,未知关联阻断关闭;重复分诊保留版本依据', () => { const s = setup(), id = s.r.incidents[0].id; s.apply('diagnose', { incidentId: id }); s.apply('triageNow', { incidentId: id }); s.apply('triagePlan', { incidentId: id }); assert.equal(s.r.incidents[0].triageHistory?.length, 2); const i = linkRepairIntent([], s.t, s.r, id, actor, { id: 'old', note: '保存', unknown: true }); assert.match(closureReason(s.r, s.r.incidents[0], [i], [s.t], actor), /核实/); const target = { ...i, id: 'existing', operationLinks: [], tenantId: 'ifm' }; assert.throws(() => linkRepairIntent([target], s.t, s.r, id, actor, { id: 'cross', note: '关联', targetId: target.id }), /租户/); target.tenantId = s.r.tenantId; target.status = 'closed'; assert.throws(() => linkRepairIntent([target], s.t, s.r, id, actor, { id: 'closed', note: '关联', targetId: target.id }), /结束/); });
|
package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/operations.test.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { actor, publishedTask } from "./operation-fixtures.js";
|
|
4
|
+
import { clone } from "../src/tasks/domain.js";
|
|
5
|
+
import { runsForTask, acceptanceLabel, healthLabel, operationReason } from "../src/operations/model.js";
|
|
6
|
+
import { applyOperation } from "../src/operations/commands.js";
|
|
7
|
+
let seq = 0;
|
|
8
|
+
const run = (t) => runsForTask(t)[0];
|
|
9
|
+
function act(t, action, more = {}) { return applyOperation(t, run(t).id, actor, { id: 'op-' + (++seq), action, expected: run(t).recordVersion, note: '已核对当前证据', checked: true, ...more }); }
|
|
10
|
+
test('仅已核实成功的生产发布派生运行记录;缺证据仍待验收', () => { const t = publishedTask(); assert.equal(runsForTask(t).length, 1); assert.equal(acceptanceLabel(run(t)), '待验收'); assert.equal(healthLabel(run(t)), '数据待提供'); for (const state of ['failed', 'deploying']) {
|
|
11
|
+
t.releases[0].state = state;
|
|
12
|
+
assert.equal(runsForTask(t).length, 0);
|
|
13
|
+
} t.releases[0].state = 'succeeded'; t.releases[0].unconfirmed = 'pending'; assert.equal(runsForTask(t).length, 0); t.releases[0].unconfirmed = undefined; t.releases[0].versions[0].environment = 'staging'; assert.equal(runsForTask(t).length, 0); });
|
|
14
|
+
test('观测健康不自动验收;缺失、失败、过期证据不得通过', () => { for (const outcome of ['missing', 'fail', 'stale']) {
|
|
15
|
+
const t = publishedTask();
|
|
16
|
+
act(t, 'observe', { outcome });
|
|
17
|
+
assert.equal(acceptanceLabel(run(t)), '待验收');
|
|
18
|
+
assert.throws(() => act(t, 'accept'), /证据/);
|
|
19
|
+
} const t = publishedTask(); act(t, 'observe', { outcome: 'pass' }); assert.equal(healthLabel(run(t)), '运行正常'); assert.equal(acceptanceLabel(run(t)), '待验收'); });
|
|
20
|
+
test('人工验收绑定证据版本及实际人,空说明或未勾选不能通过', () => { const t = publishedTask(); act(t, 'observe', { outcome: 'pass' }); assert.throws(() => act(t, 'accept', { checked: false }), /核对/); assert.throws(() => act(t, 'accept', { note: '' }), /说明/); act(t, 'accept'); assert.equal(run(t).acceptances[0].evidenceVersion, 1); assert.equal(run(t).acceptances[0].actorId, actor.id); assert.equal(acceptanceLabel(run(t)), '验收通过'); });
|
|
21
|
+
test('观察异常保留之前验收和证据,不抹掉历史结论', () => { const t = publishedTask(); act(t, 'observe', { outcome: 'pass' }); act(t, 'accept'); const previous = clone(run(t).acceptances); act(t, 'observe', { outcome: 'fail' }); assert.deepEqual(run(t).acceptances, previous); assert.equal(healthLabel(run(t)), '运行异常'); assert.equal(run(t).observations.length, 2); assert.equal(run(t).incidents.length, 1); act(t, 'observe', { outcome: 'fail' }); assert.equal(run(t).incidents.length, 1); });
|
|
22
|
+
test('无动作/环境权限和旧记录版本不能提交;源发布变化后仍保留历史', () => { const t = publishedTask(), r = run(t); assert.match(operationReason(t, r, { ...actor, grants: [] }, 'observe'), /权限/); const restricted = { ...actor, grants: actor.grants.map(g => ({ ...g, environmentIds: ['staging'] })) }; assert.match(operationReason(t, r, restricted, 'accept'), /权限/); act(t, 'observe'); assert.throws(() => act(t, 'accept', { expected: r.recordVersion }), /版本/); t.releases[0].state = 'stopped'; assert.equal(runsForTask(t).length, 1); assert.match(operationReason(t, run(t), actor, 'accept'), /发布|撤流/); });
|
|
23
|
+
test('未知响应先核实原请求;同编号重放不产生重复事件或验收', () => { const t = publishedTask(); act(t, 'observe', { outcome: 'pass' }); const c = { id: 'same-accept', action: 'accept', expected: run(t).recordVersion, note: '测试核对', checked: true, unknown: true }; applyOperation(t, run(t).id, actor, c); const snapshot = clone(run(t)); applyOperation(t, run(t).id, actor, c); assert.deepEqual(run(t), snapshot); assert.equal(acceptanceLabel(run(t)), '结果待核实'); assert.throws(() => act(t, 'observe'), /回执/); act(t, 'verify'); assert.equal(acceptanceLabel(run(t)), '验收通过'); assert.equal(run(t).acceptances.length, 1); });
|
|
24
|
+
import { operationTodos } from "../src/operations/workbench.js";
|
|
25
|
+
test('工作台验收后移除运行待办,后续异常独立保留,不重复统计相同对象', () => { const t = publishedTask(); assert.equal(operationTodos([t], actor, true).length, 1); act(t, 'observe'); act(t, 'accept'); assert.equal(operationTodos([t], actor, true).length, 0); act(t, 'observe', { outcome: 'fail' }); const todos = operationTodos([t], actor, true); assert.equal(todos.length, 1); assert.match(todos[0].id, /incident:/); assert.equal(operationTodos([t], { ...actor, grants: [] }, true).length, 0); assert.equal(operationTodos([t], actor, false)[0].allowed, false); });
|
package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/pbc-management.test.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { metadata } from "../src/shared/policy.js";
|
|
4
|
+
import { revisePbc, reviseService, assertPbcRemovable, assertServiceRemovable, assertActive, requireReason } from "../src/pbc/management.js";
|
|
5
|
+
import { assertBatch } from "../src/shared/listBatch.js";
|
|
6
|
+
const actor = { id: 'a', name: '甲', department: '研发', role: '任意', tenantId: 'heating', label: '成员', grants: [] }, at = '2026-09-11T00:00:00Z';
|
|
7
|
+
const pbc = () => ({ ...metadata(actor, at), id: 'p', name: '工单', slug: 'workorder', ebc: '服务', domain: 'example.test', owner: '甲', services: [], state: 'active', provisioned: false });
|
|
8
|
+
const patch = { name: '工单新版', ebc: '客户服务', owner: '乙', description: '职责边界' };
|
|
9
|
+
const service = { name: 'dispatch', title: '派单服务', template: 'java', purpose: '处理已确认派单', sourceIntentId: '' };
|
|
10
|
+
test('修改仅更新允许字段,审计与授权别名不随名称变更', () => { const p = pbc(), n = revisePbc(p, patch, actor, at); assert.equal(n.resourceKey, '工单'); assert.equal(n.tenantId, p.tenantId); assert.equal(n.createdBy, p.createdBy); assert.equal(n.recordVersion, 2); assert.equal(p.name, '工单'); assert.equal(revisePbc(n, { ...patch, name: '第三版' }, actor, at).resourceKey, '工单'); });
|
|
11
|
+
test('空字段、超长和归档禁止修改', () => { assert.throws(() => revisePbc(pbc(), { ...patch, name: ' ' }, actor, at)); assert.throws(() => revisePbc(pbc(), { ...patch, owner: 'x'.repeat(61) }, actor, at)); assert.throws(() => revisePbc({ ...pbc(), state: 'archived' }, patch, actor, at), /归档/); });
|
|
12
|
+
test('删除需归档并且无工程、服务和引用', () => { const p = { ...pbc(), state: 'archived' }; assert.doesNotThrow(() => assertPbcRemovable(p, [])); assert.throws(() => assertPbcRemovable(pbc(), []), /归档/); assert.throws(() => assertPbcRemovable({ ...p, provisioned: true }, []), /工程/); assert.throws(() => assertPbcRemovable(p, ['task']), /引用/); assert.throws(() => assertPbcRemovable({ ...p, services: [reviseService(pbc(), undefined, service, actor, at)] }, []), /服务/); });
|
|
13
|
+
test('批次拒绝重复、过期、已删除与混合不合法项', () => { const p = { ...pbc(), state: 'archived' }, q = { ...p, id: 'q', provisioned: true }; assert.throws(() => assertBatch([p, q], [{ ...p }, { ...q }], r => assertPbcRemovable(r, [])), /工程/); assert.equal(p.isDeleted, false); assert.throws(() => assertBatch([p], [{ ...p, recordVersion: 0 }], () => { }), /更新/); assert.throws(() => assertBatch([p], [{ ...p }, { ...p }], () => { }), /重复/); });
|
|
14
|
+
test('新增服务仅为未登记的方案,保留审计字段', () => { const s = reviseService(pbc(), undefined, service, actor, at); assert.equal(s.registered, false); assert.equal(s.tenantId, 'heating'); assert.equal(s.purpose, service.purpose); assert.equal(s.recordVersion, 1); });
|
|
15
|
+
test('服务重复标识、模板及格式校验', () => { const p = pbc(), s = reviseService(p, undefined, service, actor, at); p.services = [s]; assert.throws(() => reviseService(p, undefined, service, actor, at), /重复/); assert.throws(() => reviseService(p, undefined, { ...service, name: 'INVALID' }, actor, at)); assert.throws(() => reviseService(p, undefined, { ...service, template: 'random' }, actor, at)); });
|
|
16
|
+
test('已登记服务不能改身份或删除,可维护职责', () => { const p = pbc(), s = { ...reviseService(p, undefined, service, actor, at), registered: true }; assert.throws(() => reviseService(p, s, { ...service, name: 'new' }, actor, at), /不可修改/); assert.throws(() => assertServiceRemovable(p, s), /不可在此删除/); assert.equal(reviseService(p, s, { ...service, title: '派单与通知' }, actor, at).title, '派单与通知'); });
|
|
17
|
+
test('归档和删除对象拒绝新增使用;原因不能为空', () => { assert.throws(() => assertActive({ ...pbc(), state: 'archived' })); assert.throws(() => assertActive({ ...pbc(), isDeleted: true })); assert.throws(() => requireReason(' ')); assert.throws(() => requireReason('x'.repeat(501))); assert.doesNotThrow(() => requireReason('范围已核对')); });
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { actors, can } from "../src/shared/policy.js";
|
|
4
|
+
import { newIntent, publishVersion, confirmVersion } from "../src/intents/domain.js";
|
|
5
|
+
import { newSpec, appendVersion, beginSpecConfirmation, completeSpecConfirmation, sections } from "../src/specs/domain.js";
|
|
6
|
+
import { newDraft, validateDraft, makeJob, queryReceipt, activeStep, assertBinding } from "../src/pbc/domain.js";
|
|
7
|
+
const actor = actors.at(-1), at = '2026-09-10T09:00:00.000Z';
|
|
8
|
+
function fixture() {
|
|
9
|
+
let i = newIntent({ title: '需求', request: '处理请求', outcome: '可确认结果', context: '意图池' }, 'test', 'INT-1', at, actor);
|
|
10
|
+
i.answers = { audience: '有权人员', process: '核实后确认', boundary: '隔离资源', acceptance: '不重复执行' };
|
|
11
|
+
i = confirmVersion(publishVersion(i, at), 1, at, actor);
|
|
12
|
+
let s = appendVersion(newSpec(i, actor, at), { intentId: i.id, code: i.code, title: i.title, version: 1, confirmedAt: at }, Object.fromEntries(sections.map(k => [k.key, '内容'])), [], '首次', actor, at);
|
|
13
|
+
s = beginSpecConfirmation(s, i, actor, 1, s.recordVersion, 'request', at);
|
|
14
|
+
s.pending.acceptedAt = at;
|
|
15
|
+
s = completeSpecConfirmation(s);
|
|
16
|
+
const d = newDraft(i, actor, at), p = { ...d, id: 'pbc-' + d.slug };
|
|
17
|
+
return { i, s, d, p };
|
|
18
|
+
}
|
|
19
|
+
test('PBC 创建校验服务、唯一标识、域名与应用长度', () => { const { d, p } = fixture(); assert.doesNotThrow(() => validateDraft(d, [])); assert.throws(() => validateDraft({ ...d, services: [] }, []), /至少/); assert.throws(() => validateDraft({ ...d, services: [d.services[0], d.services[0]] }, []), /重复/); assert.throws(() => validateDraft(d, [p]), /已被使用/); assert.throws(() => validateDraft({ ...d, domain: 'invalid' }, []), /域名/); assert.throws(() => validateDraft({ ...d, slug: 'invalid_slug' }, []), /标识/); assert.doesNotThrow(() => validateDraft({ ...d, slug: '' }, [], 0)); });
|
|
20
|
+
test('同一用户确认规格并创建 PBC,旧版本不会被新草稿回改', () => { const { i, s, d, p } = fixture(); const j = makeJob(i, s, actor, p, d); assert.equal(j.createdById, s.versions[0].confirmedBy === actor.name ? actor.id : ''); assert.equal(j.steps.length, 6); d.name = '修改原对象'; assert.notEqual(j.draft.name, d.name); s.versions[0].content.overview = 'later'; assert.notEqual(j.spec.content.overview, 'later'); });
|
|
21
|
+
test('未确认规格、来源变化与跨租户拒绝归属', () => { const { i, s, p } = fixture(); assert.throws(() => makeJob(i, { ...s, versions: s.versions.map(v => ({ ...v, confirmedAt: undefined })) }, actor, p), /确认/); assert.throws(() => makeJob({ ...i, status: 'ready' }, s, actor, p), /意图/); assert.throws(() => makeJob(i, s, actor, { ...p, tenantId: 'north' }), /范围/); const limited = { ...actor, grants: [{ id: 'view', name: '查看', actions: ['pbc:view'], tenantIds: ['heating'] }] }; assert.throws(() => makeJob(i, s, limited, p), /权限/); });
|
|
22
|
+
test('PBC 范围与动作在同一授权组匹配,不跨组拼接', () => { const scoped = { ...actor, grants: [{ id: 'a', name: '甲', actions: ['pbc:view', 'pbc:link'], tenantIds: ['heating'], pbcIds: ['pbc-0'] }, { id: 'b', name: '乙', actions: ['pbc:view'], tenantIds: ['heating'], pbcIds: ['pbc-1'] }] }; assert.equal(can(scoped, 'pbc:link', 'heating', 'pbc-0'), true); assert.equal(can(scoped, 'pbc:link', 'heating', 'pbc-1'), false); assert.equal(can(scoped, 'pbc:view', 'north', 'pbc-0'), false); });
|
|
23
|
+
test('未知结果必须查询原回执,续接保留完成步骤和操作编号', () => { const { i, s, d, p } = fixture(), j = makeJob(i, s, actor, p, d); j.status = 'unknown'; j.steps[0].state = 'unknown'; j.steps[0].receipt = j.id + ':0'; const queried = queryReceipt(j); assert.equal(queried.id, j.id); assert.equal(queried.steps[0].state, 'done'); assert.equal(activeStep(queried), 1); assert.equal(j.steps[0].state, 'unknown'); const unaccepted = { ...j, steps: j.steps.map(step => ({ ...step, receipt: undefined })) }; assert.equal(queryReceipt(unaccepted).steps[0].state, 'waiting'); });
|
|
24
|
+
test('归属复核绑定展示的精确版本,禁止静默换成更新的已确认规格', () => { const { i, s } = fixture(), binding = { specVersion: 1, recordVersion: s.recordVersion, sourceVersion: 1 }; assert.doesNotThrow(() => assertBinding(i, s, binding)); assert.throws(() => assertBinding(i, { ...s, recordVersion: s.recordVersion + 1 }, binding), /版本已变化/); assert.throws(() => assertBinding(i, s, { ...binding, specVersion: 2 }), /版本已变化/); assert.throws(() => assertBinding(i, s), /重新打开/); });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { actors, can, inScope } from "../src/shared/policy.js";
|
|
4
|
+
test('role names do not decide permission and one person can confirm and create', () => {
|
|
5
|
+
const actor = structuredClone(actors.at(-1));
|
|
6
|
+
actor.role = 'custom-role';
|
|
7
|
+
actor.label = '自定义岗位';
|
|
8
|
+
assert.equal(can(actor, 'spec:confirm', 'heating'), true);
|
|
9
|
+
assert.equal(can(actor, 'pbc:create', 'heating'), true);
|
|
10
|
+
actor.grants[0].actions = actor.grants[0].actions.filter(a => a !== 'spec:confirm');
|
|
11
|
+
assert.equal(can(actor, 'spec:confirm', 'heating'), false);
|
|
12
|
+
assert.equal(can(actor, 'pbc:create', 'heating'), true);
|
|
13
|
+
});
|
|
14
|
+
test('multi-role permissions retain the scope of each assignment', () => {
|
|
15
|
+
const actor = { ...actors[2], grants: [
|
|
16
|
+
{ id: 'a', name: '供热规格确认', actions: ['spec:confirm'], tenantIds: ['heating'] },
|
|
17
|
+
{ id: 'b', name: '北方创建', actions: ['pbc:create'], tenantIds: ['north'] },
|
|
18
|
+
] };
|
|
19
|
+
assert.equal(can(actor, 'spec:confirm', 'north'), false);
|
|
20
|
+
assert.equal(can(actor, 'pbc:create', 'heating'), false);
|
|
21
|
+
assert.equal(inScope(actor, 'north', 'spec:confirm'), false);
|
|
22
|
+
assert.equal(can(actor, 'spec:confirm', 'heating'), true);
|
|
23
|
+
});
|
package/skills/i3-ui-demo/assets/templates/enterprise-admin/reference/tests/permissions.test.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { actors, metadata, can } from "../src/shared/policy.js";
|
|
4
|
+
import { resolveActor, validateRole, checkDelegation, checkMembership, deletableRole, assertVersion } from "../src/permissions/domain.js";
|
|
5
|
+
const admin = { ...actors[0], grants: [{ id: 'mgmt', name: '管理', actions: ['permission:grant', 'permission:members', 'permission:delegate'], tenantIds: ['*'], pbcIds: ['*'] }] };
|
|
6
|
+
const role = () => ({ ...metadata(admin, '2026-09-10', 'heating'), id: 'r1', name: '交付协调', code: 'delivery-editor', description: '可确认规格', enabled: true, protected: false, grants: [{ id: 'g', name: '确认规格', actions: ['spec:confirm'], tenantIds: ['heating'], pbcIds: ['*'] }] });
|
|
7
|
+
const state = () => ({ version: 1, roles: [role()], members: [{ ...metadata(admin, '2026-09-10', 'heating'), id: 'm', actorId: 'zhou', roleIds: ['r1'] }], records: [] });
|
|
8
|
+
test('权限来自成员角色关联,改名不改权', () => { const s = state(), person = actors[3]; assert.equal(can(resolveActor(person, s), 'spec:confirm', 'heating'), true); s.roles[0].name = '任意岗位'; assert.equal(can(resolveActor(person, s), 'spec:confirm', 'heating'), true); });
|
|
9
|
+
test('停用、删除角色或移除成员关联立即撤权', () => { for (const kind of ['enabled', 'isDeleted', 'member']) {
|
|
10
|
+
const s = state();
|
|
11
|
+
if (kind === 'member')
|
|
12
|
+
s.members[0].isDeleted = true;
|
|
13
|
+
else if (kind === 'enabled')
|
|
14
|
+
s.roles[0].enabled = false;
|
|
15
|
+
else
|
|
16
|
+
s.roles[0].isDeleted = true;
|
|
17
|
+
assert.equal(can(resolveActor(actors[3], s), 'spec:confirm', 'heating'), false);
|
|
18
|
+
} });
|
|
19
|
+
test('不同授权不能拼接动作和范围', () => { const r = role(); r.grants.push({ id: 'g2', name: '北方查看', actions: ['spec:view'], tenantIds: ['north'], pbcIds: ['*'] }); const s = state(); s.roles = [r]; assert.equal(can(resolveActor(actors[3], s), 'spec:confirm', 'north'), false); });
|
|
20
|
+
test('租户管理员不能授予其他租户或无限范围', () => { const limited = { ...admin, grants: admin.grants.map(g => ({ ...g, tenantIds: ['heating'] })) }; const r = role(); assert.doesNotThrow(() => checkDelegation(limited, r)); r.tenantId = 'platform'; r.grants[0].tenantIds = ['*']; assert.throws(() => checkDelegation(limited, r), /范围/); });
|
|
21
|
+
test('转授管理权限需要单独授权', () => { const limited = { ...admin, grants: admin.grants.map(g => ({ ...g, actions: ['permission:grant', 'permission:members'] })) }; const r = role(); r.grants[0].actions = ['permission:roles']; assert.throws(() => checkDelegation(limited, r), /转授/); assert.doesNotThrow(() => checkDelegation(admin, r)); });
|
|
22
|
+
test('租户角色不可包含其他租户授权', () => { const r = role(); r.grants[0].tenantIds = ['north']; assert.throws(() => checkDelegation(admin, r), /所属租户/); });
|
|
23
|
+
test('在用、保护和版本过期均拒绝', () => { const s = state(); assert.throws(() => deletableRole(s.roles[0], s.members), /关联成员/); assert.throws(() => deletableRole({ ...role(), protected: true }, []), /保护/); assert.throws(() => assertVersion(role(), 0), /已被更新/); assert.doesNotThrow(() => deletableRole(role(), [])); });
|
|
24
|
+
test('角色编码唯一与停用角色分配约束', () => { const s = state(); assert.throws(() => validateRole({ ...role(), id: 'another' }, s.roles), /已存在/); s.roles[0].enabled = false; assert.throws(() => checkMembership(admin, s.members[0], s.roles), /停用/); });
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { actors } from "../src/shared/policy.js";
|
|
4
|
+
import { standardBody } from "../src/workflows/data.js";
|
|
5
|
+
import { newTask, startTask, advanceTask, approveTask, clone } from "../src/tasks/domain.js";
|
|
6
|
+
import { captureQuality, retainReviews } from "../src/reviews/model.js";
|
|
7
|
+
import { decideReview } from "../src/reviews/commands.js";
|
|
8
|
+
import { qualityReason, createRelease, sourceKey } from "../src/releases/model.js";
|
|
9
|
+
const actor = actors[0];
|
|
10
|
+
const binding = () => ({ intentId: 'intent', spec: { number: 1, source: { intentId: 'intent', code: 'INT-1', title: '发布测试', version: 1, confirmedAt: '2026-09-14' }, content: { overview: '需求', stories: '故事', requirements: 'FR-001', acceptance: 'AC-001', success: '标准', entities: '实体', pages: '页面', assumptions: '假设' }, decisions: [], reason: '确认样例', createdBy: '演示', createdAt: '2026-09-14', confirmedBy: '演示', confirmedAt: '2026-09-14' }, specRecordVersion: 1, pbcId: 'pbc-0', project: '供热工单', tenantId: 'heating', flowId: 'flow-0', flowRecordVersion: 1, flow: { number: 1, body: standardBody(), note: '标准', by: '演示', at: '2026-09-14', submittedBy: '演示' }, agents: [] });
|
|
11
|
+
export function ready(grade = 'A') { const t = newTask(binding(), actor, '发布测试'); t.reviewPolicy = { grade, humanQuality: grade !== 'A', reason: '明确的测试策略' }; startTask(t, '测试'); advanceTask(t); approveTask(t, '测试', 1, '测试中确认原型'); for (let i = 0; i < 3; i++) {
|
|
12
|
+
const old = clone(t);
|
|
13
|
+
advanceTask(t);
|
|
14
|
+
retainReviews(old, t);
|
|
15
|
+
} return t; }
|
|
16
|
+
export const input = () => ({ name: '智能分诊首次发布', version: '1.0.0', environment: 'production', config: 'config-v1', description: '上线本次智能分诊功能', traffic: 10, errorLimit: 1, latencyLimit: 800, rollbackPlan: '异常时回退批准的稳定版本;首发停止并撤流。' });
|
|
17
|
+
export function materials(t) { return { sourceKey: sourceKey(t), commit: 'demo-commit-a1', digest: 'sha256:demo-a1', config: 'config-v1', checks: ['merge', 'build', 'functional', 'contract', 'eval', 'security', 'migration'].map(id => ({ id, name: id, status: 'pass', detail: '测试用模拟证据' })), migration: 'none', rollback: { version: '0.9.0', digest: 'sha256:demo-old', config: 'config-old', environment: 'production' }, by: '独立CI(模拟)', at: '2026-09-14' }; }
|
|
18
|
+
test('来源必须已有适用质量确认;A机器通过不代替生产授权', () => { const a = ready(); assert.equal(qualityReason(a), ''); const b = ready('B'); assert.match(qualityReason(b), /质量.*确认/); decideReview(b, captureQuality(b).id, actor, { action: 'confirm', requestId: 'quality-ok', note: '已核对', checked: true }); assert.equal(qualityReason(b), ''); const c = ready('C'); c.reviewPolicy.veto = true; assert.notEqual(qualityReason(c), ''); });
|
|
19
|
+
test('新发布草稿固定来源、审计及版本,旧任务没有材料时保持缺失', () => { const t = ready(), original = clone(t.binding), r = createRelease(t, actor, input(), 'release-1'); assert.equal(r.state, 'draft'); assert.equal(r.versions[0].sourceKey, sourceKey(t)); assert.equal(r.versions[0].materials, undefined); assert.equal(r.createdBy, actor.name); assert.deepEqual(t.binding, original); assert.equal(r.traffic, 0); });
|
|
20
|
+
test('发布材料必须匹配本任务的源版本,不能继承过期证据', () => { const t = ready(); t.releaseMaterials = materials(t); const r = createRelease(t, actor, input(), 'release-1'); assert.equal(r.versions[0].materials?.digest, 'sha256:demo-a1'); t.releaseMaterials.sourceKey = 'stale'; const next = createRelease(t, actor, { ...input(), environment: 'staging' }, 'release-2'); assert.equal(next.versions[0].materials, undefined); });
|
|
21
|
+
test('草稿创建校验权限、输入和请求编号,不重复创建', () => { const t = ready(); assert.throws(() => createRelease(t, { ...actor, grants: [] }, input(), 'r'), /权限/); assert.throws(() => createRelease(t, actor, { ...input(), name: '' }, 'r'), /名称/); const r = createRelease(t, actor, input(), 'r'); assert.equal(createRelease(t, actor, input(), 'r').id, r.id); assert.equal(t.releases.length, 1); assert.throws(() => createRelease(t, actor, { ...input(), name: '另一个' }, 'r'), /编号/); assert.throws(() => createRelease(t, actor, input(), 'second'), /已有/); assert.equal(t.releases.length, 1); });
|
|
22
|
+
import { applyRelease } from "../src/releases/commands.js";
|
|
23
|
+
let sequence = 0;
|
|
24
|
+
function command(t, action, extra = {}) { const r = t.releases[0]; return applyRelease(t, r.id, actor, { id: 'request-' + (++sequence), expected: r.recordVersion, action, note: '已核对本版内容', checked: true, ...extra }); }
|
|
25
|
+
function prepared() { const t = ready(); t.releaseMaterials = materials(t); createRelease(t, actor, input(), 'release'); command(t, 'check'); command(t, 'submit'); return t; }
|
|
26
|
+
function authorized() { const t = prepared(); command(t, 'approve'); return t; }
|
|
27
|
+
function canary() { const t = authorized(); command(t, 'start'); command(t, 'advance'); return t; }
|
|
28
|
+
test('缺少材料不能提交;检查、审核、首次流量按顺序执行', () => { const t = ready(); createRelease(t, actor, input(), 'release'); command(t, 'check'); assert.throws(() => command(t, 'submit'), /材料|证据/); const p = prepared(); assert.throws(() => command(p, 'start'), /授权/); assert.throws(() => command(p, 'approve', { checked: false }), /核对/); command(p, 'approve'); assert.equal(p.releases[0].traffic, 0); command(p, 'start'); assert.equal(p.releases[0].state, 'deploying'); command(p, 'advance'); assert.equal(p.releases[0].traffic, 10); });
|
|
29
|
+
test('不同目标环境的动作独立授权;空说明和过期页面不得通过', () => { const t = prepared(), r = t.releases[0], a = { ...actor, grants: [{ id: 'staging', name: '仅预发布', actions: ['task:view', 'artifact:view', 'release:approve'], tenantIds: ['heating'], environmentIds: ['staging'] }] }; assert.throws(() => applyRelease(t, r.id, a, { id: 'approve', expected: r.recordVersion, action: 'approve', note: '核对', checked: true }), /权限/); assert.throws(() => command(t, 'approve', { note: '' }), /说明/); assert.throws(() => command(t, 'approve', { expected: 0 }), /更新|版本/); assert.equal(r.state, 'pending'); });
|
|
30
|
+
test('修改环境或配置产生新版本,原批准保留但不再有效', () => { const t = authorized(); command(t, 'edit', { input: { ...input(), config: 'config-v2' } }); const r = t.releases[0]; assert.equal(r.versions.length, 2); assert.equal(r.state, 'draft'); assert.equal(r.approval, undefined); assert.ok(r.receipts.some(x => x.action === 'approve')); assert.throws(() => command(t, 'start'), /授权/); command(t, 'check'); assert.throws(() => command(t, 'submit'), /材料|证据/); assert.equal(r.traffic, 0); });
|
|
31
|
+
test('来源变化阻断执行及全量,但仍可停止或回滚', () => { const t = authorized(); t.artifacts.find(a => a.kind === 'code').versions.at(-1).number++; assert.throws(() => command(t, 'start'), /来源|版本/); const c = canary(); c.releaseMaterials.sourceKey = 'changed'; c.artifacts.find(a => a.kind === 'code').versions.at(-1).number++; assert.throws(() => command(c, 'advance'), /来源|版本/); command(c, 'rollback'); assert.equal(c.releases[0].state, 'rollingBack'); });
|
|
32
|
+
test('同编号同内容重试不推进两次;响应不明先查询回执', () => { const t = authorized(), r = t.releases[0], c = { id: 'same-start', expected: r.recordVersion, action: 'start', note: '执行', unknown: true }; applyRelease(t, r.id, actor, c); const saved = clone(r); applyRelease(t, r.id, actor, c); assert.deepEqual(r, saved); assert.throws(() => command(t, 'advance'), /回执/); assert.throws(() => applyRelease(t, r.id, actor, { ...c, note: '不同' }), /编号/); command(t, 'verify'); assert.equal(r.unconfirmed, undefined); command(t, 'advance'); assert.equal(r.traffic, 10); });
|
|
33
|
+
test('观察通过才能全量;失败重试有限,暂停和恢复不产生流量', () => { const t = canary(); command(t, 'pause'); assert.equal(t.releases[0].traffic, 10); command(t, 'resume'); command(t, 'advance'); assert.equal(t.releases[0].state, 'observing'); command(t, 'advance'); assert.equal(t.releases[0].traffic, 100); assert.equal(t.releases[0].state, 'succeeded'); const f = authorized(); command(f, 'start'); for (let i = 0; i < 3; i++) {
|
|
34
|
+
command(f, 'advance', { outcome: 'fail' });
|
|
35
|
+
if (i < 2)
|
|
36
|
+
command(f, 'retry');
|
|
37
|
+
} assert.throws(() => command(f, 'retry'), /次数/); assert.equal(f.releases[0].traffic, 0); });
|
|
38
|
+
test('首发无回滚目标只能停止撤流;有历史时回滚明确保留数据库状态', () => { const t = ready(); t.releaseMaterials = materials(t); delete t.releaseMaterials.rollback; createRelease(t, actor, input(), 'release'); for (const a of ['check', 'submit', 'approve', 'start', 'advance'])
|
|
39
|
+
command(t, a); assert.throws(() => command(t, 'rollback'), /目标|首发/); command(t, 'stop'); assert.equal(t.releases[0].traffic, 0); const p = canary(); command(p, 'rollback'); command(p, 'advance'); assert.equal(p.releases[0].state, 'rolledBack'); assert.match(p.releases[0].reason, /数据库/); assert.equal(p.releases[0].traffic, 0); });
|
|
40
|
+
test('只有未提交且无历史的草稿可删除', () => { const t = ready(); createRelease(t, actor, input(), 'release'); command(t, 'delete'); assert.equal(t.releases[0].isDeleted, true); const p = prepared(); command(p, 'return'); assert.throws(() => command(p, 'delete'), /历史|草稿/); assert.equal(p.releases[0].isDeleted, false); });
|
|
41
|
+
import { buildWorkbench } from "../src/workbench/model.js";
|
|
42
|
+
import { controlTask } from "../src/tasks/domain.js";
|
|
43
|
+
import { checkDelegation } from "../src/permissions/domain.js";
|
|
44
|
+
test('工作台沿用一个任务待办,发布完成后转运行验收待办', () => { const t = prepared(), data = () => buildWorkbench({ actor, active: true, intents: [], specs: [], jobs: [], pbcs: [], tasks: [t] }, 'all'); assert.equal(data().todos.length, 1); assert.equal(data().todos[0].group, '门禁与发布'); assert.equal(data().ongoing, 1); command(t, 'approve'); command(t, 'start'); command(t, 'advance'); command(t, 'advance'); command(t, 'advance'); assert.equal(data().todos.filter(x => x.group === '门禁与发布').length, 0); assert.equal(data().todos[0].group, '运行管理'); assert.equal(data().ongoing, 1); assert.equal(data().deliveries[0].label, '发布成功'); });
|
|
45
|
+
test('发布执行后不能从旧任务接管入口破坏发布状态', () => { const t = canary(); assert.throws(() => controlTask(t, 'takeover', '测试', '接管'), /发布/); assert.equal(t.status, 'ready'); });
|
|
46
|
+
test('授权管理不能超出可管理的发布环境', () => { const a = { ...actor, grants: [{ id: 'delegate', name: '预发布管理员', actions: ['permission:grant'], tenantIds: ['heating'], pbcIds: ['*'], environmentIds: ['staging'] }] }; const role = { ...a, tenantId: 'heating', grants: [{ id: 'g', name: '发布批准', actions: ['release:approve'], tenantIds: ['heating'], pbcIds: ['*'], environmentIds: ['production'] }] }; assert.throws(() => checkDelegation(a, role), /超出/); role.grants[0].environmentIds = ['staging']; assert.doesNotThrow(() => checkDelegation(a, role)); });
|
|
47
|
+
import { taskReleaseLabel } from "../src/releases/model.js";
|
|
48
|
+
test('预发布完成不能代签生产阶段,任务摘要标明环境', () => { const t = ready(); t.releaseMaterials = materials(t); delete t.releaseMaterials.rollback; const original = clone(t.stages); createRelease(t, actor, { ...input(), environment: 'staging' }, 'release'); for (const action of ['check', 'submit', 'approve', 'start', 'advance', 'advance', 'advance'])
|
|
49
|
+
command(t, action); assert.equal(t.releases[0].state, 'succeeded'); assert.deepEqual(t.stages, original); assert.equal(taskReleaseLabel(t, actor), '预发布环境 · 发布成功'); const data = buildWorkbench({ actor, active: true, intents: [], specs: [], jobs: [], pbcs: [], tasks: [t] }, 'all'); assert.equal(data.todos.length, 1); assert.match(data.todos[0].status, /生产/); assert.match(data.todos[0].path, /prepare=1/); });
|
|
50
|
+
test('终态响应不明仍保留核实待办,核实前不允许新建同环境发布', () => { const t = canary(); command(t, 'advance'); command(t, 'advance', { unknown: true }); const data = () => buildWorkbench({ actor, active: true, intents: [], specs: [], jobs: [], pbcs: [], tasks: [t] }, 'all'); assert.equal(t.releases[0].state, 'succeeded'); assert.equal(data().todos.length, 1); assert.equal(data().todos[0].status, '结果待核实'); assert.match(data().todos[0].reason, /查询原请求回执/); assert.doesNotMatch(data().todos[0].reason, /已全量|发布完成/); assert.throws(() => createRelease(t, actor, input(), 'next-release'), /已有/); command(t, 'verify'); assert.equal(data().todos.filter(x => x.group === '门禁与发布').length, 0); assert.equal(data().todos[0].group, '运行管理'); });
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { actors } from "../src/shared/policy.js";
|
|
4
|
+
import { standardBody } from "../src/workflows/data.js";
|
|
5
|
+
import { newTask, startTask, advanceTask, approveTask, controlTask, latestArtifact, clone } from "../src/tasks/domain.js";
|
|
6
|
+
import { reviewsForTask, reviewStatus, actionableReview, actionReason, canReadReview, retainReviews, reviewBudget } from "../src/reviews/model.js";
|
|
7
|
+
import { decideReview, validateDesign } from "../src/reviews/commands.js";
|
|
8
|
+
import { buildWorkbench } from "../src/workbench/model.js";
|
|
9
|
+
const actor = actors[0];
|
|
10
|
+
function binding() { return { intentId: 'intent', spec: { number: 1, source: { intentId: 'intent', code: 'INT-1', title: '测试需求', version: 1, confirmedAt: '2026-09-11' }, content: { overview: '需求', stories: '故事', requirements: 'FR-004 幂等确认', acceptance: 'AC', success: '标准', entities: '实体', pages: '页面', assumptions: '假设' }, decisions: [], reason: '确认样例', createdBy: '演示', createdAt: '2026-09-11', confirmedBy: '演示', confirmedAt: '2026-09-11' }, specRecordVersion: 1, pbcId: 'pbc-0', project: '供热工单', tenantId: 'heating', flowId: 'flow-0', flowRecordVersion: 1, flow: { number: 1, body: standardBody(), note: '标准', by: '演示', at: '2026-09-11', submittedBy: '演示' }, agents: [] }; }
|
|
11
|
+
function base() { const t = newTask(binding(), actor, '评审测试'); t.reviewPolicy = { grade: 'B', humanQuality: true, reason: '本确认点需要人工' }; startTask(t, '发起人'); advanceTask(t); return t; }
|
|
12
|
+
function step(t, result = 'pass') { const old = structuredClone(t); advanceTask(t, result); retainReviews(old, t); }
|
|
13
|
+
function quality(result = 'pass') { const t = base(); approveTask(t, '历史核对人', 1, '已核对原型'); step(t); step(t); step(t, result); return t; }
|
|
14
|
+
function payload(action = 'confirm') { return { action, requestId: crypto.randomUUID(), note: '核对当前版本与依据', checked: true, content: action === 'edit' ? '新设计:按租户、建议版本和请求编号实现幂等确认。' : undefined, impact: action === 'replan' ? '重新设计幂等边界并重新实现、执行测试和验证。' : undefined }; }
|
|
15
|
+
function active(t) { const r = actionableReview(t); assert.ok(r); return r; }
|
|
16
|
+
function input(t) { return { actor, active: true, intents: [], specs: [], jobs: [], pbcs: [], tasks: [t] }; }
|
|
17
|
+
test('评审按绑定版本呈现,查看不改写任务,分项无证据不显示通过', () => { const t = quality(), before = JSON.stringify(t), r = active(t); assert.equal(r.kind, 'quality'); assert.equal(reviewStatus(t, r), 'pending'); assert.ok(r.refs.some(x => x.kind === 'code' && x.version === 1)); assert.equal(JSON.stringify(t), before); assert.equal(r.checks.find(c => c.name === 'evals').status, 'unavailable'); });
|
|
18
|
+
test('旧版原型确认保留历史操作者;新验证到达后旧检查中记录失效', () => { const t = base(); approveTask(t, '历史核对人', 1, '已核对原型'); step(t); step(t); const prior = active(t); assert.equal(reviewStatus(t, prior), 'checking'); step(t); assert.equal(reviewStatus(t, prior), 'superseded'); assert.equal(reviewsForTask(t).filter(r => ['pending', 'checking', 'blocked'].includes(reviewStatus(t, r))).length, 1); const r = reviewsForTask(t).find(r => r.kind === 'prototype'); assert.equal(r.decision.actor, '历史核对人'); assert.match(r.decision.note, /已核对原型/); });
|
|
19
|
+
test('机器失败不能通过人工确认改成成功', () => { const t = quality('fail'), r = active(t), before = structuredClone(t); assert.equal(r.findings[0].id, 'ROB-AC003.2'); assert.equal(reviewStatus(t, r), 'blocked'); assert.throws(() => decideReview(t, r.id, actor, payload()), /重复确认|派单/); assert.deepEqual(t, before); });
|
|
20
|
+
test('修改生成新设计版本;原设计、失败报告及规格与流程绑定不变', () => { const t = quality('fail'), r = active(t), old = structuredClone(t), p = payload('edit'), d = decideReview(t, r.id, actor, p); assert.equal(d.newVersion, 2); assert.equal(t.status, 'waiting'); assert.equal(t.reworks, 1); assert.equal(latestArtifact(t, 'design').content, p.content); assert.deepEqual(t.binding, old.binding); for (const a of old.artifacts)
|
|
21
|
+
for (const v of a.versions)
|
|
22
|
+
assert.deepEqual(t.artifacts.find(x => x.id === a.id).versions[v.number - 1], v); assert.equal(reviewStatus(t, active(t)), 'checking'); });
|
|
23
|
+
test('保存不等于批准;设计复核通过并确认后才能恢复实现', () => { const t = quality('fail'); decideReview(t, active(t).id, actor, payload('edit')); const r = active(t); assert.throws(() => decideReview(t, r.id, actor, payload()), /新设计|等待/); assert.throws(() => advanceTask(t), /可推进/); validateDesign(t, r.id, 'pass'); assert.equal(reviewStatus(t, active(t)), 'pending'); decideReview(t, r.id, actor, payload()); assert.equal(t.status, 'running'); assert.equal(t.currentId, 'build'); assert.equal(t.approvedDesignVersion, 2); });
|
|
24
|
+
test('重新实现保留人工设计并产生新代码、测试和独立验证', () => { const t = quality('fail'); decideReview(t, active(t).id, actor, payload('edit')); const r = active(t); validateDesign(t, r.id, 'pass'); decideReview(t, r.id, actor, payload()); const design = clone(latestArtifact(t, 'design')); step(t); assert.deepEqual(latestArtifact(t, 'design'), design); assert.equal(latestArtifact(t, 'code').number, 2); assert.ok(latestArtifact(t, 'code').inputs.some(i => i.id === 'design' && i.version === 2)); assert.equal(t.checksPassed, false); step(t); step(t); assert.equal(latestArtifact(t, 'test').iteration, 1); assert.equal(latestArtifact(t, 'verify').iteration, 1); assert.equal(reviewStatus(t, active(t)), 'pending'); });
|
|
25
|
+
test('设计复核失败不可确认,后续结果保留前次失败报告版本', () => { const t = quality(); decideReview(t, active(t).id, actor, payload('edit')); const id = active(t).id; validateDesign(t, id, 'fail'); assert.equal(reviewStatus(t, active(t)), 'blocked'); assert.throws(() => decideReview(t, id, actor, payload()), /未覆盖/); const failed = clone(latestArtifact(t, 'review')); validateDesign(t, id, 'pass'); assert.equal(t.artifacts.find(a => a.kind === 'review').versions.length, 2); assert.deepEqual(t.artifacts.find(a => a.kind === 'review').versions[0], failed); });
|
|
26
|
+
test('重新规划保留意见与影响,不改写来源和流程版本', () => { const t = quality(), original = clone(t.binding), p = payload('replan'); const d = decideReview(t, active(t).id, actor, p); assert.ok(d.nextReviewId); assert.equal(d.impact, p.impact); assert.match(latestArtifact(t, 'design').content, /重新规划意见/); assert.deepEqual(t.binding, original); assert.equal(reviewStatus(t, active(t)), 'checking'); });
|
|
27
|
+
test('确认仅完成当前质量点,不执行生产发布或归档', () => { const t = quality(); decideReview(t, active(t).id, actor, payload()); assert.equal(t.status, 'ready'); assert.equal(t.currentId, 'approval'); assert.equal(t.stages.find(s => s.nodeId === 'release').state, 'pending'); assert.equal(t.stages.find(s => s.nodeId === 'archive').state, 'pending'); assert.equal(actionableReview(t), undefined); });
|
|
28
|
+
test('相同提交返回原结果,编号不能跨记录或复用不同内容', () => { const t = quality(), id = active(t).id, p = payload('edit'); const d = decideReview(t, id, actor, p), before = structuredClone(t); assert.deepEqual(decideReview(t, id, actor, p), d); assert.deepEqual(t, before); assert.throws(() => decideReview(t, id, actor, { ...p, note: '更改说明' }), /同一提交编号/); assert.throws(() => decideReview(t, active(t).id, actor, p), /同一提交编号/); });
|
|
29
|
+
test('动作和资源决定权限;岗位名称不授权,撤权与跨租户不可读写', () => { const t = quality(), r = active(t), read = { ...actor, label: 'AI工程师', grants: [{ id: 'read', name: '只读', actions: ['task:view', 'artifact:view'], tenantIds: ['heating'], pbcIds: ['pbc-0'] }] }; assert.ok(canReadReview(read, t)); assert.match(actionReason(t, r, read, 'edit'), /权限/); assert.throws(() => decideReview(t, r.id, read, payload()), /权限/); assert.equal(canReadReview({ ...read, grants: [] }, t), false); assert.equal(canReadReview({ ...read, grants: [{ ...read.grants[0], tenantIds: ['other'] }] }, t), false); assert.equal(canReadReview({ ...read, grants: [{ ...read.grants[0], pbcIds: ['pbc-other'] }] }, t), false); });
|
|
30
|
+
test('必填、长度与未修改校验失败不生成决策或新版本', () => { const t = quality(), r = active(t), old = structuredClone(t); for (const p of [{ ...payload(), note: '' }, { ...payload(), note: 'x'.repeat(1001) }, { ...payload(), checked: false }, { ...payload('replan'), impact: '' }, { ...payload('edit'), content: '' }, { ...payload('edit'), content: latestArtifact(t, 'design').content }])
|
|
31
|
+
assert.throws(() => decideReview(t, r.id, actor, p)); assert.deepEqual(t, old); });
|
|
32
|
+
test('达到有限次数须接管,接管不重置预算或判定通过', () => { const t = quality(); t.reworks = t.retryLimit; t.reviews = []; assert.match(actionReason(t, active(t), actor, 'edit'), /次数/); const before = clone(t.artifacts); controlTask(t, 'takeover', '接管人', '达到处理上限'); assert.equal(reviewStatus(t, active(t)), 'suspended'); controlTask(t, 'resume', '接管人', '回到原阶段'); assert.equal(t.reworks, 2); assert.deepEqual(t.artifacts, before); const n = t.binding.flow.body.nodes.find(n => n.id === 'build'); n.kind = 'loop'; n.limit = 1; assert.equal(reviewBudget(t), 1); });
|
|
33
|
+
test('A档无额外人工点;C档敏感停点不能一键跳过', () => { const a = quality(); a.reviews = []; a.reviewPolicy = { grade: 'A', humanQuality: false, reason: '预置机器检查' }; assert.equal(reviewStatus(a, reviewsForTask(a).find(r => r.kind === 'quality')), 'automatic'); assert.equal(actionableReview(a), undefined); const c = quality(); c.reviews = []; c.reviewPolicy = { grade: 'C', humanQuality: true, veto: true, reason: '敏感停点' }; assert.equal(reviewStatus(c, active(c)), 'blocked'); assert.throws(() => decideReview(c, active(c).id, actor, payload()), /敏感停点/); });
|
|
34
|
+
test('工作台同一任务门禁去重,质量确认后转发布待办;无产物权限不泄露评审', () => { const t = quality(), data = buildWorkbench(input(t), 'all'); assert.equal(data.todos.length, 1); assert.equal(data.todos[0].group, '质量与评审'); assert.equal(data.ongoing, 1); decideReview(t, active(t).id, actor, payload()); assert.equal(buildWorkbench(input(t), 'all').todos.length, 1); assert.equal(buildWorkbench(input(t), 'all').todos[0].group, '门禁与发布'); const limited = { ...actor, grants: [{ id: 'task', name: '任务只读', actions: ['task:view'], tenantIds: ['heating'] }] }; assert.equal(buildWorkbench({ ...input(quality()), actor: limited }, 'all').todos.length, 0); });
|
|
35
|
+
test('旧依据缺失或出现新版本,拒绝继续提交旧记录', () => { const t = quality(), r = active(t); t.artifacts.find(a => a.kind === 'code').versions.push({ ...clone(latestArtifact(t, 'code')), number: 2 }); assert.equal(reviewStatus(t, r), 'superseded'); assert.throws(() => decideReview(t, r.id, actor, payload()), /版本已变化/); const x = quality(), q = active(x); x.artifacts = x.artifacts.filter(a => a.kind !== 'verify'); assert.equal(reviewStatus(x, q), 'superseded'); });
|
|
36
|
+
test('原型通过统一评审入口确认,不能提前修改尚未生成的设计', () => { const t = base(), r = active(t); assert.equal(r.kind, 'prototype'); assert.match(actionReason(t, r, actor, 'edit'), /原型确认后/); decideReview(t, r.id, actor, payload()); assert.equal(t.prototypeApproved, true); assert.equal(t.currentId, 'build'); });
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { validateValue,useForm } from '../runtime/forms.js';
|
|
4
|
+
import { computed } from '../runtime/state.js';
|
|
5
|
+
import { matchRoute } from '../runtime/router.js';
|
|
6
|
+
|
|
7
|
+
test('form validation retains zero prices and rejects whitespace and invalid service URLs',async()=>{
|
|
8
|
+
assert.equal(await validateValue(0,[{required:true,type:'number',min:0}]),'');
|
|
9
|
+
assert.notEqual(await validateValue(-1,[{type:'number',min:0}]),'');
|
|
10
|
+
assert.notEqual(await validateValue(' ',[{required:true,whitespace:true}]),'');
|
|
11
|
+
assert.notEqual(await validateValue('not-a-service-url',[{type:'url'}]),'');
|
|
12
|
+
assert.equal(await validateValue('https://models.example.invalid/v1',[{type:'url'}]),'');
|
|
13
|
+
});
|
|
14
|
+
test('asynchronous uniqueness rules block saving without discarding form input',async()=>{
|
|
15
|
+
const form=useForm({alias:'duplicate'},[{path:'alias',rules:[{validator:async()=>{throw Error('标识已存在');}}]}]);
|
|
16
|
+
await assert.rejects(form.formRef.value.validate(),/校验/);
|
|
17
|
+
assert.equal(form.formData.alias,'duplicate');assert.equal(form.errors.alias,'标识已存在');
|
|
18
|
+
});
|
|
19
|
+
test('hidden fields follow current model type and validation accepts required multiple selections',async()=>{
|
|
20
|
+
const form=useForm({kind:'chat',dimension:undefined,capabilities:['文本']});
|
|
21
|
+
form.fields.value=[{path:'dimension',hidden:computed(()=>form.formData.kind!=='embedding'),rules:[{required:true}]},{path:'capabilities',hidden:computed(()=>form.formData.kind==='embedding'),rules:[{required:true,type:'array',min:1}]}];
|
|
22
|
+
await form.formRef.value.validate();form.formData.kind='embedding';await assert.rejects(form.formRef.value.validate());form.formData.dimension=1536;await form.formRef.value.validate();
|
|
23
|
+
});
|
|
24
|
+
test('native hash routes retain deep-link identifiers and query parameters',()=>{
|
|
25
|
+
const routes=[{path:'/tasks/new'},{path:'/tasks/:id/artifacts/:artifact'},{path:'/tasks/:id'}];
|
|
26
|
+
const route=matchRoute('#/tasks/task-example-1/artifacts/verify?version=1',routes);
|
|
27
|
+
assert.deepEqual(route.params,{id:'task-example-1',artifact:'verify'});assert.equal(route.query.version,'1');assert.equal(route.fullPath,'/tasks/task-example-1/artifacts/verify?version=1');assert.deepEqual(matchRoute('/tasks/new',routes).params,{});
|
|
28
|
+
});
|