diff --git a/.github/workflows/hand-verification-nightly.yml b/.github/workflows/hand-verification-nightly.yml new file mode 100644 index 0000000000000000000000000000000000000000..935451d591150756f8039f67933e983ddc6b5661 --- /dev/null +++ b/.github/workflows/hand-verification-nightly.yml @@ -0,0 +1,20 @@ +name: Hand Verification Nightly + +on: + schedule: + - cron: "0 16 * * *" # 00:00 UTC+8 + workflow_dispatch: + +jobs: + hand-verification-offline: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version-file: package.json + - name: Install + run: bun install --frozen-lockfile + - name: Hand verification smoke (offline steps) + run: HAND_VERIFICATION_ONLY=entity-pipeline,ubml-knowledge,page-roundtrip,raw-roundtrip,verify-hints,farris-alias bun run qa:hand-verification diff --git a/.gitignore b/.gitignore index 265268a13fab55c64d31e7a467db5f25e541986d..c6439adc8693514dacce762926964e1a18fb702e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ dist-*/ .env .env.* !.env.example +!deploy/.env.knowledge-pro.example .DS_Store .idea/ .vscode/ diff --git a/.kb/aliases.seed.json b/.kb/aliases.seed.json new file mode 100644 index 0000000000000000000000000000000000000000..8294676b0c73c637c45ba737c97c543cbeaab579 --- /dev/null +++ b/.kb/aliases.seed.json @@ -0,0 +1,12 @@ +{ + "aliases": { + "手风琴": "accordion", + "折叠面板": "accordion", + "数据表格": "list-view", + "表格": "list-view", + "Mission 任务": "mission-store", + "报销单": "fybxd", + "AppGen": "generate-app-core", + "知识库索引": "indexer" + } +} diff --git a/bun.lock b/bun.lock index fdf1e5b0102835bc4f28b24d5c01d4938ee86f0d..7d0f1349ff96719cbfdb1e6231e734bcc2376852 100644 --- a/bun.lock +++ b/bun.lock @@ -244,6 +244,7 @@ "@thisbeyond/solid-dnd": "0.7.5", "effect": "catalog:", "luxon": "catalog:", + "mermaid": "11.12.0", "solid-js": "catalog:", "virtua": "catalog:", }, @@ -567,6 +568,7 @@ "ubml-component-compile": "./src/component/cli/component-compile.ts", "ubml-javagen": "./src/javagen/cli/javagen.ts", "ubml-app-generate": "./src/app/cli/app-generate.ts", + "ubml-app-decompile": "./src/app/cli/app-decompile.ts", }, "dependencies": { "js-yaml": "^4.1.0", @@ -818,6 +820,8 @@ "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], + "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.71.2", "https://registry.npmmirror.com/@anthropic-ai/sdk/-/sdk-0.71.2.tgz", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ=="], "@anycable/core": ["@anycable/core@0.9.2", "https://registry.npmmirror.com/@anycable/core/-/core-0.9.2.tgz", { "dependencies": { "nanoevents": "^7.0.1" } }, "sha512-x5ZXDcW/N4cxWl93CnbHs/u7qq4793jS2kNPWm+duPrXlrva+ml2ZGT7X9tuOBKzyIHf60zWCdIK7TUgMPAwXA=="], @@ -930,6 +934,18 @@ "@babel/types": ["@babel/types@8.0.0", "", { "dependencies": { "@babel/helper-string-parser": "^8.0.0", "@babel/helper-validator-identifier": "^8.0.0" } }, "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw=="], + "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], + + "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.0.3", "https://registry.npmmirror.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", { "dependencies": { "@chevrotain/gast": "11.0.3", "@chevrotain/types": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ=="], + + "@chevrotain/gast": ["@chevrotain/gast@11.0.3", "https://registry.npmmirror.com/@chevrotain/gast/-/gast-11.0.3.tgz", { "dependencies": { "@chevrotain/types": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q=="], + + "@chevrotain/regexp-to-ast": ["@chevrotain/regexp-to-ast@11.0.3", "https://registry.npmmirror.com/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", {}, "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA=="], + + "@chevrotain/types": ["@chevrotain/types@11.0.3", "https://registry.npmmirror.com/@chevrotain/types/-/types-11.0.3.tgz", {}, "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ=="], + + "@chevrotain/utils": ["@chevrotain/utils@11.0.3", "https://registry.npmmirror.com/@chevrotain/utils/-/utils-11.0.3.tgz", {}, "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ=="], + "@clack/core": ["@clack/core@1.0.0-alpha.1", "https://registry.npmmirror.com/@clack/core/-/core-1.0.0-alpha.1.tgz", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-rFbCU83JnN7l3W1nfgCqqme4ZZvTTgsiKQ6FM0l+r0P+o2eJpExcocBUWUIwnDzL76Aca9VhUdWmB2MbUv+Qyg=="], "@clack/prompts": ["@clack/prompts@1.0.0-alpha.1", "https://registry.npmmirror.com/@clack/prompts/-/prompts-1.0.0-alpha.1.tgz", { "dependencies": { "@clack/core": "1.0.0-alpha.1", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-07MNT0OsxjKOcyVfX8KhXBhJiyUbDP1vuIAcHc+nx5v93MJO23pX3X/k3bWz6T3rpM9dgWPq90i4Jq7gZAyMbw=="], @@ -1092,6 +1108,10 @@ "@hono/node-server": ["@hono/node-server@1.19.14", "https://registry.npmmirror.com/@hono/node-server/-/node-server-1.19.14.tgz", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], + "@iconify/types": ["@iconify/types@2.0.0", "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], + + "@iconify/utils": ["@iconify/utils@3.1.3", "https://registry.npmmirror.com/@iconify/utils/-/utils-3.1.3.tgz", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "import-meta-resolve": "^4.2.0" } }, "sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw=="], + "@internationalized/date": ["@internationalized/date@3.12.2", "https://registry.npmmirror.com/@internationalized/date/-/date-3.12.2.tgz", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-FY1Y+H64NDs+HAF6omlnWxm3mEpfgaCSWtL5l551ZZfImA+kGjPFgrnJrGjH6lfmLL0g8Z/mBu1R3kufeCp6Jw=="], "@internationalized/number": ["@internationalized/number@3.6.7", "https://registry.npmmirror.com/@internationalized/number/-/number-3.6.7.tgz", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg=="], @@ -1156,6 +1176,8 @@ "@malept/flatpak-bundler": ["@malept/flatpak-bundler@0.4.0", "https://registry.npmmirror.com/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", { "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.0", "lodash": "^4.17.15", "tmp-promise": "^3.0.2" } }, "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q=="], + "@mermaid-js/parser": ["@mermaid-js/parser@0.6.3", "https://registry.npmmirror.com/@mermaid-js/parser/-/parser-0.6.3.tgz", { "dependencies": { "langium": "3.3.1" } }, "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA=="], + "@microsoft/api-extractor": ["@microsoft/api-extractor@7.58.9", "https://registry.npmmirror.com/@microsoft/api-extractor/-/api-extractor-7.58.9.tgz", { "dependencies": { "@microsoft/api-extractor-model": "7.33.8", "@microsoft/tsdoc": "~0.16.0", "@microsoft/tsdoc-config": "~0.18.1", "@rushstack/node-core-library": "5.23.1", "@rushstack/rig-package": "0.7.3", "@rushstack/terminal": "0.24.0", "@rushstack/ts-command-line": "5.3.10", "diff": "~8.0.2", "minimatch": "10.2.3", "resolve": "~1.22.1", "semver": "~7.7.4", "source-map": "~0.6.1", "typescript": "5.9.3" }, "bin": { "api-extractor": "bin/api-extractor" } }, "sha512-S2UF4yza5GoxCmf7hJQNxJNZN9ltOVuOQv8Dy+Z21aol5ERoBNMdWcQHm4MJMPPItW4H/4rZD906iaf4mUojJA=="], "@microsoft/api-extractor-model": ["@microsoft/api-extractor-model@7.33.8", "https://registry.npmmirror.com/@microsoft/api-extractor-model/-/api-extractor-model-7.33.8.tgz", { "dependencies": { "@microsoft/tsdoc": "~0.16.0", "@microsoft/tsdoc-config": "~0.18.1", "@rushstack/node-core-library": "5.23.1" } }, "sha512-aIcoQggPyer3B6Ze3usz0YWC/oBwUHfRH5ETUsr+oT2BRA6SfTJl7IKPcPZkX4UR+PohowzW4uMxsvjrn8vm+w=="], @@ -1674,6 +1696,68 @@ "@types/cross-spawn": ["@types/cross-spawn@6.0.6", "https://registry.npmmirror.com/@types/cross-spawn/-/cross-spawn-6.0.6.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA=="], + "@types/d3": ["@types/d3@7.4.3", "https://registry.npmmirror.com/@types/d3/-/d3-7.4.3.tgz", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="], + + "@types/d3-array": ["@types/d3-array@3.2.2", "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.2.tgz", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-axis": ["@types/d3-axis@3.0.6", "https://registry.npmmirror.com/@types/d3-axis/-/d3-axis-3.0.6.tgz", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw=="], + + "@types/d3-brush": ["@types/d3-brush@3.0.6", "https://registry.npmmirror.com/@types/d3-brush/-/d3-brush-3.0.6.tgz", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A=="], + + "@types/d3-chord": ["@types/d3-chord@3.0.6", "https://registry.npmmirror.com/@types/d3-chord/-/d3-chord-3.0.6.tgz", {}, "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-contour": ["@types/d3-contour@3.0.6", "https://registry.npmmirror.com/@types/d3-contour/-/d3-contour-3.0.6.tgz", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="], + + "@types/d3-delaunay": ["@types/d3-delaunay@6.0.4", "https://registry.npmmirror.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", {}, "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="], + + "@types/d3-dispatch": ["@types/d3-dispatch@3.0.7", "https://registry.npmmirror.com/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", {}, "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA=="], + + "@types/d3-drag": ["@types/d3-drag@3.0.7", "https://registry.npmmirror.com/@types/d3-drag/-/d3-drag-3.0.7.tgz", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ=="], + + "@types/d3-dsv": ["@types/d3-dsv@3.0.7", "https://registry.npmmirror.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", {}, "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-fetch": ["@types/d3-fetch@3.0.7", "https://registry.npmmirror.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", { "dependencies": { "@types/d3-dsv": "*" } }, "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA=="], + + "@types/d3-force": ["@types/d3-force@3.0.10", "https://registry.npmmirror.com/@types/d3-force/-/d3-force-3.0.10.tgz", {}, "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="], + + "@types/d3-format": ["@types/d3-format@3.0.4", "https://registry.npmmirror.com/@types/d3-format/-/d3-format-3.0.4.tgz", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="], + + "@types/d3-geo": ["@types/d3-geo@3.1.0", "https://registry.npmmirror.com/@types/d3-geo/-/d3-geo-3.1.0.tgz", { "dependencies": { "@types/geojson": "*" } }, "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ=="], + + "@types/d3-hierarchy": ["@types/d3-hierarchy@3.1.7", "https://registry.npmmirror.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", {}, "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.1.1.tgz", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-polygon": ["@types/d3-polygon@3.0.2", "https://registry.npmmirror.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", {}, "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="], + + "@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "https://registry.npmmirror.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="], + + "@types/d3-random": ["@types/d3-random@3.0.4", "https://registry.npmmirror.com/@types/d3-random/-/d3-random-3.0.4.tgz", {}, "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.9.tgz", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-scale-chromatic": ["@types/d3-scale-chromatic@3.1.0", "https://registry.npmmirror.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", {}, "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="], + + "@types/d3-selection": ["@types/d3-selection@3.0.11", "https://registry.npmmirror.com/@types/d3-selection/-/d3-selection-3.0.11.tgz", {}, "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w=="], + + "@types/d3-shape": ["@types/d3-shape@3.1.8", "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.8.tgz", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.4.tgz", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-time-format": ["@types/d3-time-format@4.0.3", "https://registry.npmmirror.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + + "@types/d3-transition": ["@types/d3-transition@3.0.9", "https://registry.npmmirror.com/@types/d3-transition/-/d3-transition-3.0.9.tgz", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg=="], + + "@types/d3-zoom": ["@types/d3-zoom@3.0.8", "https://registry.npmmirror.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="], + "@types/debug": ["@types/debug@4.1.13", "https://registry.npmmirror.com/@types/debug/-/debug-4.1.13.tgz", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], "@types/estree": ["@types/estree@1.0.9", "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], @@ -1682,6 +1766,8 @@ "@types/gensync": ["@types/gensync@1.0.5", "", {}, "sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg=="], + "@types/geojson": ["@types/geojson@7946.0.16", "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.16.tgz", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="], + "@types/hast": ["@types/hast@3.0.4", "https://registry.npmmirror.com/@types/hast/-/hast-3.0.4.tgz", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], "@types/http-cache-semantics": ["@types/http-cache-semantics@4.2.0", "https://registry.npmmirror.com/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", {}, "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q=="], @@ -1956,6 +2042,10 @@ "character-entities-legacy": ["character-entities-legacy@3.0.0", "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + "chevrotain": ["chevrotain@11.0.3", "https://registry.npmmirror.com/chevrotain/-/chevrotain-11.0.3.tgz", { "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", "@chevrotain/regexp-to-ast": "11.0.3", "@chevrotain/types": "11.0.3", "@chevrotain/utils": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw=="], + + "chevrotain-allstar": ["chevrotain-allstar@0.3.1", "https://registry.npmmirror.com/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", { "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { "chevrotain": "^11.0.0" } }, "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw=="], + "chokidar": ["chokidar@4.0.3", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "chownr": ["chownr@3.0.0", "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], @@ -2022,6 +2112,8 @@ "cors": ["cors@2.8.6", "https://registry.npmmirror.com/cors/-/cors-2.8.6.tgz", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + "cose-base": ["cose-base@1.0.3", "https://registry.npmmirror.com/cose-base/-/cose-base-1.0.3.tgz", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="], + "cross-dirname": ["cross-dirname@0.1.0", "https://registry.npmmirror.com/cross-dirname/-/cross-dirname-0.1.0.tgz", {}, "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q=="], "cross-fetch": ["cross-fetch@3.2.0", "https://registry.npmmirror.com/cross-fetch/-/cross-fetch-3.2.0.tgz", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="], @@ -2036,10 +2128,84 @@ "csstype": ["csstype@3.2.3", "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + "cytoscape": ["cytoscape@3.34.0", "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.34.0.tgz", {}, "sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg=="], + + "cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "https://registry.npmmirror.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="], + + "cytoscape-fcose": ["cytoscape-fcose@2.2.0", "https://registry.npmmirror.com/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="], + + "d3": ["d3@7.9.0", "https://registry.npmmirror.com/d3/-/d3-7.9.0.tgz", { "dependencies": { "d3-array": "3", "d3-axis": "3", "d3-brush": "3", "d3-chord": "3", "d3-color": "3", "d3-contour": "4", "d3-delaunay": "6", "d3-dispatch": "3", "d3-drag": "3", "d3-dsv": "3", "d3-ease": "3", "d3-fetch": "3", "d3-force": "3", "d3-format": "3", "d3-geo": "3", "d3-hierarchy": "3", "d3-interpolate": "3", "d3-path": "3", "d3-polygon": "3", "d3-quadtree": "3", "d3-random": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "d3-selection": "3", "d3-shape": "3", "d3-time": "3", "d3-time-format": "4", "d3-timer": "3", "d3-transition": "3", "d3-zoom": "3" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="], + + "d3-array": ["d3-array@3.2.4", "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-axis": ["d3-axis@3.0.0", "https://registry.npmmirror.com/d3-axis/-/d3-axis-3.0.0.tgz", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="], + + "d3-brush": ["d3-brush@3.0.0", "https://registry.npmmirror.com/d3-brush/-/d3-brush-3.0.0.tgz", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="], + + "d3-chord": ["d3-chord@3.0.1", "https://registry.npmmirror.com/d3-chord/-/d3-chord-3.0.1.tgz", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="], + + "d3-color": ["d3-color@3.1.0", "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-contour": ["d3-contour@4.0.2", "https://registry.npmmirror.com/d3-contour/-/d3-contour-4.0.2.tgz", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="], + + "d3-delaunay": ["d3-delaunay@6.0.4", "https://registry.npmmirror.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="], + + "d3-dispatch": ["d3-dispatch@3.0.1", "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="], + + "d3-drag": ["d3-drag@3.0.0", "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="], + + "d3-dsv": ["d3-dsv@3.0.1", "https://registry.npmmirror.com/d3-dsv/-/d3-dsv-3.0.1.tgz", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="], + + "d3-ease": ["d3-ease@3.0.1", "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-fetch": ["d3-fetch@3.0.1", "https://registry.npmmirror.com/d3-fetch/-/d3-fetch-3.0.1.tgz", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="], + + "d3-force": ["d3-force@3.0.0", "https://registry.npmmirror.com/d3-force/-/d3-force-3.0.0.tgz", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="], + + "d3-format": ["d3-format@3.1.2", "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.2.tgz", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-geo": ["d3-geo@3.1.1", "https://registry.npmmirror.com/d3-geo/-/d3-geo-3.1.1.tgz", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="], + + "d3-hierarchy": ["d3-hierarchy@3.1.2", "https://registry.npmmirror.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-polygon": ["d3-polygon@3.0.1", "https://registry.npmmirror.com/d3-polygon/-/d3-polygon-3.0.1.tgz", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="], + + "d3-quadtree": ["d3-quadtree@3.0.1", "https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="], + + "d3-random": ["d3-random@3.0.1", "https://registry.npmmirror.com/d3-random/-/d3-random-3.0.1.tgz", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="], + + "d3-sankey": ["d3-sankey@0.12.3", "https://registry.npmmirror.com/d3-sankey/-/d3-sankey-0.12.3.tgz", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="], + + "d3-scale": ["d3-scale@4.0.2", "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "https://registry.npmmirror.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="], + + "d3-selection": ["d3-selection@3.0.0", "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="], + + "d3-shape": ["d3-shape@3.2.0", "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "d3-transition": ["d3-transition@3.0.1", "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="], + + "d3-zoom": ["d3-zoom@3.0.0", "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="], + + "dagre-d3-es": ["dagre-d3-es@7.0.11", "https://registry.npmmirror.com/dagre-d3-es/-/dagre-d3-es-7.0.11.tgz", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw=="], + "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "https://registry.npmmirror.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], "date-fns": ["date-fns@3.6.0", "https://registry.npmmirror.com/date-fns/-/date-fns-3.6.0.tgz", {}, "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww=="], + "dayjs": ["dayjs@1.11.21", "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.21.tgz", {}, "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA=="], + "debounce-fn": ["debounce-fn@6.0.0", "https://registry.npmmirror.com/debounce-fn/-/debounce-fn-6.0.0.tgz", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ=="], "debug": ["debug@4.4.3", "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -2062,6 +2228,8 @@ "defu": ["defu@6.1.7", "https://registry.npmmirror.com/defu/-/defu-6.1.7.tgz", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + "delaunator": ["delaunator@5.1.0", "https://registry.npmmirror.com/delaunator/-/delaunator-5.1.0.tgz", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ=="], + "delayed-stream": ["delayed-stream@1.0.0", "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], "denque": ["denque@2.1.0", "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], @@ -2322,6 +2490,8 @@ "gtoken": ["gtoken@8.0.0", "https://registry.npmmirror.com/gtoken/-/gtoken-8.0.0.tgz", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="], + "hachure-fill": ["hachure-fill@0.5.2", "https://registry.npmmirror.com/hachure-fill/-/hachure-fill-0.5.2.tgz", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], + "happy-dom": ["happy-dom@20.10.6", "https://registry.npmmirror.com/happy-dom/-/happy-dom-20.10.6.tgz", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "buffer-image-size": "^0.6.4", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.21.0" } }, "sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw=="], "has-flag": ["has-flag@4.0.0", "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], @@ -2380,6 +2550,8 @@ "ini": ["ini@7.0.0", "https://registry.npmmirror.com/ini/-/ini-7.0.0.tgz", {}, "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w=="], + "internmap": ["internmap@1.0.1", "https://registry.npmmirror.com/internmap/-/internmap-1.0.1.tgz", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], + "ioredis": ["ioredis@5.11.1", "https://registry.npmmirror.com/ioredis/-/ioredis-5.11.1.tgz", { "dependencies": { "@ioredis/commands": "1.10.0", "cluster-key-slot": "1.1.1", "debug": "4.4.3", "denque": "2.1.0", "redis-errors": "1.2.0", "redis-parser": "3.0.0", "standard-as-callback": "2.1.0" } }, "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A=="], "ip-address": ["ip-address@10.2.0", "https://registry.npmmirror.com/ip-address/-/ip-address-10.2.0.tgz", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="], @@ -2486,12 +2658,18 @@ "keyv": ["keyv@4.5.4", "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + "khroma": ["khroma@2.1.0", "https://registry.npmmirror.com/khroma/-/khroma-2.1.0.tgz", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="], + "kind-of": ["kind-of@6.0.3", "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], "kolorist": ["kolorist@1.8.0", "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="], "kubernetes-types": ["kubernetes-types@1.30.0", "https://registry.npmmirror.com/kubernetes-types/-/kubernetes-types-1.30.0.tgz", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], + "langium": ["langium@3.3.1", "https://registry.npmmirror.com/langium/-/langium-3.3.1.tgz", { "dependencies": { "chevrotain": "~11.0.3", "chevrotain-allstar": "~0.3.0", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", "vscode-uri": "~3.0.8" } }, "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w=="], + + "layout-base": ["layout-base@1.0.2", "https://registry.npmmirror.com/layout-base/-/layout-base-1.0.2.tgz", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="], + "lazy-val": ["lazy-val@1.0.5", "https://registry.npmmirror.com/lazy-val/-/lazy-val-1.0.5.tgz", {}, "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q=="], "light-my-request": ["light-my-request@6.6.0", "https://registry.npmmirror.com/light-my-request/-/light-my-request-6.6.0.tgz", { "dependencies": { "cookie": "^1.0.1", "process-warning": "^4.0.0", "set-cookie-parser": "^2.6.0" } }, "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A=="], @@ -2564,6 +2742,8 @@ "merge2": ["merge2@1.4.1", "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + "mermaid": ["mermaid@11.12.0", "https://registry.npmmirror.com/mermaid/-/mermaid-11.12.0.tgz", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.1", "@mermaid-js/parser": "^0.6.2", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.18", "dompurify": "^3.2.5", "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.21", "marked": "^16.2.1", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-ZudVx73BwrMJfCFmSSJT84y6u5brEoV8DOItdHomNLz32uBjNrelm7mg95X7g+C6UoQH/W6mBLGDEDv73JdxBg=="], + "micromark-util-character": ["micromark-util-character@2.1.1", "https://registry.npmmirror.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], "micromark-util-encode": ["micromark-util-encode@2.0.1", "https://registry.npmmirror.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], @@ -2728,6 +2908,8 @@ "package-json-from-dist": ["package-json-from-dist@1.0.1", "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + "package-manager-detector": ["package-manager-detector@1.7.0", "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.7.0.tgz", {}, "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ=="], + "pacote": ["pacote@21.5.1", "https://registry.npmmirror.com/pacote/-/pacote-21.5.1.tgz", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "@npmcli/run-script": "^10.0.0", "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^13.0.0", "npm-packlist": "^10.0.1", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" }, "bin": { "pacote": "bin/index.js" } }, "sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg=="], "parse-conflict-json": ["parse-conflict-json@5.0.1", "https://registry.npmmirror.com/parse-conflict-json/-/parse-conflict-json-5.0.1.tgz", { "dependencies": { "json-parse-even-better-errors": "^5.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" } }, "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ=="], @@ -2740,6 +2922,8 @@ "path-browserify": ["path-browserify@1.0.1", "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + "path-data-parser": ["path-data-parser@0.1.0", "https://registry.npmmirror.com/path-data-parser/-/path-data-parser-0.1.0.tgz", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], + "path-exists": ["path-exists@4.0.0", "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-expression-matcher": ["path-expression-matcher@1.6.0", "https://registry.npmmirror.com/path-expression-matcher/-/path-expression-matcher-1.6.0.tgz", {}, "sha512-e5y7RCLHKjemsgQ4eqGJtPyr10ILz25HO7flzxhTV8bgvd5yHx98DGtCAtbVW9f2TqnYI/gEVZd+vz7snrdPTw=="], @@ -2788,6 +2972,10 @@ "poe-oauth": ["poe-oauth@0.0.10", "https://registry.npmmirror.com/poe-oauth/-/poe-oauth-0.0.10.tgz", {}, "sha512-XohR2aTnXT6U3KAly540b7IcJT7yxa4d8PKibeqShpeLN1KLRHb5FBJ9WY42IhDQxOrF/GNndc+nnsliJtPlSA=="], + "points-on-curve": ["points-on-curve@0.2.0", "https://registry.npmmirror.com/points-on-curve/-/points-on-curve-0.2.0.tgz", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], + + "points-on-path": ["points-on-path@0.2.1", "https://registry.npmmirror.com/points-on-path/-/points-on-path-0.2.1.tgz", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="], + "postcss": ["postcss@8.5.15", "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], "postcss-selector-parser": ["postcss-selector-parser@7.1.4", "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg=="], @@ -2904,14 +3092,20 @@ "roarr": ["roarr@2.15.4", "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="], + "robust-predicates": ["robust-predicates@3.0.3", "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.3.tgz", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="], + "rollup": ["rollup@4.62.2", "https://registry.npmmirror.com/rollup/-/rollup-4.62.2.tgz", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.2", "@rollup/rollup-android-arm64": "4.62.2", "@rollup/rollup-darwin-arm64": "4.62.2", "@rollup/rollup-darwin-x64": "4.62.2", "@rollup/rollup-freebsd-arm64": "4.62.2", "@rollup/rollup-freebsd-x64": "4.62.2", "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", "@rollup/rollup-linux-arm-musleabihf": "4.62.2", "@rollup/rollup-linux-arm64-gnu": "4.62.2", "@rollup/rollup-linux-arm64-musl": "4.62.2", "@rollup/rollup-linux-loong64-gnu": "4.62.2", "@rollup/rollup-linux-loong64-musl": "4.62.2", "@rollup/rollup-linux-ppc64-gnu": "4.62.2", "@rollup/rollup-linux-ppc64-musl": "4.62.2", "@rollup/rollup-linux-riscv64-gnu": "4.62.2", "@rollup/rollup-linux-riscv64-musl": "4.62.2", "@rollup/rollup-linux-s390x-gnu": "4.62.2", "@rollup/rollup-linux-x64-gnu": "4.62.2", "@rollup/rollup-linux-x64-musl": "4.62.2", "@rollup/rollup-openbsd-x64": "4.62.2", "@rollup/rollup-openharmony-arm64": "4.62.2", "@rollup/rollup-win32-arm64-msvc": "4.62.2", "@rollup/rollup-win32-ia32-msvc": "4.62.2", "@rollup/rollup-win32-x64-gnu": "4.62.2", "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA=="], + "roughjs": ["roughjs@4.6.6", "https://registry.npmmirror.com/roughjs/-/roughjs-4.6.6.tgz", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="], + "router": ["router@2.2.0", "https://registry.npmmirror.com/router/-/router-2.2.0.tgz", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], "run-applescript": ["run-applescript@7.1.0", "https://registry.npmmirror.com/run-applescript/-/run-applescript-7.1.0.tgz", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], "run-parallel": ["run-parallel@1.2.0", "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "rw": ["rw@1.3.3", "https://registry.npmmirror.com/rw/-/rw-1.3.3.tgz", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="], + "rxjs": ["rxjs@7.8.2", "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.2.tgz", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], "s-js": ["s-js@0.4.9", "https://registry.npmmirror.com/s-js/-/s-js-0.4.9.tgz", {}, "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ=="], @@ -3050,6 +3244,8 @@ "stubborn-utils": ["stubborn-utils@1.0.2", "https://registry.npmmirror.com/stubborn-utils/-/stubborn-utils-1.0.2.tgz", {}, "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg=="], + "stylis": ["stylis@4.4.0", "https://registry.npmmirror.com/stylis/-/stylis-4.4.0.tgz", {}, "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA=="], + "sumchecker": ["sumchecker@3.0.1", "https://registry.npmmirror.com/sumchecker/-/sumchecker-3.0.1.tgz", { "dependencies": { "debug": "^4.1.0" } }, "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="], "supports-color": ["supports-color@7.2.0", "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -3106,6 +3302,8 @@ "ts-algebra": ["ts-algebra@2.0.0", "https://registry.npmmirror.com/ts-algebra/-/ts-algebra-2.0.0.tgz", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], + "ts-dedent": ["ts-dedent@2.3.0", "https://registry.npmmirror.com/ts-dedent/-/ts-dedent-2.3.0.tgz", {}, "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg=="], + "ts-morph": ["ts-morph@18.0.0", "https://registry.npmmirror.com/ts-morph/-/ts-morph-18.0.0.tgz", { "dependencies": { "@ts-morph/common": "~0.19.0", "code-block-writer": "^12.0.0" } }, "sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA=="], "tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -3198,8 +3396,16 @@ "vscode-jsonrpc": ["vscode-jsonrpc@8.2.1", "https://registry.npmmirror.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", {}, "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ=="], + "vscode-languageserver": ["vscode-languageserver@9.0.1", "https://registry.npmmirror.com/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="], + + "vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "https://registry.npmmirror.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="], + + "vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "https://registry.npmmirror.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="], + "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + "vscode-uri": ["vscode-uri@3.0.8", "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.0.8.tgz", {}, "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="], + "vue": ["vue@3.5.24", "https://registry.npmmirror.com/vue/-/vue-3.5.24.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.24", "@vue/compiler-sfc": "3.5.24", "@vue/runtime-dom": "3.5.24", "@vue/server-renderer": "3.5.24", "@vue/shared": "3.5.24" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg=="], "vue-demi": ["vue-demi@0.14.10", "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", { "peerDependencies": { "@vue/composition-api": "^1.0.0-rc.1", "vue": "^3.0.0-0 || ^2.6.0" }, "optionalPeers": ["@vue/composition-api"], "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" } }, "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg=="], @@ -3352,6 +3558,8 @@ "@ai-sdk/vercel/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "https://registry.npmmirror.com/@ai-sdk/provider-utils/-/provider-utils-4.0.21.tgz", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="], + "@antfu/install-pkg/tinyexec": ["tinyexec@1.2.4", "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.2.4.tgz", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], + "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "https://registry.npmmirror.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "https://registry.npmmirror.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], @@ -3396,6 +3604,10 @@ "@babel/traverse/@babel/parser": ["@babel/parser@8.0.0", "", { "dependencies": { "@babel/types": "^8.0.0" }, "bin": "./bin/babel-parser.js" }, "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ=="], + "@chevrotain/cst-dts-gen/lodash-es": ["lodash-es@4.17.21", "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], + + "@chevrotain/gast/lodash-es": ["lodash-es@4.17.21", "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], + "@electron/asar/commander": ["commander@5.1.0", "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], "@electron/asar/glob": ["glob@7.2.3", "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], @@ -3604,6 +3816,8 @@ "cacache/lru-cache": ["lru-cache@11.5.1", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], + "chevrotain/lodash-es": ["lodash-es@4.17.21", "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], + "clone-response/mimic-response": ["mimic-response@1.0.1", "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], "code-agent/typescript": ["typescript@5.9.3", "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], @@ -3612,6 +3826,16 @@ "cross-spawn/which": ["which@2.0.2", "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "https://registry.npmmirror.com/cose-base/-/cose-base-2.2.0.tgz", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="], + + "d3-dsv/commander": ["commander@7.2.0", "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "d3-dsv/iconv-lite": ["iconv-lite@0.6.3", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "d3-sankey/d3-array": ["d3-array@2.12.1", "https://registry.npmmirror.com/d3-array/-/d3-array-2.12.1.tgz", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="], + + "d3-sankey/d3-shape": ["d3-shape@1.3.7", "https://registry.npmmirror.com/d3-shape/-/d3-shape-1.3.7.tgz", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + "dir-compare/minimatch": ["minimatch@3.1.5", "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], "dom-serializer/entities": ["entities@4.5.0", "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -3660,6 +3884,10 @@ "lightningcss/detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "mermaid/marked": ["marked@16.4.2", "https://registry.npmmirror.com/marked/-/marked-16.4.2.tgz", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + + "mermaid/uuid": ["uuid@11.1.1", "https://registry.npmmirror.com/uuid/-/uuid-11.1.1.tgz", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ=="], + "micromatch/picomatch": ["picomatch@2.3.2", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "minipass-flush/minipass": ["minipass@3.3.6", "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -3750,6 +3978,8 @@ "vite-plugin-solid/@babel/core": ["@babel/core@7.28.4", "https://registry.npmmirror.com/@babel/core/-/core-7.28.4.tgz", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="], + "vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "https://registry.npmmirror.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="], + "vue/@vue/shared": ["@vue/shared@3.5.24", "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.24.tgz", {}, "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -4070,6 +4300,10 @@ "cross-spawn/which/isexe": ["isexe@2.0.0", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "https://registry.npmmirror.com/layout-base/-/layout-base-2.0.1.tgz", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], + + "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "https://registry.npmmirror.com/d3-path/-/d3-path-1.0.9.tgz", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + "dir-compare/minimatch/brace-expansion": ["brace-expansion@1.1.15", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.15.tgz", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg=="], "electron-builder/yargs/cliui": ["cliui@8.0.1", "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], diff --git a/deploy/.env.knowledge-pro.example b/deploy/.env.knowledge-pro.example new file mode 100644 index 0000000000000000000000000000000000000000..d774adb4080d7311d7c8c20c4f98fcd5f8768196 --- /dev/null +++ b/deploy/.env.knowledge-pro.example @@ -0,0 +1,27 @@ +# Knowledge Pro tier — copy to deploy/.env.knowledge-pro (gitignored) or export in shell. +# Do NOT commit secrets. + +# --- Docker Compose (pro-up.sh) --- +# QDRANT_BIND=127.0.0.1:6333 +# ZOEKT_BIND=127.0.0.1:6070 +# ZOEKT_IMAGE=sourcegraph/zoekt-webserver:0.0.0-202407231348-f019367e3c + +# --- Knowledge CLI / MCP (after pro-up) --- +KB_TIER=pro +QDRANT_URL=http://127.0.0.1:6333 +ZOEKT_URL=http://127.0.0.1:6070 + +# Recommended for CI / first run without model download: +# KB_EMBED_PROVIDER=fallback + +# Optional OpenAI-compatible / Ollama embeddings (production Pro): +# KB_EMBED_PROVIDER=ollama +# KB_EMBED_MODEL=bge-m3 +# KB_EMBED_ENDPOINT=http://127.0.0.1:11434 + +# Data directory (default ~/.code-agent) +# CODE_AGENT_DATA_DIR=~/.code-agent + +# Control API (bun run dev / packages/knowledge control) +# KB_CONTROL_PORT=3090 +# KB_CONTROL_BIND=127.0.0.1 diff --git a/deploy/docker-compose.knowledge-pro.yml b/deploy/docker-compose.knowledge-pro.yml new file mode 100644 index 0000000000000000000000000000000000000000..81f38e13069a7cb385a8e96fa3eeec3e712ceb69 --- /dev/null +++ b/deploy/docker-compose.knowledge-pro.yml @@ -0,0 +1,38 @@ +# Knowledge Pro tier — Qdrant (+ optional Zoekt) +# Usage: script/knowledge/pro-up.sh +# See docs/KNOWLEDGE-PRO.md + +name: code-agent-knowledge-pro + +services: + qdrant: + image: qdrant/qdrant:v1.12.5 + restart: unless-stopped + ports: + - "${QDRANT_BIND:-127.0.0.1:6333}:6333" + volumes: + - qdrant-data:/qdrant/storage + healthcheck: + test: ["CMD-SHELL", "bash -c ':> /dev/tcp/127.0.0.1/6333' || exit 1"] + interval: 5s + timeout: 3s + retries: 12 + start_period: 10s + + zoekt: + image: ${ZOEKT_IMAGE:-sourcegraph/zoekt-webserver:0.0.0-202407231348-f019367e3c} + profiles: ["zoekt"] + restart: unless-stopped + command: + - -listen + - "0.0.0.0:6070" + - -index + - /data/index + ports: + - "${ZOEKT_BIND:-127.0.0.1:6070}:6070" + volumes: + - zoekt-index:/data/index + +volumes: + qdrant-data: + zoekt-index: diff --git a/docs/API-WIKI.md b/docs/API-WIKI.md index 40923127b7554d8edf6a9ff856b9c9389237696b..002d4afc6cbe1f160a9e48128218d9e9f8849bb7 100644 --- a/docs/API-WIKI.md +++ b/docs/API-WIKI.md @@ -35,6 +35,23 @@ M4 在索引阶段从 **symbols.json** 与 **catalog** 生成 `api-catalog.json` /repos//api-catalog.json ``` -条目类型:`function`、`class`、`interface`、`method`、`component` 等导出符号。 +条目类型:`function`、`class`、`interface`、`method`、`component` 等导出符号,以及 **`http-route`**(控制面 REST 路由,由静态抽取器从 `*-routes.ts` 解析)。 + +### HTTP 路由刷新(P2-02) + +从 `packages/knowledge/src/control/*-routes.ts` 抽取 `GET/POST … /mission/…` 等路由,合并进 `api-catalog.json`: + +```bash +# 预览抽取结果(不写 catalog) +bun run --cwd packages/knowledge kb api-routes-refresh . --id code-agent --dry-run + +# 写入 catalog(需已 kb index) +bun run --cwd packages/knowledge kb api-routes-refresh . --id code-agent + +# 冒烟 +bun run kb:api-routes:smoke +``` + +全量 `kb index` 也会自动合并 HTTP 路由。扩展 Java `@GetMapping` 等注解见 `http-route-extract.ts`。 > 路由前缀为 `/api-wiki`,避免与 OpenCode 后端 `/api/*` 冲突。 diff --git a/docs/C1-C24-ACCEPTANCE-CHECKLIST.md b/docs/C1-C24-ACCEPTANCE-CHECKLIST.md index 50d8e65ca96e69fdb7d3799857188e73ff4e9019..31edfe331c8bef641771ca24d53312efa2d5b86d 100644 --- a/docs/C1-C24-ACCEPTANCE-CHECKLIST.md +++ b/docs/C1-C24-ACCEPTANCE-CHECKLIST.md @@ -433,6 +433,8 @@ **推荐手验清单(Mission + UBML + Tasks 关键路径)**:见 [`HAND-VERIFICATION-MISSION-UBML-TASKS.md`](./HAND-VERIFICATION-MISSION-UBML-TASKS.md)(T/M/U 编号 + Phase 7 + AppGen Apps) +**可自动化子集(P3-06)**:见 [`HAND-VERIFICATION-AUTOMATION.md`](./HAND-VERIFICATION-AUTOMATION.md) — `bun run qa:hand-verification` + **逐步操作手册**:见 [`C1-C24-MANUAL-QA.md`](./C1-C24-MANUAL-QA.md)(37 项 QA-01–QA-37,8 个 Session) ```bash diff --git a/docs/HAND-VERIFICATION-AUTOMATION.md b/docs/HAND-VERIFICATION-AUTOMATION.md new file mode 100644 index 0000000000000000000000000000000000000000..42594f36894f23034a599d9063aa778efd5fd1c9 --- /dev/null +++ b/docs/HAND-VERIFICATION-AUTOMATION.md @@ -0,0 +1,75 @@ +# 手验清单自动化(P3-06) + +将 OpenSpec 归档 tasks 中**可离线自动化**的「手验」项收敛为冒烟脚本,降低回归成本。完整 UI 手验仍见 [`HAND-VERIFICATION-MISSION-UBML-TASKS.md`](./HAND-VERIFICATION-MISSION-UBML-TASKS.md)。 + +## 一键运行 + +```bash +# 默认:不依赖 LLM;dev 未起时跳过 web-shell 步骤 +bun run qa:hand-verification + +# dev 已起(bun run dev)时要求 web 步骤也通过 +bun run qa:hand-verification:strict + +# 只跑某一步(便于定位失败) +HAND_VERIFICATION_ONLY=verify-hints bun run qa:hand-verification +``` + +失败时会打印**步骤 id、archive 来源与子脚本输出**,便于对照原文档手验项。 + +## 步骤与 archive 映射 + +| id | 来源(archive / Phase) | 自动化内容 | 需要 dev | +|----|-------------------------|------------|----------| +| `entity-pipeline` | `2026-06-29-ubml-entity-pipeline` §6.3 | `kb_be_verify` / `kb_be_decompile` / `beEdit` FYBXD | 否 | +| `ubml-knowledge` | `2026-06-22-ubml-pagegen-m-a-knowledge-pack` §6.6 | `kb_ubml_list`、component、template、exemplar | 否 | +| `page-roundtrip` | P3-01 | entity/page compile → decompile 语义一致 | 否 | +| `raw-roundtrip` | P3-04 | `raw:` section round-trip | 否 | +| `verify-hints` | P3-03 | 校验失败中文 hint、无堆栈泄露 | 否 | +| `farris-alias` | P2-05 | FYBXD 别名召回评测 | 否 | +| `web-shell` | M5 layout §5.4(部分) | `GET /` + backend `/global/health` | 是 | + +## 单项命令 + +```bash +bun run kb:be:pipeline-smoke # entity pipeline(若已加 npm script) +bun packages/knowledge/script/be-pipeline-smoke.ts +bun run kb:ubml:knowledge-smoke +bun run ubml:roundtrip:smoke +bun run ubml:raw:smoke +bun run ubml:verify-hints:smoke +bun run kb:farris:alias-smoke +bun run qa:web-shell:smoke +``` + +## 环境变量 + +| 变量 | 说明 | +|------|------| +| `HAND_VERIFICATION_STRICT=1` | web-shell 步骤在 dev 未起时**失败**(非 skip) | +| `HAND_VERIFICATION_ONLY=id1,id2` | 只跑指定步骤 id | +| `UBML_*_SMOKE=0` | 跳过对应 ubml 子冒烟(与既有 roundtrip 一致) | +| `WEB_SHELL_SMOKE=0` | 跳过 web HTTP 检查 | +| `WEB_URL` / `OPENCODE_URL` | 覆盖前端/后端地址(默认 `:3000` / `:4096`) | + +## 前置条件 + +- **离线步骤**:`bun install` 后即可,**无需 API Key / LLM**。 +- **web-shell**:另开终端 `bun run dev`,再跑 `qa:hand-verification:strict`。 +- **Maven / Gitee / Playwright 全链路 UI**:仍属人工或专项脚本(见 [`C1-C24-MANUAL-QA.md`](./C1-C24-MANUAL-QA.md))。 + +## CI(可选) + +仓库根目录 `.github/workflows/hand-verification-nightly.yml` 在 nightly 跑离线步骤(不含 web-shell)。本地 PR 前推荐: + +```bash +bun run qa:hand-verification +bun run typecheck +``` + +## 扩展新手验自动化 + +1. 在 `openspec/changes/archive/.../tasks.md` 选手验项,确认可无 LLM、无 Gitee 重复跑。 +2. 在 `script/` 或 `packages/*/script/` 增加专项 smoke(单职责、exit 非 0 时打印步骤名)。 +3. 在 `script/qa/hand-verification-smoke.ts` 的 `steps` 数组登记 `id` + `archive` 引用。 +4. 更新本表。 diff --git a/docs/KNOWLEDGE-PRO.md b/docs/KNOWLEDGE-PRO.md new file mode 100644 index 0000000000000000000000000000000000000000..adb7e10f76c915c2c66aecca59636b625f685023 --- /dev/null +++ b/docs/KNOWLEDGE-PRO.md @@ -0,0 +1,166 @@ +# Knowledge Pro 档部署指南(P2-04) + +Pro 档在 Lite 基础上将**向量检索**迁至 **Qdrant**,词法检索优先 **Zoekt**(无索引或不可用时自动回退 ripgrep)。MCP 工具签名与 `repos.json` 数据模型与 Lite 完全一致,可从 Lite **重新索引**平滑升级,无需数据迁移脚本。 + +> 产品背景见 [05 · 知识服务与上下文工程](./product-design/05-knowledge-and-context.md)。Lite 快速入门见 [KNOWLEDGE.md](./KNOWLEDGE.md)。 + +## 1. 前置条件 + +| 项 | 要求 | +|----|------| +| Docker | Docker Desktop 或 engine(Compose v2) | +| Bun | 与仓库一致(`bun@1.3.14+`) | +| 磁盘 | Qdrant 卷约数百 MB(视索引规模);Zoekt 索引另计 | +| 内存 | 建议 ≥8 GB(Qdrant + 可选 Zoekt + 嵌入模型) | + +## 2. 一键启动(推荐) + +```bash +# 启动 Qdrant(Pro 向量库,必需) +bash script/knowledge/pro-up.sh + +# 可选:同时启动 Zoekt webserver(大仓词法加速) +bash script/knowledge/pro-up.sh --zoekt + +# 停止 +bash script/knowledge/pro-up.sh down + +# 查看状态 +bash script/knowledge/pro-up.sh status +``` + +Compose 文件:`deploy/docker-compose.knowledge-pro.yml`(独立于 dual-ide,仅知识 Pro 栈)。 + +### 环境变量模板 + +```bash +cp deploy/.env.knowledge-pro.example deploy/.env.knowledge-pro +# 编辑 deploy/.env.knowledge-pro(此文件已被 .gitignore 忽略,勿提交密钥) +``` + +`pro-up.sh` 会自动 `source deploy/.env.knowledge-pro`(若存在)。 + +| 变量 | 默认 | 说明 | +|------|------|------| +| `QDRANT_BIND` | `127.0.0.1:6333` | Qdrant 宿主机绑定 | +| `ZOEKT_BIND` | `127.0.0.1:6070` | Zoekt 绑定(`--zoekt` 时) | +| `ZOEKT_IMAGE` | sourcegraph/zoekt-webserver 镜像 | 可换自建镜像 | +| `KB_TIER` | `pro` | 知识服务档位 | +| `QDRANT_URL` | `http://127.0.0.1:6333` | CLI/MCP 连接 Qdrant | +| `ZOEKT_URL` | `http://127.0.0.1:6070` | CLI/MCP 连接 Zoekt | +| `KB_EMBED_PROVIDER` | 见 KNOWLEDGE.md | 冒烟可用 `fallback` | + +## 3. 端口与进程 + +| 服务 | 端口 | 说明 | +|------|------|------| +| Qdrant HTTP | `6333` | 向量存储 REST/gRPC | +| Zoekt webserver | `6070` | 可选,POST `/api/search` | +| KB 控制面 | `3090` | `bun run dev` 时由 knowledge control 提供 | +| OpenCode 后端 | `4096` | 与 Pro 栈独立 | +| Web UI | `3000` | 与 Pro 栈独立 | + +默认均绑定 **127.0.0.1**,勿将 Qdrant/Zoekt 暴露到公网。 + +## 4. Lite → Pro 迁移 + +1. **保持** `~/.code-agent/repos.json` 与 Lite 索引 artifact(`graph.json` / `symbols.json` 等)不变。 +2. 启动 Pro 栈:`bash script/knowledge/pro-up.sh`。 +3. 导出环境变量(或写入 `deploy/.env.knowledge-pro`): + + ```bash + export KB_TIER=pro + export QDRANT_URL=http://127.0.0.1:6333 + export ZOEKT_URL=http://127.0.0.1:6070 + ``` + +4. **重新索引**(向量写入 Qdrant;Lite 本地 SQLite 向量不再使用): + + ```bash + bun run --cwd packages/knowledge kb index /abs/path/to/repo --id myrepo --tier pro + ``` + +5. 验证: + + ```bash + bun run kb:pro:smoke + # 或 + bun run --cwd packages/knowledge kb locate "索引入口" --repo myrepo + bun run --cwd packages/knowledge kb status + ``` + +差异摘要: + +| 能力 | Lite | Pro | +|------|------|-----| +| 向量 | bun:sqlite 进程内 | Qdrant | +| 词法 | ripgrep | Zoekt 优先,失败/空结果回退 ripgrep | +| Docker | 不需要 | Qdrant 必需;Zoekt 可选 | +| MCP 工具 | 相同 | 相同 | + +## 5. 与 `bun run dev` 联动 + +`script/dev.sh` 会启动 KB 控制面(`:3090`),但**不会**自动拉起 Qdrant/Zoekt。推荐工作流: + +```bash +# 终端 1:Pro 基础设施 +bash script/knowledge/pro-up.sh + +# 终端 2:应用(后端 + Web + KB control) +export KB_TIER=pro QDRANT_URL=http://127.0.0.1:6333 ZOEKT_URL=http://127.0.0.1:6070 +bun run dev +``` + +在 Web UI **知识库**工作台添加仓库时选择 **Pro** 档位,控制面会使用相同环境变量索引到 Qdrant。 + +## 6. 冒烟与 CI + +```bash +# 需 Docker 已启动 Qdrant +bun run kb:pro:smoke + +# 跳过索引(已有 pro 索引 artifact) +KB_PRO_SKIP_INDEX=1 bun run kb:pro:smoke + +# 额外检查 Zoekt 可达性 +KB_PRO_CHECK_ZOEKT=1 bash script/knowledge/pro-up.sh --zoekt +KB_PRO_CHECK_ZOEKT=1 bun run kb:pro:smoke +``` + +根目录 npm scripts: + +- `kb:pro:up` — 同 `pro-up.sh` +- `kb:pro:down` — 停止栈 +- `kb:pro:smoke` — Qdrant 健康 + Pro 档 `kb locate` + +## 7. 故障排查 + +### Qdrant 起不来 / 端口占用 + +```bash +bash script/knowledge/pro-up.sh status +docker compose -f deploy/docker-compose.knowledge-pro.yml logs qdrant +lsof -i :6333 # macOS/Linux 查占用 +``` + +修改 `QDRANT_BIND=127.0.0.1:6334` 后同步改 `QDRANT_URL`。 + +### `kb locate` 无结果 + +- 确认已 `kb index --tier pro` 且 `kb status` 中对应 repo `ready: true`。 +- 冒烟可设 `KB_EMBED_PROVIDER=fallback`;生产建议 `local-onnx` 或 Ollama。 +- Zoekt 空索引时词法会回退 ripgrep,不影响基本检索。 + +### Pro 档仍显示 lite + +检查进程环境:`echo $KB_TIER`。MCP 客户端需在 `env` 中显式设置 `KB_TIER=pro` 与 `QDRANT_URL`。 + +### dual-ide Compose 与独立 Pro 栈 + +历史文档中的 `deploy/docker-compose.dual-ide.yml --profile pro` 仍可用(IDE Host 全家桶场景)。**本指南的一键脚本**使用 `deploy/docker-compose.knowledge-pro.yml`,仅知识 Pro 依赖,更轻量。 + +## 8. 安全 + +- 勿提交 `deploy/.env.knowledge-pro`、PAT 或 API Key。 +- 使用 `deploy/.env.knowledge-pro.example` 作为模板。 +- 提交前可运行 `bash script/kb/sanitize-secrets.sh`。 diff --git a/docs/KNOWLEDGE.md b/docs/KNOWLEDGE.md index 49a272a2331b9a6cffb8b7d9b280e5b43b8addb0..b6e07970d70669f2f17314d9a2398cc5e11d835a 100644 --- a/docs/KNOWLEDGE.md +++ b/docs/KNOWLEDGE.md @@ -24,18 +24,30 @@ bun run --cwd packages/knowledge kb locate "需求关键词" --repo myrepo ## 2. Pro 档(团队、多仓、规模化) -复用 dual-ide 的 Compose,启用 `pro` profile 拉起 Zoekt 与 Qdrant: +独立 Compose + 一键脚本(P2-04),详见 **[KNOWLEDGE-PRO.md](./KNOWLEDGE-PRO.md)**。 ```bash -# Zoekt 需提供 webserver 镜像(自建 sourcegraph/zoekt),Qdrant 用官方镜像 -ZOEKT_IMAGE= \ -docker compose -f deploy/docker-compose.dual-ide.yml --profile pro up -d qdrant zoekt - -# 以 Pro 档索引/检索 -KB_TIER=pro QDRANT_URL=http://127.0.0.1:6333 ZOEKT_URL=http://127.0.0.1:6070 \ - bun run --cwd packages/knowledge kb index /abs/path/to/repo --id myrepo --tier pro +# 启动 Qdrant(必需);可选 --zoekt +bash script/knowledge/pro-up.sh +# 或 +bun run kb:pro:up + +# 环境变量(可复制 deploy/.env.knowledge-pro.example → deploy/.env.knowledge-pro) +export KB_TIER=pro +export QDRANT_URL=http://127.0.0.1:6333 +export ZOEKT_URL=http://127.0.0.1:6070 +export KB_EMBED_PROVIDER=fallback # 冒烟;生产见 §4 + +# 索引与验证 +bun run --cwd packages/knowledge kb index /abs/path/to/repo --id myrepo --tier pro +bun run kb:pro:smoke + +# 停止 +bun run kb:pro:down ``` +仍可使用 dual-ide 全家桶:`docker compose -f deploy/docker-compose.dual-ide.yml --profile pro up -d qdrant zoekt`(见 KNOWLEDGE-PRO.md §7)。 + 两档数据模型一致,可从 Lite 平滑升级 Pro(重新索引即可,无迁移断层)。 ## 3. 客户端 MCP 配置 @@ -59,6 +71,26 @@ KB_TIER=pro QDRANT_URL=http://127.0.0.1:6333 ZOEKT_URL=http://127.0.0.1:6070 \ 工具:`kb_locate`、`kb_plan`、`kb_explain`、`kb_arch`、`kb_change_impact`、`kb_dependencies`、`kb_module_card`、`kb_list_repos`、`kb_status`。 +### DesignBase 架构卡导入(P2-01) + +从 `docs/product-design/` 等 Markdown 目录按 `##` 章节切分,写入知识库 `arch/designbase-*.md`,供 `kb_arch` 与向量检索使用。 + +```bash +# 仅导入 DesignBase(预览,不写文件) +bun run --cwd packages/knowledge kb designbase-import . --id code-agent --dry-run + +# 正式导入(默认扫描 docs/product-design/) +bun run --cwd packages/knowledge kb designbase-import . --id code-agent + +# 扩展扫描目录(逗号分隔) +bun run --cwd packages/knowledge kb designbase-import . --dirs docs/product-design,docs + +# 仓库根目录冒烟 +bun run kb:designbase:import-smoke +``` + +重复导入会按文件 hash 跳过未改 Markdown;`kb index` 全量索引时也会自动合并 DesignBase 卡。 + ## 4. 嵌入提供方 | 提供方 | 说明 | 关键变量 | @@ -100,3 +132,33 @@ bun run --cwd packages/knowledge eval:impact [impact-tasks.json] ``` 验收门槛:中文定位 ≥9/9、影响面 3/3(与 Python 试点等价或更优),需配置真实嵌入提供方并对目标仓库索引后运行。 + +### 检索评测集 + CI 门禁(P2-03) + +固定评测集 `packages/knowledge/eval/retrieval-tasks.json`(≥20 条中文 locate/plan/explain),默认阈值 **80%**。 + +```bash +# 本地(需已 kb index code-agent) +bun run kb:eval:retrieval + +# CI:fallback 嵌入 + 全量索引 + 评测 +bun run kb:eval:retrieval:ci + +# 仅报告不失败 +KB_EVAL_WARN_ONLY=1 bun run kb:eval:retrieval + +# 跳过索引(已有 artifact) +KB_EVAL_SKIP_INDEX=1 bun run kb:eval:retrieval:ci +``` + +追加用例:在 `retrieval-tasks.json` 增加 `{ id, mode, label, query, expectAny[] }`——`label` 为中文需求描述,`query` 为定位关键词(推荐导出符号名),`expectAny` 为命中路径子串。 + +Gitee Go / 其他 CI 可调用: + +```yaml +# 示例步骤 +- run: bun run kb:eval:retrieval:ci + env: + KB_EMBED_PROVIDER: fallback + KB_EVAL_THRESHOLD: "0.8" +``` diff --git a/docs/MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md b/docs/MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md new file mode 100644 index 0000000000000000000000000000000000000000..7c145a27d61077a00a5e42dfc3fa0b93d993a58f --- /dev/null +++ b/docs/MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md @@ -0,0 +1,211 @@ +# EntityGen MoF v0 验收清单 + +> 对照 [10 · MOF 元建模与实体优先生成](./product-design/10-mof-entity-modeling.md) §6 四阶段路线。 +> 归档日期:2026-07-04 · **验收结论:全部通过(2026-07-04 手验确认)** +> OpenSpec 归档:`openspec/changes/archive/2026-07-04-ubml-{entity-metamodel,entity-modeling-ops,model-right-panel,page-metamodel,model-tab-pages}/` + +**图例** + +| 符号 | 含义 | +|------|------| +| ✅ | 已实现,可用下方「自动化命令」验证 | +| 🔶 | 已实现,需本机浏览器 / LLM / 人工确认 | +| ⏭ | 文档明确 defer 或 non-goal,本期不验收 | + +--- + +## 0. 一键预检(建议最先跑) + +```bash +bun install + +# E1 + E4 元模型一致性 +bun test packages/ubml-codegen/test/entity-metamodel-consistency.test.ts +bun test packages/ubml-codegen/test/page-metamodel-consistency.test.ts + +# E2 建模操作 +bun test packages/ubml-codegen/test/entity-edit-operations.test.ts +bun packages/knowledge/script/be-modeling-ops-smoke.ts +bun packages/knowledge/eval/eval-entity-generation-core.ts + +# E3 + 页面模型页签 +bun test packages/extension/src/lib/model-preview-model.test.ts + +# 金样三道闸 +bun run ubml:entity:verify packages/extension/assets/ubml-entity/exemplars/fybxd.be.yaml +bun run ubml:verify packages/extension/assets/ubml/exemplars/fybxdj-card.yaml +``` + +| 检查项 | 预期(2026-07-04 执行) | +|--------|-------------------------| +| 元模型 + 操作 + 模型页签测试 | **25 pass**,0 fail | +| `be-modeling-ops-smoke` | `be modeling ops smoke ok` | +| `eval-entity-generation-core` | `taskCount: 10`,`goldenExemplarOk: true` | +| FYBXD entity verify | ok | +| FYBXDJ card verify | ok | + +--- + +## E1 · 元模型显式化(`ubml-entity-metamodel`) + +| # | 验收项 | 状态 | 自动化验证 | +|---|--------|------|------------| +| E1-01 | `ubml-entity.metamodel.yaml` 覆盖 FYBXD 全部建模元素 | ✅ | 文件存在 + consistency test「FYBXD maps to metamodel」 | +| E1-02 | `metamodel/README.md` 维护边界说明 | ✅ | 打开 `packages/extension/assets/ubml-entity/metamodel/README.md` | +| E1-03 | 元模型读取器 `entity/metamodel/{types,load,prompt}.ts` | ✅ | `entity-metamodel-consistency.test.ts` loader 场景 | +| E1-04 | datatype 与 `datatypes.index.json` 一致 | ✅ | consistency test datatype 场景 | +| E1-05 | catalog 约束指向真实文件 | ✅ | consistency test catalog 场景 | +| E1-06 | `entitygen.md` 含 `METAMODEL:constraints` 段 | ✅ | `grep METAMODEL packages/extension/assets/ubml-entity/prompts/entitygen.md` | +| E1-07 | 现有编译/校验行为不变 | ✅ | `bun run ubml:entity:verify …/fybxd.be.yaml` | + +**Living spec**:[`openspec/specs/ubml-entity-metamodel/spec.md`](../openspec/specs/ubml-entity-metamodel/spec.md) + +**⏭ 本期不做**:从元模型生成 Zod / `gsp-business-entity.schema.json` + +--- + +## E2 · 建模操作集(`ubml-entity-modeling-ops`) + +| # | 验收项 | 状态 | 自动化验证 | +|---|--------|------|------------| +| E2-01 | 9 类 typed 操作注册 | ✅ | `entity-edit-operations.test.ts` | +| E2-02 | legacy 五操作仍可用 | ✅ | 同上 + MCP `kb_be_edit` description | +| E2-03 | 失败返回 `{ ok, yaml, issues, hint, changedPaths }` | ✅ | association unknown ref / decimal precision 测试 | +| E2-04 | `kb_be_edit` MCP op 枚举扩展 | ✅ | `packages/knowledge/src/mcp/server.ts` | +| E2-05 | `be-modeling-ops-smoke.ts` 冒烟 | ✅ | `bun packages/knowledge/script/be-modeling-ops-smoke.ts` | +| E2-06 | 10 条中文评测任务集 | ✅ | `entity-generation-tasks.json`(be-01~be-10) | +| E2-07 | eval runner 基线 | ✅ | `eval-entity-generation-core.ts` 输出 metrics | +| E2-08 | `entitygen.md` 骨架 + 受控精修工作流 | ✅ | 读 prompt 第 3~4 步 | +| E2-09 | Agent 自动化 eval(10/10 verifyPassRate) | ⏭ | 基线 runner 已交付;Agent 自动化列为后续 Issue | +| E2-10 | 操作参数 JSON Schema 从元模型生成 | ⏭ | §4.3 远期;当前为 TS 手写校验 | + +**Living spec**:[`openspec/specs/ubml-entity-modeling-ops/spec.md`](../openspec/specs/ubml-entity-modeling-ops/spec.md) +**Agent 工作流 spec 更新**:[`openspec/specs/ubml-entity-agent/spec.md`](../openspec/specs/ubml-entity-agent/spec.md) + +--- + +## E3 · 模型页签(`ubml-model-right-panel`) + +| # | 验收项 | 状态 | 自动化验证 | +|---|--------|------|------------| +| E3-01 | 右侧栏 `model` 页签类型(单实例) | ✅ | `right-panel.tsx` + `right-panel-labels.ts` | +| E3-02 | `ubml-model-tab-store` + `ubml-model-bridge` | ✅ | 文件存在 | +| E3-03 | `ubml-model-panel.tsx` 左导航 + 图/文件切换 | ✅ | 文件存在 | +| E3-04 | `model-preview-model.test.ts`(FYBXD refs/children) | ✅ | `bun test …/model-preview-model.test.ts` | +| E3-05 | `previewEntityYaml` → `openUbmlModelTab` | ✅ | `ubml-entity-artifact-actions.ts` | +| E3-06 | 对话 auto-preview → 模型页签 | ✅ | M3-01 手验通过 | +| E3-07 | FYBXD ER:子实体 + 外部关联 | ✅ | M3-02 手验通过 | +| E3-08 | 关闭 model tab 清理 payload | ✅ | M3-03 手验通过 | +| E3-09 | 工程全量扫描 / manifest 导航 | ⏭ | E3 首版 defer | +| E3-10 | 多实体合并全景 ER | ⏭ | 首版 partial;单 tab 内多条目切换 | + +**Living spec**:[`openspec/specs/ubml-model-right-panel/spec.md`](../openspec/specs/ubml-model-right-panel/spec.md) + +--- + +## E4 · 页面元模型(`ubml-page-metamodel`) + +| # | 验收项 | 状态 | 自动化验证 | +|---|--------|------|------------| +| E4-01 | `ubml-page.metamodel.yaml` + README | ✅ | 文件存在 | +| E4-02 | `page/metamodel/{types,load,prompt}.ts` | ✅ | `page-metamodel-consistency.test.ts` | +| E4-03 | FYBXDJ card 可映射到元类 | ✅ | consistency test | +| E4-04 | `pagegen.md` 元模型约束段 | ✅ | `grep METAMODEL packages/extension/assets/ubml/prompts/pagegen.md` | +| E4-05 | `docs/UBML-EXTERNAL-REFERENCE.md` | ✅ | 文件存在 | +| E4-06 | 现有 page verify 不变 | ✅ | `bun run ubml:verify …/fybxdj-card.yaml` | +| E4-07 | crossref.ts 元模型驱动替换 | ⏭ | E4 non-goal | + +**Living spec**:[`openspec/specs/ubml-page-metamodel/spec.md`](../openspec/specs/ubml-page-metamodel/spec.md) + +--- + +## 扩展 · 模型页签页面导航(`ubml-model-tab-pages`) + +| # | 验收项 | 状态 | 自动化验证 | +|---|--------|------|------------| +| MTP-01 | `UbmlModelEntry` 支持 `kind: entity \| page` | ✅ | `ubml-model-tab-store.ts` | +| MTP-02 | 导航分组「实体 be/」「页面 pages/」 | ✅ | `model-preview-model.test.ts` page 场景 | +| MTP-03 | 页面结构视图 `PagePreviewContent` | ✅ | `page-preview-model.ts` + component | +| MTP-04 | `previewUbmlYaml` → `openUbmlModelTab` | ✅ | `ubml-artifact-actions.ts` | +| MTP-05 | 页面预览手验 | ✅ | M3-04 手验通过 | + +**Living spec**:[`openspec/specs/ubml-model-tab-pages/spec.md`](../openspec/specs/ubml-model-tab-pages/spec.md) + +--- + +## 手验步骤(✅ 已于 2026-07-04 全部通过) + +前置:`bun run dev`(后端 :4096 + 前端 :3000),已配置 LLM provider。 + +### M3-01 · 对话生成实体 → 模型页签 + +1. 打开 Agent Home,发送 `@ubml-entitygen` 或粘贴含 `entity:` YAML 的回复(或触发 FYBXD 范例生成)。 +2. 消息完成后应 **自动 preview**(`UbmlAutoPreviewWatcher`)。 +3. 右侧栏出现或激活 **「模型」** 页签;左侧导航有 **FYBXD**。 +4. **关系图** 可见 Mermaid ER;**文件** 视图可见 YAML。 + +### M3-02 · 工作台 FYBXD 预览 + +1. 工作台 → 实体 → 打开 `fybxd.be.yaml` → **预览**。 +2. 模型页签导航:**FYBXD**;ER 含 **FYBXDMX**、**FXBXDFJ** 包含边。 +3. 外部关联节点/摘要含 **GspUser**、**SysOrg**、**Projects**。 + +### M3-03 · 关闭页签清理 + +1. 关闭「模型」页签。 +2. 重新打开 model tab → 空状态提示;**entity-preview** 页签不受影响。 + +### M3-04 · 页面进入模型页签 + +1. 工作台或对话预览 **fybxdj-card.yaml**(card 页)。 +2. 模型导航 **页面 pages/** 下出现 card 条目。 +3. **结构** 视图显示 title、entity 绑定、字段绑定表。 + +### M3-05 · 受控精修(可选,需 knowledge 服务) + +1. 启动 knowledge MCP / `bun run dev` 全栈。 +2. 对空实体 YAML 依次调用 `kb_be_edit`:`addScalarField` → `addAssociationField`(GspUser)→ `addUdtField`。 +3. 每步 `ok: true` 或失败时 `issues[].hint` 可定位参数。 + +--- + +## §6.7 里程碑完成度 + +| 里程碑条件 | 状态 | +|------------|------| +| FYBXD 元模型解释 + 一致性测试 | ✅ | +| ≥10 条中文 eval 任务 | ✅ | +| eval 输出通过率 / 修复轮次指标 | ⏭ | 基线 runner 已交付;Agent 自动化列为后续 | +| 生成/预览后模型页签可见 | ✅ | M3 手验通过 | +| OpenSpec 归档 + living spec | ✅ 2026-07-04 | +| `06-roadmap.md` 同步 MoF v0 | ⏭ 可选后续 PR | +| CI 强制元模型测试 | ⏭ 可选后续 PR | + +--- + +## 归档记录 + +| 变更 | 归档路径 | +|------|----------| +| E1 `ubml-entity-metamodel` | `openspec/changes/archive/2026-07-04-ubml-entity-metamodel/` | +| E2 `ubml-entity-modeling-ops` | `openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/` | +| E3 `ubml-model-right-panel` | `openspec/changes/archive/2026-07-04-ubml-model-right-panel/` | +| E4 `ubml-page-metamodel` | `openspec/changes/archive/2026-07-04-ubml-page-metamodel/` | +| 页面模型页签 | `openspec/changes/archive/2026-07-04-ubml-model-tab-pages/` | + +**同步至 main specs**: + +- `openspec/specs/ubml-entity-metamodel/spec.md`(新建) +- `openspec/specs/ubml-entity-modeling-ops/spec.md`(新建) +- `openspec/specs/ubml-model-right-panel/spec.md`(新建) +- `openspec/specs/ubml-page-metamodel/spec.md`(新建) +- `openspec/specs/ubml-model-tab-pages/spec.md`(新建) +- `openspec/specs/ubml-entity-agent/spec.md`(更新:骨架+受控精修工作流) + +--- + +## 相关文档 + +- [10-mof-entity-modeling.md](./product-design/10-mof-entity-modeling.md) +- [UBML-EXTERNAL-REFERENCE.md](./UBML-EXTERNAL-REFERENCE.md) +- [UBML-ENTITY-VERIFY.md](./UBML-ENTITY-VERIFY.md) diff --git a/docs/PHASE-1-3-ACCEPTANCE-CHECKLIST.md b/docs/PHASE-1-3-ACCEPTANCE-CHECKLIST.md new file mode 100644 index 0000000000000000000000000000000000000000..7eb5371d5639793328f55d42382955b56f47b284 --- /dev/null +++ b/docs/PHASE-1-3-ACCEPTANCE-CHECKLIST.md @@ -0,0 +1,125 @@ +# Phase 1~3 验收清单(P1 / P2 / P3) + +> 对照 [09 路线图 Issue 清单](./product-design/09-roadmap-community-issues.md) 与 [06 实现路线图](./product-design/06-roadmap.md)。 +> 生成日期:2026-07-04 +> **结论**:19 项任务**代码与自动化冒烟均已交付**;下表标 🔶 的 4 项需你在本机按「手验步骤」确认(依赖 Docker / JDK+Maven / 浏览器 + LLM)。 + +**图例** + +| 符号 | 含义 | +|------|------| +| ✅ | 已实现,可用下方「自动化命令」验证 | +| 🔶 | 已实现,自动化未覆盖或需特定环境,请做「手验步骤」 | +| ⏭ | 明确标注为 stretch / 可选,未纳入本期 | + +--- + +## 0. 一键预检(建议最先跑) + +```bash +bun install +bun run typecheck # 注意:desktop 包可能有既有报错,ubml-codegen/extension 应无新增 +bun run --cwd packages/ubml-codegen test +bun run qa:hand-verification # P3-06:6 步离线 + 可选 web-shell(dev 未起时 web 可 skip) +bun run ubml:app:generate:golden-smoke +``` + +| 检查项 | 预期 | +|--------|------| +| `ubml-codegen test` | 147 tests,0 fail(3 skip 为 javagen tier B / maven 环境) | +| `qa:hand-verification` | 7/7 pass 或 6 pass + web-shell skip | +| `ubml:app:generate:golden-smoke` | `Golden E2E OK` | + +--- + +## Phase 1 — AppGen 端到端(P1-01~P1-07) + +| 编号 | 任务 | 状态 | 自动化验证 | 手验步骤(若 🔶) | +|------|------|------|------------|-------------------| +| **P1-01** | 编排器批量 pagegen | ✅ | `bun run --cwd packages/ubml-codegen test test/app-generate.test.ts`
`bun run ubml:app:generate -- packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml --out /tmp/fybxd-out` → 检查 `/tmp/fybxd-out/pages/*.frm.json` ≥2 | 🔶 **Apps 工作台**:`bun run dev` → 工作台 Apps → Generate → Preview 可见 page stages | +| **P1-02** | Agent 自动修复回环 | ✅ | `bun test packages/extension/src/lib/ubml-appgen-prompt.test.ts`
`bun test packages/ubml-codegen/test/format-app-issues.test.ts` | 🔶 改坏 `derive.from` → Generate 失败 → **Fix with Agent** → prompt 含 hints 表 | +| **P1-03** | MAVEN 编译硬闸 | ✅ | `bun test packages/ubml-codegen/test/maven-gate.test.ts`
`UBML_MAVEN_SMOKE=0 bun run ubml:app:generate:maven-smoke`(无 JDK 时 skip) | 🔶 本机有 **JDK 17+ + Maven**:`bun run ubml:app:generate:maven-smoke --strict` exit 0 | +| **P1-04** | 黄金样本 E2E | ✅ | `bun run ubml:app:generate:golden-smoke`
`bun test packages/ubml-codegen/test/app-golden-e2e.test.ts` | — | +| **P1-05** | 应用级反编译 | ✅ | `bun run ubml:app:decompile:roundtrip-smoke`
`bun test packages/ubml-codegen/test/app-decompile.test.ts` | — | +| **P1-06** | Javagen 档位 B | ✅ | `bun test packages/ubml-codegen/test/javagen-tier-b.test.ts`
`bun run ubml:javagen:tier-b-smoke` | — | +| **P1-07** | webcmp/webcmd 逻辑 | ✅ | `bun run ubml:weblogic:smoke`
`bun test packages/ubml-codegen/test/weblogic-compile.test.ts` | — | + +**P1 相关文档**:[`UBML-APPGEN.md`](./UBML-APPGEN.md)、[`UBML-JAVAGEN.md`](./UBML-JAVAGEN.md) + +**P1 可选未做**:`kb_app_verify` MCP tool(P1-02 stretch 4.1~4.2)— 标记 ⏭ + +--- + +## Phase 2 — 知识库增强(P2-01~P2-06) + +| 编号 | 任务 | 状态 | 自动化验证 | 手验步骤(若 🔶) | +|------|------|------|------------|-------------------| +| **P2-01** | DesignBase 架构卡导入 | ✅ | `bun run kb:designbase:import-smoke`
`bun test packages/knowledge/src/control/designbase-import.test.ts` | — | +| **P2-02** | API Wiki 路由抽取 | ✅ | `bun run kb:api-routes:smoke`
见 [`API-WIKI.md`](./API-WIKI.md) | 可选:`bun run dev` → Context → API Wiki 浏览 `http-route` 条目 | +| **P2-03** | 检索评测 + CI 门禁 | ✅ | `bun run kb:eval:retrieval:ci`(需本地索引;无索引时见脚本提示)
`bun test packages/knowledge/src/retrieval/eval-retrieval-core.test.ts` | 首次需 `kb index` 或按 KNOWLEDGE.md 建索引 | +| **P2-04** | Pro 档一键部署 | ✅ | 脚本与文档:`docs/KNOWLEDGE-PRO.md`、`script/knowledge/pro-up.sh` | 🔶 **需 Docker**:`bun run kb:pro:up` → `bun run kb:pro:smoke` | +| **P2-05** | Farris 别名扩充 | ✅ | `bun run kb:farris:alias-smoke`(15/15) | — | +| **P2-06** | DSL exemplar + intent | ✅ | `bun run ubml:exemplars:smoke` | 各 `packages/extension/assets/ubml*/exemplars/intent.md` 人工 spot-check | + +**P2 相关文档**:[`KNOWLEDGE.md`](./KNOWLEDGE.md)、[`KNOWLEDGE-PRO.md`](./KNOWLEDGE-PRO.md) + +--- + +## Phase 3 — 编译器质量与体验(P3-01~P3-06) + +| 编号 | 任务 | 状态 | 自动化验证 | 手验步骤(若 🔶) | +|------|------|------|------------|-------------------| +| **P3-01** | round-trip 回归 | ✅ | `bun run ubml:roundtrip:smoke`
`bun test packages/ubml-codegen/test/roundtrip-regression.test.ts` | — | +| **P3-02** | derive 类型边界 | ✅ | `bun test packages/ubml-codegen/test/derive-type-boundary.test.ts` | — | +| **P3-03** | 校验 hints 友好化 | ✅ | `bun run ubml:verify-hints:smoke`
`bun test packages/ubml-codegen/test/verify-hints.test.ts` | 可选:`bun run dev` → 故意 verify 失败 → Artifact 卡见中文 hint | +| **P3-04** | `raw:` 逃逸文档与测试 | ✅ | `bun run ubml:raw:smoke`
见 [`UBML-RAW-ESCAPE.md`](./UBML-RAW-ESCAPE.md) | — | +| **P3-05** | Agent Home 消息时间线 | ✅ | `bun test packages/extension/src/pages/agent-home/home-session-target.test.ts` | 🔶 `bun run dev` → `/` 选项目与会话 → 中央见历史消息;点「在完整会话中继续」进 session 路由 | +| **P3-06** | 手验清单自动化 | ✅ | `bun run qa:hand-verification`
见 [`HAND-VERIFICATION-AUTOMATION.md`](./HAND-VERIFICATION-AUTOMATION.md) | strict 模式:`bun run qa:hand-verification:strict`(需 dev) | + +**P3 相关文档**:[`UBML-ROUNDTRIP.md`](./UBML-ROUNDTRIP.md)、[`UBML-RAW-ESCAPE.md`](./UBML-RAW-ESCAPE.md) + +--- + +## 汇总:仍需你本机确认的 4 项(🔶) + +| # | 任务 | 前置 | 操作 | +|---|------|------|------| +| 1 | P1-01 Apps Preview | `bun run dev` + LLM 可选 | 工作台 Apps → Generate → Preview 见 page stages | +| 2 | P1-02 Fix with Agent | `bun run dev` + LLM | 改坏 app YAML → Generate fail → Fix with Agent → hints 在 prompt | +| 3 | P1-03 Maven 硬闸 | JDK + Maven | `bun run ubml:app:generate:maven-smoke --strict` | +| 4 | P2-04 Pro 部署 | Docker | `bun run kb:pro:up` && `bun run kb:pro:smoke` | + +其余 **15/19** 项可通过上表「自动化验证」列命令在无 LLM、无 Docker 条件下验收。 + +--- + +## OpenSpec 归档 + +**状态**:✅ 2026-07-04 已全部归档并同步 living specs。 + +| 变更 | 归档路径 | Main spec | +|------|----------|-----------| +| `agent-home-message-timeline` | `archive/2026-07-04-agent-home-message-timeline/` | `openspec/specs/agent-home/spec.md` | +| `hand-verification-automation` | `archive/2026-07-04-hand-verification-automation/` | `openspec/specs/qa-hand-verification/spec.md`(新建) | +| `knowledge-api-wiki-extract` | `archive/2026-07-04-knowledge-api-wiki-extract/` | `openspec/specs/api-wiki/spec.md` | +| `knowledge-designbase-import` | `archive/2026-07-04-knowledge-designbase-import/` | `openspec/specs/knowledge-service/spec.md` | +| `knowledge-farris-aliases` | `archive/2026-07-04-knowledge-farris-aliases/` | 同上 | +| `knowledge-pro-deploy` | `archive/2026-07-04-knowledge-pro-deploy/` | 同上 | +| `knowledge-retrieval-eval` | `archive/2026-07-04-knowledge-retrieval-eval/` | 同上 | +| `ubml-exemplars-intent` | `archive/2026-07-04-ubml-exemplars-intent/` | `openspec/specs/ubml-appgen/spec.md`(新建) | +| `ubml-roundtrip-regression` | `archive/2026-07-04-ubml-roundtrip-regression/` | `openspec/specs/ubml-entity-decompiler/spec.md` | +| `ubml-derive-type-boundary` | `archive/2026-07-04-ubml-derive-type-boundary/` | `openspec/specs/ubml-app-derive/spec.md` | +| `ubml-verify-hints` | `archive/2026-07-04-ubml-verify-hints/` | `openspec/specs/ubml-page-verify/spec.md`(新建) | +| `ubml-page-raw-escape` | `archive/2026-07-04-ubml-page-raw-escape/` | `openspec/specs/ubml-dsl-compiler/spec.md` | + +P1 相关 AppGen 变更已于 2026-07-03 归档(见 `archive/2026-07-03-ubml-appgen-*` 等)。 + +--- + +## 验收签字区(手验完成后填写) + +| Phase | 自动化预检 | 手验 🔶 | 验收人 | 日期 | +|-------|-----------|---------|--------|------| +| P1 | ☐ | ☐ | | | +| P2 | ☐ | ☐ | | | +| P3 | ☐ | ☐ | | | diff --git a/docs/UBML-APP-DERIVE.md b/docs/UBML-APP-DERIVE.md index e1c157e95848ebb436f097fb9ca0e48e14f676b4..21a1134561931cc94bc0b3ef2d1620178e26e7a4 100644 --- a/docs/UBML-APP-DERIVE.md +++ b/docs/UBML-APP-DERIVE.md @@ -38,14 +38,44 @@ ubml-derive [options] ### BE 类型 → DBO 列 -| MDataType | DBO | -|-----------|-----| -| String | Varchar(`IsMultiLanguage` → NVarchar) | -| Text | Clob | -| Integer | Int | -| Decimal | Decimal | -| DateTime / Date | TimeStamp | -| Boolean | Bit | +| MDataType | DBO | 说明 | +|-----------|-----|------| +| String | Varchar | `IsMultiLanguage` → NVarchar | +| Text | Clob | | +| Integer | Int | | +| Decimal | Decimal | `Precision`/`Scale`,默认 18/2 | +| DateTime / Date | TimeStamp | | +| Boolean | Bit | | +| Binary | VarBinary | `Length` | +| Enum(`ObjectType=Enum`) | Varchar | BE 仍为 `MDataType: String`;VO 保留 `ContainEnumValues` | + +源码矩阵:`packages/ubml-codegen/src/derive/type-matrix.ts`(`BE_TO_DBO_TYPE_MATRIX`)。 + +### Nullable 规则(DBO) + +| 条件 | `isNullable` | +|------|----------------| +| 主键列(`ColumnGenerateID.ElementID`) | `false` | +| `IsRequire: true` | `false` | +| 子表 `ParentID` | 恒为 `true` | +| 其他 | `true` | + +### 边界回归(P3-02) + +固定 fixture `test/fixtures/derive-boundary.be.json` 覆盖 DateTime / Decimal / Enum / Nullable: + +```bash +bun run --cwd packages/ubml-codegen test test/derive-type-boundary.test.ts +bun run --cwd packages/ubml-codegen test +``` + +### 已知限制 + +见 `DERIVE_KNOWN_LIMITATIONS`(`type-matrix.ts`): + +- Association 在 DBO 按 Varchar 列派生,关联语义保留在 VO `Mapping` +- UDT 子元素仅在 VO 展开,不单独 DBO 表 +- Binary / 复杂 ValidationTrigger 未纳入边界 fixture ## API diff --git a/docs/UBML-APPGEN.md b/docs/UBML-APPGEN.md index d6ed005c2fd6af88f225c5a963f681b629e6fe59..332d291be45785a6bdac67fbe84bbf95c9715ffd 100644 --- a/docs/UBML-APPGEN.md +++ b/docs/UBML-APPGEN.md @@ -1,6 +1,6 @@ # UBML AppGen 编排(app: 清单) -`app:` YAML 将 entity / derive / query / component / pageflow / javagen 子管线编排为一次端到端生成。 +`app:` YAML 将 entity / derive / query / component / **page** / pageflow / javagen 子管线编排为一次端到端生成。 ## CLI @@ -8,16 +8,56 @@ bun run ubml:app:generate -- \ packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml \ --out ./out/fybxd-app + +# 可选:Java 构件 MAVEN 编译硬闸(crossref 通过后执行) +bun run ubml:app:generate -- \ + packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml \ + --out ./out/fybxd-app \ + --maven ``` -输出目录含 `manifest.json` 与子目录 `be/`、`derive/`、`query/`、`components/`、`pageflow/`、`java/`。 +输出目录含 `manifest.json` 与子目录 `be/`、`derive/`、`query/`、`components/`、`pages/`、`pageflow/`、`java/`;启用 `--maven` 时另含 `maven-gate/`(最小 pom + 编译用 Java 布局)。 + +### CLI 参数 + +| 参数 | 说明 | +|------|------| +| `--out dir` | 写出 artifact 树 | +| `--maven` | crossref 通过后跑 `mvn compile` | +| `--skip-maven` | 显式跳过 Maven(即使其他 env 开启) | +| `--maven-home path` | 指定 Maven 安装目录 | +| `--java-version 17` | 编译目标 JDK(默认 17) | + +### Exit codes + +| Code | 含义 | +|------|------| +| 0 | DSL/crossref(及 Maven,若启用)均通过 | +| 1 | 校验或 Maven 编译失败 | +| 2 | Maven 不可用(未安装 / timeout) | + +环境变量 `UBML_MAVEN_SMOKE=0` 等同跳过 Maven gate(CI 无 Maven 时使用)。 + +冒烟: + +```bash +bun run ubml:app:generate:maven-smoke # 无 mvn 时 skip exit 0 +bun run ubml:app:generate:maven-smoke --strict # 无 mvn 时 exit 2 + +# 黄金样本 E2E 语义回归(FYBXD vs ROBXDJChenshj fixtures) +bun run ubml:app:generate:golden-smoke +UBML_GOLDEN_E2E=0 bun run ubml:app:generate:golden-smoke # CI 跳过 +``` + +单测内亦包含 `packages/ubml-codegen/test/app-golden-e2e.test.ts`。 ## DSL 形状 见 `packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml`。 - `sources.*` 路径相对 `packages/extension/assets/` 或 repo 根 -- 省略 `sources` 时对 FYBXD 使用内置默认 exemplar 路径 +- `pages[]` 列出页面 code;`sources.pages` 映射到 pagegen YAML +- 省略 `pages[]` 且配置 `sources.frmDir` 时,pageflow 仍可使用静态 `.frm.json` fixture(legacy fallback) ## 程序 API @@ -33,26 +73,80 @@ const result = await generateAppFromFile("fybxd-app.yaml", { writeOut: "./out" } 2. derive(`derive.from` + `derive.targets`) 3. query compile(各 `queries[]`,BE 来自 derive.from) 4. component compile -5. pageflow compile(可选 `frmDir`) -6. javagen(`logic` 含 writeback 时生成回写类) +5. **page compile**(各 `pages[]`,pagegen) +6. pageflow compile(消费已编译 frm 索引,或 legacy `frmDir`) +7. javagen(`logic` 含 writeback 时生成回写类) +8. **maven**(可选 `--maven`:crossref 通过后 `mvn compile`) ## 校验 -各子管线 validation + `crossref` stage(BE 引用闭合、pageflow code 对齐、writeback 逻辑一致)。 +各子管线 validation + `crossref` stage(BE 引用闭合、pages ↔ pageflow frm 对齐、writeback 逻辑一致)。 ## 关联 - 总方案:`docs/UBML-APPGEN-DESIGN.md` §5.5、A-5 - 子管线:各 `UBML-*-COMPILE.md` / `UBML-APP-DERIVE.md` / `UBML-JAVAGEN.md` +- Phase 1:`docs/product-design/09-roadmap-community-issues.md` P1-01 / P1-02 / P1-03 + +## 应用级反编译(P1-05) + +从参考工程 JSON 布局扫描并反编译为 `app.yaml` + 子 DSL: + +```bash +# 输入:含 be/ query/ components/ pageflow/ pages/ 的 JSON 工程目录 +bun run ubml:app:decompile -- /path/to/project [--out ./decompiled] + +# 冒烟:generate → JSON 工程 → decompile → generate → golden +bun run ubml:app:decompile:roundtrip-smoke +UBML_APP_DECOMPILE_SMOKE=0 bun run ubml:app:decompile:roundtrip-smoke # CI 跳过 +``` + +**扫描规则**:递归查找 `*.be.json`、`*.qo.json`、`*.ccmp.json`、`*.wscmp.json`、`*.pf.json`、`*.frm.json`。 + +**反编译覆盖**: + +| 构件 | 状态 | +|------|------| +| BE | ✅ 复用 entity decompiler | +| Query | ✅ datasource 模式(FieldMap + ExternalParameters) | +| Component | ✅ event / writeback | +| Pageflow | ✅ pages 路由 | +| Page | ✅ 复用 page decompiler(按 code 推断 type) | +| Java → logic | ⚠️ 从 BE rules + writeback 构件推断 | + +**限制**:参考工程独有字段(如 `SecID`/`SecLevel`)与 exemplar YAML 差集在 round-trip golden 中按 P1-04 约定忽略;完整 ROBXDJChenshj 9 页 UI 反编译不在首期范围。 + +## Agent 修复回环(P1-02) + +`@ubml-appgen` 与工作台 Apps 支持 **≤3 轮**结构化校验修复: + +1. **Generate** 后若 `validation.valid === false`,可点击 **Fix with Agent** +2. Extension 将 `validation.issues`(含 `hint`)格式化为 repair prompt 派发给 Agent +3. **Ask Agent** 若上次 Generate 失败,会自动附带 issues 摘要 +4. 轮次上限 3;超限后 Fix with Agent 禁用并提示手动编辑 + +格式化 API: + +```ts +import { formatAppIssuesForAgent } from "@code-agent/ubml-codegen/app-generate" +``` + +## MAVEN 编译硬闸(P1-03) + +`--maven` 在 javagen 产物写出后,于 `{out}/maven-gate/` 组装最小 Maven 工程并执行 `mvn -q compile`。 + +- 仅当 **crossref 已通过** 时运行;否则 JSON 中 `maven.skipped=true` 且 `skipReason` 说明原因 +- 编译失败时 `stages[]` 含 `maven` stage,issues 含文件路径与行号(可解析时) +- 浏览器 Generate **不**跑 Maven;Agent 勿声称 Maven 成功除非 CLI 显式 `--maven` ## 首期不做 - 自然语言 → 全栈 DSL 自动生成 -- MAVEN 硬闸 +- GS Cloud 平台 JAR 依赖 / `mvn test` / 部署链 ## 工作台 -UBML 侧栏 → **Pages | Entities | Apps**。Apps 页编辑 `app:` YAML,**Generate** 在浏览器内编排(FYBXD bundled sources),Preview 展示 stages 与 artifacts 总览;**Ask Agent** 派发 `@ubml-appgen`;**Writeback** 将产物批量写入当前项目(默认 `ubml-generated/{bizobject}/`)。 +UBML 侧栏 → **Pages | Entities | Apps**。Apps 页编辑 `app:` YAML,**Generate** 在浏览器内编排(FYBXD bundled sources),Preview 展示 stages 与 artifacts 总览;**Ask Agent** 派发 `@ubml-appgen`;**Fix with Agent** 在 Generate 失败时回灌结构化 hints(≤3 轮);**Writeback** 将产物批量写入当前项目(默认 `ubml-generated/{bizobject}/`)。 ### 写回 diff --git a/docs/UBML-ENTITY-VERIFY.md b/docs/UBML-ENTITY-VERIFY.md index 06d2e5484704c39b1bfa9e2a928d5e79b79ba6fb..fb2d2ffae50a8aafeeb253a1e37dd8913cb21cc7 100644 --- a/docs/UBML-ENTITY-VERIFY.md +++ b/docs/UBML-ENTITY-VERIFY.md @@ -33,7 +33,17 @@ bun run --cwd packages/ubml-codegen entity-verify path/to/model.be.yaml } ``` -失败时含 `stage`、`issues[]`(可选 `path`、`hints[]`)、`attemptHint`。 +失败时含 `stage`、`issues[]`(`path`、`hints[]` 建议值列表、**`hint` 中文修复指引**、`suggestion` 首选建议)、`attemptHint`。 + +常见错误 hint 由 `packages/ubml-codegen/src/validation/verify-hints.ts` 生成,Agent 可消费 `formatVerifyIssuesForAgent(issues)` 输出 Markdown 表格。 + +| 错误类型 | hint 示例 | +|----------|-----------| +| 未知 editor | 可改用 input-group、date-picker… | +| 未知 association.ref | 检查 BE catalog / Refs | +| 未知 field type | 列出可用 type | +| page.entity 缺失 | 设置为 FYBXD 等 entity.code | +| YAML 语法 | 检查缩进与引号 | ## 反编译 diff --git a/docs/UBML-EXTERNAL-REFERENCE.md b/docs/UBML-EXTERNAL-REFERENCE.md new file mode 100644 index 0000000000000000000000000000000000000000..4c1d3ca5cfb0164bc5fb8123043bc7dae7a762a4 --- /dev/null +++ b/docs/UBML-EXTERNAL-REFERENCE.md @@ -0,0 +1,69 @@ +# UBML External Reference(交叉引用) + +> 说明 MOF 元模型中 `reference.external: true` 与 AppGen / page verify 中手写 crossref 校验的关系。E4 仅文档化与元模型声明,**不替换** `packages/ubml-codegen/src/app/validate/crossref.ts`。 + +## 1. 概念 + +**External 引用**:M2 元模型中指向**另一元模型或 catalog 数据**的引用,不包含于当前文档树内(`containment: false`,`external: true`)。 + +| 引用点 | 源元模型 | 目标 | 运行时校验(现状) | +|--------|----------|------|-------------------| +| `Page.entity` | ubml-page | BusinessEntity code | page verify / compile context | +| `FieldBinding.bind` | ubml-page | Entity 字段 code | page verify(entity 上下文) | +| `AssociationField.ref` | ubml-entity | referenceable-be catalog | entity DSL semantic validation | +| `Pageflow.pages.*` | ubml-app | Page frm code | AppGen `crossref.ts` | +| `Component.BeCode` | ubml-component | Entity code | AppGen `crossref.ts` | + +## 2. 元模型中的声明 + +### 页面域(`ubml-page.metamodel.yaml`) + +```yaml +# Page.entity → 外部业务实体 +- { name: entity, type: BusinessEntity, containment: false, external: true } + +# FieldBinding.bind → 实体字段 +- { name: bind, type: EntityField, containment: false, external: true } +constraints: + - name: bind-exists-on-entity + check: "page(self).entity.allFields().has(self.bind)" + - name: editor-in-catalog + catalog: components +``` + +### 实体域(`ubml-entity.metamodel.yaml`) + +关联、UDT 等字段的 catalog 约束已在实体元模型中声明(`ref-in-catalog`、`udt-in-catalog`)。 + +## 3. 与 AppGen crossref 的对应 + +`validateCrossref()`(`app/validate/crossref.ts`)当前覆盖: + +- `app.entities[]` ↔ 编译产物 BE 模型 +- `app.pages[]` ↔ 页面编译结果 +- `app.pageflow` ↔ pageflow 中引用的 frm +- `app.components` ↔ component BE 引用 +- `app.weblogic` ↔ webcmd / page 源 + +**演进路径(远期,非 E4):** + +1. 从各域元模型导出 external 引用清单(source class.attr → target type) +2. 生成 crossref 规则表或共享 TS 校验函数 +3. AppGen 与单页 verify 共用「解析 external 引用是否可落地」内核 +4. 新增模型种类时只扩展元模型 + 规则表,避免在 `crossref.ts` 堆 if 分支 + +## 4. E4 边界 + +| 项 | E4 | +|----|-----| +| 元模型 YAML 声明 external + constraint | ✓ | +| 一致性测试对齐 catalog / 金样 | ✓ | +| 替换 crossref.ts | ✗ | +| 模型页签展示 page 导航 | ✗(后续变更) | + +## 5. 相关文件 + +- `packages/extension/assets/ubml/metamodel/ubml-page.metamodel.yaml` +- `packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml` +- `packages/ubml-codegen/src/app/validate/crossref.ts` +- `docs/product-design/10-mof-entity-modeling.md` §3.4、§6.5 diff --git a/docs/UBML-JAVAGEN.md b/docs/UBML-JAVAGEN.md index ef2cf3357fe315a6f28e10e1cf52fd33980ac1fc..eae18712d542844f5fc97a53435e8ca694a303a9 100644 --- a/docs/UBML-JAVAGEN.md +++ b/docs/UBML-JAVAGEN.md @@ -43,6 +43,35 @@ const { files } = compileJavagen(beModel) 默认跳过 Maven 编译(`UBML_MAVEN_SMOKE=0`)。档位 A 首期产出可编译骨架 + 注释契约,后续 A-5 编排可接入端到端验收。 +## 档位 B(受约束 API 白名单) + +非模板逻辑通过 **tier B logic spec YAML** 生成,仅允许白名单 API(`BeBuffer` / `LogicSession` 等): + +```bash +bun run ubml:javagen -- \ + packages/ubml-codegen/test/fixtures/fybxd.be.json \ + --tier-b packages/ubml-codegen/test/fixtures/fybxd-check-quota.logic-b.yaml \ + --out ./out/java-tier-b +``` + +- 白名单定义:`packages/ubml-codegen/src/javagen/whitelist/default-api-whitelist.yaml` +- 生成物含 platform stub 类 + 可选 JUnit 5 测试桩(`*Test.java`) +- 白名单外 API 调用在生成后校验阶段报错(`whitelistViolations`) + +冒烟(需 JDK + Maven): + +```bash +bun run ubml:javagen:tier-b-smoke +UBML_JAVAGEN_TIER_B_SMOKE=0 bun run ubml:javagen:tier-b-smoke +``` + +### 档位 A / B 边界 + +| 档位 | 输入 | 输出 | +|------|------|------| +| A | BE rules 中已知模板 code | 空类 + 注释契约 | +| B | `logic-b.yaml` spec + 白名单 | 方法体骨架 + stub + 测试桩 | + ## 关联 - 设计:`docs/UBML-APPGEN-DESIGN.md` §7、A-4 diff --git a/docs/UBML-PAGEGEN.md b/docs/UBML-PAGEGEN.md index da94c22feb5700dbd5d898cc6c1062878b5a115b..675e34c6cef2a45c38e83fdfdde5507240cce808 100644 --- a/docs/UBML-PAGEGEN.md +++ b/docs/UBML-PAGEGEN.md @@ -1,6 +1,6 @@ # UBML 页面生成(M-B~M-I) -语义 DSL(YAML)→ Farris 页面 JSON 的编译、校验与预览。总体方案见 [UBML-PAGEGEN-DESIGN.md](./UBML-PAGEGEN-DESIGN.md);知识包见 [UBML-KNOWLEDGE.md](./UBML-KNOWLEDGE.md)。**P0 收工验收**:[UBML-PAGEGEN-P0-ACCEPTANCE.md](./UBML-PAGEGEN-P0-ACCEPTANCE.md)。 +语义 DSL(YAML)→ Farris 页面 JSON 的编译、校验与预览。总体方案见 [UBML-PAGEGEN-DESIGN.md](./UBML-PAGEGEN-DESIGN.md);知识包见 [UBML-KNOWLEDGE.md](./UBML-KNOWLEDGE.md)。**P0 收工验收**:[UBML-PAGEGEN-P0-ACCEPTANCE.md](./UBML-PAGEGEN-P0-ACCEPTANCE.md)。**`raw:` 逃逸节点**:[UBML-RAW-ESCAPE.md](./UBML-RAW-ESCAPE.md)(P3-04)。 ## 支持页面类型(M-G) @@ -57,10 +57,12 @@ UBML_RENDER_SMOKE=0 bun run ubml:verify -- path/to/page.yaml "message": "Unknown editor \"textarea\"", "hints": ["input-group", "date-picker", "text"] }], - "attemptHint": "Fix DSL issues from hints and re-run verify; limit automatic repair loops to 3 attempts." + "attemptHint": "根据每条 issue 的 hint、suggestion 与 hints 修复 DSL 后重新 verify;自动修复循环不超过 3 次。" } ``` +每条 `issues[]` 可含 `hint`(中文指引)与 `suggestion`(首选建议值)。 + ## Agent 修复回环(MCP) 工具 **`kb_ubml_verify`** 与 CLI 输出同形(`model` 默认摘要化): @@ -68,7 +70,7 @@ UBML_RENDER_SMOKE=0 bun run ubml:verify -- path/to/page.yaml - `yaml` — 内联 DSL 文本 - `path` — 工作区内 YAML 路径 -推荐回环:检索 `kb_ubml_exemplar` → 生成/修改 YAML → `kb_ubml_verify` → 根据 `issues[].hints` 修正(≤3 轮)。 +推荐回环:检索 `kb_ubml_exemplar` → 生成/修改 YAML → `kb_ubml_verify` → 根据 `issues[].hint` / `hints` 修正(≤3 轮)。 冒烟: diff --git a/docs/UBML-RAW-ESCAPE.md b/docs/UBML-RAW-ESCAPE.md new file mode 100644 index 0000000000000000000000000000000000000000..0eb2d0f7c193b55f9823f9056aae7787a4937fc2 --- /dev/null +++ b/docs/UBML-RAW-ESCAPE.md @@ -0,0 +1,187 @@ +# UBML `raw:` 逃逸节点(P3-04) + +当 DSL 尚无对应语法、但 Farris 页面 JSON 里已有(或需要)某类节点时,用 **`type: raw` + `raw:`** 把原生 Farris 结构原样编入页面模型。编译器**透传**、反编译器**尽量还原**;verify 仍校验 DSL 形状与编译结果。 + +相关设计背景:[UBML-PAGEGEN-DESIGN.md](./UBML-PAGEGEN-DESIGN.md) §逃逸通道、[UBML-PAGE-METAMODEL-DESIGN.md](./UBML-PAGE-METAMODEL-DESIGN.md) §P7。 + +Round-trip 回归总览见 [UBML-ROUNDTRIP.md](./UBML-ROUNDTRIP.md)。 + +--- + +## 1. 何时使用 `raw:` + +| 场景 | 建议 | +|------|------| +| 反馈类控件(`notify` / `modal` / `loading`)尚未有 DSL | ✅ 用 `raw:` 透传 | +| 自定义 `section` / 装饰性布局,编译器未建模 | ✅ 用 `raw:` | +| 反编译时遇到未知 Farris 节点 | ✅ 自动落成 `type: raw`(带 warning) | +| 能用 `fields` + `editor` 表达的表单字段 | ❌ 不要用 `raw:`,用 form section | +| 能用 `data-grid` / `filter-bar` 表达的列表页 | ❌ 不要用 `raw:` | +| 长期维护的核心业务字段 | ❌ 优先扩充 DSL,再考虑 raw | + +**原则**:`raw:` 是**逃生口**,不是默认写法。能建模的尽量建模,便于 verify、反编译与 Agent 增量编辑。 + +--- + +## 2. DSL 形状 + +页面 section(顶层或 `content-container` 内): + +```yaml +sections: + - title: 基本信息 + fields: + - label: 名称 + bind: name + editor: input-group + + - title: 自定义横幅 + type: raw + raw: + id: custom-banner # 建议始终提供,便于 round-trip 对齐 + type: section + mainTitle: 自定义横幅 + appearance: + class: demo-custom-banner + contents: + - id: banner-text + type: notify + message: 这是 raw 透传的 Farris 节点 +``` + +规则: + +- `type` 必须为字面量 `raw` +- `raw` 为对象,键值即 Farris 节点字段(`id`、`type`、`appearance` 等) +- `title` 可选,仅影响 YAML 可读性;编译时若 `raw.id` 缺失会用 title 生成 id + +完整范例:`packages/extension/assets/ubml/exemplars/raw-custom-section.yaml` + +--- + +## 3. 编译与反编译行为 + +**编译**(`compilePageModel`):遇到 `isRawSection` 时 `{ ...section.raw }` 合并进 Farris 树,不改写 payload。 + +**反编译**(`decompilePageModel`): + +- 能识别为 form / grid / filter-bar 的 → 结构化 DSL +- 无法映射的兄弟节点 → `{ type: raw, raw: <原节点> }`,并产生 warning +- form section 内无 form-group → 整段 section 保留为 raw + +--- + +## 4. 与 verify 的关系 + +| 阶段 | 对 `raw:` 的处理 | +|------|------------------| +| DSL 解析 | Zod 校验 `type: raw` + `raw: Record` | +| 编译 | 透传进模型 | +| JSON Schema | 校验编译后的 Farris 模型(raw 节点须符合 schema) | +| 渲染冒烟 | 与整页一起渲染;非法 Farris 结构在此阶段失败 | + +含合法 `raw:` 的页面可通过 verify(渲染冒烟可用 `UBML_RENDER_SMOKE=0` 跳过): + +```bash +bun run ubml:verify -- packages/extension/assets/ubml/exemplars/raw-custom-section.yaml +UBML_RENDER_SMOKE=0 bun run ubml:verify -- path/to/page-with-notify-raw.yaml +``` + +verify 失败时仍走 P3-03 中文 hints;`raw` 内 JSON Schema 错误会指向编译后模型路径。 + +--- + +## 5. Round-trip 保证与已知限制 + +### 已保证(有回归测试) + +- **card 页**顶层 `type: raw` section:`raw.id` 与关键字段在 compile → decompile → recompile 后保留 +- 同一页多个 raw 节点(如 `notify` + 自定义 `section`)可并存 +- 范例 YAML 双次 round-trip 后模型仍含 `custom-banner` / 嵌套 `banner-text` + +测试:`packages/ubml-codegen/test/raw-section-roundtrip.test.ts` + +```bash +bun run --cwd packages/ubml-codegen test test/raw-section-roundtrip.test.ts +``` + +### 已知限制 + +| 限制 | 说明 | +|------|------| +| **layout + content-container** | 反编译对 `ContentContainer` 整段可能落成外层 `raw`,内层 form/raw 嵌在 `raw.contents` 中,DSL 结构会与手写 YAML 不同,但 **Farris id 与 payload 仍在模型内** | +| **layout 反编译** | 整体仍标注「待后续」;勿依赖 layout 页 DSL 字面 round-trip | +| **无 `raw.id`** | 仅能按 JSON 子串匹配,易 fragile;生产 YAML 请写 `id` | +| **Entity BE** | 实体侧 `raw:` 逃逸在设计文档中,页面专节以 page DSL 为准 | + +若 round-trip 丢失带 `id` 的 raw payload,视为 **bug**,请提 Issue 并附 YAML。 + +--- + +## 6. 正例与反例 + +### ✅ 正例:card 页追加 notify + +```yaml +page: + type: card + title: 保存提示 + entity: Order + sections: + - title: 基本信息 + fields: + - { label: 单号, bind: code, editor: input-group } + - title: 保存成功 + type: raw + raw: + id: save-toast + type: notify + message: 保存成功 + appearance: { class: success-toast } +``` + +### ✅ 正例:反编译自动 raw(无需手写 type) + +从 Farris JSON 反编译时,未知节点会自动变成 raw section,可再 verify / 编辑。 + +### ❌ 反例:用 raw 代替普通字段 + +```yaml +# 不要这样 — 无法用 addField/patchField,verify hints 也无法指向 bind +- title: 名称 + type: raw + raw: + type: form-group + label: 名称 + editor: { type: input-group } +``` + +应写: + +```yaml +- title: 基本信息 + fields: + - { label: 名称, bind: name, editor: input-group } +``` + +### ❌ 反例:省略 id 的大块 JSON + +```yaml +- type: raw + raw: + type: section + contents: [ ... 大量节点 ... ] +``` + +难以 diff、Agent 难增量改;应拆成多个带 `id` 的 raw section,或推动 DSL 建模。 + +--- + +## 7. 贡献者工作流 + +1. 新增/修改含 `raw:` 的 exemplar → `packages/extension/assets/ubml/exemplars/` +2. `bun run ubml:verify -- ` +3. 若声称 round-trip 稳定,在 `test/raw-section-roundtrip.test.ts` 增加用例或扩展现有 helper +4. 文档更新本页「已知限制」节 + +首次贡献可参考 [product-design/07-contributing.md](./product-design/07-contributing.md) 与路线图 P3-04。 diff --git a/docs/UBML-ROUNDTRIP.md b/docs/UBML-ROUNDTRIP.md new file mode 100644 index 0000000000000000000000000000000000000000..7c0b0acd13bcc079c8cef7a3af13f13ea1a4e67f --- /dev/null +++ b/docs/UBML-ROUNDTRIP.md @@ -0,0 +1,69 @@ +# UBML 反编译 Round-Trip 回归(P3-01) + +验证 **compile → decompile → recompile** 后语义一致,防止编译器与反编译器独立演进时破坏不变量。 + +## 快速运行 + +```bash +# 包内测试(含故意破坏 DSL 的负例) +bun run --cwd packages/ubml-codegen test test/roundtrip-regression.test.ts + +# 仓库根目录冒烟 +bun run ubml:roundtrip:smoke + +# CI 跳过 +UBML_ROUNDTRIP_SMOKE=0 bun run ubml:roundtrip:smoke +``` + +单机运行时间通常 **<5s**。 + +## 固定样本 + +| 类型 | 样本 | 路径 | +|------|------|------| +| Entity YAML | FYBXD 报销单 | `packages/extension/assets/ubml-entity/exemplars/fybxd.be.yaml` | +| Entity JSON | 同上 seed | `packages/extension/assets/ubml-entity/exemplars/fybxd.be.json` | +| Page YAML | FYBXDJ 卡片 | `packages/extension/assets/ubml/exemplars/fybxdj-card.yaml` | +| Page YAML | FYBXDJ 管理列表 | `packages/extension/assets/ubml/exemplars/fybxdj-management.yaml` | +| Page YAML | raw 逃逸示例 | `packages/extension/assets/ubml/exemplars/raw-custom-section.yaml` | + +含 `raw:` 节点的专项 round-trip 与使用说明见 [UBML-RAW-ESCAPE.md](./UBML-RAW-ESCAPE.md)。 + +## 语义 diff 规则 + +### Entity(`semanticEquivalentEntityModels`) + +比较 Header、字段 Code/Name/MDataType、子实体、规则 Code/ComponentName、ManagerActions、Refs。 + +**可忽略项**(`DEFAULT_ENTITY_ROUNDTRIP_IGNORE`): + +| 项 | 原因 | +|----|------| +| `ChildTriggerInfo` 明细 | 反编译 v1 不还原高级 trigger 元数据 | +| `SecID` / `SecLevel` | 参考工程独有,exemplar YAML 未建模(比较时双侧忽略) | +| `ResourceMetadata` Refs | 反编译 v1 不还原资源元数据引用 | + +### Page(`semanticEquivalentPageDsl`) + +比较 canonical YAML 文本(type/title/entity/toolbar/sections/fields)。 + +Farris 节点 `id` 在 round-trip 中会重新生成,**不参与**比较;以 DSL 稳定性为准。 + +## 新增样本 + +1. 在 `packages/extension/assets/ubml*/exemplars/` 添加 YAML/JSON; +2. 在 `test/roundtrip-regression.test.ts` 增加 `test(...)`; +3. 可选:在 `script/ubml/roundtrip-smoke.ts` 的 `cases` 数组注册; +4. 若参考工程有 exemplar 未覆盖的字段,在 `EntityRoundtripCompareOptions.ignoreReferenceOnlyFields` 登记。 + +## API + +```ts +import { + runEntityRoundtripFromYaml, + runPageRoundtripFromYaml, + semanticEquivalentEntityModels, +} from "@code-agent/ubml-codegen" // 经 src/roundtrip 导出 +``` + +实现位于 `packages/ubml-codegen/src/roundtrip/`。 diff --git a/docs/product-design/05-knowledge-and-context.md b/docs/product-design/05-knowledge-and-context.md index d77237efd3ec177928c293b2ba498ac1f309cd5d..0b40aa17dddb972237b8e7a49efb6d534d3ccbf7 100644 --- a/docs/product-design/05-knowledge-and-context.md +++ b/docs/product-design/05-knowledge-and-context.md @@ -40,7 +40,9 @@ API Wiki → 有哪些接口、契约是什么 | 档位 | 你需要准备 | 适合 | |------|------------|------| | **Lite(默认)** | 无 Docker;ripgrep + 本地向量模型 | 个人开发 | -| **Pro** | Docker;Zoekt + Qdrant | 大仓、团队 | +| **Pro** | Docker;Qdrant(必需)+ 可选 Zoekt | 大仓、团队 | + +Pro 一键部署与 Lite→Pro 迁移:[KNOWLEDGE-PRO.md](../KNOWLEDGE-PRO.md)。 Lite 已针对**中文**优化(bge-m3 多语模型 + 分词 + 组件别名),避免「中文搜不到」的问题。 diff --git a/docs/product-design/07-contributing.md b/docs/product-design/07-contributing.md index a64236c29ca0031ad4996db9f9b2cca43d8a297b..42ec453aa9f45fab27742b97d9a8efc4a464a290 100644 --- a/docs/product-design/07-contributing.md +++ b/docs/product-design/07-contributing.md @@ -49,7 +49,7 @@ bun run ubml:compile -- packages/extension/assets/ubml/exemplars/card.yaml bun run ubml:verify -- packages/extension/assets/ubml/exemplars/card.yaml ``` -遇到问题见 [docs/SETUP.md](../SETUP.md);UBML 入门见 [docs/UBML-CodeAgent-介绍.md](../UBML-CodeAgent-介绍.md)。 +遇到问题见 [docs/SETUP.md](../SETUP.md);UBML 入门见 [docs/UBML-CodeAgent-介绍.md](../UBML-CodeAgent-介绍.md)。页面 DSL **`raw:` 逃逸**见 [UBML-RAW-ESCAPE.md](../UBML-RAW-ESCAPE.md)。 --- @@ -101,7 +101,7 @@ Commit 写**为什么改**,不要只罗列文件名。Vendor 同步单独提 |--------|------------| | **AppGen v1** | 完整应用生成端到端(P1-01 ~ P1-07) | | **知识库 v1** | 架构卡导入、检索评测、别名与 exemplar(P2-xx) | -| **编译器质量 v1** | round-trip、hints、Agent Home(P3-xx) | +| **编译器质量 v1** | round-trip、hints、Agent Home、手验自动化(P3-xx) | | **文档与社区** | 文档、教程、OpenSpec 模板(D-xx) | | **开发活动工作台 v1** | 需求入口、Mission 验收区(W-01 ~ W-02) | | **验收与归档 v1** | 校验联动、档案导出(W-03 ~ W-04) | @@ -116,7 +116,7 @@ Commit 写**为什么改**,不要只罗列文件名。Vendor 同步单独提 | 方向 | 具体可做 | 包 | |------|----------|-----| | 文档/范例 | 补 DSL exemplar、改进 product-design 配图 | `docs/`、`assets/` | -| 编译器 | round-trip 回归、错误 hints 中文化 | `ubml-codegen` | +| 编译器 | round-trip 回归、错误 hints 中文化、[raw: 逃逸](../UBML-RAW-ESCAPE.md) 文档与测试 | `ubml-codegen` | | 知识库 | 评测集、中文别名、架构卡 | `knowledge` | | 产品体验 | Artifact 卡、预览面板 | `extension` | diff --git a/docs/product-design/10-mof-entity-modeling.md b/docs/product-design/10-mof-entity-modeling.md new file mode 100644 index 0000000000000000000000000000000000000000..cbfd69908978c8bc466a66d6eba828e82e8824f8 --- /dev/null +++ b/docs/product-design/10-mof-entity-modeling.md @@ -0,0 +1,726 @@ +# 10 · MOF 元建模与实体优先生成主线 + +> 本篇回答:**如何用 MOF 四层元建模架构收敛目前分散的生成方案?如何让大模型从一句自然语言稳定地生成符合平台格式的业务实体模型?模型页签在界面上长什么样、怎么实现?** +> +> 阅读前建议先读 [04 · UBML 主线](./04-ubml-pipeline.md)(五步闭环)与 [05 · 知识服务](./05-knowledge-and-context.md)(三库与 MCP 工具)。本篇是这两篇的深化:把「AI 写语义 DSL,编译器保正确性」升级为**元模型驱动**的体系,并以**业务实体**为第一个走通的模型。 + +--- + +## 0. 方案评估结论 + +总体判断:**方案方向合理,且适合当前仓库的演进阶段**。它没有推翻现有 `entity:` DSL、`ubml-codegen` 编译器和三道闸,而是在现有体系之上补一层显式元模型,把分散在 Zod、JSON Schema、catalog、提示词和预览组件里的规则收敛为可治理的事实源。这条路线能解决两个真实痛点:一是生成规则散落导致贡献者难以判断该改哪里;二是自然语言生成缺少可见结果,用户无法直观看到实体关系。 + +从技术可行性看,方案成立的关键有三点: + +| 判断点 | 评估 | +|--------|------| +| 与现有架构是否兼容 | 兼容。现有 `entity:` YAML 继续作为 M1 表示,`packages/ubml-codegen` 继续负责确定性编译,元模型先作为描述与一致性测试接入,不要求重写编译器。 | +| 是否能提高生成稳定性 | 能。稳定性提升不来自更长提示词,而来自缩小生成面、catalog 接地、受控编辑操作和三道闸修复回环的组合。尤其是「建模操作工具化」能把一次大文档生成拆成小步可校验操作。 | +| 是否容易被社区贡献者理解 | 基本可理解。MOF 术语需要控制,不应把完整 OMG MOF / OCL 引入项目;文档中把 M3 限定为 7 个构造,是必要的降复杂度设计。 | + +但需要收敛三个边界,避免方案过度工程: + +1. **首期不做完整 OCL 引擎**。`constraint.check` 先作为人读规约与测试用例命名,真正执行由 TypeScript 校验函数承载;等约束稳定后再考虑表达式解释器。 +2. **首期不从元模型生成完整平台 JSON Schema**。`gsp-business-entity.schema.json` 对应平台格式,字段多且历史包袱重。E1 只做一致性测试与 DSL/操作 schema 派生,完整产物 schema 生成放到远期。 +3. **首期不追求全自动 AppGen**。目标收窄为业务实体:一句自然语言生成 `.be.yaml`,校验通过后在模型页签可视化。页面、查询、应用编排只作为后续推广目标。 + +因此,本方案建议作为 `AppGen v1` 之前的**实体优先收敛线**推进:先把一个核心模型做稳、做可见,再向页面和完整应用扩展。 + +--- + +## 1. 背景:为什么要收敛,为什么从实体开始 + +当前仓库已经有一条能跑的生成链:`entity:` / `page:` / `app:` YAML DSL → `packages/ubml-codegen` 确定性编译 → 多道校验 → 修复回环 → 预览写回。但站在贡献者视角,这套方案存在两个明显问题: + +**方案分散**。「什么是合法的实体模型」这件事,目前散落在至少四处,且相互之间靠人工保持一致: + +| 载体 | 位置 | 角色 | +|------|------|------| +| Zod 解析规则 | `packages/ubml-codegen/src/entity/dsl/schema.ts` | DSL 语法与语义校验 | +| JSON Schema | `packages/extension/assets/ubml-entity/gsp-business-entity.schema.json` | 编译产物结构校验 | +| 类型/引用清单 | `datatypes.index.json`、`udt.catalog.json`、`referenceable-be.catalog.json`、`components.catalog.json` | 合法值域 | +| Agent 提示词 | `assets/ubml-entity/prompts/entitygen.md`、`agents/ubml-entitygen.md` | 告诉模型「该怎么写」 | + +四处各自演进,没有一个「唯一事实源」。新增一种字段类型,要同时改 Zod、改 JSON Schema、改 catalog、改提示词——漏改任何一处,生成质量就悄悄劣化,而且没人能一眼看出四处是否一致。 + +**看不到生成效果**。生成的模型目前以文件形式落在输出目录,或在工作台的编辑器里以 YAML 文本呈现。用户在对话面板发出需求后,没有一个直观的地方回答「生成了哪些模型?实体之间是什么关系?」——缺少一个面向模型的可视化视图。 + +因此本篇的策略是:**先聚焦业务实体这一个模型,把「自然语言 → 元模型约束下的生成 → 校验 → 可视化呈现」的完整闭环走通**,再把同一套架构推广到页面等其他模型。选实体作为突破口有三个理由: + +1. 实体是全链路的事实源——`.dbo`/`.vo`/`.eapi` 从它派生,页面绑定它的字段(见 [04 §3](./04-ubml-pipeline.md#3-应用模型长什么样生成顺序)),实体走通了,下游就有了地基; +2. 实体的结构复杂度适中且高度规则化(字段、子实体、关联、枚举、UDT、规则),是检验元建模方案覆盖能力的理想样本; +3. 仓库已有 FYBXD(报销单)金样:DSL 金样 `assets/ubml-entity/exemplars/fybxd.be.yaml`(77 行)与平台格式参考 `FYBXD.json`(约 1200 行 GspMetadata),验收有据可依。 + +--- + +## 2. MOF 四层架构总览 + +[MOF(Meta-Object Facility)](https://www.omg.org/mof/)是 OMG 的元建模标准,UML、BPMN 等建模语言都构建在它之上。它的核心思想是把「模型」分成四层,每一层是上一层的**实例**: + +```text +M3 元元模型 定义「什么是元模型」的最小语言 + ↓ 实例化 (MetaClass / MetaAttribute / MetaReference / MetaConstraint) +M2 元模型 用 M3 语言定义的 UBML 各领域元模型 + ↓ 实例化 (BusinessEntity / EntityObject / Field / Page / Section …) +M1 用户模型 用户建出来的具体模型 + ↓ 实例化 (FYBXD 报销单、FYBXDMX 明细、FYBXDJ 卡片页 …) +M0 运行时实例 跑起来之后的业务数据 + (张三 2026 年 6 月那张 1280 元的报销单) +``` + +对照本仓库,各层的落点是: + +| MOF 层 | 本仓库对应物 | 现状 | +|--------|--------------|------| +| M3 | 本篇 §3.2 定义的**元建模语言**(一份很小的 YAML 词汇表) | ✅ 已内嵌于 metamodel YAML(7 构造) | +| M2 | `ubml-entity.metamodel.yaml`、`ubml-page.metamodel.yaml` 等元模型定义文件 | ✅ EntityGen MoF v0(2026-07-04 归档) | +| M1 | `entity:` / `page:` YAML DSL 及其编译产物(`.be.json` / `.frm.json`) | ✅ 已有 | +| M0 | 运行时业务数据 | 平台职责,不在本仓库范围 | + +一句话概括本篇的核心动作:**把目前隐式的 M2 显式化为一份机器可读的元模型定义,让 Zod 规则、JSON Schema、catalog 校验、提示词片段、ER 图、编辑操作全部从它派生或与它对齐**。这不是推翻现有编译器,而是给现有体系补上缺失的「唯一事实源」。 + +MOF 还有一个常被忽略但对本项目极其关键的概念:**反射式 API(Reflective API)**——任何 M1 模型都可以通过一组统一的、由 M2 约束的操作(创建元素、设置属性、建立引用)来构造和修改,而不是直接手写序列化文本。§4 将说明:这正是让大模型稳定产出合法模型的钥匙。 + +--- + +## 3. UBML 元建模语言(M2 定义语言) + +### 3.1 设计原则 + +1. **小而完备**:M3 词汇控制在 7 个构造以内,能覆盖实体和页面两个领域即可,不追求 MOF/EMOF 的完整实现; +2. **YAML 承载,纯声明式**:与现有 DSL 同一技术栈,社区贡献者不需要学新工具; +3. **单一事实源**:所有派生物(校验、提示词、图、文档)从元模型生成或用一致性测试对齐,禁止双头维护; +4. **约束一等公民**:值域约束(「关联必须指向 catalog 里存在的实体」)与结构约束(「子实体不能再嵌套子实体」)都要能在元模型里声明,而不是埋在编译器代码里; +5. **渐进接入**:元模型先作为「描述性规约 + 一致性测试」上线,不要求第一天就改写编译器(见 §6 路线)。 + +### 3.2 元语言核心构造(M3) + +| 构造 | 含义 | 对应 MOF/EMOF 概念 | +|------|------|--------------------| +| `metamodel` | 一个领域元模型的根,含名称与版本 | Package | +| `class` | 元类:一类模型元素(如「字段」「子实体」) | Class | +| `attribute` | 元属性:元素自身的标量属性(code、name、length…) | Property (attribute) | +| `reference` | 元引用:元素之间的关系,**必须声明是否为包含(containment)** | Property (reference) | +| `enum` | 封闭取值集(如字段类型枚举) | Enumeration | +| `datatype` | 基础数据类型及其到平台类型的映射 | DataType | +| `constraint` | 约束:结构不变式或值域校验,可引用 catalog | Constraint (OCL 的轻量替代) | + +其中 `reference` 的 **containment** 区分是 MOF 的精髓,也刚好切中实体建模的两类关系: + +- **包含引用**(`containment: true`):整体-部分关系,删除整体级联删除部分。对应实体的 `children`(FYBXD 包含 FYBXDMX 明细)——平台格式里的 `ContainChildObjects`; +- **交叉引用**(`containment: false`):对等引用关系,只存外键。对应实体的 `association` 字段(报销人员 → GspUser)——平台格式里的 `ChildAssociations` + `Refs`。 + +ER 图的两种连线(实线菱形 / 箭头)、级联删除语义、`Refs[]` 依赖清单的生成,全部由这一个标志推导,不需要额外机制。 + +### 3.3 业务实体元模型(M2 实例) + +下面用元语言定义业务实体元模型的骨架。这份 YAML 就是未来的 `ubml-entity.metamodel.yaml`,每一行都能在 FYBXD 金样中找到对应物: + +```yaml +metamodel: + name: ubml-entity + version: "0.1" + + datatypes: + - { name: string, platform: { mdataType: String, requiresLength: true } } + - { name: text, platform: { mdataType: Text } } + - { name: integer, platform: { mdataType: Integer } } + - { name: decimal, platform: { mdataType: Decimal, requiresLength: true, requiresPrecision: true } } + - { name: datetime, platform: { mdataType: DateTime } } + - { name: boolean, platform: { mdataType: Boolean } } + + enums: + - name: EntityKind + literals: [bill, dictionary, config] + - name: RuleTiming + literals: [beforeSave, afterModify, afterCreate] + + classes: + - name: BusinessEntity + doc: 一个业务实体元数据,编译为 GspMetadata(Header + Refs + Content) + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + - { name: namespace, type: QualifiedName, required: true } + - { name: kind, type: EntityKind, default: bill } + references: + - { name: root, type: EntityObject, containment: true, multiplicity: "1" } + - { name: managerActions, type: ManagerAction, containment: true, multiplicity: "0..*" } + + - name: EntityObject + doc: 实体对象节点;根对象编译为 MainObject,子对象编译为 ContainChildObjects[] + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + references: + - { name: fields, type: Field, containment: true, multiplicity: "1..*" } + - { name: children, type: EntityObject, containment: true, multiplicity: "0..*" } + - { name: rules, type: Rule, containment: true, multiplicity: "0..*" } + constraints: + - name: no-grandchildren + doc: 子实体不允许再包含子实体(平台限制为两级对象树) + check: "self.parent != null implies self.children.isEmpty()" + + - name: Field + abstract: true + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + + - name: ScalarField # { code: TotalSum, type: decimal, length: 18, precision: 2 } + superType: Field + attributes: + - { name: type, type: DataTypeRef, required: true } + - { name: length, type: Integer } + - { name: precision, type: Integer } + - { name: required, type: Boolean, default: false } + - { name: multiLanguage, type: Boolean, default: false } + - { name: billCode, type: Boolean, default: false } + constraints: + - { name: length-required-by-type, check: "datatype(self.type).requiresLength implies self.length != null" } + + - name: EnumField # BillType:差旅费 / 手机费 / 交通费 + superType: Field + references: + - { name: values, type: EnumValue, containment: true, multiplicity: "1..*" } + constraints: + - { name: single-default, check: "self.values->select(default).size() <= 1" } + + - name: AssociationField # Employee → GspUser,编译为 ChildAssociations + Refs 条目 + superType: Field + references: + - { name: ref, type: BusinessEntity, containment: false, external: true } + attributes: + - { name: refFields, type: IdentifierList, doc: 带出的引用字段,如 [Code, Name] } + constraints: + - { name: ref-in-catalog, check: "catalog('referenceable-be').has(self.ref)" } + - { name: refFields-exist, check: "catalog('referenceable-be').elements(self.ref).containsAll(self.refFields)" } + + - name: UdtField # BillState / ProcessInstance / AttachmentInfo + superType: Field + references: + - { name: udt, type: UnifiedDataType, containment: false, external: true } + constraints: + - { name: udt-in-catalog, check: "catalog('udt').has(self.udt)" } + + - name: Rule + abstract: true + # 派生出 Validation(校验)、Determination(联动计算)、Action(业务动作), + # 各自声明 timing / triggerFields / component 等属性与约束,此处从略 + + - name: ManagerAction # SubmitToApprove 等,common: true 时须命中 components.catalog + attributes: + - { name: code, type: Identifier, required: true } + - { name: common, type: Boolean, default: false } + constraints: + - { name: common-in-catalog, check: "self.common implies catalog('components').has(self.code)" } +``` + +对照检查:FYBXD 金样里的每一个语法现象——普通字段、`billCode`、多语言、枚举(BillType/AuditStatus)、关联(GspUser/SysOrg/Projects)、UDT(BillState/ProcessInstance/AttachmentInfo)、两个子实体、三类规则、七个管理动作——都能落在上述元类和约束上。而 `FYBXD.json` 里那些 GUID、`ColumnID`、`I18nResourceInfoPrefix`、`MappingRelation`、系统字段(ID/ParentID/Version)等机械内容,**不进入元模型**——它们是编译器(M1→平台格式的变换)的职责,这正是「AI 想语义、编译器保正确」原则在元模型层的体现。 + +`constraint` 的 `check` 表达式采用一个 OCL 风格的极简子集(导航、集合谓词、`catalog()` 函数),第一版甚至可以不做表达式引擎——每条约束绑定一个 TypeScript 校验函数,`check` 先作为**人读的规约文档**存在,后续再逐步机器化。 + +### 3.4 页面元模型:验证覆盖性 + +同一套 M3 词汇定义页面元模型,证明元语言不是为实体特制的: + +```yaml +metamodel: + name: ubml-page + version: "0.1" + classes: + - name: Page + attributes: + - { name: type, type: PageType, required: true } # card / management-list / … + - { name: title, type: LocalizedString, required: true } + references: + - { name: entity, type: BusinessEntity, containment: false, external: true } + - { name: sections, type: Section, containment: true, multiplicity: "1..*" } + - { name: toolbar, type: ToolbarAction, containment: true, multiplicity: "0..*" } + + - name: Section + references: + - { name: fields, type: FieldBinding, containment: true, multiplicity: "1..*" } + + - name: FieldBinding + attributes: + - { name: label, type: LocalizedString } + - { name: editor, type: EditorRef } # 值域来自 components.index.json + references: + - { name: bind, type: Field, containment: false, external: true } + constraints: + - { name: bind-exists-on-entity, check: "page(self).entity.allFields().has(self.bind)" } + - { name: editor-in-index, check: "catalog('components-index').has(self.editor)" } +``` + +注意 `FieldBinding.bind` 是一个跨元模型的交叉引用——页面字段绑定实体字段。目前 AppGen 的 crossref 校验(`packages/ubml-codegen/src/app/validate/crossref.ts`)用手写代码检查这件事;元模型化之后,crossref 就是「校验所有 `external: true` 引用可解析」这一条通用规则,新增模型种类不再需要新写 crossref 代码。 + +### 3.5 元模型驱动的派生物 + +显式化 M2 的价值在于它能「一源多用」。从一份元模型定义出发,以下产物可以生成或对齐: + +```text + ubml-entity.metamodel.yaml(唯一事实源) + │ + ┌──────────────┬────────────────┼────────────────┬──────────────┐ + ▼ ▼ ▼ ▼ ▼ + Zod 校验规则 JSON Schema 提示词片段 ER 图元数据 编辑操作集 + (DSL 三道闸 (产物结构 (合法类型/枚举/ (模型页签 (§4 建模工具的 + 的第一道) 校验) 约束清单,注入 可视化) 操作签名与 + entitygen 提示词) 校验依据) +``` + +| 派生物 | 现状载体 | 元模型化后 | +|--------|----------|-----------| +| DSL 校验 | 手写 Zod(`entity/dsl/schema.ts`) | 第一阶段:一致性测试对齐;远期:代码生成 | +| 产物校验 | 手工维护的 `gsp-business-entity.schema.json` | 同上 | +| 值域清单 | 四个 catalog JSON | catalog 保留为**数据**,元模型的 `catalog()` 约束声明**谁在什么位置必须查哪个 catalog** | +| 提示词 | 人工撰写 `entitygen.md` | 「合法字段类型表」「约束清单」等段落由元模型渲染生成,人工只写工作流部分 | +| ER 图 | `entity-preview-model.ts` 手工推导 | 由 containment / 交叉引用直接映射为图的边 | +| 文档 | 手写 | 元类与约束的 `doc` 字段可渲染为参考手册 | + +--- + +## 4. 稳定生成方案:让大模型可靠地产出合法实体模型 + +这是全篇技术上最关键的一节。先把问题拆开,再对比候选方案,最后给出推荐组合。 + +### 4.1 先分清三类「正确性」 + +「模型生成不稳定」其实混合了三个性质完全不同的问题,混在一起讨论就会得出「多写点规则试试」这类含糊结论: + +| 类别 | 含义 | 反例 | 适合的手段 | +|------|------|------|-----------| +| **格式正确性** | 产出是语法合法、结构完整的模型文档 | YAML 缩进错、缺 `code`、`ContainEnumValues` 拼错 | **不靠提示词**——靠缩小生成面(DSL)、结构化输出、确定性编译 | +| **值域正确性** | 引用的类型、实体、UDT、构件真实存在 | 编造 `type: money`、关联不存在的 `Customer` 实体 | catalog 检索 + 元模型 `catalog()` 约束即时校验 | +| **语义正确性** | 模型如实表达了自然语言需求 | 需求说「明细金额汇总到主表」,生成时漏了 determination | exemplar 检索、需求对照清单、校验-修复回环、人审预览 | + +一个直接推论:**知识体系和建模规则(提示词层面的手段)只能改善后两类,对第一类几乎无能为力**——提示词只能提高模型「倾向于」写对格式的概率,无法给出保证,且上下文越长遵从率越低。这就是单靠「知识体系 + 建模规则」方案不牢靠的根本原因。 + +### 4.2 候选方案对比 + +| # | 方案 | 解决哪类正确性 | 优点 | 局限 | 结论 | +|---|------|----------------|------|------|------| +| A | 提示词规则 + 知识体系(few-shot、建模守则) | 值域△ 语义△ | 零改造、易迭代 | 无保证;规则越多遵从越差;换模型效果漂移 | **辅助**,且内容应从元模型生成 | +| B | 微调 / 蒸馏专用模型 | 格式△ 语义△ | 输出风格稳定 | 样本量小(目前金样一位数);训练与维护成本高;基座升级即作废;仍无硬保证 | **现阶段不做**,留作数据积累后的远期选项 | +| C | 结构化输出 / 约束解码(JSON Schema strict mode、function calling) | 格式✓ | 语法层面硬保证 | 只保语法不保语义;对 YAML 不适用;依赖 provider 能力 | **采用**,用在工具调用参数上(见 E) | +| D | 语义 DSL + 确定性编译器 | 格式✓✓ | 生成面从 1200 行 JSON 缩到 77 行 YAML;机械细节全部编译器兜底 | DSL 本身仍可能写错(拼错枚举值、漏必填项) | **底座**,仓库已有 ✅ | +| E | **建模操作工具化**(MOF 反射式 API → MCP 工具) | 格式✓✓ 值域✓ | 每一步操作参数极小、可被 schema 硬约束、可即时校验即时反馈;错误定位到单步而非整个文档 | 需要设计操作集与会话内模型状态 | **本篇核心增量**,推荐 | +| F | 校验-修复回环(结构化 issues + hint,≤3 轮) | 三类兜底 | 已验证有效 | 每轮有 token 与时延成本;应作为兜底而非主路径 | **保留**,仓库已有 ✅ | +| G | 检索增强(catalog / exemplar / intent 检索) | 值域✓ 语义△ | 把「世界上有什么可用」注入上下文 | 覆盖面取决于策展质量 | **保留并元模型化**,仓库已有 ✅ | + +### 4.3 推荐:五层防线,核心是「大模型不直接产出最终文本」 + +推荐组合为 **D + E + C + G + F**,按生成时序组织成五层防线: + +```text +第 1 层 值域接地(G) 生成前:kb_be_exemplar 按意图检索范例; + kb_be_datatype / kb_be_ref / kb_be_udt 查出可用值域 +第 2 层 骨架草案(D+C) 一次性产出小的 entity: DSL 草案(只含字段/子实体骨架), + 立即过三道闸——草案小,错了也易修 +第 3 层 受控精修(E+C) 关联、枚举、UDT、规则等易错部分不靠一次生成, + 改为逐条调用建模操作,每个操作参数由 JSON Schema + 硬约束,执行时按元模型约束即时校验、即时报错 +第 4 层 整体校验(F) compile → schema → roundtrip 语义等价; + 失败给结构化 issues + hint,修复回环 ≤3 轮 +第 5 层 人审预览 模型页签展示 ER 图与字段表,人确认后写回 +``` + +**第 3 层是本篇相对现状的核心增量**,展开说明。 + +MOF 的反射式 API 思想是:模型不是「一篇文档」,而是「一组由元模型约束的对象」,构造模型的正规方式是调用 `createInstance` / `set` / `add` 这类细粒度操作。把这个思想搬到 LLM 场景,就是把「生成一个大文档」这个高风险动作,替换为「执行一系列小的建模操作」: + +```text +kb_be_create { code, name, namespace, kind } → 创建实体骨架 +kb_be_edit { op: addField, object, field } → 加标量字段 +kb_be_edit { op: addChild, code, name, fields } → 加子实体(含关系) +kb_be_edit { op: addAssociation, object, field, ref, refFields } + → 加关联(含关系) +kb_be_edit { op: addEnumField / addUdtField / addRule / patchField / removeField … } +``` + +仓库已有 `kb_be_edit` 的 `addField` / `addChild` / `addRule` / `patchField` / `removeField` 五个操作(见 [UBML-ENTITY-VERIFY.md](../UBML-ENTITY-VERIFY.md)),所以这不是从零建设,而是三点补强: + +1. **操作集补全**:按 §3.3 元模型补齐 `addAssociation`、`addEnumField`、`addUdtField`、`addManagerAction`,使每一类元类实例都有对应的构造操作; +2. **参数 schema 从元模型生成**:每个操作的入参 JSON Schema 由元模型元类定义渲染而来,配合 provider 的结构化输出能力(方案 C),模型在语法层面写不出非法参数; +3. **单步即时校验**:操作执行时立即跑该元类的约束(`ref-in-catalog`、`length-required-by-type`、`single-default`…),失败即返回带 hint 的结构化错误。错误反馈从「整个文档校验失败、issues 列表 20 条」变成「这一步的这个参数不对、应该从这 3 个候选里选」,修复成本相差一个数量级。 + +为什么骨架仍用一次性 DSL 草案(第 2 层)而不是全程工具调用?因为纯标量字段的出错率本来就低,逐字段调用工具徒增轮次与 token 成本。**易错的地方精修,不易错的地方批量**——关联、枚举、UDT、规则这些牵涉外部引用和交叉约束的部分才是工具化的主战场。两种模式的边界可以随实测数据调整。 + +### 4.4 知识体系与建模规则的正确定位 + +回到最初的疑问——「知识体系」「建模规则」是否合理?结论是:**合理,但它们是值域与语义层的养料,不是格式层的保险**。在推荐架构中它们各就其位: + +| 你列举的方案 | 在五层防线中的位置 | 形态 | +|--------------|--------------------|------| +| 知识体系 | 第 1 层(值域接地)与第 3 层的校验数据源 | catalog(数据)+ exemplar(范例)+ intent 索引(检索),由 [05 知识服务](./05-knowledge-and-context.md)承载 | +| 建模规则 | 拆成两半:**可机检的**进元模型 `constraint`(第 3、4 层硬校验);**启发式的**进提示词(第 2 层,如「单据类实体通常有编号、金额、状态字段」「明细金额通常需要汇总 determination」) | 元模型 YAML + `entitygen.md` | + +判别标准很简单:一条规则如果能写成断言,就不要写成叮嘱。「关联必须指向 catalog 中存在的实体」写成叮嘱是浪费,写成约束是保证;「报销类单据通常需要审批状态」无法断言,留在提示词里发挥启发作用。 + +### 4.5 端到端生成时序(实体优先版) + +```text +用户(对话面板):「创建差旅报销单:单头有报销人、部门、项目、总金额, + 明细记录费用日期、金额、发票号,金额需要汇总校验」 + │ + ▼ +① @ubml-entitygen 意图分析 + kb_be_exemplar("差旅报销") → 命中 FYBXD 范例 + kb_be_ref → GspUser / SysOrg / Projects 可关联 + │ + ▼ +② 骨架草案:产出 entity: DSL(code/name/namespace + 标量字段 + children) + → 立即 kb_be_verify(dsl 闸) + │ + ▼ +③ 受控精修(逐操作,每步即时校验) + addAssociation(Employee → GspUser) ✓ + addAssociation(Department → SysOrg) ✓ + addUdtField(BillState) ✓ + addRule(sumAmount: 明细 Amount → 主表 TotalSum, afterModify) ✓ + addRule(ValidateAmount, beforeSave) ✓ + │ + ▼ +④ 整体校验:compile → schema → roundtrip 语义等价 + 失败 → 结构化 issues + hint → 修复回环(≤3 轮) + │ + ▼ +⑤ 模型页签自动打开:左侧文件导航出现新实体, + 右侧 ER 图展示 FYBXD ─◆ FYBXDMX 包含关系、FYBXD ─▶ GspUser 关联 + 人工确认 → 写回工程 +``` + +--- + +## 5. 模型页签:对话面板右侧的模型视图 + +### 5.1 交互设计 + +在对话面板右侧栏新增「模型」页签,与既有的「更改 / 终端 / 编辑器」并列。页签内部左右分栏: + +```text +┌─ 右侧栏 ──────────────────────────────────────────────┐ +│ 更改 │ 终端 │ 编辑器 │ ★模型 │ + │ +├───────────────┬───────────────────────────────────────┤ +│ 模型导航 │ [ 关系图 ] [ 文件 ] ← 视图切换 │ +│ │ │ +│ ▾ 实体 be/ │ ┌────────┐ │ +│ ● FYBXD │ │ FYBXD │报销单 │ +│ 报销单 │ └──┬──┬──┘ │ +│ ▸ 页面 pages/ │ 包含◆│ │──▶ GspUser(关联) │ +│ ▸ 查询 query/ │ ┌──────┴┐ └─────▶ SysOrg │ +│ │ │FYBXDMX│ ┌────────┐ │ +│ │ │ 明细 │ │FXBXDFJ │附件 │ +│ │ └───────┘ └────────┘ │ +│ │ (点击节点 → 下方字段表 / 跳转文件视图)│ +└───────────────┴───────────────────────────────────────┘ +``` + +- **模型导航**:按模型种类分组列出当前工程 / 本次生成会话产出的模型文件(`*.be.yaml`、`*.be.json` 及后续的页面、查询等)。生成新模型后自动刷新并高亮新增项; +- **关系图视图**(默认):以 ER 图呈现实体关系——包含关系(子实体)与交叉关联(association)用不同线型区分,节点展示实体 code 与中文名,点击节点展开字段表(code / 名称 / 类型 / 长度 / 必填);关联到外部实体(GspUser 等 catalog 实体)以浅色节点呈现,表明「引用而非本工程定义」; +- **文件视图**:只读代码视图展示 DSL YAML 或编译后 JSON,可一键切换;后续可接入受控编辑; +- 图的语义直接来自元模型:`containment: true` 的引用画成包含边,`external: true` 的画成关联边——这就是 §3.5 说的「ER 图元数据由元模型派生」。 + +### 5.2 技术方案 + +右侧栏已有成熟的多页签机制,此功能是标准扩展(各文件均在 `packages/extension/src/` 下): + +| 步骤 | 改动点 | 参考既有实现 | +|------|--------|--------------| +| 1. 注册页签类型 | `pages/layout/right-panel.tsx`:`RightPanelTabType` 增加 `"model"`,补 `TAB_TYPE_ICONS` 与 `OPTIONAL_TAB_TYPES` | 既有 `"preview"` / `"entity-preview"` 类型 | +| 2. 状态 store | 新建 `lib/ubml-model-tab-store.ts`:当前选中文件、视图模式(图/文件)、文件清单 | `lib/ubml-entity-preview-tab-store.ts` | +| 3. 打开桥接 | 新建 `lib/ubml-model-bridge.ts`:`openUbmlModelTab()`,按 key 去重、生成后自动打开 | `lib/ubml-preview-bridge.ts` | +| 4. 内容组件 | 新建 `pages/workbench/ubml-model-panel.tsx`:左导航 + 右内容双栏;在 `project-workspace-shell.tsx` 的 `renderTabContent` 挂载 | — | +| 5. 文件导航数据 | 扫描工程内模型文件与生成输出目录(`manifest.json` 列出的产物) | AppGen 输出结构见 [UBML-APPGEN.md](../UBML-APPGEN.md) | +| 6. ER 图渲染 | 复用 `lib/entity-preview-model.ts` 的 `buildEntityPreviewFromDsl()` 产出的结构;多实体全景图可先用 Mermaid `erDiagram` 快速落地,交互增强(点击展开字段表)再换自绘 SVG | 工作台 `EntityPreviewContent` 已有单实体字段表 + ER | +| 7. 生成联动 | `@ubml-entitygen` 生成/校验通过后调用 `openUbmlModelTab()` 并刷新导航 | 既有生成完成后打开 preview 的联动 | + +关键取舍:**不新造预览体系**。实体的字段表与 ER 渲染在工作台 `EntityPreviewContent` 中已存在,模型页签的增量是「多文件导航 + 多实体全景关系图 + 与对话生成流程的联动」,单实体渲染逻辑全部复用。 + +--- + +## 6. 实施路线:实体优先,四个阶段 + +### 6.1 阶段总览 + +| 阶段 | 目标 | 核心产物 | 依赖 | 建议 OpenSpec 变更 | +|------|------|----------|------|--------------------| +| **E1 元模型显式化** ✅ | 把实体 DSL 的结构、类型和值域约束显式化 | `ubml-entity.metamodel.yaml`、元模型读取器、一致性测试、提示词片段渲染 | 无 | `ubml-entity-metamodel` | +| **E2 建模操作集** ✅ | 让大模型通过受控操作稳定构造实体模型 | 新增 `kb_be_edit` 操作、操作参数 schema、单步校验、实体生成评测集 | E1 | `ubml-entity-modeling-ops` | +| **E3 模型页签** ✅ | 在对话面板右侧看见模型文件与实体关系图 | `model` 右栏页签、模型导航、ER 图/文件双视图、生成联动 | 可与 E2 并行 | `ubml-model-right-panel` | +| **E4 页面推广** ✅ | 把同一套元模型机制推广到页面模型 | `ubml-page.metamodel.yaml`、通用 external 引用校验、页面进入模型导航 | E1–E3 | `ubml-page-metamodel` + `ubml-model-tab-pages` | + +端到端验收(E1–E3 完成时):在对话面板输入一句中文需求 → 生成实体模型三道闸通过 → 模型页签展示 ER 图 → 与 FYBXD 金样做 roundtrip 语义等价比对通过 → 写回工程。**这就是「用一个模型走通完整流程」的可演示定义。** + +每个阶段进入实现前按 [07 · 协作开发指南](./07-contributing.md)走 OpenSpec 流程(`/opsx:propose` → 审阅 → `/opsx:apply` → `/opsx:archive`),元模型文件本身也纳入 living spec 管理。 + +### 6.2 E1:元模型显式化 + +E1 是低风险起步阶段,原则是**只增加事实源与测试,不改变现有生成行为**。这一阶段完成后,仓库仍然用现有 Zod、catalog 和编译器跑实体生成,但社区贡献者已经能从一份文件看懂「实体模型语言到底由哪些元类、字段类型、引用和值域约束组成」。 + +建议文件结构: + +| 文件 | 类型 | 责任 | +|------|------|------| +| `packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml` | 新建 | 业务实体 M2 元模型,覆盖 §3.3 的元类、类型、枚举、约束与 doc | +| `packages/extension/assets/ubml-entity/metamodel/README.md` | 新建 | 说明元模型文件如何维护,哪些内容是首期强约束,哪些只是文档约束 | +| `packages/ubml-codegen/src/entity/metamodel/load.ts` | 新建 | 读取并解析实体元模型,输出稳定的 TypeScript 数据结构 | +| `packages/ubml-codegen/src/entity/metamodel/types.ts` | 新建 | 定义 `UbmlMetamodel`、`MetaClass`、`MetaAttribute`、`MetaReference`、`MetaConstraint` 等类型 | +| `packages/ubml-codegen/src/entity/metamodel/prompt.ts` | 新建 | 从元模型渲染给 Agent 使用的「合法字段类型 / 关联 / UDT / 约束」提示词片段 | +| `packages/ubml-codegen/test/entity-metamodel-consistency.test.ts` | 新建 | 检查元模型与 `datatypes.index.json`、现有 DSL schema、FYBXD 金样保持一致 | +| `packages/extension/assets/ubml-entity/prompts/entitygen.md` | 修改 | 引入由元模型渲染的规则段落,人工提示词只保留工作流和策略 | + +首期一致性测试不用追求覆盖 `gsp-business-entity.schema.json` 的所有字段。建议只验证三类最有价值的不变量: + +| 不变量 | 检查方式 | +|--------|----------| +| 元模型声明的 `datatypes` 与 `datatypes.index.json` 中的 DSL 类型一致 | 读取两份文件,比对 `string/text/integer/decimal/datetime/boolean/enum/association/udt` 等类型集合 | +| 元模型字段分类能覆盖 FYBXD 金样 | 解析 `assets/ubml-entity/exemplars/fybxd.be.yaml`,确认普通字段、枚举、关联、UDT、子实体、规则、管理动作都能映射到元模型中的类 | +| 元模型 catalog 约束与 catalog 文件存在性一致 | `referenceable-be`、`udt`、`components` 三个 catalog 名称必须能解析到真实文件 | + +E1 验收命令建议: + +```bash +bun test packages/ubml-codegen/test/entity-metamodel-consistency.test.ts +bun run ubml:be:verify packages/extension/assets/ubml-entity/exemplars/fybxd.be.yaml +``` + +E1 的退出标准: + +- `ubml-entity.metamodel.yaml` 能覆盖 FYBXD 金样中的全部建模元素; +- 元模型读取器能在 Node/Bun 测试环境稳定解析; +- 一致性测试能在 catalog 缺项或类型名不一致时失败; +- 现有实体编译、校验、预览行为不发生变化。 + +### 6.3 E2:建模操作集 + +E2 是提升生成稳定性的核心阶段。目标不是让大模型「更会写 YAML」,而是让它尽量通过**小粒度、可校验、可回滚**的建模操作来构造实体。 + +现有 MCP 已有 `kb_be_edit`,输入大致是: + +```text +{ yaml, op: addField | addChild | addRule | patchField | removeField, args } +``` + +E2 在此基础上补齐实体元模型中的关键构造操作: + +| 操作 | 作用 | 关键校验 | +|------|------|----------| +| `addScalarField` | 添加 string/text/integer/decimal/datetime/boolean 字段 | 类型存在;decimal 必须有 precision;需要长度的类型必须有 length | +| `addEnumField` | 添加枚举字段 | 枚举值至少 1 个;默认值最多 1 个;code 不重复 | +| `addAssociationField` | 添加外部实体关联 | `ref` 必须命中 `referenceable-be.catalog.json`;`refFields` 必须存在于被引用实体 | +| `addUdtField` | 添加 UDT 字段 | `udt` 必须命中 `udt.catalog.json` | +| `addChildObject` | 添加子实体 | 子实体 code 不重复;不允许孙级子实体;自动补 ParentID 由编译器负责 | +| `addDetermination` | 添加联动计算规则 | source child/field、target field 必须存在;component 可命中 catalog 或按命名约定生成 | +| `addValidation` | 添加校验规则 | triggerFields 必须存在;timing 合法 | +| `addBizAction` | 添加对象动作 | 参数类型合法 | +| `addManagerAction` | 添加管理动作 | `common: true` 时 code 必须命中 `components.catalog.json` | + +实现上建议先保持 `kb_be_edit` 一个工具入口,但把内部拆成注册表: + +```text +beEdit(input) + ├─ parse current YAML + ├─ find operation handler by op + ├─ validate args with operation schema + ├─ apply immutable edit to DSL AST + ├─ run affected constraints from metamodel + ├─ run full kb_be_verify(可配置为 fast/full) + └─ return { yaml, ok, issues, hints, changedPaths } +``` + +建议文件结构: + +| 文件 | 类型 | 责任 | +|------|------|------| +| `packages/ubml-codegen/src/entity/edit/operations.ts` | 新建或扩展 | 操作注册表与 `EntityEditOperation` 类型 | +| `packages/ubml-codegen/src/entity/edit/add-association-field.ts` | 新建 | 关联字段操作 | +| `packages/ubml-codegen/src/entity/edit/add-enum-field.ts` | 新建 | 枚举字段操作 | +| `packages/ubml-codegen/src/entity/edit/add-udt-field.ts` | 新建 | UDT 字段操作 | +| `packages/ubml-codegen/src/entity/edit/add-manager-action.ts` | 新建 | 管理动作操作 | +| `packages/knowledge/src/mcp/server.ts` | 修改 | 扩展 `kb_be_edit` 的 `op` 枚举和 description | +| `packages/knowledge/src/service.ts` | 修改 | 调用新的编辑操作注册表 | +| `packages/knowledge/script/be-modeling-ops-smoke.ts` | 新建 | 以 FYBXD 为目标跑受控编辑冒烟 | +| `packages/knowledge/eval/entity-generation-tasks.json` | 新建 | 自然语言需求评测集 | +| `packages/knowledge/eval/eval-entity-generation-core.ts` | 新建 | 统计一次通过率、修复轮次、错误类型 | + +E2 需要先建立**生成质量基线**,否则无法证明方案变好。建议至少准备 10 条中文需求,覆盖: + +1. 单据 + 明细 + 汇总; +2. 字典主数据; +3. 有两个外部关联的实体; +4. 有枚举字段的实体; +5. 有 UDT 附件字段的实体; +6. 有 beforeSave 校验; +7. 有 afterCreate 默认值; +8. 有管理动作; +9. 刻意包含模糊字段名(测试命名规范); +10. 刻意包含不存在的引用(测试 catalog hint)。 + +评测指标建议: + +| 指标 | 含义 | 首期目标 | +|------|------|----------| +| `verifyPassRate` | 最终三道闸通过率 | 10/10 | +| `firstPassRate` | 首次生成不修复即通过比例 | 先跑现状基线,再要求 E2 高于基线 | +| `avgRepairRounds` | 平均修复轮次 | 低于现状基线 | +| `invalidCatalogReferenceCount` | 编造引用次数 | 接近 0 | +| `manualEditRequiredCount` | 需要人工改 YAML 的次数 | 0 或清楚记录原因 | + +E2 验收命令建议: + +```bash +bun test packages/ubml-codegen/test/entity-edit-operations.test.ts +bun packages/knowledge/script/be-modeling-ops-smoke.ts +bun packages/knowledge/eval/eval-entity-generation-core.ts +``` + +E2 的退出标准: + +- 新增操作能把空实体逐步编辑成 FYBXD 语义等价模型的核心结构; +- 每个操作失败时返回结构化 `issues` 与 `hints`,错误能定位到操作参数; +- `@ubml-entitygen` 提示词明确采用「骨架草案 + 受控精修」工作流; +- 生成质量评测集可以重复运行并产出指标。 + +### 6.4 E3:模型页签 + +E3 解决「生成效果不可见」的问题。它应当先做**只读可视化**,不要在首版同时引入图上编辑,否则 UI 状态、受控编辑和写回会互相放大复杂度。 + +建议首版范围: + +| 能力 | 首版是否做 | 说明 | +|------|------------|------| +| 右侧栏新增 `model` 页签 | 做 | 与 `preview` / `entity-preview` 并列 | +| 模型文件导航 | 做 | 首版可只展示本次生成/预览 payload 中的实体模型,工程全量扫描后置 | +| 实体 ER 图 | 做 | 复用 `entity-preview-model.ts` 的 Mermaid 输出,补外部关联节点 | +| 字段表 | 做 | 复用现有实体预览字段结构 | +| 文件视图 | 做 | YAML/JSON 只读展示 | +| 图上编辑 | 不做 | 后续接 E2 的受控编辑操作 | +| 多实体全景图 | 部分做 | 首版先支持单实体 + 外部引用;多文件聚合后置 | + +建议文件结构: + +| 文件 | 类型 | 责任 | +|------|------|------| +| `packages/extension/src/lib/ubml-model-tab-store.ts` | 新建 | 模型页签 payload:模型列表、选中文件、视图模式 | +| `packages/extension/src/lib/ubml-model-bridge.ts` | 新建 | `openUbmlModelTab()`,生成后打开或刷新模型页签 | +| `packages/extension/src/lib/model-preview-model.ts` | 新建 | 把一个或多个 `EntityPreviewModel` 聚合为模型导航与关系图数据 | +| `packages/extension/src/pages/workbench/ubml-model-panel.tsx` | 新建 | 左侧导航 + 右侧图/文件视图 | +| `packages/extension/src/pages/layout/right-panel.tsx` | 修改 | 增加 `model` tab 类型、图标、可添加类型 | +| `packages/extension/src/pages/layout/right-panel-labels.ts` | 修改 | 增加中英文 tab 文案 | +| `packages/extension/src/pages/layout/project-workspace-shell.tsx` | 修改 | 渲染 `ModelTabContent`,关闭页签时清理 payload | +| `packages/extension/src/lib/ubml-entity-artifact-actions.ts` | 修改 | `previewEntityYaml()` 成功后可同时刷新模型页签 | + +需要注意右侧栏现状:手动加号菜单对每种 tab 类型只保留一个实例;`preview` / `entity-preview` 的桥接函数可以通过 key 创建多个预览 tab。模型页签建议采用**一个常驻 tab + 内部多模型导航**,不要每个模型一个右栏 tab,这样更符合「模型空间」的心智,也避免右栏被多个实体页签挤满。 + +E3 验收命令建议: + +```bash +bun test packages/extension/src/lib/model-preview-model.test.ts +bun run typecheck +``` + +手工验收场景: + +1. 在实体工作台打开 FYBXD 金样,点击预览; +2. 右侧出现或刷新「模型」页签; +3. 左侧导航能看到 `FYBXD`; +4. 关系图能看到 FYBXD 包含 `FYBXDMX`、`FXBXDFJ`,并关联 `GspUser`、`SysOrg`、`Projects`; +5. 切换「文件」视图能看到原始 YAML; +6. 关闭页签后 payload 清理,不影响现有 `entity-preview` 页签。 + +### 6.5 E4:页面推广 + +E4 不应早于 E1–E3 的实体闭环。推广到页面时只复用两件经过验证的东西:元模型描述方式与模型页签导航;不要把页面编译器一次性改造成元模型驱动。 + +首版页面元模型只覆盖最小集合: + +| 页面概念 | 覆盖范围 | +|----------|----------| +| `Page` | `type/title/entity/sections/toolbar` | +| `Section` | `title/layout/fields` | +| `FieldBinding` | `label/bind/editor` | +| `Action` | 工具栏动作与按钮 | +| `RawNode` | 记录 `raw:` 逃逸节点,不解析内部结构 | + +E4 的主要收益是把 crossref 从「AppGen 内部手写校验」逐步抽象为通用 external 引用解析:页面绑定实体字段、查询引用实体、页面流引用页面,最终都能用一套引用解析模型描述。 + +### 6.6 推荐 Issue 拆分 + +为了便于社区推进,建议不要按「E1/E2/E3」各开一个大 Issue,而是拆成可独立合并的小任务。完整 Issue 正文与 Gitee 批量登记见 **[11 · 实体元建模社区 Issue](./11-mof-entity-community-issues.md)**;里程碑名:**`实现自然语言创建并预览业务实体模型`**。 + +| Issue | 标题建议 | 产出 | +|-------|----------|------| +| M1 | Add explicit UBML entity metamodel asset | `ubml-entity.metamodel.yaml` + README | +| M2 | Add entity metamodel parser and consistency tests | parser/types/tests | +| M3 | Render entitygen prompt constraints from metamodel | prompt renderer + prompt 接入 | +| M4 | Add BE edit operations for association/enum/UDT fields | 三个操作 + 单测 | +| M5 | Add BE edit operations for determinations and manager actions | 规则/管理动作操作 + 单测 | +| M6 | Add entity generation evaluation dataset | 10 条中文需求 + eval runner | +| M7 | Add right-panel model tab shell | tab 类型、store、bridge、空状态 | +| M8 | Add model navigation and entity ER graph | 模型导航 + Mermaid 图 + 字段表 | +| M9 | Wire entity generation preview into model tab | `previewEntityYaml()` / 生成成功后的联动 | +| M10 | Draft page metamodel and generic external reference concept | 页面元模型草案 + crossref 设计文档 | + +批量登记:`GITEE_TOKEN=xxx bun run script/gitee/create-gitee-issues.ts --mof` + +推荐先做 M1–M3,再并行 M4–M6 与 M7–M9。M10 等实体闭环稳定后再启动。 + +### 6.7 首轮里程碑定义 + +首轮里程碑命名建议:`EntityGen MoF v0`。它只承诺一件事:**让业务实体生成有显式语言定义、可重复评测、可视化结果**。 + +> **状态(2026-07-04)**:E1–E4 与模型页签页面扩展已归档;验收见 [`docs/MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md`](../MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md)。 + +里程碑完成条件: + +- `FYBXD` 金样可由元模型解释并通过一致性测试; +- 至少 10 条中文实体需求进入 eval,能输出通过率与修复轮次; +- 对话/工作台生成或预览实体后,右侧「模型」页签展示实体结构和关系图; +- 文档、OpenSpec living spec、README 导航同步更新。 + +--- + +## 7. 与现有体系的关系:收敛而非推翻 + +| 现有能力 | 本方案下的去向 | +|----------|----------------| +| `entity:` YAML DSL | 保留,是 M1 的人读/AI 写表示;元模型是它的形式化定义 | +| `ubml-codegen` 确定性编译器 | 保留,负责 M1 → 平台格式(GUID、系统字段、Refs 等机械展开);不要求改写 | +| 三道闸 + 修复回环 | 保留为第 4 层防线;issues 的 hint 逐步改为从元模型约束生成 | +| 四个 catalog JSON | 保留为数据;「谁必须查哪个 catalog」的规则上收到元模型 | +| `kb_be_*` MCP 工具 | 扩展为完整建模操作集(§4.3) | +| Zod / JSON Schema | 短期用一致性测试对齐元模型,远期由元模型生成 | +| 工作台实体编辑器 / 预览 | 保留;模型页签复用其渲染组件 | +| 提示词 / Agent 定义 | 值域与约束段落由元模型渲染,工作流段落人工维护 | + +风险与对策: + +- **元模型与实现脱节**(最大风险):一致性测试是强制闸——E1 起任何改动 catalog/Zod/Schema 而不同步元模型的 PR 会挂 CI; +- **操作集会话状态管理**:受控精修要求会话内持有「当前模型」,需要明确定义模型状态的存取与并发边界(E2 的 OpenSpec design 重点); +- **过度工程**:M3 词汇冻结在 7 个构造,不实现完整 OCL;表达式引擎按需演进,首版允许约束以 TypeScript 函数落地。 + +--- + +## 8. 社区贡献切入点 + +| 方向 | 适合谁 | 起点 | +|------|--------|------| +| 元模型定义与一致性测试(E1) | 熟悉 TypeScript / 对建模理论有兴趣 | §3.3 骨架 + `packages/ubml-codegen/src/entity/dsl/schema.ts` 对照 | +| 建模操作集(E2) | 熟悉 MCP / `packages/knowledge` | `kb_be_edit` 现有五操作的实现 | +| 模型页签 UI(E3) | 熟悉 SolidJS / 前端可视化 | §5.2 步骤表 + `right-panel.tsx` | +| ER 图渲染 | 前端可视化 | `entity-preview-model.ts` + Mermaid erDiagram | +| 生成质量评测 | QA / 数据方向 | 为 E2 验收建自然语言需求评测集(参照 `packages/knowledge/eval/` 的做法) | + +任务将按本篇 §6 拆解为 Gitee Issue 登记,见 [11 · 实体元建模社区 Issue](./11-mof-entity-community-issues.md)(里程碑 **`实现自然语言创建并预览业务实体模型`**)。认领方式见 [07 · 协作开发指南](./07-contributing.md)。 + +--- + +## 9. 小结 + +- **一个事实源**:把散落四处的「什么是合法实体模型」收敛为一份显式元模型(M2),Zod、JSON Schema、catalog 约束、提示词、ER 图从它派生或与它对齐; +- **一把钥匙**:格式稳定性不靠提示词约束兜底,靠「大模型不直接产出最终文本」——语义 DSL 缩小生成面、建模操作工具化(MOF 反射式 API)让每一步都被 schema 硬约束并即时校验; +- **一条闭环**:实体优先,从一句自然语言到模型页签里的 ER 图,四个阶段走通全流程,再推广到页面模型。 + +上一篇:[09 · 路线图社区 Issue](./09-roadmap-community-issues.md) diff --git a/docs/product-design/11-mof-entity-community-issues.md b/docs/product-design/11-mof-entity-community-issues.md new file mode 100644 index 0000000000000000000000000000000000000000..351b39129db0e85fa95ee41906fc76f19b5f9724 --- /dev/null +++ b/docs/product-design/11-mof-entity-community-issues.md @@ -0,0 +1,448 @@ +# 11 · 实体元建模社区 Issue 清单(EntityGen MoF) + +> 本文是 [10 · MOF 元建模与实体优先生成](./10-mof-entity-modeling.md) §6.6 的**完整 Issue 正文**,格式与 [09 路线图 Issue](./09-roadmap-community-issues.md) 一致,可经 `script/gitee/create-gitee-issues.ts` 批量登记到 Gitee。 +> +> **Gitee 里程碑**:`实现自然语言创建并预览业务实体模型` + +**与 09 / 08 的分工**: + +| 文档 | Issue 前缀 | 主题 | +|------|------------|------| +| [08 §8](./08-development-activity-workbench.md#8-社区-issue-清单gitee-里程碑) | `W-xx` | Mission 工作台六阶段 | +| [09](./09-roadmap-community-issues.md) | `P1` / `P2` / `P3` / `D` | AppGen、知识库、编译器质量 | +| **本文** | `M1` ~ `M10` | 自然语言生成业务实体 + 模型页签预览 | + +--- + +## 1. 使用方式 + +1. 在 Gitee 创建 Issue 时,把对应小节整段粘贴到正文; +2. 标题用「`[M1]` + 建议标题」; +3. 打上里程碑 **`实现自然语言创建并预览业务实体模型`**; +4. 认领前在评论里说明打算怎么做;较大功能先走 [OpenSpec SDD](../SDD-WORKFLOW.md)。 + +**批量登记**: + +```bash +GITEE_TOKEN=xxx bun run script/gitee/create-gitee-issues.ts --mof +GITEE_TOKEN=xxx bun run script/gitee/create-gitee-issues.ts --mof --dry-run +GITEE_TOKEN=xxx bun run script/gitee/create-gitee-issues.ts --mof --recreate +``` + +**实施状态(2026-07-04)**:M1–M10 对应能力已在主仓交付;OpenSpec 变更已归档。登记 Issue 后可将已完成项 **关闭** 并链到 [`MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md`](../MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md),供社区对照与后续迭代认领。 + +--- + +## 2. Issue 速览 + +| 编号 | 一句话说明 | 难度 | 适合谁 | 对应阶段 | +|------|------------|------|--------|----------| +| **M1** | 新增显式业务实体元模型 YAML 资产 | 低–中 | 熟悉 YAML / 建模 | E1 | +| **M2** | 元模型读取器 + 与 catalog/FYBXD 一致性测试 | 中 | TypeScript | E1 | +| **M3** | 从元模型渲染 entitygen 提示词约束片段 | 低–中 | 熟悉 Agent 提示词 | E1 | +| **M4** | 补齐关联 / 枚举 / UDT 三类 BE 编辑操作 | 中 | ubml-codegen + MCP | E2 | +| **M5** | 补齐联动计算 / 校验 / 管理动作编辑操作 | 中–高 | ubml-codegen + MCP | E2 | +| **M6** | 10 条中文实体生成评测集 + eval runner | 中 | QA / knowledge | E2 | +| **M7** | 右侧栏「模型」页签壳(store / bridge / 空状态) | 低–中 | SolidJS 前端 | E3 | +| **M8** | 模型导航 + 实体 ER 关系图 + 字段表 | 中 | 前端可视化 | E3 | +| **M9** | 实体预览 / 生成成功后联动打开模型页签 | 低–中 | extension | E3 | +| **M10** | 页面元模型草案 + 通用 external 引用设计 | 中 | 编译器 / 建模 | E4 | + +**推荐顺序**:M1 → M2 → M3;之后 M4–M6 与 M7–M9 可并行;M10 在实体闭环稳定后启动。 + +--- + +## 3. E1 元模型显式化(M1–M3) + +--- + +#### M1 · 新增显式 UBML 业务实体元模型资产 + +> **一句话**:用一份 YAML 说清楚「合法实体模型由哪些元类、类型和约束组成」。 + +| 项 | 内容 | +|----|------| +| **难度** | 低–中 | +| **主要改动的包** | `packages/extension/assets/ubml-entity` | +| **建议标题** | `[M1] 新增显式 UBML 业务实体元模型资产` | +| **前置依赖** | 无 | + +**背景** + +当前「什么是合法实体」散落在 Zod、catalog、JSON Schema 和提示词四处。E1 的第一步是把 M2 元模型**显式写成一份机器可读的 YAML**,以 FYBXD 金样为覆盖基准,但不改变现有编译行为。 + +**你要做什么** + +1. 新建 `packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml`,覆盖 BusinessEntity、EntityObject、Field 子类、Rule、ManagerAction 等元类; +2. 声明 datatypes、enums、`containment` / `external` 引用与 catalog 约束; +3. 新建 `metamodel/README.md`,说明维护方式与首期不做 OCL 引擎的边界; +4. 对照 `assets/ubml-entity/exemplars/fybxd.be.yaml`,确认每个语法现象都能映射到元类。 + +**主要涉及的文件** + +- `packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml` +- `packages/extension/assets/ubml-entity/metamodel/README.md` +- `docs/product-design/10-mof-entity-modeling.md` §3.3(参考骨架) + +**怎样算做完** + +- [ ] FYBXD 金样中的字段、子实体、关联、UDT、枚举、规则、管理动作均可映射到元模型; +- [ ] README 说明 M3 七种构造与「约束先做人读规约」的约定; +- [ ] 现有 `entity:` 编译与校验行为无变化。 + +--- + +#### M2 · 元模型读取器与一致性测试 + +> **一句话**:在代码里能稳定读取元模型,并用测试保证它与 catalog、FYBXD 金样一致。 + +| 项 | 内容 | +|----|------| +| **难度** | 中 | +| **主要改动的包** | `packages/ubml-codegen` | +| **建议标题** | `[M2] 元模型读取器与一致性测试` | +| **前置依赖** | M1 | + +**背景** + +元模型 YAML 只有被程序读取并参与测试,才能成为「唯一事实源」。本任务实现轻量 parser/types,并建立三类不变量测试。 + +**你要做什么** + +1. 新建 `src/entity/metamodel/load.ts`、`types.ts`,解析元模型为稳定 TS 结构; +2. 新建 `test/entity-metamodel-consistency.test.ts`: + - 元模型 `datatypes` 与 `datatypes.index.json` 一致; + - FYBXD 金样元素可映射到元类; + - catalog 名称(referenceable-be / udt / components)可解析到真实文件; +3. 在 `packages/ubml-codegen/src/index.ts` 导出必要 API(若需要)。 + +**主要涉及的文件** + +- `packages/ubml-codegen/src/entity/metamodel/load.ts` +- `packages/ubml-codegen/src/entity/metamodel/types.ts` +- `packages/ubml-codegen/test/entity-metamodel-consistency.test.ts` + +**怎样算做完** + +- [ ] `bun test packages/ubml-codegen/test/entity-metamodel-consistency.test.ts` 通过; +- [ ] catalog 缺项或类型名不一致时测试失败; +- [ ] 不改变现有 Zod DSL 校验逻辑。 + +--- + +#### M3 · 从元模型渲染 entitygen 提示词约束 + +> **一句话**:把「合法类型 / 约束清单」从元模型自动生成,注入 entitygen 提示词,减少双头维护。 + +| 项 | 内容 | +|----|------| +| **难度** | 低–中 | +| **主要改动的包** | `packages/ubml-codegen` + `packages/extension/assets` | +| **建议标题** | `[M3] 从元模型渲染 entitygen 提示词约束` | +| **前置依赖** | M2 | + +**背景** + +提示词里的值域与约束段落若手写,极易与 catalog 和 Zod 脱节。应从元模型渲染「合法字段类型表」「catalog 约束摘要」等片段,人工提示词只保留工作流策略。 + +**你要做什么** + +1. 新建 `src/entity/metamodel/prompt.ts`,渲染 Agent 可用的约束 Markdown 片段; +2. 修改 `assets/ubml-entity/prompts/entitygen.md`,引入渲染段落(或构建时注入占位符); +3. 确保渲染结果与 M2 一致性测试同源。 + +**主要涉及的文件** + +- `packages/ubml-codegen/src/entity/metamodel/prompt.ts` +- `packages/extension/assets/ubml-entity/prompts/entitygen.md` + +**怎样算做完** + +- [ ] entitygen 提示词含由元模型生成的类型 / 约束段落; +- [ ] 修改元模型 datatypes 后,渲染片段与一致性测试同步变化; +- [ ] 现有实体生成流程可正常加载提示词。 + +--- + +## 4. E2 建模操作集(M4–M6) + +--- + +#### M4 · BE 编辑操作:关联 / 枚举 / UDT 字段 + +> **一句话**:扩展 `kb_be_edit`,让大模型用受控小步操作添加易错的三类字段。 + +| 项 | 内容 | +|----|------| +| **难度** | 中 | +| **主要改动的包** | `packages/ubml-codegen` + `packages/knowledge` | +| **建议标题** | `[M4] BE 编辑操作:关联 / 枚举 / UDT 字段` | +| **前置依赖** | M2 | + +**背景** + +关联、枚举、UDT 是最常编造引用或漏必填项的三类字段。应通过 `addAssociationField` / `addEnumField` / `addUdtField`(或等价 op)逐步构造,每步即时校验 catalog 与元模型约束。 + +**你要做什么** + +1. 在 `src/entity/edit/` 实现三类操作及参数 schema; +2. 单步校验:`ref-in-catalog`、`single-default`、`udt-in-catalog` 等; +3. 扩展 `packages/knowledge/src/mcp/server.ts` 与 `service.ts` 的 `kb_be_edit` op 枚举; +4. 补充 `test/entity-edit-operations.test.ts` 覆盖成功与失败 hint。 + +**主要涉及的文件** + +- `packages/ubml-codegen/src/entity/edit/operations.ts` +- `packages/knowledge/src/mcp/server.ts` +- `packages/knowledge/src/service.ts` +- `packages/ubml-codegen/test/entity-edit-operations.test.ts` + +**怎样算做完** + +- [ ] 三类操作可把空实体补全为含关联 / 枚举 / UDT 的结构; +- [ ] 失败时返回结构化 `issues` + 可定位参数的 `hints`; +- [ ] `bun test packages/ubml-codegen/test/entity-edit-operations.test.ts` 通过。 + +--- + +#### M5 · BE 编辑操作:联动计算 / 校验 / 管理动作 + +> **一句话**:补齐 determination、validation、bizAction、managerAction 等规则与管理动作操作。 + +| 项 | 内容 | +|----|------| +| **难度** | 中–高 | +| **主要改动的包** | `packages/ubml-codegen` + `packages/knowledge` | +| **建议标题** | `[M5] BE 编辑操作:联动计算 / 校验 / 管理动作` | +| **前置依赖** | M4 | + +**背景** + +FYBXD 金样含汇总 determination、金额校验、审批动作等。需让 Agent 通过受控操作添加规则与管理动作,而不是一次生成整段 YAML。 + +**你要做什么** + +1. 实现 `addDetermination` / `addValidation` / `addBizAction` / `addManagerAction`(或统一 op 名); +2. 校验 triggerFields、timing、component catalog、`common: true` 管理动作等; +3. 新建 `packages/knowledge/script/be-modeling-ops-smoke.ts`,以 FYBXD 为目标跑冒烟; +4. 更新 `docs/UBML-ENTITY-VERIFY.md` 操作说明。 + +**主要涉及的文件** + +- `packages/ubml-codegen/src/entity/edit/operations.ts` +- `packages/knowledge/script/be-modeling-ops-smoke.ts` +- `docs/UBML-ENTITY-VERIFY.md` + +**怎样算做完** + +- [ ] 冒烟脚本逐步编辑接近 FYBXD 核心结构; +- [ ] 管理动作 `common: true` 时 catalog 校验生效; +- [ ] Agent 提示词采用「骨架草案 + 受控精修」工作流描述。 + +--- + +#### M6 · 实体生成质量评测集 + +> **一句话**:准备 ≥10 条中文自然语言需求,统计通过率与修复轮次,证明 E2 真的更稳。 + +| 项 | 内容 | +|----|------| +| **难度** | 中 | +| **主要改动的包** | `packages/knowledge/eval` | +| **建议标题** | `[M6] 实体生成质量评测集` | +| **前置依赖** | M4(可与 M5 并行) | + +**背景** + +没有可重复评测,就无法证明「建模操作化」比纯提示词更稳。需参照 `packages/knowledge/eval/` 现有做法建立实体专用任务集与 runner。 + +**你要做什么** + +1. 新建 `eval/entity-generation-tasks.json`(≥10 条中文需求,覆盖 §6.3 所列场景); +2. 新建 `eval/eval-entity-generation-core.ts`,输出 `verifyPassRate`、`avgRepairRounds` 等; +3. 文档说明如何本地运行与解读指标。 + +**主要涉及的文件** + +- `packages/knowledge/eval/entity-generation-tasks.json` +- `packages/knowledge/eval/eval-entity-generation-core.ts` +- `docs/MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md`(可链接) + +**怎样算做完** + +- [ ] 评测集 ≥10 条且场景覆盖清单完整; +- [ ] runner 可重复执行并输出 JSON / 控制台摘要; +- [ ] 文档写明首期指标目标与 Agent 自动化 eval 的边界。 + +--- + +## 5. E3 模型页签(M7–M9) + +--- + +#### M7 · 右侧栏「模型」页签壳 + +> **一句话**:在对话面板右侧增加「模型」页签类型,打通 store 与 open 桥接,先做空状态。 + +| 项 | 内容 | +|----|------| +| **难度** | 低–中 | +| **主要改动的包** | `packages/extension` | +| **建议标题** | `[M7] 右侧栏「模型」页签壳` | +| **前置依赖** | 无(可与 E2 并行) | + +**背景** + +用户生成实体后看不到「模型空间」。先在右侧栏注册 `model` 页签,建立 payload store 与 `openUbmlModelTab()` 桥接,内容区可暂为占位。 + +**你要做什么** + +1. `right-panel.tsx` / `right-panel-labels.ts` 增加 `model` tab 类型与图标; +2. 新建 `ubml-model-tab-store.ts`、`ubml-model-bridge.ts`; +3. `project-workspace-shell.tsx` 挂载空面板组件; +4. 采用「一个常驻 model tab + 内部导航」策略,避免每实体一个 tab。 + +**主要涉及的文件** + +- `packages/extension/src/lib/ubml-model-tab-store.ts` +- `packages/extension/src/lib/ubml-model-bridge.ts` +- `packages/extension/src/pages/layout/right-panel.tsx` +- `packages/extension/src/pages/layout/project-workspace-shell.tsx` + +**怎样算做完** + +- [ ] 手动可从 UI 添加「模型」页签并看到空状态; +- [ ] `openUbmlModelTab()` 去重打开 / 刷新同一 tab; +- [ ] 关闭 tab 后 payload 清理。 + +--- + +#### M8 · 模型导航与实体 ER 关系图 + +> **一句话**:左导航列出实体模型,右侧用 Mermaid ER 图 + 字段表展示结构与关系。 + +| 项 | 内容 | +|----|------| +| **难度** | 中 | +| **主要改动的包** | `packages/extension` | +| **建议标题** | `[M8] 模型导航与实体 ER 关系图` | +| **前置依赖** | M7 | + +**背景** + +工作台已有单实体预览组件;模型页签的增量是多文件导航、多实体关系全景与外部关联节点。应复用 `entity-preview-model.ts`,避免重造预览体系。 + +**你要做什么** + +1. 新建 `model-preview-model.ts`,聚合 `EntityPreviewModel` 为导航 + 图数据; +2. 新建 `ubml-model-panel.tsx`:左导航 + 右图 / 文件视图切换; +3. 集成 Mermaid `erDiagram`(或现有 mermaid 组件),区分包含边与关联边; +4. 补充 `model-preview-model.test.ts`。 + +**主要涉及的文件** + +- `packages/extension/src/lib/model-preview-model.ts` +- `packages/extension/src/pages/workbench/ubml-model-panel.tsx` +- `packages/extension/src/components/mermaid-diagram.tsx`(若需) +- `packages/extension/src/lib/model-preview-model.test.ts` + +**怎样算做完** + +- [ ] FYBXD 预览可见 FYBXD ↔ FYBXDMX 包含、GspUser 等外部关联; +- [ ] 点击节点可展开字段表;文件视图可读 YAML; +- [ ] `bun test packages/extension/src/lib/model-preview-model.test.ts` 通过。 + +--- + +#### M9 · 实体预览 / 生成联动模型页签 + +> **一句话**:实体 verify / preview 成功后自动打开或刷新「模型」页签。 + +| 项 | 内容 | +|----|------| +| **难度** | 低–中 | +| **主要改动的包** | `packages/extension` | +| **建议标题** | `[M9] 实体预览 / 生成联动模型页签` | +| **前置依赖** | M8 | + +**背景** + +模型页签只有与生成 / 预览流程联动,才能完成「自然语言 → 看见 ER 图」闭环。verify 不应强制打开 preview(仅 Preview 按钮触发),但 preview 成功应刷新模型页签。 + +**你要做什么** + +1. 修改 `ubml-entity-artifact-actions.ts`:`previewEntityYaml()` 成功后调用 `openUbmlModelTab()`; +2. 对话内 artifact 卡预览路径同样刷新模型页签; +3. Dev Lab / 工作台路由不误把实体 YAML 推到页面编辑器(surface 路由正确)。 + +**主要涉及的文件** + +- `packages/extension/src/lib/ubml-entity-artifact-actions.ts` +- `packages/extension/src/lib/ubml-model-bridge.ts` +- `packages/extension/src/components/ubml-artifact-card.tsx` + +**怎样算做完** + +- [ ] 实体工作台 Preview 后模型页签自动展示 FYBXD; +- [ ] Verify alone 不自动开 Preview; +- [ ] 对话生成实体卡片 Preview 后模型页签同步更新。 + +--- + +## 6. E4 页面推广(M10) + +--- + +#### M10 · 页面元模型草案与通用 external 引用 + +> **一句话**:用同一套 M3 词汇描述页面模型,并把 crossref 抽象为 external 引用解析。 + +| 项 | 内容 | +|----|------| +| **难度** | 中 | +| **主要改动的包** | `packages/extension/assets/ubml` + `packages/ubml-codegen` | +| **建议标题** | `[M10] 页面元模型草案与通用 external 引用` | +| **前置依赖** | M1–M3,建议 M7–M9 稳定后 | + +**背景** + +实体闭环走通后,同一元建模机制应推广到页面:Page / Section / FieldBinding 最小集合,以及 `FieldBinding.bind → entity field` 的 crossref 通用化。 + +**你要做什么** + +1. 新建 `assets/ubml/metamodel/ubml-page.metamodel.yaml` + README; +2. 新建 `src/page/metamodel/` 读取器与 `page-metamodel-consistency.test.ts`; +3. 模型页签导航增加 `pages/` 分组(只读展示即可); +4. 文档说明 crossref 从手写校验过渡到 external 引用的路径。 + +**主要涉及的文件** + +- `packages/extension/assets/ubml/metamodel/ubml-page.metamodel.yaml` +- `packages/ubml-codegen/src/page/metamodel/` +- `packages/ubml-codegen/test/page-metamodel-consistency.test.ts` +- `openspec/specs/ubml-model-tab-pages/spec.md` + +**怎样算做完** + +- [ ] 页面元模型覆盖 FYBXD 卡片页 exemplar 的核心结构; +- [ ] 一致性测试通过; +- [ ] 模型页签可展示页面条目(可与实体并列导航)。 + +--- + +## 7. Gitee 里程碑与认领提示 + +| Gitee 里程碑 | 范围 | Issue 前缀 | +|--------------|------|------------| +| **`实现自然语言创建并预览业务实体模型`** | 本文 §3–§6 | M1 ~ M10 | + +| 你是… | 建议从这些 Issue 开始 | +|--------|----------------------| +| **第一次贡献** | M1、M3、M7 | +| **熟悉 ubml-codegen** | M2、M4、M5、M10 | +| **熟悉 MCP / knowledge** | M4、M5、M6 | +| **熟悉 SolidJS / 前端** | M7、M8、M9 | + +**提 PR 时请附上**:关联 Issue 编号(如 `[M4]`)、验证命令、若动到生成管线请附 exemplar 路径。 + +相关文档:[10 MOF 元建模](./10-mof-entity-modeling.md) · [07 协作指南](./07-contributing.md) · [MOF 验收清单](../MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md) diff --git a/docs/product-design/README.md b/docs/product-design/README.md index cdf425ee4707af4576832ae981360ed3fc598208..3a80a308a3aa38bed28a10e3ccdd37f9199aafbc 100644 --- a/docs/product-design/README.md +++ b/docs/product-design/README.md @@ -11,8 +11,8 @@ |--------|----------| | 第一次了解项目 | 本页 → [01 背景与愿景](./01-background-and-vision.md) → [06 路线图](./06-roadmap.md) | | 准备搭环境、写代码 | [03 技术架构](./03-technical-architecture.md) → [07 协作开发指南](./07-contributing.md) | -| 想领任务做功能 | [06 路线图 §5](./06-roadmap.md#5-社区-issue-清单) · [08 工作台 Issue](./08-development-activity-workbench.md#8-社区-issue-清单gitee-里程碑) · [09 路线图 Issue](./09-roadmap-community-issues.md) | -| 做 UBML 编译/生成 | [04 UBML 主线](./04-ubml-pipeline.md) | +| 想领任务做功能 | [06 路线图 §5](./06-roadmap.md#5-社区-issue-清单) · [08 工作台 Issue](./08-development-activity-workbench.md#8-社区-issue-清单gitee-里程碑) · [09 路线图 Issue](./09-roadmap-community-issues.md) · [11 实体 MoF Issue](./11-mof-entity-community-issues.md) | +| 做 UBML 编译/生成 | [04 UBML 主线](./04-ubml-pipeline.md) → [10 MOF 元建模](./10-mof-entity-modeling.md) | | 做知识库 / MCP | [05 知识服务](./05-knowledge-and-context.md) | **已在 Gitee 登记的可认领任务**:https://gitee.com/ubml/code-agent/issues(里程碑:`AppGen v1`、`开发活动工作台 v1`、`知识库 v1` 等) @@ -53,6 +53,8 @@ code-agent 是在 [OpenCode](https://github.com/anomalyco/opencode) 智能体引 | 07 | [协作开发指南](./07-contributing.md) | 环境、红线、OpenSpec、PR 清单 | | 08 | [开发活动工作台](./08-development-activity-workbench.md) | Mission 六阶段、页面流、W-xx Issue 全文 | | 09 | [路线图社区 Issue](./09-roadmap-community-issues.md) | P1/P2/P3/D Issue 全文与 Gitee 里程碑 | +| 10 | [MOF 元建模与实体优先生成](./10-mof-entity-modeling.md) | 元建模语言、稳定生成五层防线、模型页签([MoF v0 验收清单](../MOF-ENTITYGEN-V0-ACCEPTANCE-CHECKLIST.md)) | +| 11 | [实体元建模社区 Issue](./11-mof-entity-community-issues.md) | M1–M10 Issue 全文;里程碑「实现自然语言创建并预览业务实体模型」 | --- diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/.openspec.yaml b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..43e65ca6e6676e6633758c5c953465c553b5b879 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-03 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/design.md b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/design.md new file mode 100644 index 0000000000000000000000000000000000000000..dcd43ea3cccd12856e8b028d9f0b5ceb58f043da --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/design.md @@ -0,0 +1,129 @@ +## Context + +- **路线图**:P1-02([09-roadmap-community-issues.md](../../docs/product-design/09-roadmap-community-issues.md) §3) +- **参照模式**:pagegen `kb_ubml_verify` 3 轮(`assets/ubml/prompts/pagegen.md`);entitygen `kb_be_verify` 3 轮 +- **现状**:`@ubml-appgen` agent 定义有「max 3 rounds」但未实现结构化回灌;工作台 Generate 与 Ask Agent **解耦** +- **前置**:A-5 app 编排 ✅;Generate 返回 `AppGenerateResult.validation.issues` + +## Goals / Non-Goals + +**Goals** + +- App YAML 产出后 **自动触发 verify**(浏览器 Generate 或 Agent 自调 CLI) +- 校验 issues 格式化为 Agent 可消费 hints(stage / path / message / hint / suggestedFix) +- ≤3 轮自动修复回环;超限明确 UI/日志提示 +- 工作台「Fix with Agent」一键回灌当前 issues +- 不破坏现有手动编辑 + Generate 流程 +- 单测覆盖:一次通过、两轮修复后通过、超限停止 + +**Non-Goals** + +- 服务端无人值守 Agent 循环(首期人在环确认每轮 Agent 回复) +- 自动写回项目目录 +- Maven 编译失败修复(P1-03) +- 子 DSL 文件自动 patch(Agent 仍产出完整 YAML 文本) + +## Decisions + +### D1: 双轨实现策略 + +| 轨道 | 职责 | 优先级 | +|------|------|--------| +| **A. Prompt 驱动** | Agent 按 appgen.md 自行循环 verify(对齐 pagegen) | 首期必做 | +| **B. 工作台编排** | Extension 在 Generate 失败后构建 follow-up prompt | 首期必做 | +| **C. MCP kb_app_verify** | Agent 在 OpenCode 会话内调 MCP 验证 | stretch | + +A + B 组合:Agent 在 IDE 内可调 MCP/CLI;工作台用户点「Fix with Agent」时 Extension 注入结构化 issues。 + +### D2: Hints 数据结构 + +扩展 `AppValidationIssue`(additive,optional 字段): + +```ts +interface AppValidationIssue { + path: string + message: string + stage?: string + hint?: string // 人类可读修复建议 + suggestedFix?: string // 可选 YAML 片段或字段值 +} +``` + +`formatAppIssuesForAgent(issues, round, maxRounds)` → markdown block: + +```markdown +## 校验失败(第 2/3 轮) + +| stage | path | message | hint | +|-------|------|---------|------| +| crossref | derive.from | ... | 将 FYBXD 加入 entities[] | +``` + +### D3: crossref 典型 hints 映射 + +| issue 模式 | hint | +|------------|------| +| derive.from ∉ entities | `将 "${from}" 加入 app.entities[] 或修正 derive.from` | +| pageflow frm 缺失 | `在 app.pages[] 增加 "${frm}" 并配置 sources.pages.${frm}` | +| logic writeback 无 javagen | `在 app.logic[] 保留 writeback 并确认 BE 含回写规则占位` | +| component BeCode 未知 | `修正 component DSL 的 be 引用或扩展 entities[]` | + +### D4: Prompt API 扩展 + +```ts +type UbmlAppgenRepairContext = { + round: number + maxRounds: number + issues: AppValidationIssue[] + lastYaml: string +} + +formatUbmlAppgenAgentPrompt(input, guide?) +formatUbmlAppgenRepairPrompt(repair: UbmlAppgenRepairContext, guide?) +``` + +Repair prompt 以「仅修正下列 issues,勿改动已通过 stage 对应配置」开头。 + +### D5: 工作台 UX + +``` +[Generate] [Generate & Repair Loop] [Ask Agent] [Fix with Agent (disabled until fail)] +``` + +- **Generate**:现有行为不变 +- **Fix with Agent**:`lastResult.validation.valid === false` 时启用;调用 `formatUbmlAppgenRepairPrompt` + `home.startNewAgent` +- **Repair loop state**:`repairRound` signal(0~3),显示在 status 区 +- Agent 返回 YAML 后用户可粘贴/自动合并到 editor → 再 Generate(半自动回环) + +全自动回环(无需用户粘贴)留 Phase 1 后期或需 Dev Lab artifact 通道。 + +### D6: Agent 定义更新 + +`ubml-appgen.md` 增加: + +1. 产出 YAML 后立即 `ubml:app:generate` 或 `kb_app_verify` +2. 按 stage 分类处理 failures +3. 3 轮上限后输出剩余 issues 清单 + +### D7: 测试策略 + +| 用例 | 方法 | +|------|------| +| hints 格式化 | 单元测试 `format-app-issues.test.ts` | +| repair prompt 含 round 计数 | `ubml-appgen-prompt.test.ts` | +| crossref hint 覆盖 | 改 derive.from 触发 issue + hint | +| 两轮修复 | mock issues 序列 → prompt 轮次递增 | + +## Risks + +| 风险 | 缓解 | +|------|------| +| Agent 忽略 hints | prompt 中表格化 + 明确「只改失败 stage」 | +| 浏览器无法调 MCP | 工作台用 in-browser Generate 作 verify | +| 与子 DSL 分离 | issues.path 指向 app.sources 或子文件逻辑路径 | +| 与 P1-01 pages stage 新增 failures | crossref hints 预留 page 相关规则 | + +## Migration + +- `AppValidationIssue.hint` optional — 无 breaking change +- 现有 Generate / Ask Agent 行为默认不变,新按钮 opt-in diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/proposal.md b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..b949b0485db22bad587f06e9b8369335a03d4645 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/proposal.md @@ -0,0 +1,78 @@ +## Why + +Phase 1 AppGen 端到端 **P1-02**:`@ubml-appgen` Agent 与工作台 **Generate** 已能产出 `app.yaml` 并跑 crossref,但 **verify 失败时仍靠人工改 YAML**。pagegen(`kb_ubml_verify` ≤3 轮)与 entitygen(`kb_be_verify` ≤3 轮)已有成熟的「校验 → hints → 再生成」模式,AppGen 应复用同一哲学,减少人在环次数。 + +当前 gap: + +- `ubml-appgen-prompt.ts` 仅一行提及「≤3 轮」,无结构化 hints 格式与失败 stage 指引 +- `assets/ubml-app/prompts/appgen.md` 标注为草稿,未对齐 pagegen 的 verify 失败处理章节 +- 工作台 Ask Agent 一次性派发,**不**在 Agent 返回后自动触发 Generate / 回灌 issues +- crossref issues 缺少面向 Agent 的 `hints` 字段(path + message 已有,无修复建议) + +## What Changes + +### 轨 1:结构化 hints 与 prompt 对齐 + +| 模块 | 说明 | +|------|------| +| `packages/ubml-codegen/src/app/validate/` | 新增 `format-app-issues.ts`:将 `AppValidationIssue[]` 格式化为 Agent/MCP 可消费 hints | +| `packages/ubml-codegen/src/app/validate/crossref.ts` | 典型 crossref 错误附带 `hint` 建议(如 derive.from 不在 entities) | +| `packages/extension/assets/ubml-app/prompts/appgen.md` | 对齐 pagegen.md:verify 失败处理、stage 分类、3 轮上限 | +| `packages/extension/assets/ubml-app/agents/ubml-appgen.md` | 强化自动修复回环工作流 | + +### 轨 2:Extension prompt 与编排 + +| 模块 | 说明 | +|------|------| +| `packages/extension/src/lib/ubml-appgen-prompt.ts` | 支持 `repairContext`(轮次、issues、hints);`formatAppRepairFollowUp()` | +| `packages/extension/src/lib/ubml-appgen-prompt.test.ts` | 覆盖 hints 格式化、follow-up prompt | + +### 轨 3:工作台修复回环 UX + +| 模块 | 说明 | +|------|------| +| `packages/extension/src/pages/workbench/ubml-app-editor.tsx` | 「Generate & Repair」或 Agent 返回后自动 verify;失败时一键「Fix with Agent」回灌 | +| `packages/extension/src/lib/ubml-appgen-repair-loop.ts` | 纯函数:管理轮次计数、是否超限、构建 follow-up prompt | + +### 轨 4:可选 MCP 工具(若 Agent 需服务端 verify) + +| 模块 | 说明 | +|------|------| +| `packages/knowledge/src/mcp/` | 可选 `kb_app_verify(yaml)` — 包装 `generateAppFromYaml`,返回 stages + hints | + +首期以 **prompt 驱动 + 工作台编排** 为主;MCP 工具为 stretch(Agent 在 CLI 环境可直接调 `ubml:app:generate`)。 + +### 明确不做 + +- 修改 vendor 包 +- 自动修改子 DSL 文件(仅改 app.yaml 与子 sources 路径声明) +- MAVEN 硬闸(**P1-03** 独立 change) +- 超过 3 轮仍静默重试 + +## Capabilities + +### Modified Capabilities + +- `ubml-app-workbench`:修复回环 UX、Agent follow-up 派发、轮次状态展示 +- `ubml-appgen-orchestrator`:validation issues 含可选 `hint` 字段 + +### New Capabilities(可选 stretch) + +- `ubml-appgen-agent`:Agent 工作流规约(verify 回环、hints 格式、3 轮上限) + +## Impact + +| 区域 | 影响 | +|------|------| +| `packages/extension/src/lib/` | **扩展** — prompt、repair-loop | +| `packages/extension/src/pages/workbench/` | **扩展** — ubml-app-editor | +| `packages/extension/assets/ubml-app/` | **更新** — prompts、agents | +| `packages/ubml-codegen/src/app/validate/` | **扩展** — hints 格式化、crossref hints | +| `packages/knowledge/src/mcp/` | **可选** — kb_app_verify | +| vendor / fork | **无** | + +**前置依赖**:app 编排 CLI ✅;与 **P1-01 pagegen** 无硬依赖,但完整回环验收建议 P1-01 完成后测多 stage 失败场景。 + +**Rollback**:revert prompt/UX 扩展;crossref hint 字段为 additive,不影响现有校验逻辑。 + +**参考**:社区 Issue **P1-02**;`docs/product-design/09-roadmap-community-issues.md` §3。 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/specs/ubml-app-workbench/spec.md b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/specs/ubml-app-workbench/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..b8cf2cc5469b305fbecd441a15f4571bf1354170 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/specs/ubml-app-workbench/spec.md @@ -0,0 +1,54 @@ +# ubml-app-workbench + +Extension UBML 工作台 **Apps** 入口:`app:` YAML 编辑、浏览器内 Generate、应用总览 Preview、Ask Agent 派发 `@ubml-appgen`、**校验失败修复回环**。 + +## MODIFIED Requirements + +### Requirement: Appgen agent dispatch + +Workbench SHALL provide Ask Agent using `@ubml-appgen` prompt. Install script SHALL copy `ubml-appgen.md` agent definition. + +When the latest Generate result has `validation.valid === false`, workbench SHALL expose a **Fix with Agent** action that dispatches a follow-up prompt containing structured validation issues and hints (round counter ≤ 3). + +#### Scenario: Ask Agent with intent + +- **WHEN** user enters intent and clicks Ask Agent with active project +- **THEN** new agent session starts with appgen prompt including intent and current YAML draft + +#### Scenario: Fix with Agent after validation failure + +- **WHEN** Generate fails with crossref or sub-pipeline issues +- **AND** user clicks Fix with Agent +- **THEN** a new agent session starts with repair prompt listing issues by stage with hints +- **AND** prompt indicates current repair round out of maximum 3 + +#### Scenario: Fix with Agent disabled when valid + +- **WHEN** latest Generate result has validation.valid true +- **THEN** Fix with Agent control is disabled or hidden + +### Requirement: In-browser app generation + +Apps editor SHALL invoke `generateAppFromDsl` with bundled FYBXD sub-DSL sources and display validation stages **including repair round status when user is in a repair loop**. + +#### Scenario: FYBXD exemplar generates successfully + +- **WHEN** user loads fybxd app exemplar and clicks Generate +- **THEN** all stages report valid and Preview shows artifact list + +#### Scenario: Display repair round in status + +- **WHEN** user has triggered Fix with Agent after a failed Generate +- **THEN** status area shows repair round `n/3` until success or limit reached + +## ADDED Requirements + +### Requirement: Repair loop limit enforcement + +Workbench repair loop SHALL NOT dispatch more than 3 consecutive repair prompts for the same editing session without user acknowledgment. + +#### Scenario: Block fourth repair round + +- **WHEN** user has already dispatched 3 repair prompts without achieving validation.valid +- **THEN** Fix with Agent is disabled +- **AND** status shows remaining issues and manual edit guidance diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/specs/ubml-appgen-agent/spec.md b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/specs/ubml-appgen-agent/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..675bdc6e46bcef0714e7dd7ef0c770b8478a14b0 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/specs/ubml-appgen-agent/spec.md @@ -0,0 +1,43 @@ +# ubml-appgen-agent + +`@ubml-appgen` Agent 工作流:自然语言 → `app:` YAML → 编排 verify → 结构化 hints 修复回环(≤3 轮)。 + +## ADDED Requirements + +### Requirement: Three-round verify repair loop + +The `@ubml-appgen` agent workflow SHALL after producing or modifying `app:` YAML run app-level verification (CLI `ubml:app:generate` or equivalent) and repair failures using structured issues **up to 3 rounds** before reporting remaining issues to the user. + +#### Scenario: Single-round success + +- **WHEN** agent produces app YAML that passes all stages on first verify +- **THEN** agent reports success with stage summary and does not enter repair loop + +#### Scenario: Two-round repair + +- **WHEN** first verify fails with crossref issue containing hint +- **AND** agent applies hint and re-verifies +- **THEN** second verify passes and agent reports round count + +#### Scenario: Stop after third failed round + +- **WHEN** verify still fails after 3 repair attempts +- **THEN** agent stops looping and lists remaining issues with manual next steps + +### Requirement: Stage-classified failure handling + +Agent prompt assets (`appgen.md`, `ubml-appgen.md`) SHALL document failure handling by stage id prefix: `entity:*`, `derive`, `query:*`, `component`, `page:*`, `pageflow`, `javagen`, `crossref`. + +#### Scenario: Prompt documents crossref repair + +- **WHEN** maintainer reads `assets/ubml-app/prompts/appgen.md` +- **THEN** document includes crossref failure section with exemplar hints + +### Requirement: No false MAVEN claims + +Agent SHALL NOT claim MAVEN compile success unless Maven gate was explicitly run (P1-03). + +#### Scenario: Agent defers Maven to CLI + +- **WHEN** user asks for compile verification without --maven +- **THEN** agent states DSL/crossref validation only and mentions optional `--maven` for Java compile gate diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/specs/ubml-appgen-orchestrator/spec.md b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/specs/ubml-appgen-orchestrator/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..3db3eacb04e7224d480093d1d04f34c50ba9e829 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/specs/ubml-appgen-orchestrator/spec.md @@ -0,0 +1,23 @@ +# ubml-appgen-orchestrator + +`app:` YAML 应用清单驱动 entity / derive / query / component / pageflow / javagen 编排生成,位于 `packages/ubml-codegen/src/app/`。 + +## MODIFIED Requirements + +### Requirement: Cross-model consistency checks + +Crossref and sub-pipeline validation issues SHALL support optional **`hint`** and **`suggestedFix`** fields to guide Agent repair loops. + +#### Scenario: Crossref issue includes hint for derive.from mismatch + +- **WHEN** derive.from is not in app.entities +- **THEN** crossref issue includes hint suggesting adding the entity or correcting derive.from + +### Requirement: Agent-consumable issue formatting + +The product SHALL provide `formatAppIssuesForAgent(issues, round, maxRounds)` returning markdown suitable for `@ubml-appgen` repair prompts. + +#### Scenario: Format issues table for repair round 2 + +- **WHEN** formatAppIssuesForAgent is called with 2 issues and round=2, maxRounds=3 +- **THEN** output includes heading with round counter and table columns stage, path, message, hint diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/tasks.md b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..f4101360d191883579152330b1c7d38c82d396a7 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-agent-repair-loop/tasks.md @@ -0,0 +1,40 @@ +## 轨 1:hints 格式化(ubml-codegen) + +- [x] 1.1 `AppValidationIssue` 增加可选 `hint`、`suggestedFix` 字段(types + crossref) +- [x] 1.2 `validate/format-app-issues.ts` — `formatAppIssuesForAgent(issues, round, maxRounds)` +- [x] 1.3 `crossref.ts` — 典型错误附带 hint(derive.from、pageflow frm、writeback、component BeCode) +- [x] 1.4 `test/format-app-issues.test.ts` — 表格 markdown 输出、空 issues + +## 轨 2:Agent prompt 与资产 + +- [x] 2.1 重写 `assets/ubml-app/prompts/appgen.md` — 对齐 pagegen verify 失败处理章节 +- [x] 2.2 更新 `assets/ubml-app/agents/ubml-appgen.md` — 3 轮回环、stage 分类、禁止虚假 MAVEN 声明 +- [x] 2.3 `ubml-appgen-prompt.ts` — `UbmlAppgenRepairContext`、`formatUbmlAppgenRepairPrompt()` +- [x] 2.4 `ubml-appgen-prompt.test.ts` — repair prompt 含轮次、issues 表、YAML 草稿 + +## 轨 3:工作台 repair loop UX + +- [x] 3.1 `ubml-appgen-repair-loop.ts` — `canRepairRound()`、`nextRepairRound()`、`buildRepairPrompt()` +- [x] 3.2 `ubml-app-editor.tsx` — 「Fix with Agent」按钮(validation fail 时启用) +- [x] 3.3 status 区展示 repair 轮次 `第 n/3 轮` +- [x] 3.4 Ask Agent 派发时可选附带 `lastResult.validation.issues`(若刚 Generate 失败) +- [ ] 3.5 手验:`bun run dev` → 故意改坏 derive.from → Generate fail → Fix with Agent → prompt 含 hints + +## 轨 4:可选 MCP(stretch) + +- [ ] 4.1 `kb_app_verify` MCP tool — 输入 app yaml,返回 `{ ok, stages, issues, hintsMarkdown }`(stretch,未做) +- [ ] 4.2 MCP smoke + appgen.md 引用该工具(stretch,未做) + +## 轨 5:测试与文档 + +- [x] 5.1 crossref hint 单测:pageflow frm 缺失含 hint +- [x] 5.2 集成场景:mock 两轮 issues → repair prompt round 递增 +- [x] 5.3 更新 `docs/UBML-APPGEN.md` — Agent 修复回环说明 +- [x] 5.4 归档后 sync delta specs + +## 验证清单 + +- [x] `bun run --cwd packages/ubml-codegen test` 全绿(82) +- [x] `bun test src/lib/ubml-appgen-prompt.test.ts` 全绿(7) +- [x] Fix with Agent 在 validation.valid=true 时 disabled +- [x] 3 轮后 `canRepairRound()` 返回 false 且有明确提示 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/.openspec.yaml b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..43e65ca6e6676e6633758c5c953465c553b5b879 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-03 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/design.md b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/design.md new file mode 100644 index 0000000000000000000000000000000000000000..dfe2711b6f528eda994b15848cecbb89130f9188 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/design.md @@ -0,0 +1,12 @@ +## 设计要点 + +- **输入**:标准 JSON 工程布局(`be/`、`query/`、`components/`、`pageflow/`、`pages/`) +- **输出**:`app.yaml` + 子 DSL;`sources.*` 使用 repo 相对路径 +- **Round-trip**:`writeAppProjectFromResult` → `decompileAppFromProject` → `generateAppFromYaml` → P1-04 golden +- **logic**:从 entity rules determinations/actions + writeback 构件推断 + +## 不做 + +- 外部 ROBXDJChenshj 路径硬编码 CI 依赖 +- Java 源码 → logic 反编译 +- query custom 模式完整反编译 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/proposal.md b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..1dcc975aea5279392e53bf3d8238e1e229991e3a --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/proposal.md @@ -0,0 +1,18 @@ +## Why + +Phase 1 **P1-05**:单模型反编译(entity/page)已有,但缺少**应用级**「完整工程 JSON → app.yaml + 子 DSL → 再 generate」闭环,棕地迁移与参考工程编辑路径不完整。 + +## What Changes + +- `packages/ubml-codegen/src/app/decompile/` — 扫描、query/component/pageflow 反编译、编排 +- `ubml-app-decompile` CLI +- round-trip 测试 + smoke(对接 P1-04 golden) +- `docs/UBML-APPGEN.md` 反编译章节 + +## Capabilities + +- `ubml-appgen-orchestrator` — 新增 app decompile 需求 + +## Impact + +- 不改 vendor;query/component/pageflow 反编译为 FYBXD 首期子集,可后续扩展 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/specs/ubml-appgen-orchestrator/spec.md b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/specs/ubml-appgen-orchestrator/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..37cae504568998025b08fd071cde7f96c9ac0292 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/specs/ubml-appgen-orchestrator/spec.md @@ -0,0 +1,27 @@ +## ADDED Requirements + +### Requirement: App-level decompile from project directory + +CLI `ubml-app-decompile` SHALL scan a project directory for UBML JSON artifacts and emit: + +- Top-level `app.yaml` +- Sub-DSL YAML files for entities, queries, components, pageflow, and pages +- `sources` map with repo-relative paths resolvable by `generateAppFromYaml` + +The scanner SHALL recognize `*.be.json`, `*.qo.json`, `*.ccmp.json`, `*.wscmp.json`, `*.pf.json`, and `*.frm.json`. + +#### Scenario: Decompile FYBXD project layout + +- **WHEN** decompile runs on a project written from FYBXD generate output +- **THEN** `app.yaml` lists entities, queries, components, pages, and pageflow +- **AND** sub-DSL files are written under the output directory + +#### Scenario: Round-trip golden regression + +- **WHEN** generate → write project JSON → decompile → generate runs on FYBXD exemplar +- **THEN** second generate passes P1-04 golden semantic compare + +#### Scenario: Skip decompile smoke + +- **WHEN** `UBML_APP_DECOMPILE_SMOKE=0` +- **THEN** roundtrip smoke script exits 0 without running diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/tasks.md b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..b46e0feedcbc704c617c629d8f0a535a012339c7 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-app-decompile/tasks.md @@ -0,0 +1,27 @@ +## 轨 1:扫描与工程布局 + +- [x] 1.1 `scan-project.ts` — 递归扫描 + `writeAppProjectFromResult` +- [x] 1.2 bizobject/namespace 从 BE 推断 + +## 轨 2:子模型反编译 + +- [x] 2.1 `decompile-query.ts` — datasource QO +- [x] 2.2 `decompile-component.ts` — ccmp/wscmp +- [x] 2.3 `decompile-pageflow.ts` +- [x] 2.4 `decompile-app.ts` — entity/page 复用 + logic 推断 + +## 轨 3:CLI 与测试 + +- [x] 3.1 `cli/app-decompile.ts` + package bin +- [x] 3.2 `test/app-decompile.test.ts` — 单元 + round-trip golden +- [x] 3.3 `script/app-decompile-roundtrip-smoke.ts` + +## 轨 4:文档与 spec + +- [x] 4.1 `docs/UBML-APPGEN.md` 反编译章节 +- [x] 4.2 sync living spec + +## 验证 + +- [x] `bun test` 全绿含 app-decompile +- [x] roundtrip smoke exit 0 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/.openspec.yaml b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..43e65ca6e6676e6633758c5c953465c553b5b879 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-03 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/design.md b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/design.md new file mode 100644 index 0000000000000000000000000000000000000000..04cebab9620a9fd7d5736a1742f0fe8216a760c6 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/design.md @@ -0,0 +1,39 @@ +## 上下文 + +FYBXD exemplar(`fybxd-app.yaml`)驱动完整 AppGen 编排。参考工程 ROBXDJChenshj 产物已炼制为 `packages/ubml-codegen/test/fixtures/` 下的 JSON baseline(与 derive/query/component/pageflow 单测共用)。 + +## 目标 + +一条命令 / 一个测试文件验证:**app 清单 → 全管线 compile → 语义等价于参考 baseline**。 + +## 设计 + +### 比较策略 + +| 产物 | 比较方式 | +|------|----------| +| BE | `semanticEquivalentBeExemplar` — 字段/子对象/ref 闭包;忽略 exemplar YAML 未声明的 `SecID`/`SecLevel` | +| DBO | `semanticEquivalentDbo` + `ignoreColumnIds` + 忽略 reference-only 列 | +| VO | `semanticEquivalentVo` + `ignoreBaselineElements` | +| Query / Component / Pageflow | 复用现有 `semanticEquivalent*`(与单测相同 param 配置) | +| Pages | header + validation;3 页 code 齐全 | +| Java | 5 个预期 className(含 writeback) | + +### 跳过与 CI + +- `UBML_GOLDEN_E2E=0` — smoke 脚本 skip exit 0 +- 测试默认在 `bun test` 内运行(无 Maven / 无外部路径依赖) +- `--strict` smoke 参数保留与 maven-smoke 一致模式(golden 无额外 env 硬依赖) + +### 破坏性测试 + +篡改 BE 后 golden diff 输出带 `be:` 前缀的可读差异列表。 + +## 风险 + +- exemplar YAML 与参考 export 字段差集需维护 `FYBXD_REFERENCE_ONLY_BE_FIELDS` +- 列 ID 不比较(yaml 编译 GUID 与 export 不同);类型/PK/nullable/列 code 仍严格比较 + +## 回滚 + +删除 `src/app/golden/`、`app-golden-e2e.test.ts`、smoke 脚本;revert derive options 扩展。 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/proposal.md b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..7734659f118c039b1d30c150161acb2153191b2e --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/proposal.md @@ -0,0 +1,51 @@ +## Why + +Phase 1 AppGen 端到端 **P1-04**:各子管线已有单测,但缺少 **app.yaml → 全量产物 → 与 ROBXDJChenshj 参考语义对齐** 的端到端回归。编排改动(pagegen 串联、crossref、javagen)可能引入静默退化而无法被分散单测捕获。 + +## What Changes + +### 轨 1:`packages/ubml-codegen/src/app/golden/` + +| 模块 | 说明 | +|------|------| +| `compare-app-golden.ts` | 编排 `AppGenerateResult` 与各子管线 semantic compare | +| `compare-be.ts` | BE exemplar 语义对比(忽略参考工程独有字段 SecID/SecLevel) | +| `compare-java.ts` / `compare-page.ts` | Java 类清单与 page header 校验 | +| `load-baseline.ts` | 从 `test/fixtures/` 加载 ROBXDJChenshj 炼制 baseline | + +### 轨 2:测试与冒烟 + +| 模块 | 说明 | +|------|------| +| `test/app-golden-e2e.test.ts` | FYBXD 黄金样本 E2E + 破坏性 diff 可读性 + 确定性 | +| `script/app-generate-golden-smoke.ts` | 一条命令跑 golden;`UBML_GOLDEN_E2E=0` 跳过 | + +### 轨 3:derive semantic compare 扩展 + +- `semanticEquivalentDbo` 支持 `ignoreColumnIds`、`ignoreBaselineColumns`(yaml 编译路径 vs 参考 export 路径) + +### 明确不做 + +- 完整 ROBXDJChenshj 9 页 UI 像素级 diff +- 依赖外部参考工程路径的 CI 强依赖(可选 env 留 P1-05) +- Maven gate 纳入 golden(P1-03 独立 smoke) + +## Capabilities + +### Modified Capabilities + +- `ubml-appgen-orchestrator`:黄金样本 E2E 回归要求 + +## Impact + +| 区域 | 影响 | +|------|------| +| `packages/ubml-codegen/src/app/golden/` | **新建** | +| `packages/ubml-codegen/test/app-golden-e2e.test.ts` | **新建** | +| `packages/ubml-codegen/src/derive/semantic-compare.ts` | **扩展** dbo options | +| `script/app-generate-golden-smoke.ts` | **新建** | +| `docs/UBML-APPGEN.md` | **更新** golden 用法 | + +**前置依赖**:P1-01 pagegen ✅、P1-03 Maven(独立,非 golden 硬依赖) + +**参考**:社区 Issue **P1-04** diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/specs/ubml-appgen-orchestrator/spec.md b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/specs/ubml-appgen-orchestrator/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..e08ade22d6ede396b82982c124c4add5bd688df0 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/specs/ubml-appgen-orchestrator/spec.md @@ -0,0 +1,35 @@ +## ADDED Requirements + +### Requirement: Golden sample E2E regression + +The product SHALL provide an end-to-end golden regression for the FYBXD app exemplar that: + +- Runs full `generateAppFromYaml` (entity → derive → query → component → page → pageflow → javagen) +- Compares output semantically against ROBXDJChenshj-derived baselines in `packages/ubml-codegen/test/fixtures/` +- Ignores reference-only BE fields absent from the exemplar YAML (`SecID`, `SecLevel`) +- Ignores DBO column id drift when comparing yaml-compile vs reference-export paths + +The product SHALL expose `compareAppToFybxdGoldenFromRepo(result, repoRoot)` returning `{ ok, differences[] }`. + +The product SHALL expose `formatGoldenDiff(result)` for readable CI / CLI output. + +#### Scenario: FYBXD golden E2E passes + +- **WHEN** `app-golden-e2e.test.ts` runs generate on fybxd-app.yaml +- **THEN** semantic compare returns ok=true + +#### Scenario: Tampered output fails with readable diff + +- **WHEN** compiled BE is mutated before compare +- **THEN** compare returns ok=false +- **AND** differences include prefixed stage labels (e.g. `be:`) + +#### Scenario: Skip golden smoke + +- **WHEN** `UBML_GOLDEN_E2E=0` +- **THEN** golden smoke script exits 0 without running compare + +#### Scenario: Deterministic generation + +- **WHEN** generate runs twice on the same fybxd-app.yaml +- **THEN** stages and artifacts JSON are identical diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/tasks.md b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..82927b2fff04c1d2bbcc51e0d51e8d3a0ec259ac --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-golden-e2e/tasks.md @@ -0,0 +1,30 @@ +## 轨 1:golden 模块 + +- [x] 1.1 `src/app/golden/types.ts` +- [x] 1.2 `load-baseline.ts` — FYBXD fixtures 加载 +- [x] 1.3 `compare-be.ts` — exemplar BE 语义对比 +- [x] 1.4 `compare-java.ts` / `compare-page.ts` +- [x] 1.5 `compare-app-golden.ts` — 编排各子管线 semantic compare + `formatGoldenDiff` + +## 轨 2:derive 扩展 + +- [x] 2.1 `semanticEquivalentDbo` — `ignoreColumnIds`、`ignoreBaselineColumns` +- [x] 2.2 `semanticEquivalentVo` — `ignoreBaselineElements` + +## 轨 3:测试 + +- [x] 3.1 `test/app-golden-e2e.test.ts` — FYBXD 全链路 golden pass +- [x] 3.2 破坏性 BE tamper → diff 可读 +- [x] 3.3 确定性:两次 generate stages/artifacts 相同 + +## 轨 4:冒烟与文档 + +- [x] 4.1 `script/app-generate-golden-smoke.ts` + `ubml:app:generate:golden-smoke` +- [x] 4.2 `UBML_GOLDEN_E2E=0` skip +- [x] 4.3 更新 `docs/UBML-APPGEN.md` +- [x] 4.4 sync delta spec → `openspec/specs/ubml-appgen-orchestrator/spec.md` + +## 验证清单 + +- [x] `bun run --cwd packages/ubml-codegen test` 全绿 +- [x] `bun run ubml:app:generate:golden-smoke` exit 0 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/.openspec.yaml b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..43e65ca6e6676e6633758c5c953465c553b5b879 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-03 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/design.md b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/design.md new file mode 100644 index 0000000000000000000000000000000000000000..ec6b05faa8b272f6f3a597b5e6500611f1d9e71e --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/design.md @@ -0,0 +1,155 @@ +## Context + +- **路线图**:P1-03([09-roadmap-community-issues.md](../../docs/product-design/09-roadmap-community-issues.md) §3) +- **验收基线**:`docs/UBML-APPGEN-DESIGN.md` §1 — Java 构件能通过 MAVEN 编译 +- **现状**:javagen 档位 A 产出 Tier A placeholder 空类;无 Maven 集成 +- **参照**:平台部署链 `bin/MAVEN`;首期仅 `mvn compile` 最小工程 + +## Goals / Non-Goals + +**Goals** + +- CLI `ubml-app-generate --maven` 在 Java 写出后执行 Maven compile +- 编译通过 → exit 0,stage `maven` valid +- 编译失败 → exit 非 0,JSON 含结构化 `{ file, line, message }[]` +- 未安装 Maven / `mvn` 不在 PATH → 清晰错误,exit 2(与 validation fail 区分) +- env `UBML_MAVEN_SMOKE=0` 或 `--skip-maven` 跳过(CI 无 Maven 环境) +- FYBXD exemplar + `--maven` 本地 smoke 通过 + +**Non-Goals** + +- 引入 GS Cloud / Inspur 平台依赖 JAR +- `mvn test`、打包、部署 +- 浏览器内执行 Maven +- 自动修复 Java 编译错误 + +## Decisions + +### D1: Maven 工程布局 + +在 `--out` 目录(或 temp dir)生成: + +```text +{out}/maven-gate/ + pom.xml + src/main/java/ ← 镜像 javagen files 的 relativePath +``` + +`pom.xml` 最小配置: + +```xml + + 4.0.0 + ubml.appgen + {bizobject}-gate + 1.0.0-SNAPSHOT + + 17 + 17 + UTF-8 + + +``` + +JDK 版本可配置 `--java-version 17`(默认 17)。 + +### D2: 执行时机与 stage 顺序 + +```text +... → javagen → crossref → maven (optional) +``` + +- `--maven` 且 crossref valid 时才跑 Maven(避免无意义编译) +- crossref 已 fail 时跳过 Maven,manifest 注明 `maven: skipped (crossref invalid)` +- Maven fail 时 `validation.valid = false`,issues 并入总 validation + +### D3: Maven 调用 + +```ts +async function runMavenCompile(projectDir: string, options: { + mavenHome?: string + timeoutMs?: number // default 120_000 +}): Promise +``` + +- 命令:`${mavenHome}/bin/mvn` 或 `mvn` on PATH +- args:`-f {projectDir}/pom.xml -q compile` +- 捕获 stdout+stderr;非零 exit → parse errors + +### D4: 错误解析 + +解析 Maven compiler 典型行: + +```text +[ERROR] .../SumAmount.java:[12,5] error: ... +``` + +输出: + +```json +{ + "stage": "maven", + "issues": [ + { "path": "java/.../SumAmount.java", "line": 12, "message": "error: ..." } + ] +} +``` + +解析失败时 fallback 整条 stderr 摘要 issue。 + +### D5: CLI 接口 + +```bash +ubml-app-generate app.yaml --out ./out --maven [--maven-home /path/to/maven] [--java-version 17] +ubml-app-generate app.yaml --out ./out --skip-maven # 显式跳过 +UBML_MAVEN_SMOKE=0 ubml-app-generate app.yaml # env 跳过 +``` + +JSON stdout 增加: + +```json +{ + "ok": true, + "stages": [..., { "id": "maven", "valid": true }], + "maven": { "skipped": false, "durationMs": 1234 } +} +``` + +### D6: Exit codes + +| Code | 含义 | +|------|------| +| 0 | validation + maven(若启用)均通过 | +| 1 | DSL/crossref/maven 校验失败 | +| 2 | Maven 不可用(未安装、timeout) | + +### D7: 测试策略 + +| 用例 | 条件 | +|------|------| +| FYBXD maven pass | `which mvn` 存在时跑;否则 `test.skipIf` | +| 故意语法错误 | fixture java 含 `{` → maven stage fail | +| 无 mvn | mock PATH → exit 2 + 清晰 message | +| skip flags | `--skip-maven` 不 emit maven stage | + +### D8: CI 集成文档 + +`docs/UBML-APPGEN.md` 说明: + +- 本地开发:`--maven` 可选 +- CI job:安装 Maven 3.9+ / Temurin 17 后跑 FYBXD golden +- 无 Maven runner:`UBML_MAVEN_SMOKE=0` 跳过 + +## Risks + +| 风险 | 缓解 | +|------|------| +| CI 无 Maven | skip env + 文档 | +| javagen 未来引入平台 import | 档位 A 无外部依赖;档位 B 另立 pom profile | +| mvn 慢 | timeout + `-q`;仅 compile | +| Windows PATH | 文档注明;首期 macOS/Linux CI | + +## Migration + +- `--maven` opt-in,默认行为不变 +- `docs/UBML-APPGEN.md`「首期不做 MAVEN 闸」删除 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/proposal.md b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..eff2da37d3dd379dc86977ac442e994f82272ae2 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/proposal.md @@ -0,0 +1,65 @@ +## Why + +Phase 1 AppGen 端到端 **P1-03**:AppGen 生成的 Java 构件能否通过 **MAVEN 编译**是应用级验收的硬指标(`docs/UBML-APPGEN-DESIGN.md` §1 成功标准第 3 条),但当前 `ubml-app-generate` CLI **只做 DSL / crossref 校验,不跑真实 Java 编译**。Mission 工作台合并前验收闸(W-03-02)依赖本任务提供底层能力。 + +javagen 档位 A 已产出可解析的 `.java` 骨架(空类 / 占位注释),理论上应能通过 `javac`/`mvn compile`;缺少 Maven 闸意味着 Java 语法错误或包结构问题无法被 AppGen 流水线捕获。 + +## What Changes + +### 轨 1:`packages/ubml-codegen/src/app/maven/` + +| 模块 | 说明 | +|------|------| +| `maven-gate.ts` | 在临时或 `--out` 目录组装最小 Maven 工程,执行 `mvn -q compile` | +| `maven-project.ts` | 生成 `pom.xml` + 目录布局(匹配 javagen `relativePath`) | +| `parse-maven-errors.ts` | 解析 stdout/stderr → `{ file, line, column?, message }[]` | +| `types.ts` | `MavenGateResult`、`MavenGateIssue` | + +### 轨 2:CLI 与编排集成 + +| 模块 | 说明 | +|------|------| +| `cli/app-generate.ts` | 新增 `--maven`、`--maven-home`、`--skip-maven`(或 env `UBML_MAVEN_SMOKE=0`) | +| `generate-app.ts` | `AppGenerateOptions.maven?: boolean`;成功后可选调用 maven gate | +| `generate-app-core.ts` | 新增 stage `maven`(在 crossref 之后或作为最终闸) | +| `types.ts` | `AppGenerateStage` 支持 maven issues | + +### 轨 3:冒烟与文档 + +| 模块 | 说明 | +|------|------| +| `packages/ubml-codegen/test/maven-gate.test.ts` | 有 Maven 时 FYBXD 编译通过;故意语法错误失败 | +| `script/` 或 package script | `ubml:app:generate:maven-smoke` 或扩展现有 smoke | +| `docs/UBML-APPGEN.md` | 删除「首期不做 MAVEN 闸」;文档 `--maven` 用法与 CI 集成 | + +### 明确不做 + +- 完整 GS Cloud 依赖树 / 平台 JAR 引入(首期最小 pom:仅 JDK 编译空类) +- `mvn test` / 集成测试(留 P1-06 javagen 档位 B) +- 浏览器内跑 Maven(工作台仅展示 CLI 结果或跳过提示) +- mdpkg / publish / hotload 部署链(Phase 4) +- 修改 vendor 包 + +## Capabilities + +### Modified Capabilities + +- `ubml-appgen-orchestrator`:可选 Maven compile stage;CLI `--maven`;结构化编译错误输出 + +## Impact + +| 区域 | 影响 | +|------|------| +| `packages/ubml-codegen/src/app/maven/` | **新建** | +| `packages/ubml-codegen/src/app/cli/app-generate.ts` | **扩展** | +| `packages/ubml-codegen/src/app/generate-app.ts` | **扩展** | +| `packages/ubml-codegen/test/` | **新建** maven-gate 测试 | +| `docs/UBML-APPGEN.md` | **更新** | +| `packages/extension/` | **无**(首期) | +| vendor / fork | **无** | + +**前置依赖**:javagen 档位 A ✅;与 P1-01 无硬依赖。 + +**Rollback**:删除 `src/app/maven/`;CLI flags 移除;文档恢复。 + +**参考**:社区 Issue **P1-03**;W-03-02 合并验收闸。 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/specs/ubml-appgen-orchestrator/spec.md b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/specs/ubml-appgen-orchestrator/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..8b10fd80835792e814c4b289576e86558040891a --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/specs/ubml-appgen-orchestrator/spec.md @@ -0,0 +1,80 @@ +# ubml-appgen-orchestrator + +`app:` YAML 应用清单驱动 entity / derive / query / component / pageflow / javagen 编排生成,位于 `packages/ubml-codegen/src/app/`。 + +## MODIFIED Requirements + +### Requirement: App generate CLI + +CLI `ubml-app-generate` with `--out` writes manifest.json and artifact tree. + +CLI SHALL support optional **`--maven`** to run a Maven compile gate on generated Java files after successful crossref validation. + +CLI SHALL support **`--skip-maven`** and environment variable **`UBML_MAVEN_SMOKE=0`** to skip the Maven gate. + +CLI SHALL support optional **`--maven-home`** and **`--java-version`** (default 17). + +#### Scenario: CLI writes output tree + +- **WHEN** CLI succeeds with `--out ./out` +- **THEN** `./out/manifest.json` exists and lists artifact paths + +#### Scenario: Maven gate passes + +- **WHEN** CLI runs with `--maven` on FYBXD exemplar +- **AND** Maven is installed +- **AND** crossref validation passes +- **THEN** exit code is 0 +- **AND** stages include `{ id: "maven", valid: true }` + +#### Scenario: Maven gate fails on compile error + +- **WHEN** generated Java contains syntax errors +- **AND** CLI runs with `--maven` +- **THEN** exit code is 1 +- **AND** maven stage issues include file path and line when parseable + +#### Scenario: Maven unavailable + +- **WHEN** CLI runs with `--maven` but `mvn` is not on PATH +- **THEN** exit code is 2 +- **AND** stderr explains Maven is required but not found + +#### Scenario: Skip Maven gate + +- **WHEN** CLI runs with `--skip-maven` or `UBML_MAVEN_SMOKE=0` +- **THEN** maven stage is omitted or marked skipped +- **AND** exit code follows DSL/crossref validation only + +### Requirement: Orchestrated multi-pipeline generation + +Generation SHALL run in order: entity compile → derive → query compile → component compile → pageflow compile → javagen → crossref → **maven (optional)**. + +When crossref validation fails, Maven gate SHALL be skipped. + +#### Scenario: Maven skipped when crossref invalid + +- **WHEN** crossref stage is invalid +- **AND** `--maven` is set +- **THEN** maven stage is skipped with reason documented in output JSON + +## ADDED Requirements + +### Requirement: Maven project materialization + +When Maven gate is enabled, the product SHALL write a minimal Maven project under the output directory mirroring javagen `relativePath` layout and execute `mvn -q compile`. + +#### Scenario: Java files laid out for compile + +- **WHEN** maven gate runs with javagen output containing `com/example/Foo.java` +- **THEN** file exists at `{out}/maven-gate/src/main/java/com/example/Foo.java` +- **AND** `{out}/maven-gate/pom.xml` exists with compiler source/target matching `--java-version` + +### Requirement: Structured Maven error output + +Maven compile failures SHALL be parsed into issues with `stage: "maven"`, `path`, optional `line`, and `message`. + +#### Scenario: Parse compiler error line + +- **WHEN** Maven stderr contains `[ERROR] .../Foo.java:[12,5] error: ...` +- **THEN** maven issue includes path Foo.java, line 12, and error message diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/tasks.md b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..b12dce6e9376284dc778068c13c39386f1cb80fd --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-maven-gate/tasks.md @@ -0,0 +1,40 @@ +## 轨 1:Maven gate 核心 + +- [x] 1.1 新建 `src/app/maven/types.ts` — `MavenGateResult`、`MavenGateIssue`、`MavenGateOptions` +- [x] 1.2 `maven/maven-project.ts` — `writeMavenProject(outDir, javaFiles, options)` 生成 pom + src layout +- [x] 1.3 `maven/parse-maven-errors.ts` — 解析 compiler `[ERROR]` 行 +- [x] 1.4 `maven/maven-gate.ts` — `runMavenGate(outDir, javaFiles, options)` 执行 mvn compile +- [x] 1.5 单测:`parse-maven-errors.test.ts`(fixture stderr 样本) + +## 轨 2:编排与 CLI 集成 + +- [x] 2.1 `generate-app.ts` — crossref 通过后可选调用 maven gate,push stage `maven` +- [x] 2.2 `types.ts` — `AppGenerateOptions.maven`、`skipMaven`、`mavenHome`、`javaVersion` +- [x] 2.3 `cli/app-generate.ts` — flags: `--maven`、`--maven-home`、`--java-version`、`--skip-maven` +- [x] 2.4 CLI JSON 输出增加 `maven: { skipped, durationMs, issues? }` +- [x] 2.5 Exit code:validation fail=1,maven unavailable=2 + +## 轨 3:FYBXD 回归与 skip 行为 + +- [x] 3.1 `test/maven-gate.test.ts` — `test.skipIf(!hasMavenCompileEnv())` FYBXD compile pass +- [x] 3.2 故意破坏 Java syntax fixture → maven stage fail + 可读 issues +- [x] 3.3 `UBML_MAVEN_SMOKE=0` / `--skip-maven` 不跑 maven、不 fail +- [x] 3.4 crossref invalid 时 skip maven 且 manifest 注明原因 + +## 轨 4:冒烟脚本 + +- [x] 4.1 根 `package.json` — `ubml:app:generate:maven-smoke` +- [x] 4.2 无 Maven/Java 时脚本友好 skip(`--strict` exit 2) + +## 轨 5:文档 + +- [x] 5.1 更新 `docs/UBML-APPGEN.md` — CLI 示例、exit codes、CI env +- [x] 5.2 更新 `assets/ubml-app/prompts/appgen.md` — MAVEN 默认跳过说明与 `--maven` 触发条件 +- [x] 5.3 sync delta spec 到 `openspec/specs/ubml-appgen-orchestrator/spec.md` + +## 验证清单 + +- [x] `bun run --cwd packages/ubml-codegen test` 全绿(无 JDK 时 maven compile 用例 skip) +- [x] 无 `--maven` 时行为与现网一致 +- [x] 无 Java 时 `--maven` 返回 unavailable + exit 2 +- [ ] 本地有 JDK+Maven 时 FYBXD `--maven` exit 0(环境依赖,CI/手验) diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/.openspec.yaml b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..43e65ca6e6676e6633758c5c953465c553b5b879 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-03 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/design.md b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/design.md new file mode 100644 index 0000000000000000000000000000000000000000..da58fb50a9aaaa87af6b60d203e7a8687be8371e --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/design.md @@ -0,0 +1,118 @@ +## Context + +- **路线图**:Phase 1 AppGen 端到端 P1-01([09-roadmap-community-issues.md](../../docs/product-design/09-roadmap-community-issues.md) §3) +- **前置**:A-5 `ubml-appgen-orchestrator` 已归档;pagegen 全链路(M-A~M-I)✅ +- **现状**:`app.pages[]` 在 schema 存在但未执行;pageflow 用 `loadFrmIndex(frmDir)` 读静态 fixture +- **参照**:pageflow exemplar 声明 3 个 frm(FYBXDJ / FYBXDJManagement / FYBXDJQuery);首期至少编译其中 2 个 + +## Goals / Non-Goals + +**Goals** + +- `app.pages[]` 驱动 pagegen 批量编译,每页产生 `page:{code}` stage +- pageflow 优先消费编排器产出的 frm 内存索引,而非外部 `frmDir` fixture +- crossref 校验 pages ↔ pageflow ↔ 编译产物三方一致 +- CLI `--out` 与 writeback 含 `pages/{code}.frm.json` +- FYBXD exemplar 扩展场景测试通过(≥2 pages) +- 浏览器 Apps Generate 与 CLI 行为一致(bundled page YAML) + +**Non-Goals** + +- 从自然语言自动生成 page YAML +- 前端 webcmp/webcmd(P1-07) +- MAVEN 闸(P1-03) +- 完整 9 页 ROBXDJChenshj 对齐(首期 2~3 页 exemplar 即可) + +## Decisions + +### D1: 编排顺序调整 + +```text +entity → derive → query → component → page(s) → pageflow → javagen → crossref +``` + +page 阶段在 pageflow **之前**,以便 pageflow 编译器通过 `frmIndex`(内存 Map)而非 `frmDir` 获取 `.frm` Header.Code。 + +### D2: sources.pages 形状 + +```yaml +app: + pages: [FYBXDJ, FYBXDJQuery] + sources: + pages: + FYBXDJ: ubml/exemplars/fybxdj-card.yaml + FYBXDJQuery: ubml/exemplars/fybxdj-query.yaml + # frmDir 保留为 fallback:当 pages[] 为空但 pageflow 存在时仍可用 fixture +``` + +路径解析复用 `resolveAppSources` 既有 candidate 逻辑(repo 根 / assets 根 / extension assets)。 + +### D3: AppBundleInput 与结果类型扩展 + +```ts +interface AppBundleInput { + // ...existing + pageYamls?: Map +} + +interface AppGenerateResult { + // ...existing + pages?: Record +} +``` + +`GenerateAppFromDslOptions` 新增 `pageCompileCtx: CompileContext`(与 pagegen CLI 共用 assets 目录)。 + +### D4: pageflow 集成方式 + +`compilePageflowFromYaml` 已支持 `frmIndex?: FrmIndex`。编排器在 page 阶段结束后构建: + +```ts +const frmIndex = buildFrmIndexFromPages(pages) // code → parsed .frm json +pageflow = await compilePageflowFromYaml(yaml, { frmIndex }) +``` + +当 `pages[]` 为空且 `sources.frmDir` 存在时,**保持现有行为**(向后兼容)。 + +### D5: crossref 扩展 + +| 检查 | 规则 | +|------|------| +| pages 声明 | `app.pages[]` 每项均有编译结果且 stage valid | +| pageflow 闭合 | pageflow.pages[].frm ∈ 已编译 page codes | +| 反向 | 已编译 page code 若出现在 pageflow 中则必须通过 validation | +| 遗留 frmDir | 若仅用 frmDir、无 pages[],crossref 规则不变 | + +### D6: 输出布局 + +``` +{out}/ + pages/FYBXDJ.frm.json + pages/FYBXDJQuery.frm.json + ...(be/ derive/ query/ components/ pageflow/ java/ 不变) +``` + +`collectAppArtifacts` 增加 `{ kind: "frm", code }`;Preview Mermaid 可后续着色(非本变更阻塞项)。 + +### D7: FYBXD page exemplar 策略 + +1. 以 `test/fixtures/frm/FYBXDJ*.frm.json` 为语义目标 +2. 用 `decompilePageFromJson` 反编译为 page YAML,或手工编写最小 card/list DSL(绑定 FYBXD BE) +3. 验收:编译产物 `Header.Code` 与 fixture 一致,validation.valid + +首期交付 **FYBXDJ**(card)+ **FYBXDJQuery**(list/query);FYBXDJManagement 可作为第三个 stretch goal。 + +## Risks + +| 风险 | 缓解 | +|------|------| +| page YAML 难从 fixture 反编译 | 允许最小可编译子集,不要求 layout 字节一致 | +| 浏览器 bundle 体积增大 | 仅 bundle 2~3 个 page YAML(与 entity/query 同级) | +| pageflow 与 pagegen API 漂移 | 编排层薄包装,复用 `compilePageFromYamlWithContext` | +| 破坏现有 FYBXD smoke | 保留 frmDir fallback;测试同时覆盖 pages 路径 | + +## Migration + +- `fybxd-app.yaml` 增加 `pages[]` + `sources.pages`;测试迁移到 pages 路径 +- `frmDir` 不立即删除,文档标注 deprecated fallback +- 无 breaking API change(新增可选字段) diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/proposal.md b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..b1a7f87f29d8194a51aaaf5823c5df79b892a6c3 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/proposal.md @@ -0,0 +1,67 @@ +## Why + +Phase 1 AppGen 端到端(P1-01)的首要缺口:`app:` 清单 DSL 已声明 `pages[]`,A-5 编排器也已串联 entity / derive / query / component / pageflow / javagen,但 **pagegen 未接入**——pageflow 仍依赖 `sources.frmDir` 指向静态 `.frm.json` fixture(`packages/ubml-codegen/test/fixtures/frm`),而非从 page YAML 实时编译。 + +没有 pagegen 批量生成,AppGen 产出的应用缺少完整前端页面,无法与 FYBXD / `ROBXDJChenshj` 参考工程对齐,也阻塞 P1-04 黄金样本 E2E 与 P1-07 webcmp 集成。 + +## What Changes + +### 扩展 `packages/ubml-codegen/src/app/` + +| 模块 | 说明 | +|------|------| +| `dsl/schema.ts` | `sources.pages` 路径映射(`Record`) | +| `resolve-sources.ts` | 解析 `app.pages[]` → page YAML 绝对路径;FYBXD 默认 exemplar | +| `generate-app-core.ts` | 新增 page 编译阶段(component 之后、pageflow 之前);`pages` 结果写入 `AppGenerateResult` | +| `validate/crossref.ts` | 校验 `app.pages[]` ↔ pageflow.pages[].frm ↔ 已编译 frm | +| `plan-writeback.ts` | 输出 `pages/{code}.frm.json` | +| `types.ts` | `AppGenerateResult.pages` 字段 | + +### FYBXD page exemplar 资产 + +| 路径 | 说明 | +|------|------| +| `packages/extension/assets/ubml/exemplars/fybxdj*.yaml` | 至少 2 个页面(card + list/query),对齐 pageflow 中 FYBXDJ / FYBXDJQuery | +| `packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml` | 增加 `pages[]` 与 `sources.pages`;`frmDir` 改为可选 fallback | + +### Extension 浏览器路径 + +| 模块 | 说明 | +|------|------| +| `packages/extension/src/ubml-app/catalog.ts` | bundled page YAML 加入 `getFybxdAppBundle()` | +| `packages/extension/src/ubml-app/browser-generate.ts` | 传递 `pageYamls` 到编排器 | + +### 文档 + +- 更新 `docs/UBML-APPGEN.md` 编排顺序与输出布局 +- 关联 `docs/product-design/09-roadmap-community-issues.md` P1-01 + +### 明确不做(留后续 P1-xx) + +- `@ubml-appgen` Agent 自动修复回环(**P1-02**) +- MAVEN 编译硬闸(**P1-03**) +- 黄金样本 E2E 回归套件(**P1-04**) +- webcmp/webcmd 逻辑生成(**P1-07**) +- 修改 vendor 包 + +## Capabilities + +### Modified Capabilities + +- `ubml-appgen-orchestrator`:编排 pipeline 增加 pagegen 阶段;crossref 扩展页面引用闭合;writeback 含 `.frm` 产物。 + +## Impact + +| 区域 | 影响 | +|------|------| +| `packages/ubml-codegen/src/app/` | **扩展** — resolver / pipeline / crossref / writeback / types | +| `packages/extension/assets/ubml/` | **新建** — FYBXD page exemplar YAML | +| `packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml` | **扩展** — pages + sources.pages | +| `packages/extension/src/ubml-app/` | **扩展** — browser bundle | +| `packages/ubml-codegen/test/app-generate.test.ts` | **扩展** — 多页面 smoke | +| `docs/UBML-APPGEN.md` | **更新** | +| vendor / fork | **无** | + +**Rollback**:revert `src/app/` 扩展与 exemplar 资产;pageflow 恢复 `frmDir` fixture 行为。 + +**参考**:`docs/UBML-APPGEN-DESIGN.md` §2.1 前端 `.frm` 清单;社区 Issue **P1-01**。 diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/specs/ubml-appgen-orchestrator/spec.md b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/specs/ubml-appgen-orchestrator/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..c18257e4e9bda8a19184f3300e3db95b3cf2f2b9 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/specs/ubml-appgen-orchestrator/spec.md @@ -0,0 +1,80 @@ +# ubml-appgen-orchestrator + +`app:` YAML 应用清单驱动 entity / derive / query / component / **page** / pageflow / javagen 编排生成,位于 `packages/ubml-codegen/src/app/`。 + +## MODIFIED Requirements + +### Requirement: Orchestrated multi-pipeline generation + +Generation SHALL run in order: entity compile → derive → query compile → component compile → **page compile (when `pages[]` declared)** → pageflow compile → javagen. + +Each step SHALL aggregate validation into `stages[]` with stage ids `page:{code}` for each compiled page. + +#### Scenario: FYBXD app generates all artifact groups including pages + +- **WHEN** generate runs on fybxd-app exemplar with `pages[]` and `sources.pages` configured +- **THEN** result includes be, derive, query, components, **frm pages**, pageflow, and java files +- **AND** all stages report valid + +#### Scenario: Page stage skipped when pages empty + +- **WHEN** `app.pages` is omitted or empty and `sources.frmDir` is provided +- **THEN** page compile stages are not emitted +- **AND** pageflow MAY use frmDir index as before + +### Requirement: Cross-model consistency checks + +Crossref SHALL verify: + +- Query/component BE bindings resolve to compiled entities +- Pageflow validation passes when frm index is available (from compiled pages or frmDir) +- **Each `app.pages[]` entry has a valid compiled frm with matching Header.Code** +- **Each pageflow `pages[].frm` references a compiled page when `app.pages[]` is non-empty** +- logic containing writeback enables javagen writeback template + +#### Scenario: Crossref fails when pageflow references missing frm + +- **WHEN** pageflow lists frm `FYBXDJManagement` but `app.pages[]` does not include it and no frmDir fallback exists +- **THEN** crossref stage fails with structured issue naming the missing frm code + +#### Scenario: Crossref fails on unknown entity in derive + +- **WHEN** derive.from references entity not in manifest +- **THEN** crossref stage fails with structured issue + +### Requirement: App generate CLI + +CLI `ubml-app-generate` with `--out` writes manifest.json and artifact tree **including `pages/*.frm.json` when pages are compiled**. + +#### Scenario: CLI writes page artifacts + +- **WHEN** CLI succeeds with `--out ./out` and app manifest declares `pages: [FYBXDJ, FYBXDJQuery]` +- **THEN** `./out/pages/FYBXDJ.frm.json` and `./out/pages/FYBXDJQuery.frm.json` exist + +## ADDED Requirements + +### Requirement: Page source resolution in app manifest + +The `app.sources` map SHALL support optional `pages: Record` mapping page codes to page YAML paths relative to assets root or repo root. + +When `app.pages[]` lists a code without an explicit `sources.pages` entry, the resolver SHALL attempt known default exemplar paths for FYBXD pages or fail with a clear error. + +#### Scenario: Resolve FYBXD page sources from defaults + +- **WHEN** fybxd-app.yaml lists `pages: [FYBXDJ]` without explicit sources.pages.FYBXDJ +- **AND** a default exemplar path is registered for FYBXDJ +- **THEN** resolver returns absolute path to the page YAML + +#### Scenario: Reject missing page source + +- **WHEN** `app.pages[]` includes code `UnknownPage` with no sources.pages entry and no default +- **THEN** resolution fails before compile with error naming the page code + +### Requirement: Browser app generation includes pages + +Browser-safe app generation (`app-browser` / extension Apps Generate) SHALL bundle FYBXD page YAML assets and produce the same page stages as CLI for the fybxd exemplar. + +#### Scenario: Apps Generate emits page stages + +- **WHEN** user clicks Generate on fybxd-app exemplar in Apps workbench +- **THEN** result stages include `page:FYBXDJ` (and other configured pages) with valid=true diff --git a/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/tasks.md b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..7f1d07683df64cd564d4b6a77285912a6a83b0a1 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-appgen-pagegen-integration/tasks.md @@ -0,0 +1,52 @@ +## 轨 1:DSL 与 source resolver + +- [x] 1.1 `dsl/schema.ts` — `sources.pages` 可选 `Record` +- [x] 1.2 `resolve-sources.ts` — 解析 `app.pages[]` → `Map`;FYBXD 默认路径(FYBXDJ、FYBXDJQuery) +- [x] 1.3 `loadBundleFromSources` — 读取 page YAML 文本到 `AppBundleInput.pageYamls` +- [x] 1.4 单测:缺 page source 抛错;默认 FYBXD 路径解析成功 + +## 轨 2:FYBXD page exemplar 资产 + +- [x] 2.1 创建 `packages/extension/assets/ubml/exemplars/fybxdj-card.yaml`(绑定 FYBXD,Header.Code=FYBXDJ) +- [x] 2.2 创建 `packages/extension/assets/ubml/exemplars/fybxdj-query.yaml`(Header.Code=FYBXDJQuery) +- [x] 2.3 可选:`fybxdj-management.yaml`(FYBXDJManagement,stretch) +- [x] 2.4 验证:单独 `ubml:compile` 各 page YAML 通过 validation + +## 轨 3:编排器 page 阶段 + +- [x] 3.1 `types.ts` — `AppGenerateResult.pages`、`AppBundleInput.pageYamls` +- [x] 3.2 `generate-app-core.ts` — page 循环调用 `compilePageFromYamlWithContext`;push `page:{code}` stage +- [x] 3.3 `buildFrmIndexFromPages()` — 从编译结果构建 pageflow 所需 `FrmIndex` +- [x] 3.4 pageflow 阶段改用 `{ frmIndex }`(pages 非空时);保留 frmDir fallback +- [x] 3.5 `collectAppArtifacts` — 增加 `{ kind: "frm", code }` +- [x] 3.6 `generate-app.ts` — 加载 `CompileContext`(page assets dir)传入编排器 + +## 轨 4:crossref 与 writeback + +- [x] 4.1 `validate/crossref.ts` — pages ↔ pageflow frm 双向闭合校验 +- [x] 4.2 `plan-writeback.ts` — 输出 `pages/{code}.frm.json` +- [x] 4.3 单测:故意缺 page → pageflow/crossref 失败且 issue 可读 + +## 轨 5:FYBXD app exemplar 与回归 + +- [x] 5.1 更新 `fybxd-app.yaml`:`pages: [FYBXDJ, FYBXDJQuery]` + `sources.pages` +- [x] 5.2 扩展 `test/app-generate.test.ts`:断言 page stages、frm artifacts、crossref +- [x] 5.3 CLI 冒烟:`ubml:app:generate fybxd-app.yaml --out ./out` 含 pages 目录 +- [x] 5.4 `bun run --cwd packages/ubml-codegen test` 全绿 + +## 轨 6:Extension 浏览器路径 + +- [x] 6.1 `catalog.ts` — bundled page YAML 加入 `getFybxdAppBundle().pageYamls` +- [x] 6.2 `browser-generate.ts` — 传递 pageYamls + pageCompileCtx +- [ ] 6.3 手验:`bun run dev` → Apps → Generate → Preview 见 page stages + +## 轨 7:文档 + +- [x] 7.1 更新 `docs/UBML-APPGEN.md` — 编排顺序、sources.pages、输出布局 +- [x] 7.2 归档后 sync delta spec 到 `openspec/specs/ubml-appgen-orchestrator/spec.md` + +## 验证清单 + +- [x] `bun run typecheck` 无新增错误(ubml-codegen / extension 范围;desktop 既有错误除外) +- [x] FYBXD app generate 产出 ≥2 个 `.frm.json` 且 validation.valid +- [x] frmDir-only 旧路径仍可通过(backward compat 测试或文档说明) diff --git a/openspec/changes/archive/2026-07-03-ubml-javagen-tier-b/.openspec.yaml b/openspec/changes/archive/2026-07-03-ubml-javagen-tier-b/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..43e65ca6e6676e6633758c5c953465c553b5b879 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-javagen-tier-b/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-03 diff --git a/openspec/changes/archive/2026-07-03-ubml-javagen-tier-b/proposal.md b/openspec/changes/archive/2026-07-03-ubml-javagen-tier-b/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..b38c16d3b16faf8c4335df62e43bb26e390475aa --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-javagen-tier-b/proposal.md @@ -0,0 +1,14 @@ +## Why + +P1-06:javagen 档位 A 仅覆盖模板化逻辑;非模板业务需要**白名单 API 约束**下的可编译骨架 + 测试桩,为 Agent 受约束生成与工作台 W-03-05 提供基础。 + +## What Changes + +- API 白名单 YAML + `validateJavaAgainstWhitelist` +- tier B logic spec YAML + `compileTierBLogic` +- JUnit 5 测试桩 + platform stub 类 +- CLI `--tier-b`、Maven `test-compile` 冒烟 + +## Capabilities + +- 新增 `ubml-javagen` spec delta(或 extend orchestrator) diff --git a/openspec/changes/archive/2026-07-03-ubml-javagen-tier-b/tasks.md b/openspec/changes/archive/2026-07-03-ubml-javagen-tier-b/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..0100cce9a681b5413ce8eb9841663107fac7c07b --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-javagen-tier-b/tasks.md @@ -0,0 +1,21 @@ +## 轨 1:白名单 + +- [x] 1.1 `whitelist/default-api-whitelist.yaml` +- [x] 1.2 `validate-java-api.ts` + +## 轨 2:tier B 生成 + +- [x] 2.1 `tier-b/schema.ts` + `parse-spec.ts` +- [x] 2.2 `render-tier-b.ts` + `compile-tier-b.ts` +- [x] 2.3 集成 `compileJavagen` + CLI `--tier-b` + +## 轨 3:测试与 Maven + +- [x] 3.1 `test/fixtures/fybxd-check-quota.logic-b.yaml` +- [x] 3.2 `test/javagen-tier-b.test.ts` +- [x] 3.3 `maven-project` 支持 test sources + JUnit pom +- [x] 3.4 `script/javagen-tier-b-smoke.ts` + +## 轨 4:文档 + +- [x] 4.1 更新 `docs/UBML-JAVAGEN.md` diff --git a/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/.openspec.yaml b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b4bbeb946f4c4d9310ee8730c5f89564bc95e9f0 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/.openspec.yaml @@ -0,0 +1 @@ +schema: spec-driven diff --git a/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/design.md b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/design.md new file mode 100644 index 0000000000000000000000000000000000000000..7bb21f0454c414c8adc6eb912d7c81d35ef66411 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/design.md @@ -0,0 +1,14 @@ +## Context + +参考 `docs/UBML-APPGEN-DESIGN.md` 中 `FYBXDJ_frm_Controller` 与 `.webcmp`/`.ts` 产物形态。 + +## Decisions + +1. **输入 DSL**:独立 `webcmd:` YAML(非扩展 component DSL),与 page toolbar 语义对齐。 +2. **回退**:`app.weblogic[]` 无 `sources.weblogic` 时,从已编译 page 的 `toolbar[]` 自动推导命令。 +3. **产物**:JSON webcmp 模型 + TS class(`FrmContext.callService` 桩);轻量 TS 语法校验,非完整 tsc。 +4. **集成点**:page 编译之后、javagen 之前;artifact kind `webcmp`。 + +## Risks + +- 真实平台 webcmp schema 可能与当前简化 Header/Content 有差异 → P1 仅语义对齐 FYBXD toolbar/save。 diff --git a/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/proposal.md b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..6af1c259de5eef36d955c24e5245bfc63f65f242 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/proposal.md @@ -0,0 +1,14 @@ +## Why + +P1-07:pagegen 已能生成 `.frm` 布局,但前端命令/事件逻辑(`.webcmp`、`.ts`)仍靠手工。AppGen 端到端需要 webcmd DSL 确定性展开。 + +## What Changes + +- 新增 `webcmd:` YAML DSL 与 `packages/ubml-codegen/src/weblogic/` 编译器 +- 产出 `{Page}_frm_Controller.webcmp.json` 与 TypeScript 控制器桩 +- AppGen 编排 `weblogic` stage;manifest / writeback / crossref 集成 +- FYBXD `FYBXDJ` save 等 toolbar 命令验收 + +## Capabilities + +- `ubml-appgen-orchestrator` spec delta(weblogic stage) diff --git a/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/specs/ubml-appgen-orchestrator/spec.md b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/specs/ubml-appgen-orchestrator/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..71504f6394edeb6a6f27bc727c0a5caf244095a2 --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/specs/ubml-appgen-orchestrator/spec.md @@ -0,0 +1,35 @@ +## ADDED Requirements + +### Requirement: Weblogic webcmd generation stage + +The product SHALL support optional `app.weblogic[]` listing page codes whose frontend command logic SHALL be compiled after page compile and before javagen. + +The `app.sources` map SHALL support optional `weblogic: Record` mapping page codes to `webcmd:` YAML paths. + +When no webcmd YAML is configured for a weblogic page code, the compiler SHALL derive commands from the compiled page DSL `toolbar[]`. + +Each weblogic item SHALL emit: + +- `{Controller}.webcmp.json` with Header (WebComponent) and Content.Commands[] +- `{Controller}.ts` exporting `FrmContext` and controller class with handler methods calling `context.callService(service)` + +#### Scenario: FYBXDJ webcmd from exemplar YAML + +- **WHEN** fybxd-app.yaml includes `weblogic: [FYBXDJ]` and sources.weblogic.FYBXDJ +- **THEN** generation emits FYBXDJ_frm_Controller webcmp and TS with save/onSave command +- **AND** weblogic stage is valid + +#### Scenario: Auto-derive from page toolbar + +- **WHEN** weblogic lists a page compiled with toolbar `[add, edit, save, cancel]` and no webcmd YAML +- **THEN** commands match toolbar order with default handlers onAdd/onSave etc. + +#### Scenario: Crossref fails when weblogic page missing output + +- **WHEN** app.weblogic[] declares a page that neither compiles nor has webcmd YAML +- **THEN** crossref reports missing compiled weblogic output + +#### Scenario: Writeback includes weblogic artifacts + +- **WHEN** planAppWritebackFiles runs on FYBXD result with weblogic +- **THEN** output tree includes weblogic/*.webcmp.json and weblogic/*.ts diff --git a/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/tasks.md b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..61ca399c453ee1b9af6476f1f681d1f421ff36fb --- /dev/null +++ b/openspec/changes/archive/2026-07-03-ubml-weblogic-gen/tasks.md @@ -0,0 +1,19 @@ +## 1. webcmd DSL 与编译器 + +- [x] 1.1 `weblogic/dsl/schema.ts` — `webcmd:` zod schema +- [x] 1.2 `compile-webcmp.ts` / `render-ts.ts` — webcmp JSON + TS 生成 +- [x] 1.3 `verify-ts.ts` — 轻量语法校验 +- [x] 1.4 `fybxdj-webcmd.yaml` exemplar + +## 2. AppGen 集成 + +- [x] 2.1 `app.weblogic[]` + `sources.weblogic` in app DSL +- [x] 2.2 `generate-app-core` weblogic stage +- [x] 2.3 `plan-writeback` weblogic 产物路径 +- [x] 2.4 crossref + manifest artifacts (`webcmp`) + +## 3. 验证 + +- [x] 3.1 `test/weblogic-compile.test.ts` +- [x] 3.2 更新 `app-generate.test.ts` FYBXD 断言 +- [x] 3.3 `script/weblogic-smoke.ts` + npm script diff --git a/openspec/changes/archive/2026-07-04-agent-home-message-timeline/.openspec.yaml b/openspec/changes/archive/2026-07-04-agent-home-message-timeline/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..decbdd6c4becf91599d4dfc36cb3c7f85309a805 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-agent-home-message-timeline/.openspec.yaml @@ -0,0 +1,2 @@ +name: agent-home-message-timeline +status: active diff --git a/openspec/changes/archive/2026-07-04-agent-home-message-timeline/proposal.md b/openspec/changes/archive/2026-07-04-agent-home-message-timeline/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..50d08dd7900758392ba0afbbbacfa6ba541ad4d1 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-agent-home-message-timeline/proposal.md @@ -0,0 +1,15 @@ +## Why + +P3-05:Agent Home 中央区仍为静态品牌欢迎页,选中会话后未展示真实消息时间线(customize-agent-home 任务 5.1 未完成)。 + +## What Changes + +- `HomeSessionTimeline` — 复用 `MessageTimeline` + SDK providers +- `ChatArea` — 有项目/会话时嵌入时间线;无项目/无会话友好空状态 +- `agent-home-context` — `previewSessionId`;在 `/` 点击侧栏会话预览不强制跳转 +- `MessageTimeline` — `sessionIdOverride` 支持首页嵌入 +- 中文产品文案与「在完整会话中继续」入口 + +## Capabilities + +- agent-home spec delta diff --git a/openspec/changes/archive/2026-07-04-agent-home-message-timeline/specs/agent-home/spec.md b/openspec/changes/archive/2026-07-04-agent-home-message-timeline/specs/agent-home/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..356d1be5e720d90eebecb7b896c91039c016ac9d --- /dev/null +++ b/openspec/changes/archive/2026-07-04-agent-home-message-timeline/specs/agent-home/spec.md @@ -0,0 +1,30 @@ +## ADDED Requirements + +### Requirement: Central area shows session message timeline when a session is selected + +When the user is on Agent Home (`/`) with an active project and at least one session, the central chat area SHALL embed the same MessageTimeline component used on session routes, showing historical messages for the previewed or selected session. + +#### Scenario: User selects a session on home route + +- **WHEN** the user is on `/` with a project and clicks a session in the left panel +- **THEN** the central area displays that session's message timeline +- **AND** the sidebar highlights the selected session + +#### Scenario: User opens full session from home preview + +- **WHEN** the user clicks「在完整会话中继续」on the home timeline preview +- **THEN** the app navigates to `/:dir/session/:id` with the same session + +### Requirement: Friendly empty states on Agent Home + +The central chat area SHALL show Chinese empty-state copy when no project is selected or when a project has no sessions yet, instead of only static brand placeholder text. + +#### Scenario: No project + +- **WHEN** the user is on `/` without any project +- **THEN** the central area prompts to add a project folder from the sidebar + +#### Scenario: Project with no sessions + +- **WHEN** the user is on `/` with a project but zero sessions +- **THEN** the central area shows welcome copy and a control to start a new session diff --git a/openspec/changes/archive/2026-07-04-agent-home-message-timeline/tasks.md b/openspec/changes/archive/2026-07-04-agent-home-message-timeline/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..6b4040454684a93e89f50cfa8e4ec26386a7bb0e --- /dev/null +++ b/openspec/changes/archive/2026-07-04-agent-home-message-timeline/tasks.md @@ -0,0 +1,15 @@ +## 1. 时间线嵌入 + +- [x] 1.1 HomeSessionTimeline + useSessionTimelineHost +- [x] 1.2 MessageTimeline sessionIdOverride +- [x] 1.3 previewSessionId + 侧栏选中态 + +## 2. ChatArea 与文案 + +- [x] 2.1 三态:无项目 / 无会话 / 有会话时间线 +- [x] 2.2 中文空状态与 composer placeholder + +## 3. 验证 + +- [x] 3.1 home-session-target.test.ts +- [x] 3.2 bun run typecheck diff --git a/openspec/changes/archive/2026-07-04-hand-verification-automation/.openspec.yaml b/openspec/changes/archive/2026-07-04-hand-verification-automation/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4a14d3c1e1ca0cf4055f4201bbd6c45ca14c5425 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-hand-verification-automation/.openspec.yaml @@ -0,0 +1,2 @@ +name: hand-verification-automation +status: active diff --git a/openspec/changes/archive/2026-07-04-hand-verification-automation/proposal.md b/openspec/changes/archive/2026-07-04-hand-verification-automation/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..871b1e9379bd25d2b2586d4240eca377840395d2 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-hand-verification-automation/proposal.md @@ -0,0 +1,15 @@ +## Why + +P3-06:OpenSpec archive 中大量「手验」未自动化,回归依赖人工;需离线优先的冒烟编排。 + +## What Changes + +- `script/qa/hand-verification-smoke.ts` — 7 步编排(6 离线 + 1 HTTP web-shell) +- `script/ubml/raw-roundtrip-smoke.ts`、`verify-hints-smoke.ts` +- `script/qa/web-shell-smoke.ts` +- `docs/HAND-VERIFICATION-AUTOMATION.md` +- optional `.github/workflows/hand-verification-nightly.yml` + +## Capabilities + +- qa-hand-verification spec delta diff --git a/openspec/changes/archive/2026-07-04-hand-verification-automation/specs/qa-hand-verification/spec.md b/openspec/changes/archive/2026-07-04-hand-verification-automation/specs/qa-hand-verification/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..4b3dabd03e09281de8aecf50eb04b70c6dd72d30 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-hand-verification-automation/specs/qa-hand-verification/spec.md @@ -0,0 +1,22 @@ +## ADDED Requirements + +### Requirement: Hand verification smoke orchestrator + +The repository SHALL provide a single command that runs at least three automated checks mapped to OpenSpec archive manual verification items, without requiring LLM API keys. + +#### Scenario: Offline hand verification pass + +- **WHEN** the user runs `bun run qa:hand-verification` with dependencies installed +- **THEN** entity pipeline, UBML knowledge, and compiler round-trip smokes execute +- **AND** each step prints its archive reference and pass/fail status + +#### Scenario: Failure localization + +- **WHEN** a step fails +- **THEN** the orchestrator prints the step id and child script output +- **AND** the user can re-run a single step via `HAND_VERIFICATION_ONLY` + +#### Scenario: Optional dev stack checks + +- **WHEN** `bun run dev` is running and the user runs `bun run qa:hand-verification:strict` +- **THEN** web shell HTTP checks for `/` and backend health are included and must pass diff --git a/openspec/changes/archive/2026-07-04-hand-verification-automation/tasks.md b/openspec/changes/archive/2026-07-04-hand-verification-automation/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..aca3e581aeb33d098b1dcbbbb1de3deb10e19192 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-hand-verification-automation/tasks.md @@ -0,0 +1,14 @@ +## 1. 冒烟脚本 + +- [x] 1.1 hand-verification-smoke 编排器 +- [x] 1.2 raw-roundtrip + verify-hints + web-shell 专项脚本 + +## 2. 文档与 CI + +- [x] 2.1 HAND-VERIFICATION-AUTOMATION.md(archive 映射表) +- [x] 2.2 package.json scripts +- [x] 2.3 nightly workflow(离线步骤) + +## 3. 验证 + +- [x] 3.1 本地 qa:hand-verification 全绿 diff --git a/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/.openspec.yaml b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b4bbeb946f4c4d9310ee8730c5f89564bc95e9f0 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/.openspec.yaml @@ -0,0 +1 @@ +schema: spec-driven diff --git a/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/design.md b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/design.md new file mode 100644 index 0000000000000000000000000000000000000000..e0e09b1732121eee94677855d56a7598986746f5 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/design.md @@ -0,0 +1,5 @@ +## Decisions + +1. 正则抽取控制面惯用模式(`req.method && path ===` / `path.match` / `sub ===`),非通用 OpenAPI。 +2. 合并进现有 `api-catalog.json`,不另建索引文件。 +3. Java 仅注解 MVP(`@GetMapping` 等),主验收为 mission API。 diff --git a/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/proposal.md b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..04197b758e94aab224a52c1c6f70bb1a5e2ece08 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/proposal.md @@ -0,0 +1,14 @@ +## Why + +P2-02:API Wiki 骨架已有,但接口列表未与源码同步。需要从控制面 `*-routes.ts` 静态抽取 REST 路由供工作台浏览与 Agent `kb_api_search` 检索。 + +## What Changes + +- `http-route-extract.ts` — TS/Java HTTP 路由抽取,合并进 `api-catalog.json` +- 新 kind `http-route`;搜索含 path/method +- CLI `kb api-routes-refresh`;`kb index` 自动合并 +- extension API Wiki 面板展示 HTTP 路由 + +## Capabilities + +- `api-wiki` spec delta diff --git a/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/specs/api-wiki/spec.md b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/specs/api-wiki/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..1e9cf49ae5b036d3b3da1b11f048ad1d8c3b0f8d --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/specs/api-wiki/spec.md @@ -0,0 +1,28 @@ +## ADDED Requirements + +### Requirement: HTTP route extraction into API catalog + +The knowledge service SHALL extract HTTP routes from control-plane route handler sources (default `packages/knowledge/src/control/*-routes.ts`) and merge them into `api-catalog.json` as entries with kind `http-route`. + +Each http-route entry SHALL include `httpMethod`, `httpPath`, and optional `router` mount prefix. + +Extraction failures for individual files SHALL be logged as issues without aborting the full import. + +CLI `kb api-routes-refresh ` SHALL support `--dry-run` and optional `--dirs`. + +Repository indexing SHALL invoke HTTP route merge after symbol-based catalog build. + +#### Scenario: Mission API routes extracted + +- **WHEN** extract runs on code-agent control handlers +- **THEN** entries include GET `/mission/health` and POST `/mission/sync` + +#### Scenario: Search by path + +- **WHEN** api catalog is searched with query `/mission/health` +- **THEN** matching http-route entry is returned + +#### Scenario: API Wiki panel shows http-route kind + +- **WHEN** user filters API Wiki by type HTTP 路由 +- **THEN** mission and api-wiki control routes are listed with method and path diff --git a/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/tasks.md b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..237a8c6965e6db442e598821201c8c1269503460 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-api-wiki-extract/tasks.md @@ -0,0 +1,17 @@ +## 1. 抽取器 + +- [x] 1.1 `http-route-extract.ts` TS 控制面路由模式 +- [x] 1.2 可选 Java `@GetMapping` 等 +- [x] 1.3 合并 `api-catalog.json` + 搜索扩展 + +## 2. 集成 + +- [x] 2.1 `indexRepo` 自动 refresh +- [x] 2.2 CLI `kb api-routes-refresh` +- [x] 2.3 api-wiki-routes 按需补全 http-route + +## 3. UI 与验证 + +- [x] 3.1 extension API Wiki `http-route` 类型与展示 +- [x] 3.2 单测 + smoke +- [x] 3.3 更新 `docs/API-WIKI.md` diff --git a/openspec/changes/archive/2026-07-04-knowledge-designbase-import/.openspec.yaml b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b4bbeb946f4c4d9310ee8730c5f89564bc95e9f0 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/.openspec.yaml @@ -0,0 +1 @@ +schema: spec-driven diff --git a/openspec/changes/archive/2026-07-04-knowledge-designbase-import/design.md b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/design.md new file mode 100644 index 0000000000000000000000000000000000000000..c9c3d4ec3830d5a1b527e73459a3c1c932a142dc --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/design.md @@ -0,0 +1,5 @@ +## Decisions + +1. 默认扫描 `docs/product-design/`;`--dirs` 可扩展至 `docs/` 或 ADR 目录。 +2. 卡片 ID 前缀 `designbase-`,与 package 级 arch 卡共存。 +3. manifest 存于 `{artifact}/arch/designbase-manifest.json`,重复导入跳过未改文件。 diff --git a/openspec/changes/archive/2026-07-04-knowledge-designbase-import/proposal.md b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..1319f13d1103dbf9b6f621080daa7b3857be995c --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/proposal.md @@ -0,0 +1,14 @@ +## Why + +P2-01:架构决策与模块说明散落在 `docs/`,Agent 通过 `kb_arch` 检索时覆盖不全。需要批量导入工具把 Markdown 结构化为 arch-card 条目。 + +## What Changes + +- `designbase-import.ts` — 扫描 docs、按 ## 切分、写入 `{artifact}/arch/designbase-*.md` +- 增量 manifest(文件 hash 跳过未改) +- CLI `kb designbase-import` + `--dry-run` +- `indexRepo` 自动合并 DesignBase 卡 + +## Capabilities + +- `knowledge-service` spec delta diff --git a/openspec/changes/archive/2026-07-04-knowledge-designbase-import/specs/knowledge-service/spec.md b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/specs/knowledge-service/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..149da1b98a97283e4b49e2b5b67e16bb27b83468 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/specs/knowledge-service/spec.md @@ -0,0 +1,30 @@ +## ADDED Requirements + +### Requirement: DesignBase architecture card bulk import + +The knowledge service SHALL provide `importDesignBaseArchCards` to scan configured Markdown directories (default `docs/product-design/`), split documents at level-2 headings, and write arch-card files to `{repoArtifactDir}/arch/designbase-*.md` compatible with `kb_arch` retrieval. + +The importer SHALL maintain `designbase-manifest.json` with per-source-file content hashes and skip unchanged files on re-import. + +The importer SHALL remove stale designbase cards when source sections change. + +CLI `kb designbase-import ` SHALL support `--dry-run`, `--force`, and `--dirs dir1,dir2`. + +Full repository indexing (`kb index`) SHALL invoke DesignBase import after package arch cards are built. + +#### Scenario: Import product-design chapters + +- **WHEN** designbase-import runs on code-agent repo +- **THEN** at least one card is created from `docs/product-design/*.md` +- **AND** card content includes source path and section body + +#### Scenario: Incremental skip + +- **WHEN** designbase-import runs twice without source changes +- **THEN** second run skips unchanged files via manifest hash + +#### Scenario: Dry-run preview + +- **WHEN** designbase-import runs with `--dry-run` +- **THEN** no arch-card files or manifest are written +- **AND** result lists would-be created/updated/removed card ids diff --git a/openspec/changes/archive/2026-07-04-knowledge-designbase-import/tasks.md b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..168cc837e093c4002b19554b1e8e4bb7f734a5a8 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-designbase-import/tasks.md @@ -0,0 +1,16 @@ +## 1. 导入器 + +- [x] 1.1 `splitMarkdownSections` / `renderDesignBaseArchCard` +- [x] 1.2 增量 hash manifest + stale card 清理 +- [x] 1.3 `--dry-run` 支持 + +## 2. CLI 与集成 + +- [x] 2.1 `kb designbase-import` +- [x] 2.2 `indexRepo` 钩子 +- [x] 2.3 smoke 脚本 `kb:designbase:import-smoke` + +## 3. 验证 + +- [x] 3.1 `designbase-import.test.ts` +- [x] 3.2 更新 KNOWLEDGE.md diff --git a/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/.openspec.yaml b/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e886623bf7506d3cd518b457fc5521ad531c9a70 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/.openspec.yaml @@ -0,0 +1,2 @@ +name: knowledge-farris-aliases +status: active diff --git a/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/proposal.md b/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..9a86631416dd20f1ede30b084abc36bf388cda79 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/proposal.md @@ -0,0 +1,13 @@ +## Why + +P2-05:用户常用「报帐金额」「报销明细」等中文描述 FYBXD 组件,需扩充 Farris 别名表提升 `kb_farris_locate` 召回。 + +## What Changes + +- `component-aliases.json` +21 条 FYBXD 导向别名 +- `eval/farris-alias-tasks.json` + `script/knowledge/farris-alias-smoke.ts` +- npm `kb:farris:alias-smoke` + +## Capabilities + +- `knowledge-service` spec delta diff --git a/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/specs/knowledge-service/spec.md b/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/specs/knowledge-service/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..1d64ec4cda3a46cce0aeea1bdd37adb3a9506a6d --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/specs/knowledge-service/spec.md @@ -0,0 +1,11 @@ +## ADDED Requirements + +### Requirement: FYBXD-oriented Farris component aliases + +The Farris knowledge pack SHALL maintain Chinese aliases in `component-aliases.json` covering common FYBXD page field patterns (amount, status, lookup, grid, filter), with a documented smoke eval of alias recall. + +#### Scenario: Chinese FYBXD field locates component + +- **WHEN** `kb_farris_locate` or alias fuzzy search receives a Chinese query such as 报帐金额 or 报销明细 +- **THEN** the top hit maps to the expected Farris component (e.g. number-spinner, data-grid) +- **AND** alias entries do not duplicate conflicting keys diff --git a/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/tasks.md b/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..b0f55d29f6c50f495dd7bfea3de2ad3aafb20b48 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-farris-aliases/tasks.md @@ -0,0 +1,9 @@ +## 1. 别名 + +- [x] 1.1 ≥15 条 FYBXD 常用组件中文别名 +- [x] 1.2 无重复 key / 冲突组件 + +## 2. 验证 + +- [x] 2.1 farris-alias-tasks.json + smoke 脚本 +- [x] 2.2 npm script + script/knowledge/README.md diff --git a/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/.openspec.yaml b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9d7d5c97f50b3c7058488150218b5b5e72e44d64 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/.openspec.yaml @@ -0,0 +1,2 @@ +name: knowledge-pro-deploy +status: active diff --git a/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/design.md b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/design.md new file mode 100644 index 0000000000000000000000000000000000000000..bb23e30101d5e0804fd1defeded284de76e8fa15 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/design.md @@ -0,0 +1,11 @@ +## 决策 + +1. **独立 Compose** — `docker-compose.knowledge-pro.yml` 不耦合 dual-ide,降低「只为知识 Pro 装 IDE Host」的门槛。 +2. **Qdrant 必需、Zoekt 可选** — 默认 `pro-up.sh` 只起 Qdrant;`--zoekt` 启用 profile。词法在无 Zoekt 索引时回退 ripgrep。 +3. **环境文件** — `deploy/.env.knowledge-pro`(gitignore)+ `.example` 模板;`pro-up.sh` 自动 source。 +4. **冒烟** — `pro-smoke.ts` 检查 Qdrant `/`、Pro 档 index + locate,与 P2-03 eval 分离(更快)。 + +## 非目标 + +- Zoekt 索引流水线(仍由运维/后续任务提供);本变更只提供 webserver 容器与文档说明。 +- 修改 `script/dev.sh` 自动起 Docker(文档说明两终端工作流即可)。 diff --git a/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/proposal.md b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..62b18ae1d1a47d38c56bd9fe1be4dff9eb80976a --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/proposal.md @@ -0,0 +1,16 @@ +## Why + +P2-04:Pro 档依赖 Zoekt 与 Qdrant,搭建门槛高于 Lite。需要独立 Compose、一键脚本、环境模板与迁移文档,让贡献者 30 分钟内复现 Pro 检索环境。 + +## What Changes + +- `deploy/docker-compose.knowledge-pro.yml` — Qdrant + 可选 Zoekt profile +- `deploy/.env.knowledge-pro.example` — 环境变量模板 +- `script/knowledge/pro-up.sh`、`script/knowledge/pro-smoke.ts` +- `docs/KNOWLEDGE-PRO.md`;更新 `docs/KNOWLEDGE.md` §2 +- Zoekt 无结果时 lexical 回退 ripgrep(Pro 默认可用) +- npm:`kb:pro:up` / `kb:pro:down` / `kb:pro:smoke` + +## Capabilities + +- `knowledge-service` spec delta(Pro 一键部署) diff --git a/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/specs/knowledge-service/spec.md b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/specs/knowledge-service/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..bd9560d36d3655a82c0cd0ae6f218a9b4bba2114 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/specs/knowledge-service/spec.md @@ -0,0 +1,23 @@ +## ADDED Requirements + +### Requirement: Pro tier one-command deploy + +The product SHALL provide a standalone Docker Compose stack and shell script to start Pro-tier dependencies (Qdrant required; Zoekt optional) without requiring the full dual-ide compose profile, plus an environment variable example file and documentation for Lite-to-Pro migration. + +#### Scenario: Contributor starts Pro stack locally + +- **WHEN** a contributor runs the documented one-command Pro up script +- **THEN** Qdrant becomes reachable on the documented localhost port +- **AND** optional Zoekt can be started with a documented flag +- **AND** no secrets are committed to the repository + +#### Scenario: Pro retrieval verified after deploy + +- **WHEN** Pro stack is up and the operator runs the documented smoke command with `KB_TIER=pro` +- **THEN** at least one retrieval path (`kb locate` or equivalent API) returns hits for an indexed repository + +#### Scenario: Zoekt unavailable falls back to ripgrep + +- **WHEN** Pro tier is active and Zoekt returns no hits or is unreachable +- **THEN** lexical search falls back to ripgrep or file scan +- **AND** retrieval continues without hard failure diff --git a/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/tasks.md b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..ea2c9f533cacd7be973da0f1885178fa8bdc9343 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-pro-deploy/tasks.md @@ -0,0 +1,16 @@ +## 1. 部署资产 + +- [x] 1.1 `deploy/docker-compose.knowledge-pro.yml` +- [x] 1.2 `deploy/.env.knowledge-pro.example` +- [x] 1.3 `script/knowledge/pro-up.sh` + +## 2. 验证与文档 + +- [x] 2.1 `script/knowledge/pro-smoke.ts` + npm scripts +- [x] 2.2 `docs/KNOWLEDGE-PRO.md` +- [x] 2.3 更新 `docs/KNOWLEDGE.md` §2、`script/knowledge/README.md` +- [x] 2.4 Zoekt lexical 回退 ripgrep + +## 3. 验证 + +- [ ] 3.1 Docker 环境 `kb:pro:up` + `kb:pro:smoke` 通过 diff --git a/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/.openspec.yaml b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b4bbeb946f4c4d9310ee8730c5f89564bc95e9f0 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/.openspec.yaml @@ -0,0 +1 @@ +schema: spec-driven diff --git a/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/design.md b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/design.md new file mode 100644 index 0000000000000000000000000000000000000000..6d8131faa74390104dd4e881f64c4a2fa5bf8c56 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/design.md @@ -0,0 +1,5 @@ +## Decisions + +1. 评测针对 **code-agent 自索引**(非 farris-vue 外部仓)。 +2. 默认 `KB_EMBED_PROVIDER=fallback` 保证 CI 无模型下载。 +3. 阈值 80%,`KB_EVAL_WARN_ONLY=1` 可降级为 warning。 diff --git a/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/proposal.md b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..07e1bc35afa197336d4050c57e0b4cf43caf01a1 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/proposal.md @@ -0,0 +1,14 @@ +## Why + +P2-03:知识库改动易静默降低中文召回率,需要固定评测集与 CI 门禁(≥80%)。 + +## What Changes + +- `eval/retrieval-tasks.json` — 25 条 locate/plan/explain 用例 +- `eval/eval-retrieval-core.ts` + `script/knowledge/eval-retrieval.ts` +- `script/knowledge/eval-retrieval-ci.ts` — 索引 + 评测 +- 文档与 npm scripts + +## Capabilities + +- `knowledge-service` spec delta diff --git a/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/specs/knowledge-service/spec.md b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/specs/knowledge-service/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..3f03d14fe255c2739923ff313a44810bf8378d99 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/specs/knowledge-service/spec.md @@ -0,0 +1,24 @@ +## ADDED Requirements + +### Requirement: Retrieval evaluation harness + +The product SHALL provide `packages/knowledge/eval/retrieval-tasks.json` with at least 20 Chinese retrieval tasks covering locate, plan, and explain modes. + +Script `script/knowledge/eval-retrieval.ts` SHALL output pass rate and fail when rate is below `KB_EVAL_THRESHOLD` (default 0.8). + +Script `script/knowledge/eval-retrieval-ci.ts` SHALL index the code-agent repository with fallback embeddings and run the evaluation. + +#### Scenario: Local eval command + +- **WHEN** user runs `bun run kb:eval:retrieval` after indexing code-agent +- **THEN** a report shows pass count and percentage + +#### Scenario: CI eval meets threshold + +- **WHEN** `bun run kb:eval:retrieval:ci` runs with KB_EMBED_PROVIDER=fallback +- **THEN** exit code is 0 when pass rate >= 80% + +#### Scenario: Warn-only mode + +- **WHEN** KB_EVAL_WARN_ONLY=1 and pass rate is below threshold +- **THEN** script exits 0 with warning diff --git a/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/tasks.md b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..ea8520f9e68432ade9f81834c9de8629beb4b4f4 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-knowledge-retrieval-eval/tasks.md @@ -0,0 +1,16 @@ +## 1. 评测集与核心 + +- [x] 1.1 `retrieval-tasks.json` ≥20 条中文用例 +- [x] 1.2 `eval-retrieval-core.ts` locate/plan/explain +- [x] 1.3 单测 mock + +## 2. CLI 与 CI + +- [x] 2.1 `script/knowledge/eval-retrieval.ts` +- [x] 2.2 `script/knowledge/eval-retrieval-ci.ts` +- [x] 2.3 npm scripts + 文档 + +## 3. 验证 + +- [x] 3.1 本地/CI 跑通 ≥80% +- [x] 3.2 更新 KNOWLEDGE.md / README diff --git a/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..42eec8adfd3c7ff6b80ea62bbe08af77c2a1f482 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/.openspec.yaml @@ -0,0 +1,2 @@ +name: ubml-derive-type-boundary +status: active diff --git a/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/proposal.md b/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..d6962f0335631366ad84c5fbedcc99e9e44dcc60 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/proposal.md @@ -0,0 +1,14 @@ +## Why + +P3-02:derive 边界类型(DateTime/Decimal/Enum/Nullable)易静默出错,需 fixture + 独立测试与类型矩阵文档。 + +## What Changes + +- `test/fixtures/derive-boundary.be.json` + `test/derive-type-boundary.test.ts` +- `src/derive/type-matrix.ts` +- VO derive 修复:保留 `ContainEnumValues` / `EnumIndexType` +- 更新 `docs/UBML-APP-DERIVE.md` + +## Capabilities + +- derive spec delta diff --git a/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/specs/ubml-app-derive/spec.md b/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/specs/ubml-app-derive/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..38ba0ab9f0ce2f9fede99b1ee574659107a2f953 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/specs/ubml-app-derive/spec.md @@ -0,0 +1,25 @@ +## ADDED Requirements + +### Requirement: Derive type boundary regression + +The derive pipeline SHALL document BE→DBO type mappings and provide automated boundary tests for DateTime, Decimal, enum (ObjectType), and nullable column rules, with a minimal BE fixture. + +#### Scenario: DateTime maps to TimeStamp + +- **WHEN** a BE field has MDataType DateTime or Date +- **THEN** derived DBO column uses TimeStamp and VO preserves MDataType + +#### Scenario: Decimal precision and scale + +- **WHEN** a BE Decimal field specifies Precision and Scale +- **THEN** derived DBO column carries matching precision and scale + +#### Scenario: Enum values on VO + +- **WHEN** a BE field has ObjectType Enum and ContainEnumValues +- **THEN** derived VO element preserves ObjectType and enum value list + +#### Scenario: Nullable rules + +- **WHEN** derive builds DBO columns from IsRequire and PK/ParentID conventions +- **THEN** required and PK columns are non-nullable; ParentID is nullable diff --git a/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/tasks.md b/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..4794a7a3327b4429dacdccfb53632115d7bf1f7e --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-derive-type-boundary/tasks.md @@ -0,0 +1,13 @@ +## 1. 测试 + +- [x] 1.1 DateTime / Decimal / Enum / Nullable 四类边界用例 +- [x] 1.2 derive-boundary.be.json fixture + +## 2. 修复与文档 + +- [x] 2.1 VO enum 字段 ContainEnumValues 派生 +- [x] 2.2 type-matrix.ts + UBML-APP-DERIVE.md + +## 3. 验证 + +- [x] 3.1 packages/ubml-codegen test 全绿 diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d86f152086acb9840968fee4ce71e69c59936d4a --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-04 diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/design.md b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/design.md new file mode 100644 index 0000000000000000000000000000000000000000..4fa63654b676eb3a58f104d6050d185f09a40c70 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/design.md @@ -0,0 +1,67 @@ +## Context + +`docs/product-design/10-mof-entity-modeling.md` 定义 MOF 四层架构,E1 目标是把实体 DSL 的 M2 显式化。当前实现: + +| 载体 | 位置 | +|------|------| +| Zod | `packages/ubml-codegen/src/entity/dsl/schema.ts` | +| 产物 Schema | `gsp-business-entity.schema.json` | +| Catalog | `datatypes.index.json` 等四个 JSON | +| 提示词 | `assets/ubml-entity/prompts/entitygen.md` | + +FYBXD 金样(`exemplars/fybxd.be.yaml`,77 行)是验收基准。 + +## Goals / Non-Goals + +**Goals:** + +- 一份 `ubml-entity.metamodel.yaml` 覆盖 FYBXD 全部建模元素 +- 稳定解析器输出 TypeScript 结构,供测试与提示词渲染使用 +- 一致性测试在 catalog/类型漂移时失败 +- `entitygen.md` 的「合法类型/约束」段落由元模型渲染 + +**Non-Goals:** + +- 不从元模型生成 Zod 或 `gsp-business-entity.schema.json`(远期) +- 不实现 OCL 表达式引擎;`constraint.check` 首期为人读规约 +- 不改动编译器、verify pipeline 或 MCP 操作集(属 E2) + +## Decisions + +### 1. M3 词汇:7 构造 YAML 声明式 + +采用 `metamodel / class / attribute / reference / enum / datatype / constraint`,与产品设计 §3.2 一致。不引入完整 MOF/EMOF。 + +### 2. 元模型文件位置 + +放在 `packages/extension/assets/ubml-entity/metamodel/`,与 catalog、exemplar 同包,由 `ubml-codegen` 通过相对路径或 asset 解析读取(与现有 `EntityCompileContext` 模式一致)。 + +**备选**: 放在 `ubml-codegen` 内 — 拒绝,因 Agent 提示词与 extension 策展知识包同域。 + +### 3. 约束首期不机器执行 + +`constraint.check` 字段保留 OCL 风格文档;执行由一致性测试 + 既有 Zod 校验承担。E2 操作集再绑定单步 TS 校验函数。 + +### 4. DSL `kind` 与 Zod 对齐 + +元模型 `EntityKind` 使用 `bill | basedata`(与 `schema.ts` 一致),而非设计文档草案中的 `dictionary | config`。 + +### 5. 提示词渲染策略 + +`prompt.ts` 渲染:datatype 表、catalog 约束清单、元类 doc 摘要。`entitygen.md` 保留 `` 占位或末尾追加渲染段,人工工作流段落不动。 + +## Risks / Trade-offs + +- [元模型与 Zod 脱节] → 一致性测试强制 PR 同步;测试覆盖 datatype 集合、FYBXD 元素映射、catalog 名称 +- [过度工程] → M3 冻结 7 构造;不生成完整产物 schema +- [kind/enum 命名漂移] → 测试以 `datatypes.index.json` 与 `schema.ts` 为 ground truth + +## Migration Plan + +纯增量:新文件 + 测试 + 提示词段落替换。无数据迁移。回滚删除新增路径即可。 + +**Upstream sync impact**: none + +## Open Questions + +- `date` 类型是否在元模型 datatypes 中单独列出(`datatypes.index.json` 有 `date` 映射 DateTime)— 首期与 index 保持一致 diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/proposal.md b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..40b2d5314b562b10c7059adc590ef8eb3e9db96c --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/proposal.md @@ -0,0 +1,27 @@ +## Why + +实体 DSL 的合法结构、字段类型与 catalog 约束目前散落在 Zod schema、JSON Schema、四个 catalog 与 Agent 提示词中,缺少唯一事实源。贡献者改一处易漏改其他处,生成稳定性也难以治理。E1 阶段(见 `docs/product-design/10-mof-entity-modeling.md` §6.2)需把隐式 M2 显式化为机器可读的元模型,并通过一致性测试与现有实现对齐,**不改变**现有编译与生成行为。 + +## What Changes + +- 新增 `packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml` — 业务实体 M2 元模型(元类、datatype、enum、constraint、doc) +- 新增 `metamodel/README.md` — 维护说明与首期边界 +- 新增 `packages/ubml-codegen/src/entity/metamodel/` — 读取器、TypeScript 类型、提示词片段渲染 +- 新增 `packages/ubml-codegen/test/entity-metamodel-consistency.test.ts` — 与 `datatypes.index.json`、FYBXD 金样、catalog 文件对齐 +- 修改 `packages/extension/assets/ubml-entity/prompts/entitygen.md` — 引入元模型渲染的约束段落(工作流段落仍人工维护) + +## Capabilities + +### New Capabilities + +- `ubml-entity-metamodel`: 显式业务实体元模型资产、读取器、一致性测试与提示词片段渲染 + +### Modified Capabilities + +- (无)E1 仅增加描述性事实源与测试,不修改 `ubml-entity-dsl-compiler` 或 pipeline 的既有行为要求 + +## Impact + +- **包**: `packages/ubml-codegen`(metamodel 模块 + 测试)、`packages/extension`(assets + entitygen 提示词) +- **边界**: 仅 extension + ubml-codegen;不涉及 vendor fork/import +- **回滚**: 删除 `metamodel/` 目录与 `entity/metamodel/` 模块即可恢复;现有 Zod 编译路径不受影响 diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/specs/ubml-entity-metamodel/spec.md b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/specs/ubml-entity-metamodel/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..8ec50d6657b9ce64830de71dec1f41be768ba3f9 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/specs/ubml-entity-metamodel/spec.md @@ -0,0 +1,67 @@ +## ADDED Requirements + +### Requirement: Explicit entity metamodel asset + +The product SHALL ship a machine-readable business-entity metamodel at `packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml` using the M3 vocabulary (`metamodel`, `class`, `attribute`, `reference`, `enum`, `datatype`, `constraint`). + +The metamodel SHALL declare: + +- Primitive and composite field datatypes aligned with `datatypes.index.json` +- Meta-classes for `BusinessEntity`, `EntityObject`, scalar/enum/association/UDT fields, rules (`Validation`, `Determination`, `Action`), and `ManagerAction` +- Containment vs external references (`containment: true|false`, `external: true` for catalog-backed refs) +- Catalog constraints naming `referenceable-be`, `udt`, and `components` +- Human-readable `doc` on meta-classes and constraints + +#### Scenario: FYBXD exemplar is explainable by metamodel + +- **WHEN** `exemplars/fybxd.be.yaml` is inspected against the metamodel class definitions +- **THEN** every field kind (scalar, enum, association, udt), both child objects, rules (validation, determination, action), and manager actions map to a declared meta-class + +#### Scenario: Metamodel README documents maintenance boundaries + +- **WHEN** a contributor opens `metamodel/README.md` +- **THEN** they find which constraints are enforced by tests vs documentation-only in E1 +- **AND** instructions to keep datatypes in sync with `datatypes.index.json` + +### Requirement: Entity metamodel loader + +`packages/ubml-codegen` SHALL provide a metamodel loader that parses `ubml-entity.metamodel.yaml` into stable TypeScript types (`UbmlMetamodel`, `MetaClass`, `MetaAttribute`, `MetaReference`, `MetaConstraint`, etc.) usable in Node/Bun tests without side effects. + +#### Scenario: Loader parses default asset path + +- **WHEN** the loader reads the bundled metamodel asset +- **THEN** it returns a non-empty `classes` array including `BusinessEntity` and `ScalarField` +- **AND** parsing completes without throwing + +### Requirement: Metamodel consistency tests + +The product SHALL include `packages/ubml-codegen/test/entity-metamodel-consistency.test.ts` verifying: + +1. Metamodel `datatypes` DSL type ids match `datatypes.index.json` `types[].id` +2. FYBXD exemplar elements map to metamodel classes (field kinds, children, rules, manager actions) +3. Catalog constraint names in the metamodel resolve to existing catalog files under `assets/ubml-entity/` + +#### Scenario: Test fails on datatype drift + +- **WHEN** a datatype exists in `datatypes.index.json` but not in the metamodel +- **THEN** the consistency test fails with a message naming the missing type + +#### Scenario: Test fails on missing catalog file + +- **WHEN** the metamodel references catalog `referenceable-be` but the catalog file is absent +- **THEN** the consistency test fails + +### Requirement: Entitygen prompt constraints from metamodel + +The product SHALL render agent-facing constraint fragments from the metamodel (legal field types, catalog rules, constraint summaries) and inject them into `assets/ubml-entity/prompts/entitygen.md`, while leaving workflow/strategy sections human-authored. + +#### Scenario: Rendered fragment lists DSL field types + +- **WHEN** prompt rendering runs against the loaded metamodel +- **THEN** output includes all primitive DSL types from the metamodel datatypes section +- **AND** includes enum, association, and udt as composite kinds + +#### Scenario: Existing entity verify behavior unchanged + +- **WHEN** E1 is complete +- **THEN** `bun run ubml:be:verify packages/extension/assets/ubml-entity/exemplars/fybxd.be.yaml` still passes with no change to compile/verify semantics diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/tasks.md b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..d06144ba527c90c6f44487e15b8e19f0ab40ee84 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-metamodel/tasks.md @@ -0,0 +1,21 @@ +## 1. 元模型资产(M1) + +- [x] 1.1 新增 `ubml-entity.metamodel.yaml`,覆盖 FYBXD 全部建模元素 +- [x] 1.2 新增 `metamodel/README.md`,说明维护边界与 catalog 同步规则 + +## 2. 读取器与类型(M2) + +- [x] 2.1 新增 `entity/metamodel/types.ts` — TypeScript 元模型类型 +- [x] 2.2 新增 `entity/metamodel/load.ts` — YAML 解析与默认 asset 路径 +- [x] 2.3 新增 `entity-metamodel-consistency.test.ts` — datatype / FYBXD / catalog 三类不变量 + +## 3. 提示词渲染(M3) + +- [x] 3.1 新增 `entity/metamodel/prompt.ts` — 渲染合法类型与约束片段 +- [x] 3.2 修改 `entitygen.md` 接入元模型渲染段落(工作流段落保留人工维护) + +## 4. 验证 + +- [x] 4.1 `bun test packages/ubml-codegen/test/entity-metamodel-consistency.test.ts` 通过 +- [x] 4.2 `bun run ubml:entity:verify packages/extension/assets/ubml-entity/exemplars/fybxd.be.yaml` 仍通过 +- [x] 4.3 `packages/ubml-codegen` typecheck 无新增错误 diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d86f152086acb9840968fee4ce71e69c59936d4a --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-04 diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/design.md b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/design.md new file mode 100644 index 0000000000000000000000000000000000000000..9b6db14d832e2407c2a7719bd1ce8689fc106cb8 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/design.md @@ -0,0 +1,43 @@ +## Context + +`kb_be_edit` 现通过 `applyEntityEdit` 调用 `entity-dsl.ts` 五个低级函数。E1 元模型已声明 Field 子类与 catalog 约束。FYBXD 金样覆盖全部构造类型。 + +## Goals / Non-Goals + +**Goals:** + +- 专用操作在 apply 前校验参数(catalog、length/precision、enum default 等) +- 失败返回 `{ ok: false, issues: [{ message, hint, hints }] }` 而非裸 throw +- MCP 单入口 `kb_be_edit`,op 枚举扩展 +- 冒烟:从最小实体逐步 edit 到 FYBXD 核心结构 + +**Non-Goals:** + +- 会话内模型状态持久化(Agent 持 YAML) +- 从元模型自动生成 op JSON Schema(远期) +- LLM eval 自动化调用(首期仅任务集 + 指标骨架) + +## Decisions + +### 1. 注册表模式 + +`operations.ts` 注册 `Record`;legacy op 委托现有 `entity-dsl.ts`;新 op 在独立文件实现 validate + apply。 + +### 2. 校验复用 compile context + +操作校验使用 `EntityCompileContext` + `datatypes.index.json`,与 DSL parse 语义一致;hint 文案参考 verify-hints 风格。 + +### 3. addField 保留 + +`addField` 仍接受任意 FieldDsl;新 op 为推荐路径,参数更小、错误更准。 + +### 4. beEdit 返回值 + +成功:`{ ok: true, yaml, dsl, changedPaths }`;失败:`{ ok: false, issues, yaml: input }`。 + +**Upstream sync impact**: none + +## Risks / Trade-offs + +- [op 枚举膨胀] → 文档与 entitygen 表格化;legacy 5 op 保留 +- [duplicate validation] → 操作后仍可选 full verify;首期操作内校验 + 测试覆盖 diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/proposal.md b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..a45591ec9d6a6ec24f240336cfdfeeb748a01cb6 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/proposal.md @@ -0,0 +1,29 @@ +## Why + +E1 已显式化实体元模型,但 `kb_be_edit` 仅有 5 个通用操作,关联/枚举/UDT/管理动作等易错构造缺少单步校验与结构化 hint。E2 按 MOF 反射式 API 思路补齐受控建模操作,支撑「骨架草案 + 受控精修」生成工作流。 + +## What Changes + +- `entity/edit/operations.ts` 操作注册表 + 结构化 `EntityEditResult` +- 新增 9 个专用操作:addScalarField / addEnumField / addAssociationField / addUdtField / addChildObject / addValidation / addDetermination / addBizAction / addManagerAction +- 保留既有 addField / addChild / addRule / patchField / removeField 向后兼容 +- `kb_be_edit` MCP 扩展 op 枚举;`beEdit` 返回 `{ ok, yaml, issues, hints, changedPaths }` +- `entity-edit-operations.test.ts` + `be-modeling-ops-smoke.ts` +- `entitygen.md` 明确骨架 + 受控精修工作流 +- 评测集 `entity-generation-tasks.json` + `eval-entity-generation-core.ts`(基线) + +## Capabilities + +### New Capabilities + +- `ubml-entity-modeling-ops`: BE 受控建模操作集、MCP 集成与生成评测基线 + +### Modified Capabilities + +- `ubml-entity-agent`: entitygen 工作流增加受控精修操作说明 + +## Impact + +- **包**: `packages/ubml-codegen`, `packages/knowledge`, `packages/extension`(prompt) +- **边界**: extension + ubml-codegen + knowledge;无 vendor 变更 +- **回滚**: 移除新 op 文件,MCP 恢复旧 enum 即可 diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/specs/ubml-entity-agent/spec.md b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/specs/ubml-entity-agent/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..6c4779e408e36049bcaf42669c8f49edfa8e0f1f --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/specs/ubml-entity-agent/spec.md @@ -0,0 +1,18 @@ +## MODIFIED Requirements + +### Requirement: Entity generation agent workflow prompt + +The product SHALL ship `assets/ubml-entity/prompts/entitygen.md` describing the DSL-first BE workflow: retrieve exemplars via `kb_be_exemplar`, consult datatype/UDT/ref/component catalogs via MCP, produce `entity:` YAML DSL (not raw GspMetadata JSON), verify with `kb_be_verify`, and repair up to 3 rounds using structured hints; optional incremental edits via `kb_be_edit`. + +The workflow SHALL document a two-phase pattern: (1) skeleton draft with scalar fields and children via YAML, immediate `kb_be_verify`; (2) controlled refinement via typed `kb_be_edit` operations (`addAssociationField`, `addEnumField`, `addUdtField`, rules, manager actions) for catalog-sensitive constructs. + +#### Scenario: Prompt mentions typed edit operations + +- **WHEN** a contributor reads `entitygen.md` +- **THEN** it lists typed `kb_be_edit` operations for association, enum, UDT, and manager actions +- **AND** describes skeleton-then-refine sequencing + +#### Scenario: Agent consumes hint field + +- **WHEN** verify result is formatted for agent prompt via `formatVerifyIssuesForAgent` +- **THEN** output includes a hint column suitable for repair loops diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/specs/ubml-entity-modeling-ops/spec.md b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/specs/ubml-entity-modeling-ops/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..5f26e7ff27761460d91e7ee7a30bd03043fe8fce --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/specs/ubml-entity-modeling-ops/spec.md @@ -0,0 +1,48 @@ +## ADDED Requirements + +### Requirement: Typed entity modeling edit operations + +The product SHALL extend `kb_be_edit` / `applyEntityEdit` with dedicated operations: + +- `addScalarField`, `addEnumField`, `addAssociationField`, `addUdtField` +- `addChildObject`, `addValidation`, `addDetermination`, `addBizAction`, `addManagerAction` + +Each operation SHALL validate arguments against entity compile context (datatypes, catalogs, field existence) before mutating DSL. + +Legacy operations (`addField`, `addChild`, `addRule`, `patchField`, `removeField`) SHALL remain supported. + +#### Scenario: addAssociationField rejects unknown ref with hints + +- **WHEN** `addAssociationField` is called with `ref: NoSuchBe` +- **THEN** result has `ok: false` +- **AND** issues include a message naming the unknown ref +- **AND** issues include hints listing valid catalog BE codes + +#### Scenario: addScalarField requires decimal precision + +- **WHEN** `addScalarField` is called with `type: decimal` without `precision` +- **THEN** result has `ok: false` with a hint about required precision + +#### Scenario: addChildObject rejects duplicate child code + +- **WHEN** `addChildObject` is called with a child code already present on root +- **THEN** result has `ok: false` naming the duplicate code + +### Requirement: Structured entity edit results + +Entity edit APIs SHALL return `{ ok, yaml, issues?, changedPaths?, dsl? }` where failed edits preserve input YAML and populate structured `issues` with optional Chinese `hint` fields. + +#### Scenario: Successful edit returns updated yaml + +- **WHEN** a valid `addUdtField` completes +- **THEN** `ok` is true and `yaml` contains the new field +- **AND** `changedPaths` identifies the affected object + +### Requirement: Entity generation eval baseline + +The product SHALL ship `packages/knowledge/eval/entity-generation-tasks.json` with at least 10 Chinese requirements and `eval-entity-generation-core.ts` reporting verify pass rate metrics. + +#### Scenario: Eval runner loads task file + +- **WHEN** eval script runs with default paths +- **THEN** it loads at least 10 tasks and prints summary metrics structure diff --git a/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/tasks.md b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..ffaf4eaacebb0cfabd585f01429f2ac23ee516fc --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-entity-modeling-ops/tasks.md @@ -0,0 +1,32 @@ +## 1. 操作注册表与结果类型 + +- [x] 1.1 新增 `entity/edit/types.ts` — EntityEditResult / EntityEditIssue +- [x] 1.2 新增 `entity/edit/operations.ts` — 注册表 + `applyEntityEditOp` +- [x] 1.3 扩展 `entity-dsl.ts` — addManagerAction;legacy op 接入注册表 + +## 2. 字段类操作(M4) + +- [x] 2.1 addScalarField / addEnumField / addAssociationField / addUdtField +- [x] 2.2 addChildObject(含 no-grandchildren 校验) +- [x] 2.3 entity-edit-operations.test.ts + +## 3. 规则与管理动作(M5) + +- [x] 3.1 addValidation / addDetermination / addBizAction / addManagerAction +- [x] 3.2 测试覆盖 catalog 失败 hint + +## 4. MCP 与冒烟 + +- [x] 4.1 扩展 `kb_be_edit` op 枚举与 beEdit 结构化返回 +- [x] 4.2 be-modeling-ops-smoke.ts + +## 5. 工作流与评测(M6) + +- [x] 5.1 entitygen.md 骨架 + 受控精修段落 +- [x] 5.2 entity-generation-tasks.json + eval-entity-generation-core.ts + +## 6. 验证 + +- [x] 6.1 `bun test packages/ubml-codegen/test/entity-edit-operations.test.ts` +- [x] 6.2 `bun packages/knowledge/script/be-modeling-ops-smoke.ts` +- [x] 6.3 FYBXD verify 仍通过 diff --git a/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4f9293491e7445eb114dde97f6a67db845b3cdd3 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/.openspec.yaml @@ -0,0 +1,2 @@ +name: ubml-exemplars-intent +status: active diff --git a/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/proposal.md b/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..36ec7c4fe241ba1832eb3cd7984b67c56a80b517 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/proposal.md @@ -0,0 +1,12 @@ +## Why + +P2-06:五类 UBML DSL 已有 FYBXD exemplar,但缺少适用场景说明与一键 verify 入口。 + +## What Changes + +- 各类 `assets/ubml*/exemplars/intent.md` +- `script/ubml/exemplars-verify-smoke.ts` + npm `ubml:exemplars:smoke` + +## Capabilities + +- `ubml-appgen` spec delta(exemplar 策展) diff --git a/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/specs/ubml-appgen/spec.md b/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/specs/ubml-appgen/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..3444b7653b633f69cea175feec56aed66c27122e --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/specs/ubml-appgen/spec.md @@ -0,0 +1,10 @@ +## ADDED Requirements + +### Requirement: UBML DSL exemplars with intent and verify smoke + +Each UBML DSL category (page, entity, query, component, app/pageflow) SHALL ship at least one FYBXD-based exemplar under `packages/extension/assets/`, with an adjacent `intent.md` describing use case and verify command, and a root smoke script that runs compile/verify on all bundled exemplars. + +#### Scenario: Contributor validates exemplars + +- **WHEN** a contributor runs the documented exemplars smoke command +- **THEN** all listed exemplars pass compile or verify without secrets or absolute paths diff --git a/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/tasks.md b/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..ff5680e6c0f109d7b2707c3322279c2c8f4537af --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-exemplars-intent/tasks.md @@ -0,0 +1,8 @@ +## 1. 文档 + +- [x] 1.1 page / entity / query / component / pageflow / app intent.md + +## 2. 验证 + +- [x] 2.1 exemplars-verify-smoke.ts +- [x] 2.2 npm script diff --git a/openspec/changes/archive/2026-07-04-ubml-model-right-panel/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d86f152086acb9840968fee4ce71e69c59936d4a --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-04 diff --git a/openspec/changes/archive/2026-07-04-ubml-model-right-panel/design.md b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/design.md new file mode 100644 index 0000000000000000000000000000000000000000..8f8a175b25fa0d05215479091f121c1d96932bfc --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/design.md @@ -0,0 +1,17 @@ +## Context + +已有 `entity-preview` 多 tab(按 key)与 `EntityPreviewContent`。E3 采用**一个常驻 model tab + 内部导航**,避免右栏被多个实体页签挤满。 + +## Goals / Non-Goals + +**Goals:** 只读可视化;复用 `buildEntityPreviewFromDsl` / `EntityPreviewContent`;FYBXD 预览后可见 ER 图与 YAML。 + +**Non-Goals:** 图上编辑;工程全量扫描;多实体全景聚合(首版单实体 + 外部关联)。 + +## Decisions + +- 单 `model` tab 实例,entries 按 key upsert +- 视图模式:`graph` | `file`(YAML 只读 pre) +- `openUbmlModelTab()` 与 `openUbmlEntityPreviewTab()` 并行调用,不替换 entity-preview + +**Upstream sync impact**: none diff --git a/openspec/changes/archive/2026-07-04-ubml-model-right-panel/proposal.md b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..ce9167af2096d0f399cc6bd05d91804783980d88 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/proposal.md @@ -0,0 +1,26 @@ +## Why + +E1/E2 已建立元模型与受控建模操作,但生成结果仍以 YAML 文本或独立「实体预览」页签呈现,缺少统一的「模型空间」视图。E3 在右侧栏增加常驻「模型」页签,展示实体导航、ER 关系图与文件视图,并在 preview 成功时自动联动。 + +## What Changes + +- 右侧栏 `model` 页签类型(单实例 + 内部多模型导航) +- `ubml-model-tab-store.ts` / `ubml-model-bridge.ts` / `model-preview-model.ts` +- `ubml-model-panel.tsx` — 左导航 + 关系图/文件双视图 +- `previewEntityYaml()` 成功后刷新模型页签 +- `model-preview-model.test.ts` + +## Capabilities + +### New Capabilities + +- `ubml-model-right-panel`: 模型页签 UI 与生成/预览联动 + +### Modified Capabilities + +- `ubml-entity-workbench`: 预览成功后打开模型页签 + +## Impact + +- **包**: `packages/extension/` only +- **回滚**: 移除 model tab 类型与 bridge 调用即可 diff --git a/openspec/changes/archive/2026-07-04-ubml-model-right-panel/specs/ubml-model-right-panel/spec.md b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/specs/ubml-model-right-panel/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..ce03f9e0b6a34d6f0de1fda36b2dd6735dafb7b0 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/specs/ubml-model-right-panel/spec.md @@ -0,0 +1,28 @@ +## ADDED Requirements + +### Requirement: Right panel model tab + +The extension SHALL register a `model` right-panel tab type (single instance) with localized label 「模型」/ Model. + +The tab SHALL show a two-column layout: model navigation (grouped by kind, starting with entity) and a content area with graph/file view toggle. + +#### Scenario: User adds model tab from panel menu + +- **WHEN** user adds the model tab from the right panel + menu +- **THEN** an empty state prompts that models appear after entity preview or generation + +#### Scenario: FYBXD preview populates navigation + +- **WHEN** entity preview succeeds for FYBXD exemplar +- **THEN** the model tab lists FYBXD under entity navigation +- **AND** graph view shows containment (FYBXDMX, FXBXDFJ) and association refs (GspUser, SysOrg, Projects) via Mermaid ER output + +#### Scenario: File view shows YAML + +- **WHEN** user switches to file view on a selected model entry +- **THEN** the original YAML text is shown read-only + +#### Scenario: Closing model tab clears payload + +- **WHEN** user closes the model tab +- **THEN** model tab store entries are cleared without affecting entity-preview tabs diff --git a/openspec/changes/archive/2026-07-04-ubml-model-right-panel/tasks.md b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..0c1d2e415e7c6860b201b6bbd5615150a6c865c4 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-model-right-panel/tasks.md @@ -0,0 +1,20 @@ +## 1. Tab shell(M7) + +- [x] 1.1 `right-panel.tsx` / `right-panel-labels.ts` 增加 `model` 类型 +- [x] 1.2 `ubml-model-tab-store.ts` + `ubml-model-bridge.ts` +- [x] 1.3 `project-workspace-shell.tsx` 挂载与 cleanup + +## 2. 导航与 ER 图(M8) + +- [x] 2.1 `model-preview-model.ts` + test +- [x] 2.2 `ubml-model-panel.tsx`(导航 + graph/file 视图) + +## 3. 生成联动(M9) + +- [x] 3.1 `ubml-entity-artifact-actions.ts` 调用 `openUbmlModelTab` +- [x] 3.2 `right-panel-preview-utils.ts` 加号菜单支持 model + +## 4. 验证 + +- [x] 4.1 `bun test packages/extension/src/lib/model-preview-model.test.ts` +- [x] 4.2 extension typecheck(model 相关文件无新增错误) diff --git a/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d86f152086acb9840968fee4ce71e69c59936d4a --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-04 diff --git a/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/proposal.md b/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..423363b384e338276cea17a2d50078b45875029a --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/proposal.md @@ -0,0 +1,25 @@ +## Why + +E3 模型页签已支持实体 ER 图;E4 页面元模型已就绪。产品设计中模型导航应分组展示 `be/` 与 `pages/`。本变更将页面预览接入同一模型空间,完成 MoF v0 可视化闭环的页面侧扩展。 + +## What Changes + +- `PagePreviewModel` + `buildPagePreviewFromDsl` +- `UbmlModelEntry` 扩展 `kind: page` +- `previewUbmlYaml()` 成功后 `openUbmlModelTab` +- 模型页签左导航增加「页面 pages/」分组;页面结构视图(字段绑定表) + +## Capabilities + +### New Capabilities + +- `ubml-model-tab-pages`: 模型页签页面导航与预览联动 + +### Modified Capabilities + +- (无)不改变 page verify / compile 行为 + +## Impact + +- **包**: `packages/extension/` only +- **回滚**: 移除 page kind 与 bridge 调用即可 diff --git a/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/specs/ubml-model-tab-pages/spec.md b/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/specs/ubml-model-tab-pages/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..098061acd5b9a2247ce15ee723d9ce628985e652 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/specs/ubml-model-tab-pages/spec.md @@ -0,0 +1,16 @@ +## ADDED Requirements + +### Requirement: Model tab page navigation + +The model tab SHALL support `page` entries alongside `entity` entries, grouped under a「页面 pages/」navigation label. + +#### Scenario: Page preview adds navigation item + +- **WHEN** `previewUbmlYaml` succeeds for a card page +- **THEN** the model tab lists the page under pages navigation +- **AND** structure view shows page type, entity binding, and field bindings + +#### Scenario: Entity entries unchanged + +- **WHEN** `previewEntityYaml` succeeds +- **THEN** entity entries still appear under「实体 be/」with ER graph view diff --git a/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/tasks.md b/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..92c2f892fd59975cc41790470dce49468b98e8db --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-model-tab-pages/tasks.md @@ -0,0 +1,19 @@ +## 1. 页面预览模型 + +- [x] 1.1 `page-preview-model.ts` + `PagePreviewContent` +- [x] 1.2 扩展 `UbmlModelEntry` 支持 `kind: page` + +## 2. 模型页签 UI + +- [x] 2.1 更新 `model-preview-model.ts` 导航分组 +- [x] 2.2 更新 `ubml-model-panel.tsx` 按 kind 渲染 + +## 3. 联动 + +- [x] 3.1 `previewUbmlYaml` → `openUbmlModelTab` +- [x] 3.2 `ubml-model-bridge` 接受 page/entity 两种 payload + +## 4. 验证 + +- [x] 4.1 `model-preview-model.test.ts` 覆盖 page 导航 +- [x] 4.2 extension 相关测试通过 diff --git a/openspec/changes/archive/2026-07-04-ubml-page-metamodel/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d86f152086acb9840968fee4ce71e69c59936d4a --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-04 diff --git a/openspec/changes/archive/2026-07-04-ubml-page-metamodel/design.md b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/design.md new file mode 100644 index 0000000000000000000000000000000000000000..8694f658ea76d8d5fe80129330a8bbe7a059f114 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/design.md @@ -0,0 +1,66 @@ +## Context + +实体 E1 已交付 `ubml-entity.metamodel.yaml` + `entity/metamodel/` + 一致性测试。页面 DSL 位于 `packages/ubml-codegen/src/dsl/schema.ts`,金样 `fybxdj-card.yaml`,editor 值域来自 `assets/ubml/components.index.json`。AppGen crossref 在 `packages/ubml-codegen/src/app/validate/crossref.ts` 手写校验 entity/page/pageflow 引用。 + +产品设计 §3.4 已给出页面元模型骨架;§6.5 明确 E4 只复用「元模型描述 + 模型页签导航」两件验证过的东西,**不**一次性元模型驱动编译器。 + +## Goals / Non-Goals + +**Goals:** + +- `ubml-page.metamodel.yaml` 覆盖 FYBXDJ card 金样的 Page / form Section / FieldBinding / toolbar / raw 逃逸 +- 读取器 + 一致性测试(page type、editor catalog、entity external ref、金样元素映射) +- 文档化 generic external reference 模型,说明如何收敛 crossref +- `pagegen.md` 约束段落由元模型渲染 + +**Non-Goals:** + +- 不从元模型生成 page Zod 或 Farris JSON Schema +- 不替换或重写 `crossref.ts`(仅设计文档 + 测试对齐) +- 不扩展模型页签 UI 至页面导航(留待 E4 之后独立变更) +- 不实现 page 建模操作集(类似 E2 entity ops) + +## Decisions + +### 1. 元模型资产位置 + +`packages/extension/assets/ubml/metamodel/`,与 page exemplar、catalog 同包。 + +### 2. 最小元类集 + +| 元类 | 覆盖 | +|------|------| +| Page | type, title, entity (external), sections, toolbar | +| Section | form / content-container / data-grid / list-nav / filter-bar / raw | +| FieldBinding | label, bind (external→entity field), editor (catalog) | +| ToolbarAction | toolbar 字符串项 | +| RawNode | `type: raw` + opaque payload | + +### 3. External 引用统一描述 + +元模型 `reference.external: true` + `constraint` 文档化三类 crossref: + +- Page.entity → BusinessEntity +- FieldBinding.bind → EntityField +- (远期)Pageflow → Page、Query → Entity + +首期 `constraint.check` 为人读规约;执行仍由 Zod + crossref.ts。 + +### 4. 与 entity 元模型共享 M3 词汇 + +不新建 M3 构造;页面元模型是 M3 的第二个 M2 实例,证明元语言领域无关。 + +### 5. 提示词渲染 + +`page/metamodel/prompt.ts` 渲染合法 page type、editor catalog 约束;`pagegen.md` 使用 `` 标记段。 + +**Upstream sync impact**: none + +## Risks / Trade-offs + +- [页面 section 类型比设计骨架更复杂] → 元模型用 abstract Section + 子类或 doc 标注实际 DSL discriminated union;测试以 Zod schema 为 ground truth +- [crossref 文档与代码脱节] → 文档引用现有 crossref.ts 函数清单;变 crossref 时同步更新文档与元模型 constraint + +## Open Questions + +- `content-container` 嵌套 section 是否在 E4 元模型显式建模 — 首期用 Section 子类 + doc,测试覆盖 FYBXDJ 金样即可 diff --git a/openspec/changes/archive/2026-07-04-ubml-page-metamodel/proposal.md b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..4901e500a58458ae865222b7db78476ae4539647 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/proposal.md @@ -0,0 +1,28 @@ +## Why + +E1–E3 已在实体域走通 MOF 元模型、受控操作与模型页签。页面 DSL 的合法结构同样散落在 Zod schema、`components.index.json`、crossref 手写校验与 `pagegen.md` 中。E4(`10-mof-entity-modeling.md` §6.5 / M10)在**不改造页面编译器**前提下,显式化页面 M2 元模型草案,并定义通用 external 引用概念,为后续 crossref 抽象与模型页签扩展页面导航打地基。 + +## What Changes + +- 新增 `packages/extension/assets/ubml/metamodel/ubml-page.metamodel.yaml` — 页面 M2 元模型(Page / Section / FieldBinding / Action / RawNode 最小集) +- 新增 `metamodel/README.md` — 维护边界与 catalog 同步说明 +- 新增 `packages/ubml-codegen/src/page/metamodel/` — 读取器、类型、提示词片段渲染(复用 E1 模式) +- 新增 `packages/ubml-codegen/test/page-metamodel-consistency.test.ts` — 与 page DSL schema、FYBXDJ 金样、`components.index.json` 对齐 +- 新增 `docs/UBML-EXTERNAL-REFERENCE.md` — 通用 external 引用与 crossref 演进设计(文档规约,首期不替换 `crossref.ts`) +- 修改 `packages/extension/assets/ubml/prompts/pagegen.md` — 接入元模型渲染的约束段落(工作流仍人工维护) + +## Capabilities + +### New Capabilities + +- `ubml-page-metamodel`: 显式页面元模型资产、读取器、一致性测试、external 引用设计文档 + +### Modified Capabilities + +- (无)E4 为描述性事实源 + 测试 + 设计文档,不改变 `ubml-page-pipeline` 或 AppGen crossref 运行时行为 + +## Impact + +- **包**: `packages/ubml-codegen`(page/metamodel + 测试)、`packages/extension`(assets + pagegen 提示词)、`docs/` +- **边界**: extension + ubml-codegen + docs;无 vendor fork/import +- **回滚**: 删除 page metamodel 目录与测试即可;现有 page verify/compile 不受影响 diff --git a/openspec/changes/archive/2026-07-04-ubml-page-metamodel/specs/ubml-page-metamodel/spec.md b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/specs/ubml-page-metamodel/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..22f97b84086dc67f3f7f77d4fe8c39481a5de984 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/specs/ubml-page-metamodel/spec.md @@ -0,0 +1,69 @@ +## ADDED Requirements + +### Requirement: Explicit page metamodel asset + +The product SHALL ship a machine-readable page metamodel at `packages/extension/assets/ubml/metamodel/ubml-page.metamodel.yaml` using the same M3 vocabulary as the entity metamodel. + +The metamodel SHALL declare meta-classes for Page, Section variants (including form and raw), FieldBinding, and toolbar actions, with: + +- `Page.entity` as external reference to BusinessEntity +- `FieldBinding.bind` as external reference to entity fields +- `FieldBinding.editor` constrained against `components.index.json` +- `RawNode` for `type: raw` escape sections without parsing inner structure +- Human-readable `doc` and `constraint.check` fields (documentation-first in E4) + +#### Scenario: FYBXDJ card exemplar is explainable by metamodel + +- **WHEN** `exemplars/fybxdj-card.yaml` is inspected against metamodel class definitions +- **THEN** page type, title, entity binding, toolbar actions, form sections, and field bindings map to declared meta-classes + +#### Scenario: Metamodel README documents E4 boundaries + +- **WHEN** a contributor opens `assets/ubml/metamodel/README.md` +- **THEN** they find that constraints are documentation-first and crossref.ts is not replaced in E4 + +### Requirement: Page metamodel loader + +`packages/ubml-codegen` SHALL provide a page metamodel loader parsing `ubml-page.metamodel.yaml` into stable TypeScript types, usable in Bun tests without side effects. + +#### Scenario: Loader parses default asset path + +- **WHEN** the loader reads the bundled page metamodel asset +- **THEN** it returns classes including Page and FieldBinding +- **AND** parsing completes without throwing + +### Requirement: Page metamodel consistency tests + +The product SHALL include `packages/ubml-codegen/test/page-metamodel-consistency.test.ts` verifying: + +1. Metamodel-declared page types and editor constraints align with page DSL schema and `components.index.json` +2. FYBXDJ card exemplar elements map to metamodel classes +3. Catalog/constraint names in the metamodel resolve to existing asset files under `assets/ubml/` + +#### Scenario: Test fails on editor catalog drift + +- **WHEN** the metamodel references editor catalog constraints but `components.index.json` is missing +- **THEN** the consistency test fails + +### Requirement: Generic external reference design document + +The product SHALL document the generic external reference model in `docs/UBML-EXTERNAL-REFERENCE.md`, describing how Page.entity, FieldBinding.bind, and future cross-model refs relate to metamodel `external: true` references and the evolution path from handwritten AppGen crossref checks. + +#### Scenario: Document lists current crossref coverage + +- **WHEN** a contributor reads `docs/UBML-EXTERNAL-REFERENCE.md` +- **THEN** they find mappings from metamodel external refs to existing crossref validation in `crossref.ts` + +### Requirement: Pagegen prompt constraints from metamodel + +The product SHALL render agent-facing page constraint fragments from the page metamodel and inject them into `assets/ubml/prompts/pagegen.md`, while leaving workflow/strategy sections human-authored. + +#### Scenario: Rendered fragment lists page types and editor rules + +- **WHEN** prompt rendering runs against the loaded page metamodel +- **THEN** output includes page type constraints and editor catalog guidance + +#### Scenario: Existing page verify behavior unchanged + +- **WHEN** E4 is complete +- **THEN** `bun run ubml:verify packages/extension/assets/ubml/exemplars/fybxdj-card.yaml` still passes with no change to compile/verify semantics diff --git a/openspec/changes/archive/2026-07-04-ubml-page-metamodel/tasks.md b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..16241e8e743710a927095ee7c68a943da1fef275 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-page-metamodel/tasks.md @@ -0,0 +1,24 @@ +## 1. 页面元模型资产(M10-a) + +- [x] 1.1 新增 `ubml-page.metamodel.yaml`,覆盖 FYBXDJ card 金样 +- [x] 1.2 新增 `assets/ubml/metamodel/README.md` + +## 2. 读取器与一致性测试(M10-b) + +- [x] 2.1 新增 `page/metamodel/types.ts` + `load.ts` +- [x] 2.2 新增 `page-metamodel-consistency.test.ts`(schema / 金样 / components.index) + +## 3. External 引用设计(M10-c) + +- [x] 3.1 新增 `docs/UBML-EXTERNAL-REFERENCE.md`(crossref 演进路径,不替换 crossref.ts) + +## 4. 提示词渲染(M10-d) + +- [x] 4.1 新增 `page/metamodel/prompt.ts` + `syncPagegenMetamodelSection()` +- [x] 4.2 修改 `pagegen.md` 接入元模型约束段 + +## 5. 验证 + +- [x] 5.1 `bun test packages/ubml-codegen/test/page-metamodel-consistency.test.ts` +- [x] 5.2 FYBXDJ card verify 仍通过 +- [x] 5.3 `packages/ubml-codegen` 无新增 typecheck 错误(本变更范围) diff --git a/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ac9f6c444b70645c2c1ede69cc5da2d46e7bd051 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/.openspec.yaml @@ -0,0 +1,2 @@ +name: ubml-page-raw-escape +status: active diff --git a/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/proposal.md b/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..3c4b2c96cd18ba90c43f04a0c2a2402b306f1794 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/proposal.md @@ -0,0 +1,15 @@ +## Why + +P3-04:`raw:` 逃逸通道缺少操作文档与 round-trip 测试,贡献者不清楚何时使用、是否存在信息丢失风险。 + +## What Changes + +- `docs/UBML-RAW-ESCAPE.md` — 何时用/不用、verify 关系、正/反例、已知限制 +- `compare-raw-section.ts` — raw payload 保留断言 +- `test/raw-section-roundtrip.test.ts` — ≥2 round-trip 用例 +- exemplar `raw-custom-section.yaml` +- 贡献者指南与 UBML-PAGEGEN 链接 + +## Capabilities + +- page DSL raw section round-trip spec delta diff --git a/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/specs/ubml-dsl-compiler/spec.md b/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/specs/ubml-dsl-compiler/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..3bdf5d57243ccb685b266c6db576c966e5e2c456 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/specs/ubml-dsl-compiler/spec.md @@ -0,0 +1,22 @@ +## ADDED Requirements + +### Requirement: Raw section round-trip preservation + +Page DSL sections with `type: raw` SHALL preserve `raw.id` and non-id payload fields through compile → decompile → recompile for `card` page type. + +#### Scenario: Card page explicit raw section + +- **WHEN** a card page YAML contains a top-level `type: raw` section with `raw.id` +- **THEN** compile → decompile yields a DSL where the same `raw.id` exists with equivalent payload fields +- **AND** verify passes on the decompiled YAML + +#### Scenario: Multiple raw widgets on one page + +- **WHEN** a card page contains two or more explicit raw sections (e.g. notify + custom section) +- **THEN** each `raw.id` is preserved after round-trip +- **AND** verify passes + +#### Scenario: Documented layout limitation + +- **WHEN** raw sections are nested inside `content-container` on a `layout` page +- **THEN** documentation SHALL state that decompile may collapse structure into an outer raw blob while Farris ids remain in the compiled model diff --git a/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/tasks.md b/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..49c54d2593192989485ffce4e04b1f252a919156 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-page-raw-escape/tasks.md @@ -0,0 +1,14 @@ +## 1. 核心 + +- [x] 1.1 compare-raw-section.ts(collect + preserve 断言) +- [x] 1.2 raw-section-roundtrip.test.ts(≥2 用例 + 双次 round-trip) + +## 2. 文档与范例 + +- [x] 2.1 docs/UBML-RAW-ESCAPE.md(正/反例、verify、限制) +- [x] 2.2 exemplars/raw-custom-section.yaml +- [x] 2.3 07-contributing + UBML-PAGEGEN 链接 + +## 3. 验证 + +- [x] 3.1 raw round-trip 测试全绿 diff --git a/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b6586d0ce7170a2dc00baf05b10ec3d7cd81c7e1 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/.openspec.yaml @@ -0,0 +1,2 @@ +name: ubml-roundtrip-regression +status: active diff --git a/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/proposal.md b/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..cc9af799775115d70fd73f8e2726725acdf70cd7 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/proposal.md @@ -0,0 +1,13 @@ +## Why + +P3-01:反编译与编译独立演进时易破坏 round-trip 不变量,需要固定 page + entity 样本与语义 diff 回归。 + +## What Changes + +- `packages/ubml-codegen/src/roundtrip/` — 语义比较 + run helpers +- `test/roundtrip-regression.test.ts` — 正/负例 +- `script/ubml/roundtrip-smoke.ts` + `docs/UBML-ROUNDTRIP.md` + +## Capabilities + +- `ubml-entity-decompiler` / page decompile spec delta diff --git a/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/specs/ubml-entity-decompiler/spec.md b/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/specs/ubml-entity-decompiler/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..3f4b9753984c3e45c93a7379d881a4866308401a --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/specs/ubml-entity-decompiler/spec.md @@ -0,0 +1,21 @@ +## ADDED Requirements + +### Requirement: Page and entity round-trip regression suite + +The product SHALL provide automated round-trip tests that compile reference samples, decompile to DSL, recompile, and assert semantic equivalence with configurable ignore rules for known drift (ChildTriggerInfo, reference-only BE fields, page node ids). + +#### Scenario: FYBXD entity round-trip passes + +- **WHEN** fybxd.be.yaml or fybxd.be.json runs through compile → decompile → recompile +- **THEN** semantic diff is zero within documented ignore rules +- **AND** verify pipeline passes + +#### Scenario: FYBXD page round-trip passes + +- **WHEN** fybxdj-card.yaml runs through compile → decompile → recompile → decompile +- **THEN** page DSL canonical text is unchanged + +#### Scenario: Intentional DSL mutation fails regression + +- **WHEN** a field type or bind is deliberately altered before recompile +- **THEN** semantic compare reports differences and tests fail diff --git a/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/tasks.md b/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..2a5285bd3f17b5107deb631425fd6029486fe11d --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-roundtrip-regression/tasks.md @@ -0,0 +1,13 @@ +## 1. 核心 + +- [x] 1.1 entity/page semantic compare + ignore 配置 +- [x] 1.2 runEntityRoundtrip / runPageRoundtrip + +## 2. 测试与文档 + +- [x] 2.1 roundtrip-regression.test.ts(含负例) +- [x] 2.2 smoke + docs/UBML-ROUNDTRIP.md + +## 3. 验证 + +- [x] 3.1 测试 <30s 全绿 diff --git a/openspec/changes/archive/2026-07-04-ubml-verify-hints/.openspec.yaml b/openspec/changes/archive/2026-07-04-ubml-verify-hints/.openspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c91d044497e6cf536a5af7b11aafb057b1d26484 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-verify-hints/.openspec.yaml @@ -0,0 +1,2 @@ +name: ubml-verify-hints +status: active diff --git a/openspec/changes/archive/2026-07-04-ubml-verify-hints/proposal.md b/openspec/changes/archive/2026-07-04-ubml-verify-hints/proposal.md new file mode 100644 index 0000000000000000000000000000000000000000..22cc1411ca056d5e7bc143cdf786cddab7e24e11 --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-verify-hints/proposal.md @@ -0,0 +1,14 @@ +## Why + +P3-03:校验失败信息对 Agent/人不够 actionable,需中文 hint 与建议值。 + +## What Changes + +- `validation/verify-hints.ts` — 5+ 类高频错误中文 hint +- verify pipeline 自动 enrich issues +- extension Preview 展示 hint;page preview 改走 verify +- `test/verify-hints.test.ts` + +## Capabilities + +- page/entity verify spec delta diff --git a/openspec/changes/archive/2026-07-04-ubml-verify-hints/specs/ubml-page-verify/spec.md b/openspec/changes/archive/2026-07-04-ubml-verify-hints/specs/ubml-page-verify/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..87278d42eb024085f52f6806ac7e74517e2d1e0c --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-verify-hints/specs/ubml-page-verify/spec.md @@ -0,0 +1,15 @@ +## ADDED Requirements + +### Requirement: Actionable Chinese verify hints + +Page and entity verify pipelines SHALL attach Chinese `hint` and optional `suggestion` fields to structured issues for common failure classes (unknown editor, missing entity/bind, unknown ref/type, YAML parse, schema missing property), without exposing stack traces in user-visible messages. + +#### Scenario: Unknown editor includes Chinese hint and suggestions + +- **WHEN** page verify fails due to unknown editor +- **THEN** the issue includes `hint` in Chinese and `hints`/`suggestion` with valid editor names + +#### Scenario: Agent consumes hint field + +- **WHEN** verify result is formatted for agent prompt via `formatVerifyIssuesForAgent` +- **THEN** output includes a hint column suitable for repair loops diff --git a/openspec/changes/archive/2026-07-04-ubml-verify-hints/tasks.md b/openspec/changes/archive/2026-07-04-ubml-verify-hints/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..eef1d5204cea810fb672dbc954c68eb173262fba --- /dev/null +++ b/openspec/changes/archive/2026-07-04-ubml-verify-hints/tasks.md @@ -0,0 +1,13 @@ +## 1. Hint 引擎 + +- [x] 1.1 verify-hints.ts(page + entity) +- [x] 1.2 verify-core / verify-page / entity verify-core 集成 + +## 2. UI 与文档 + +- [x] 2.1 extension ubml-artifact-card + preview actions +- [x] 2.2 UBML-ENTITY-VERIFY.md / PAGEGEN 说明 + +## 3. 测试 + +- [x] 3.1 verify-hints.test.ts ≥5 类错误 diff --git a/openspec/specs/agent-home/spec.md b/openspec/specs/agent-home/spec.md index 1644548af4aea4f97566e923e989401e4be1f521..8658573600be36d7601d0d91a86c0d8e6f5be21a 100644 --- a/openspec/specs/agent-home/spec.md +++ b/openspec/specs/agent-home/spec.md @@ -73,3 +73,32 @@ All Agent Window UI components and styles SHALL reside under `packages/extension - **WHEN** a vendor import overwrites `packages/app` - **THEN** the Agent Window implementation under `packages/extension/` remains intact - **AND** the home route hook continues to resolve to the extension home page + +### Requirement: Central area shows session message timeline when a session is selected + +When the user is on Agent Home (`/`) with an active project and at least one session, the central chat area SHALL embed the same MessageTimeline component used on session routes, showing historical messages for the previewed or selected session. + +#### Scenario: User selects a session on home route + +- **WHEN** the user is on `/` with a project and clicks a session in the left panel +- **THEN** the central area displays that session's message timeline +- **AND** the sidebar highlights the selected session + +#### Scenario: User opens full session from home preview + +- **WHEN** the user clicks「在完整会话中继续」on the home timeline preview +- **THEN** the app navigates to `/:dir/session/:id` with the same session + +### Requirement: Friendly empty states on Agent Home + +The central chat area SHALL show Chinese empty-state copy when no project is selected or when a project has no sessions yet, instead of only static brand placeholder text. + +#### Scenario: No project + +- **WHEN** the user is on `/` without any project +- **THEN** the central area prompts to add a project folder from the sidebar + +#### Scenario: Project with no sessions + +- **WHEN** the user is on `/` with a project but zero sessions +- **THEN** the central area shows welcome copy and a control to start a new session diff --git a/openspec/specs/api-wiki/spec.md b/openspec/specs/api-wiki/spec.md index 74833054d4ba9eb2d35b55f6f253daabea2defab..d28324e998e59f8f4485af99dbbf3d92bb5f86dc 100644 --- a/openspec/specs/api-wiki/spec.md +++ b/openspec/specs/api-wiki/spec.md @@ -31,3 +31,30 @@ The knowledge MCP server SHALL expose `kb_api_search` and `kb_api_get` tools bac - **WHEN** an agent invokes `kb_api_search` with a query - **THEN** matching entries for the repo are returned + +### Requirement: HTTP route extraction into API catalog + +The knowledge service SHALL extract HTTP routes from control-plane route handler sources (default `packages/knowledge/src/control/*-routes.ts`) and merge them into `api-catalog.json` as entries with kind `http-route`. + +Each http-route entry SHALL include `httpMethod`, `httpPath`, and optional `router` mount prefix. + +Extraction failures for individual files SHALL be logged as issues without aborting the full import. + +CLI `kb api-routes-refresh ` SHALL support `--dry-run` and optional `--dirs`. + +Repository indexing SHALL invoke HTTP route merge after symbol-based catalog build. + +#### Scenario: Mission API routes extracted + +- **WHEN** extract runs on code-agent control handlers +- **THEN** entries include GET `/mission/health` and POST `/mission/sync` + +#### Scenario: Search by path + +- **WHEN** api catalog is searched with query `/mission/health` +- **THEN** matching http-route entry is returned + +#### Scenario: API Wiki panel shows http-route kind + +- **WHEN** user filters API Wiki by type HTTP 路由 +- **THEN** mission and api-wiki control routes are listed with method and path diff --git a/openspec/specs/knowledge-service/spec.md b/openspec/specs/knowledge-service/spec.md index ade114530d64292dd5b1a7bdaecdcf1d0736b2a5..b6fa9be50d04522ee1aef3be8049d853103aa1c8 100644 --- a/openspec/specs/knowledge-service/spec.md +++ b/openspec/specs/knowledge-service/spec.md @@ -346,3 +346,87 @@ Agents SHALL be able to combine Farris frontend knowledge tools with UBML page t - **WHEN** an agent has BE field types from `kb_be_datatype` or entity DSL context - **THEN** it MAY call `kb_farris_field_editor_mapping` to choose Farris editor components for those field types + +### Requirement: DesignBase architecture card bulk import + +The knowledge service SHALL provide `importDesignBaseArchCards` to scan configured Markdown directories (default `docs/product-design/`), split documents at level-2 headings, and write arch-card files to `{repoArtifactDir}/arch/designbase-*.md` compatible with `kb_arch` retrieval. + +The importer SHALL maintain `designbase-manifest.json` with per-source-file content hashes and skip unchanged files on re-import. + +The importer SHALL remove stale designbase cards when source sections change. + +CLI `kb designbase-import ` SHALL support `--dry-run`, `--force`, and `--dirs dir1,dir2`. + +Full repository indexing (`kb index`) SHALL invoke DesignBase import after package arch cards are built. + +#### Scenario: Import product-design chapters + +- **WHEN** designbase-import runs on code-agent repo +- **THEN** at least one card is created from `docs/product-design/*.md` +- **AND** card content includes source path and section body + +#### Scenario: Incremental skip + +- **WHEN** designbase-import runs twice without source changes +- **THEN** second run skips unchanged files via manifest hash + +#### Scenario: Dry-run preview + +- **WHEN** designbase-import runs with `--dry-run` +- **THEN** no arch-card files or manifest are written +- **AND** result lists would-be created/updated/removed card ids + +### Requirement: Retrieval evaluation harness + +The product SHALL provide `packages/knowledge/eval/retrieval-tasks.json` with at least 20 Chinese retrieval tasks (locate, plan, explain). + +Script `script/knowledge/eval-retrieval.ts` SHALL report pass rate and exit non-zero when below `KB_EVAL_THRESHOLD` (default 0.8). + +Script `script/knowledge/eval-retrieval-ci.ts` SHALL index code-agent with fallback embeddings and run evaluation for CI. + +#### Scenario: Local eval report + +- **WHEN** user runs `bun run kb:eval:retrieval` on an indexed code-agent repo +- **THEN** output includes pass count and percentage by mode + +#### Scenario: CI threshold gate + +- **WHEN** `bun run kb:eval:retrieval:ci` completes with pass rate >= 80% +- **THEN** exit code is 0 + +#### Scenario: Warn-only mode + +- **WHEN** KB_EVAL_WARN_ONLY=1 and pass rate is below threshold +- **THEN** script exits 0 with warning + +### Requirement: FYBXD-oriented Farris component aliases + +The Farris knowledge pack SHALL maintain Chinese aliases in `component-aliases.json` covering common FYBXD page field patterns (amount, status, lookup, grid, filter), with a documented smoke eval of alias recall. + +#### Scenario: Chinese FYBXD field locates component + +- **WHEN** `kb_farris_locate` or alias fuzzy search receives a Chinese query such as 报帐金额 or 报销明细 +- **THEN** the top hit maps to the expected Farris component (e.g. number-spinner, data-grid) +- **AND** alias entries do not duplicate conflicting keys + +### Requirement: Pro tier one-command deploy + +The product SHALL provide a standalone Docker Compose stack and shell script to start Pro-tier dependencies (Qdrant required; Zoekt optional) without requiring the full dual-ide compose profile, plus an environment variable example file and documentation for Lite-to-Pro migration. + +#### Scenario: Contributor starts Pro stack locally + +- **WHEN** a contributor runs the documented one-command Pro up script +- **THEN** Qdrant becomes reachable on the documented localhost port +- **AND** optional Zoekt can be started with a documented flag +- **AND** no secrets are committed to the repository + +#### Scenario: Pro retrieval verified after deploy + +- **WHEN** Pro stack is up and the operator runs the documented smoke command with `KB_TIER=pro` +- **THEN** at least one retrieval path (`kb locate` or equivalent API) returns hits for an indexed repository + +#### Scenario: Zoekt unavailable falls back to ripgrep + +- **WHEN** Pro tier is active and Zoekt returns no hits or is unreachable +- **THEN** lexical search falls back to ripgrep or file scan +- **AND** retrieval continues without hard failure diff --git a/openspec/specs/qa-hand-verification/spec.md b/openspec/specs/qa-hand-verification/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..c360da5b0ac430c8833f61818eef723fc44e407e --- /dev/null +++ b/openspec/specs/qa-hand-verification/spec.md @@ -0,0 +1,26 @@ +# qa-hand-verification + +手验自动化:将 OpenSpec 归档手验项映射为可离线运行的 smoke 编排命令。 + +## Requirements + +### Requirement: Hand verification smoke orchestrator + +The repository SHALL provide a single command that runs at least three automated checks mapped to OpenSpec archive manual verification items, without requiring LLM API keys. + +#### Scenario: Offline hand verification pass + +- **WHEN** the user runs `bun run qa:hand-verification` with dependencies installed +- **THEN** entity pipeline, UBML knowledge, and compiler round-trip smokes execute +- **AND** each step prints its archive reference and pass/fail status + +#### Scenario: Failure localization + +- **WHEN** a step fails +- **THEN** the orchestrator prints the step id and child script output +- **AND** the user can re-run a single step via `HAND_VERIFICATION_ONLY` + +#### Scenario: Optional dev stack checks + +- **WHEN** `bun run dev` is running and the user runs `bun run qa:hand-verification:strict` +- **THEN** web shell HTTP checks for `/` and backend health are included and must pass diff --git a/openspec/specs/ubml-app-derive/spec.md b/openspec/specs/ubml-app-derive/spec.md index 7654cc211ea09f870257db43ea084babd0d186f9..788a4b6ea16f135d61cc77cb76e0c86700805c6a 100644 --- a/openspec/specs/ubml-app-derive/spec.md +++ b/openspec/specs/ubml-app-derive/spec.md @@ -175,3 +175,27 @@ Tests SHALL NOT depend on a local igix workspace path at CI time. - **WHEN** regression test derives VO from FYBXD BE fixture with `--vo-name Expense` - **THEN** semantic equivalence function reports success against `expense.vo.json` baseline + +### Requirement: Derive type boundary regression + +The derive pipeline SHALL document BE→DBO type mappings and provide automated boundary tests for DateTime, Decimal, enum (ObjectType), and nullable column rules, with a minimal BE fixture. + +#### Scenario: DateTime maps to TimeStamp + +- **WHEN** a BE field has MDataType DateTime or Date +- **THEN** derived DBO column uses TimeStamp and VO preserves MDataType + +#### Scenario: Decimal precision and scale + +- **WHEN** a BE Decimal field specifies Precision and Scale +- **THEN** derived DBO column carries matching precision and scale + +#### Scenario: Enum values on VO + +- **WHEN** a BE field has ObjectType Enum and ContainEnumValues +- **THEN** derived VO element preserves ObjectType and enum value list + +#### Scenario: Nullable rules + +- **WHEN** derive builds DBO columns from IsRequire and PK/ParentID conventions +- **THEN** required and PK columns are non-nullable; ParentID is nullable diff --git a/openspec/specs/ubml-app-workbench/spec.md b/openspec/specs/ubml-app-workbench/spec.md index 813de7d12f12f5d6b0c3878bc84245b3cb5841c0..90ea69271171768b5909316835c4f41539e15e89 100644 --- a/openspec/specs/ubml-app-workbench/spec.md +++ b/openspec/specs/ubml-app-workbench/spec.md @@ -1,6 +1,6 @@ # ubml-app-workbench -Extension UBML 工作台 **Apps** 入口:`app:` YAML 编辑、浏览器内 Generate、应用总览 Preview、Ask Agent 派发 `@ubml-appgen`。 +Extension UBML 工作台 **Apps** 入口:`app:` YAML 编辑、浏览器内 Generate、应用总览 Preview、Ask Agent 派发 `@ubml-appgen`、**校验失败修复回环**。 ## Requirements @@ -15,13 +15,18 @@ The UBML workbench SHALL expose **Pages | Entities | Apps** sub-navigation. Apps ### Requirement: In-browser app generation -Apps editor SHALL invoke `generateAppFromDsl` with bundled FYBXD sub-DSL sources and display validation stages. +Apps editor SHALL invoke `generateAppFromDsl` with bundled FYBXD sub-DSL sources and display validation stages **including repair round status when user is in a repair loop**. #### Scenario: FYBXD exemplar generates successfully - **WHEN** user loads fybxd app exemplar and clicks Generate - **THEN** all stages report valid and Preview shows artifact list +#### Scenario: Display repair round in status + +- **WHEN** user has triggered Fix with Agent after a failed Generate +- **THEN** status area shows repair round `n/3` until success or limit reached + ### Requirement: App overview preview Preview panel SHALL list generation stages, artifact groups by kind, validation issues, and a Mermaid dependency flowchart when Generate completes. @@ -35,7 +40,31 @@ Preview panel SHALL list generation stages, artifact groups by kind, validation Workbench SHALL provide Ask Agent using `@ubml-appgen` prompt. Install script SHALL copy `ubml-appgen.md` agent definition. +When the latest Generate result has `validation.valid === false`, workbench SHALL expose a **Fix with Agent** action that dispatches a follow-up prompt containing structured validation issues and hints (round counter ≤ 3). + #### Scenario: Ask Agent with intent - **WHEN** user enters intent and clicks Ask Agent with active project - **THEN** new agent session starts with appgen prompt including intent and current YAML draft + +#### Scenario: Fix with Agent after validation failure + +- **WHEN** Generate fails with crossref or sub-pipeline issues +- **AND** user clicks Fix with Agent +- **THEN** a new agent session starts with repair prompt listing issues by stage with hints +- **AND** prompt indicates current repair round out of maximum 3 + +#### Scenario: Fix with Agent disabled when valid + +- **WHEN** latest Generate result has validation.valid true +- **THEN** Fix with Agent control is disabled + +### Requirement: Repair loop limit enforcement + +Workbench repair loop SHALL NOT dispatch more than 3 consecutive repair prompts for the same editing session without user acknowledgment. + +#### Scenario: Block fourth repair round + +- **WHEN** user has already dispatched 3 repair prompts without achieving validation.valid +- **THEN** Fix with Agent is disabled +- **AND** status shows remaining issues and manual edit guidance diff --git a/openspec/specs/ubml-appgen-agent/spec.md b/openspec/specs/ubml-appgen-agent/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..9307475e52957c1d1637aa890cd2a44d19f35528 --- /dev/null +++ b/openspec/specs/ubml-appgen-agent/spec.md @@ -0,0 +1,43 @@ +# ubml-appgen-agent + +`@ubml-appgen` Agent 工作流:自然语言 → `app:` YAML → 编排 verify → 结构化 hints 修复回环(≤3 轮)。 + +## Requirements + +### Requirement: Three-round verify repair loop + +The `@ubml-appgen` agent workflow SHALL after producing or modifying `app:` YAML run app-level verification (CLI `ubml:app:generate` or workbench Generate) and repair failures using structured issues **up to 3 rounds** before reporting remaining issues to the user. + +#### Scenario: Single-round success + +- **WHEN** agent produces app YAML that passes all stages on first verify +- **THEN** agent reports success with stage summary and does not enter repair loop + +#### Scenario: Two-round repair + +- **WHEN** first verify fails with crossref issue containing hint +- **AND** agent applies hint and re-verifies +- **THEN** second verify passes and agent reports round count + +#### Scenario: Stop after third failed round + +- **WHEN** verify still fails after 3 repair attempts +- **THEN** agent stops looping and lists remaining issues with manual next steps + +### Requirement: Stage-classified failure handling + +Agent prompt assets (`appgen.md`, `ubml-appgen.md`) SHALL document failure handling by stage id prefix: `entity:*`, `derive`, `query:*`, `component`, `page:*`, `pageflow`, `javagen`, `crossref`. + +#### Scenario: Prompt documents crossref repair + +- **WHEN** maintainer reads `assets/ubml-app/prompts/appgen.md` +- **THEN** document includes crossref failure section with exemplar hints + +### Requirement: No false MAVEN claims + +Agent SHALL NOT claim MAVEN compile success unless Maven gate was explicitly run (P1-03). + +#### Scenario: Agent defers Maven to CLI + +- **WHEN** user asks for compile verification without --maven +- **THEN** agent states DSL/crossref validation only and mentions optional `--maven` for Java compile gate diff --git a/openspec/specs/ubml-appgen-orchestrator/spec.md b/openspec/specs/ubml-appgen-orchestrator/spec.md index 0ec606ab4a166f634d3c9816dbfd137b8c9ca6b9..eb3396b2d7ec5adc6ea2ac52074c1f539bb92c5d 100644 --- a/openspec/specs/ubml-appgen-orchestrator/spec.md +++ b/openspec/specs/ubml-appgen-orchestrator/spec.md @@ -1,6 +1,6 @@ # ubml-appgen-orchestrator -`app:` YAML 应用清单驱动 entity / derive / query / component / pageflow / javagen 编排生成,位于 `packages/ubml-codegen/src/app/`。 +`app:` YAML 应用清单驱动 entity / derive / query / component / **page** / pageflow / javagen 编排生成,位于 `packages/ubml-codegen/src/app/`。 ## Requirements @@ -9,7 +9,9 @@ The product SHALL define an `app:` root with: - `bizobject`, `namespace`, `entities[]`, `derive.from`, `derive.targets` -- Optional: `queries[]`, `pageflow`, `logic[]`, `sources` path map +- Optional: `queries[]`, `pages[]`, `pageflow`, `logic[]`, `sources` path map + +The `app.sources` map SHALL support optional `pages: Record` mapping page codes to page YAML paths. #### Scenario: Parse FYBXD app exemplar @@ -21,25 +23,50 @@ The product SHALL define an `app:` root with: - **WHEN** derive.from is absent from entities list - **THEN** validation fails before compile +#### Scenario: Resolve FYBXD page sources from defaults + +- **WHEN** fybxd-app.yaml lists `pages: [FYBXDJ]` without explicit sources.pages.FYBXDJ +- **AND** a default exemplar path is registered for FYBXDJ +- **THEN** resolver returns absolute path to the page YAML + +#### Scenario: Reject missing page source + +- **WHEN** `app.pages[]` includes code `UnknownPage` with no sources.pages entry and no default +- **THEN** resolution fails before compile with error naming the page code + ### Requirement: Orchestrated multi-pipeline generation -Generation SHALL run in order: entity compile → derive → query compile → component compile → pageflow compile → javagen. +Generation SHALL run in order: entity compile → derive → query compile → component compile → **page compile (when `pages[]` declared)** → pageflow compile → javagen. -Each step SHALL aggregate validation into `stages[]`. +Each step SHALL aggregate validation into `stages[]` with stage ids `page:{code}` for each compiled page. -#### Scenario: FYBXD app generates all artifact groups +#### Scenario: FYBXD app generates all artifact groups including pages -- **WHEN** generate runs on fybxd-app exemplar with default sources -- **THEN** result includes be, derive, query, components, pageflow, and java files +- **WHEN** generate runs on fybxd-app exemplar with `pages[]` and `sources.pages` configured +- **THEN** result includes be, derive, query, components, **frm pages**, pageflow, and java files - **AND** all stages report valid +#### Scenario: Page stage skipped when pages empty + +- **WHEN** `app.pages` is omitted or empty and `sources.frmDir` is provided +- **THEN** page compile stages are not emitted +- **AND** pageflow MAY use frmDir index as before + ### Requirement: Cross-model consistency checks -Crossref SHALL verify: +Crossref and sub-pipeline validation issues SHALL support optional **`hint`** and **`suggestedFix`** fields to guide Agent repair loops. + +The product SHALL provide `formatAppIssuesForAgent(issues, round, maxRounds)` returning markdown suitable for `@ubml-appgen` repair prompts. + +#### Scenario: Crossref issue includes hint for missing pageflow frm -- Query/component BE bindings resolve to compiled entities -- Pageflow validation passes when frmDir provided -- logic containing writeback enables javagen writeback template +- **WHEN** pageflow references frm not present in compiled pages +- **THEN** crossref issue includes hint suggesting adding the page to app.pages[] and sources.pages + +#### Scenario: Format issues table for repair round 2 + +- **WHEN** formatAppIssuesForAgent is called with 2 issues and round=2, maxRounds=3 +- **THEN** output includes heading with round counter and table columns stage, path, message, hint #### Scenario: Crossref fails on unknown entity in derive @@ -48,9 +75,149 @@ Crossref SHALL verify: ### Requirement: App generate CLI -CLI `ubml-app-generate` with `--out` writes manifest.json and artifact tree. +CLI `ubml-app-generate` with `--out` writes manifest.json and artifact tree including `pages/*.frm.json` when pages are compiled. #### Scenario: CLI writes output tree - **WHEN** CLI succeeds with `--out ./out` - **THEN** `./out/manifest.json` exists and lists artifact paths + +#### Scenario: CLI writes page artifacts + +- **WHEN** CLI succeeds with `--out ./out` and app manifest declares pages +- **THEN** `./out/pages/*.frm.json` files exist for each compiled page + +### Requirement: Browser app generation includes pages + +Browser-safe app generation SHALL bundle FYBXD page YAML assets and produce the same page stages as CLI for the fybxd exemplar. + +#### Scenario: Apps Generate emits page stages + +- **WHEN** user clicks Generate on fybxd-app exemplar in Apps workbench +- **THEN** result stages include `page:FYBXDJ` (and other configured pages) with valid=true + +### Requirement: Optional Maven compile gate + +CLI `ubml-app-generate` SHALL support optional **`--maven`** to run a Maven compile gate on generated Java files after successful crossref validation. + +CLI SHALL support **`--skip-maven`** and environment variable **`UBML_MAVEN_SMOKE=0`** to skip the Maven gate. + +CLI SHALL support optional **`--maven-home`** and **`--java-version`** (default 17). + +When crossref validation fails, Maven gate SHALL be skipped with documented skipReason. + +When Maven gate is enabled, the product SHALL write a minimal Maven project under `{out}/maven-gate/` and execute `mvn -q compile`. + +Maven compile failures SHALL be parsed into issues with `stage: "maven"`, `path`, optional `line`, and `message`. + +#### Scenario: Maven gate passes + +- **WHEN** CLI runs with `--maven` on FYBXD exemplar +- **AND** Maven is installed +- **AND** crossref validation passes +- **THEN** exit code is 0 +- **AND** stages include `{ id: "maven", valid: true }` + +#### Scenario: Maven gate fails on compile error + +- **WHEN** generated Java contains syntax errors +- **AND** CLI runs with `--maven` +- **THEN** exit code is 1 +- **AND** maven stage issues include file path and line when parseable + +#### Scenario: Maven unavailable + +- **WHEN** CLI runs with `--maven` but `mvn` is not on PATH +- **THEN** exit code is 2 + +#### Scenario: Skip Maven gate + +- **WHEN** CLI runs with `--skip-maven` or `UBML_MAVEN_SMOKE=0` +- **THEN** maven stage is omitted or marked skipped +- **AND** exit code follows DSL/crossref validation only + +### Requirement: Golden sample E2E regression + +The product SHALL provide an end-to-end golden regression for the FYBXD app exemplar that: + +- Runs full `generateAppFromYaml` (entity → derive → query → component → page → pageflow → javagen) +- Compares output semantically against ROBXDJChenshj-derived baselines in `packages/ubml-codegen/test/fixtures/` +- Ignores reference-only BE fields absent from the exemplar YAML (`SecID`, `SecLevel`) +- Ignores DBO column id drift when comparing yaml-compile vs reference-export paths + +The product SHALL expose `compareAppToFybxdGoldenFromRepo(result, repoRoot)` returning `{ ok, differences[] }`. + +The product SHALL expose `formatGoldenDiff(result)` for readable CI / CLI output. + +#### Scenario: FYBXD golden E2E passes + +- **WHEN** `app-golden-e2e.test.ts` runs generate on fybxd-app.yaml +- **THEN** semantic compare returns ok=true + +#### Scenario: Tampered output fails with readable diff + +- **WHEN** compiled BE is mutated before compare +- **THEN** compare returns ok=false +- **AND** differences include prefixed stage labels (e.g. `be:`) + +#### Scenario: Skip golden smoke + +- **WHEN** `UBML_GOLDEN_E2E=0` +- **THEN** golden smoke script exits 0 without running compare + +#### Scenario: Deterministic generation + +- **WHEN** generate runs twice on the same fybxd-app.yaml +- **THEN** stages and artifacts JSON are identical + +### Requirement: App-level decompile from project directory + +CLI `ubml-app-decompile` SHALL scan a project directory for UBML JSON artifacts and emit: + +- Top-level `app.yaml` +- Sub-DSL YAML files for entities, queries, components, pageflow, and pages +- `sources` map with repo-relative paths resolvable by `generateAppFromYaml` + +The scanner SHALL recognize `*.be.json`, `*.qo.json`, `*.ccmp.json`, `*.wscmp.json`, `*.pf.json`, and `*.frm.json`. + +#### Scenario: Decompile FYBXD project layout + +- **WHEN** decompile runs on a project written from FYBXD generate output +- **THEN** `app.yaml` lists entities, queries, components, pages, and pageflow +- **AND** sub-DSL files are written under the output directory + +#### Scenario: Round-trip golden regression + +- **WHEN** generate → write project JSON → decompile → generate runs on FYBXD exemplar +- **THEN** second generate passes P1-04 golden semantic compare + +#### Scenario: Skip decompile smoke + +- **WHEN** `UBML_APP_DECOMPILE_SMOKE=0` +- **THEN** roundtrip smoke script exits 0 without running + +### Requirement: Weblogic webcmd generation stage + +The product SHALL support optional `app.weblogic[]` listing page codes whose frontend command logic SHALL be compiled after page compile and before javagen. + +The `app.sources` map SHALL support optional `weblogic: Record` mapping page codes to `webcmd:` YAML paths. + +When no webcmd YAML is configured for a weblogic page code, the compiler SHALL derive commands from the compiled page DSL `toolbar[]`. + +Each weblogic item SHALL emit `{Controller}.webcmp.json` and `{Controller}.ts` with toolbar command handlers. + +#### Scenario: FYBXDJ webcmd from exemplar YAML + +- **WHEN** fybxd-app.yaml includes `weblogic: [FYBXDJ]` and sources.weblogic.FYBXDJ +- **THEN** generation emits FYBXDJ_frm_Controller webcmp and TS with save/onSave command +- **AND** weblogic stage is valid + +#### Scenario: Auto-derive from page toolbar + +- **WHEN** weblogic lists a page compiled with toolbar and no webcmd YAML +- **THEN** commands match toolbar order with default handlers + +#### Scenario: Writeback includes weblogic artifacts + +- **WHEN** planAppWritebackFiles runs on FYBXD result with weblogic +- **THEN** output tree includes weblogic/*.webcmp.json and weblogic/*.ts diff --git a/openspec/specs/ubml-appgen/spec.md b/openspec/specs/ubml-appgen/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..e76a2af5ad2e1a50be775d0489f68c6e4fe7221e --- /dev/null +++ b/openspec/specs/ubml-appgen/spec.md @@ -0,0 +1,14 @@ +# ubml-appgen + +UBML 应用生成:DSL exemplar 资产、intent 文档与 smoke 验证。 + +## Requirements + +### Requirement: UBML DSL exemplars with intent and verify smoke + +Each UBML DSL category (page, entity, query, component, app/pageflow) SHALL ship at least one FYBXD-based exemplar under `packages/extension/assets/`, with an adjacent `intent.md` describing use case and verify command, and a root smoke script that runs compile/verify on all bundled exemplars. + +#### Scenario: Contributor validates exemplars + +- **WHEN** a contributor runs the documented exemplars smoke command +- **THEN** all listed exemplars pass compile or verify without secrets or absolute paths diff --git a/openspec/specs/ubml-dsl-compiler/spec.md b/openspec/specs/ubml-dsl-compiler/spec.md index 190808fa2f1e170870e85f3d7c6b5f8716f9a29a..a1304ad43fa287f81ca30ec7ae4ee2c0349b231e 100644 --- a/openspec/specs/ubml-dsl-compiler/spec.md +++ b/openspec/specs/ubml-dsl-compiler/spec.md @@ -144,3 +144,24 @@ The knowledge MCP server SHALL expose `kb_ubml_edit` accepting `yaml`, an operat - **WHEN** kb_ubml_edit is called with op addField on valid card yaml - **THEN** the response includes updated yaml with the new field - **AND** verify on the updated yaml can be run separately + +### Requirement: Raw section round-trip preservation + +Page DSL sections with `type: raw` SHALL preserve `raw.id` and non-id payload fields through compile → decompile → recompile for `card` page type. + +#### Scenario: Card page explicit raw section + +- **WHEN** a card page YAML contains a top-level `type: raw` section with `raw.id` +- **THEN** compile → decompile yields a DSL where the same `raw.id` exists with equivalent payload fields +- **AND** verify passes on the decompiled YAML + +#### Scenario: Multiple raw widgets on one page + +- **WHEN** a card page contains two or more explicit raw sections (e.g. notify + custom section) +- **THEN** each `raw.id` is preserved after round-trip +- **AND** verify passes + +#### Scenario: Documented layout limitation + +- **WHEN** raw sections are nested inside `content-container` on a `layout` page +- **THEN** documentation SHALL state that decompile may collapse structure into an outer raw blob while Farris ids remain in the compiled model diff --git a/openspec/specs/ubml-entity-agent/spec.md b/openspec/specs/ubml-entity-agent/spec.md index f757c9c0d1307fa345c91dcaa96ab4279a67ce11..f8cd0d73ac3afe6540e3299730ba482519755839 100644 --- a/openspec/specs/ubml-entity-agent/spec.md +++ b/openspec/specs/ubml-entity-agent/spec.md @@ -8,12 +8,25 @@ UBML 业务实体生成 Agent 指引、`ubml-entitygen` agent 资产、派发 pr The product SHALL ship `assets/ubml-entity/prompts/entitygen.md` describing the DSL-first BE workflow: retrieve exemplars via `kb_be_exemplar`, consult datatype/UDT/ref/component catalogs via MCP, produce `entity:` YAML DSL (not raw GspMetadata JSON), verify with `kb_be_verify`, and repair up to 3 rounds using structured hints; optional incremental edits via `kb_be_edit`. +The workflow SHALL document a two-phase pattern: (1) skeleton draft with scalar fields and children via YAML, immediate `kb_be_verify`; (2) controlled refinement via typed `kb_be_edit` operations (`addAssociationField`, `addEnumField`, `addUdtField`, rules, manager actions) for catalog-sensitive constructs. + #### Scenario: Prompt lists required BE MCP tools - **WHEN** a developer loads the entitygen prompt via `getUbmlEntityPrompt("entitygen")` - **THEN** the content references `kb_be_exemplar`, `kb_be_datatype`, `kb_be_udt`, `kb_be_ref`, `kb_be_component`, `kb_be_verify`, and optional `kb_be_edit` - **AND** instructs the agent to output YAML with root key `entity:` only +#### Scenario: Prompt mentions typed edit operations + +- **WHEN** a contributor reads `entitygen.md` +- **THEN** it lists typed `kb_be_edit` operations for association, enum, UDT, and manager actions +- **AND** describes skeleton-then-refine sequencing + +#### Scenario: Agent consumes hint field + +- **WHEN** verify result is formatted for agent prompt via `formatVerifyIssuesForAgent` +- **THEN** output includes a hint column suitable for repair loops + ### Requirement: UBML entitygen agent definition The product SHALL ship `assets/ubml-entity/agents/ubml-entitygen.md` as an installable OpenCode agent (subagent mode) whose body embeds the entitygen workflow. diff --git a/openspec/specs/ubml-entity-decompiler/spec.md b/openspec/specs/ubml-entity-decompiler/spec.md index b0ebd0272bba4a125222d694afd283b27cc5bdf0..8afac6574671113a373a2d51d9113e5b950bd911 100644 --- a/openspec/specs/ubml-entity-decompiler/spec.md +++ b/openspec/specs/ubml-entity-decompiler/spec.md @@ -115,3 +115,23 @@ Tool signatures SHALL follow the same registration style as existing `kb_be_*` a - **WHEN** kb_be_edit is called with op addField and valid args - **THEN** response includes updated yaml suitable for kb_be_verify + +### Requirement: Page and entity round-trip regression suite + +The product SHALL provide automated round-trip tests that compile reference samples, decompile to DSL, recompile, and assert semantic equivalence with configurable ignore rules for known drift (ChildTriggerInfo, reference-only BE fields, page node ids). + +#### Scenario: FYBXD entity round-trip passes + +- **WHEN** fybxd.be.yaml or fybxd.be.json runs through compile → decompile → recompile +- **THEN** semantic diff is zero within documented ignore rules +- **AND** verify pipeline passes + +#### Scenario: FYBXD page round-trip passes + +- **WHEN** fybxdj-card.yaml runs through compile → decompile → recompile → decompile +- **THEN** page DSL canonical text is unchanged + +#### Scenario: Intentional DSL mutation fails regression + +- **WHEN** a field type or bind is deliberately altered before recompile +- **THEN** semantic compare reports differences and tests fail diff --git a/openspec/specs/ubml-entity-metamodel/spec.md b/openspec/specs/ubml-entity-metamodel/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..e6e3d95569d0cf0dedb4fdcbcab43e6e3a27e809 --- /dev/null +++ b/openspec/specs/ubml-entity-metamodel/spec.md @@ -0,0 +1,71 @@ +# ubml-entity-metamodel + +显式业务实体 M2 元模型资产、读取器、一致性测试与 entitygen 提示词约束渲染。位于 `packages/extension/assets/ubml-entity/metamodel/` 与 `packages/ubml-codegen/src/entity/metamodel/`。 + +## Requirements + +### Requirement: Explicit entity metamodel asset + +The product SHALL ship a machine-readable business-entity metamodel at `packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml` using the M3 vocabulary (`metamodel`, `class`, `attribute`, `reference`, `enum`, `datatype`, `constraint`). + +The metamodel SHALL declare: + +- Primitive and composite field datatypes aligned with `datatypes.index.json` +- Meta-classes for `BusinessEntity`, `EntityObject`, scalar/enum/association/UDT fields, rules (`Validation`, `Determination`, `Action`), and `ManagerAction` +- Containment vs external references (`containment: true|false`, `external: true` for catalog-backed refs) +- Catalog constraints naming `referenceable-be`, `udt`, and `components` +- Human-readable `doc` on meta-classes and constraints + +#### Scenario: FYBXD exemplar is explainable by metamodel + +- **WHEN** `exemplars/fybxd.be.yaml` is inspected against the metamodel class definitions +- **THEN** every field kind (scalar, enum, association, udt), both child objects, rules (validation, determination, action), and manager actions map to a declared meta-class + +#### Scenario: Metamodel README documents maintenance boundaries + +- **WHEN** a contributor opens `metamodel/README.md` +- **THEN** they find which constraints are enforced by tests vs documentation-only in E1 +- **AND** instructions to keep datatypes in sync with `datatypes.index.json` + +### Requirement: Entity metamodel loader + +`packages/ubml-codegen` SHALL provide a metamodel loader that parses `ubml-entity.metamodel.yaml` into stable TypeScript types (`UbmlMetamodel`, `MetaClass`, `MetaAttribute`, `MetaReference`, `MetaConstraint`, etc.) usable in Node/Bun tests without side effects. + +#### Scenario: Loader parses default asset path + +- **WHEN** the loader reads the bundled metamodel asset +- **THEN** it returns a non-empty `classes` array including `BusinessEntity` and `ScalarField` +- **AND** parsing completes without throwing + +### Requirement: Metamodel consistency tests + +The product SHALL include `packages/ubml-codegen/test/entity-metamodel-consistency.test.ts` verifying: + +1. Metamodel `datatypes` DSL type ids match `datatypes.index.json` `types[].id` +2. FYBXD exemplar elements map to metamodel classes (field kinds, children, rules, manager actions) +3. Catalog constraint names in the metamodel resolve to existing catalog files under `assets/ubml-entity/` + +#### Scenario: Test fails on datatype drift + +- **WHEN** a datatype exists in `datatypes.index.json` but not in the metamodel +- **THEN** the consistency test fails with a message naming the missing type + +#### Scenario: Test fails on missing catalog file + +- **WHEN** the metamodel references catalog `referenceable-be` but the catalog file is absent +- **THEN** the consistency test fails + +### Requirement: Entitygen prompt constraints from metamodel + +The product SHALL render agent-facing constraint fragments from the metamodel (legal field types, catalog rules, constraint summaries) and inject them into `assets/ubml-entity/prompts/entitygen.md`, while leaving workflow/strategy sections human-authored. + +#### Scenario: Rendered fragment lists DSL field types + +- **WHEN** prompt rendering runs against the loaded metamodel +- **THEN** output includes all primitive DSL types from the metamodel datatypes section +- **AND** includes enum, association, and udt as composite kinds + +#### Scenario: Existing entity verify behavior unchanged + +- **WHEN** E1 is complete +- **THEN** `bun run ubml:entity:verify packages/extension/assets/ubml-entity/exemplars/fybxd.be.yaml` still passes with no change to compile/verify semantics diff --git a/openspec/specs/ubml-entity-modeling-ops/spec.md b/openspec/specs/ubml-entity-modeling-ops/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..204c90f6febea07b0b6865b2693dd11a04874712 --- /dev/null +++ b/openspec/specs/ubml-entity-modeling-ops/spec.md @@ -0,0 +1,52 @@ +# ubml-entity-modeling-ops + +业务实体受控建模操作集、`kb_be_edit` MCP 扩展与实体生成评测基线。位于 `packages/ubml-codegen/src/entity/edit/` 与 `packages/knowledge/eval/`。 + +## Requirements + +### Requirement: Typed entity modeling edit operations + +The product SHALL extend `kb_be_edit` / `runEntityEdit` with dedicated operations: + +- `addScalarField`, `addEnumField`, `addAssociationField`, `addUdtField` +- `addChildObject`, `addValidation`, `addDetermination`, `addBizAction`, `addManagerAction` + +Each operation SHALL validate arguments against entity compile context (datatypes, catalogs, field existence) before mutating DSL. + +Legacy operations (`addField`, `addChild`, `addRule`, `patchField`, `removeField`) SHALL remain supported. + +#### Scenario: addAssociationField rejects unknown ref with hints + +- **WHEN** `addAssociationField` is called with `ref: NoSuchBe` +- **THEN** result has `ok: false` +- **AND** issues include a message naming the unknown ref +- **AND** issues include hints listing valid catalog BE codes + +#### Scenario: addScalarField requires decimal precision + +- **WHEN** `addScalarField` is called with `type: decimal` without `precision` +- **THEN** result has `ok: false` with a hint about required precision + +#### Scenario: addChildObject rejects duplicate child code + +- **WHEN** `addChildObject` is called with a child code already present on root +- **THEN** result has `ok: false` naming the duplicate code + +### Requirement: Structured entity edit results + +Entity edit APIs SHALL return `{ ok, yaml, issues?, changedPaths?, dsl? }` where failed edits preserve input YAML and populate structured `issues` with optional Chinese `hint` fields. + +#### Scenario: Successful edit returns updated yaml + +- **WHEN** a valid `addUdtField` completes +- **THEN** `ok` is true and `yaml` contains the new field +- **AND** `changedPaths` identifies the affected object + +### Requirement: Entity generation eval baseline + +The product SHALL ship `packages/knowledge/eval/entity-generation-tasks.json` with at least 10 Chinese requirements and `eval-entity-generation-core.ts` reporting verify pass rate metrics. + +#### Scenario: Eval runner loads task file + +- **WHEN** eval script runs with default paths +- **THEN** it loads at least 10 tasks and prints summary metrics structure diff --git a/openspec/specs/ubml-javagen/spec.md b/openspec/specs/ubml-javagen/spec.md index 9c833cf5fb26c97dd8fff58ec075c17babfd89d6..b5396eba0358ff469f5167175325c79c1ec4d591 100644 --- a/openspec/specs/ubml-javagen/spec.md +++ b/openspec/specs/ubml-javagen/spec.md @@ -40,3 +40,29 @@ CLI `ubml-javagen` with `--be` and `--out`. - **WHEN** CLI runs against fybxd.be.json with `--out ./java` - **THEN** `./java` contains at least four `.java` source files + +### Requirement: Tier B constrained API generation + +The generator SHALL support **tier B** logic specs (YAML) that emit method bodies using only APIs declared in `default-api-whitelist.yaml`. + +The generator SHALL validate emitted Java against the whitelist and report `whitelistViolations` for non-whitelisted receiver methods. + +The generator SHALL optionally emit JUnit 5 test stubs and platform stub classes for Maven `compile` + `test-compile`. + +CLI SHALL accept `--tier-b ` to merge tier B output with tier A. + +#### Scenario: FYBXD CheckExpenseQuota tier B + +- **WHEN** compile runs with fybxd-check-quota.logic-b.yaml +- **THEN** FYBXDCheckExpenseQuotaValidationController is emitted with getDecimal/getQuota calls +- **AND** whitelistViolations is empty + +#### Scenario: Reject non-whitelisted API + +- **WHEN** generated source calls a method not in the whitelist for the receiver type +- **THEN** validation reports a violation naming the method + +#### Scenario: Skip tier B maven smoke + +- **WHEN** `UBML_JAVAGEN_TIER_B_SMOKE=0` +- **THEN** tier B maven smoke script exits 0 without running diff --git a/openspec/specs/ubml-model-right-panel/spec.md b/openspec/specs/ubml-model-right-panel/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..9b43b9d3b5fee18d9ed534e0d316882a6398c25b --- /dev/null +++ b/openspec/specs/ubml-model-right-panel/spec.md @@ -0,0 +1,32 @@ +# ubml-model-right-panel + +对话面板右侧「模型」页签:多模型导航、实体 ER 图/文件双视图、生成与预览联动。位于 `packages/extension/src/lib/ubml-model-*` 与 `pages/workbench/ubml-model-panel.tsx`。 + +## Requirements + +### Requirement: Right panel model tab + +The extension SHALL register a `model` right-panel tab type (single instance) with localized label 「模型」/ Model. + +The tab SHALL show a two-column layout: model navigation (grouped by kind, starting with entity) and a content area with graph/file view toggle. + +#### Scenario: User adds model tab from panel menu + +- **WHEN** user adds the model tab from the right panel + menu +- **THEN** an empty state prompts that models appear after entity preview or generation + +#### Scenario: FYBXD preview populates navigation + +- **WHEN** entity preview succeeds for FYBXD exemplar +- **THEN** the model tab lists FYBXD under entity navigation +- **AND** graph view shows containment (FYBXDMX, FXBXDFJ) and association refs (GspUser, SysOrg, Projects) via Mermaid ER output + +#### Scenario: File view shows YAML + +- **WHEN** user switches to file view on a selected model entry +- **THEN** the original YAML text is shown read-only + +#### Scenario: Closing model tab clears payload + +- **WHEN** user closes the model tab +- **THEN** model tab store entries are cleared without affecting entity-preview tabs diff --git a/openspec/specs/ubml-model-tab-pages/spec.md b/openspec/specs/ubml-model-tab-pages/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..b78c962cded83401e4983944a402237c27603054 --- /dev/null +++ b/openspec/specs/ubml-model-tab-pages/spec.md @@ -0,0 +1,20 @@ +# ubml-model-tab-pages + +模型页签扩展:页面模型导航与结构视图,与实体条目并列。位于 `packages/extension/src/lib/page-preview-model.ts` 与 `components/page-preview-content.tsx`。 + +## Requirements + +### Requirement: Model tab page navigation + +The model tab SHALL support `page` entries alongside `entity` entries, grouped under a「页面 pages/」navigation label. + +#### Scenario: Page preview adds navigation item + +- **WHEN** `previewUbmlYaml` succeeds for a card page +- **THEN** the model tab lists the page under pages navigation +- **AND** structure view shows page type, entity binding, and field bindings + +#### Scenario: Entity entries unchanged + +- **WHEN** `previewEntityYaml` succeeds +- **THEN** entity entries still appear under「实体 be/」with ER graph view diff --git a/openspec/specs/ubml-page-metamodel/spec.md b/openspec/specs/ubml-page-metamodel/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..8c21efa630a3b9d5cd85ab2cfd3f042b1a311b6e --- /dev/null +++ b/openspec/specs/ubml-page-metamodel/spec.md @@ -0,0 +1,73 @@ +# ubml-page-metamodel + +显式页面 M2 元模型资产、读取器、一致性测试、external 引用设计文档与 pagegen 提示词约束渲染。位于 `packages/extension/assets/ubml/metamodel/` 与 `packages/ubml-codegen/src/page/metamodel/`。 + +## Requirements + +### Requirement: Explicit page metamodel asset + +The product SHALL ship a machine-readable page metamodel at `packages/extension/assets/ubml/metamodel/ubml-page.metamodel.yaml` using the same M3 vocabulary as the entity metamodel. + +The metamodel SHALL declare meta-classes for Page, Section variants (including form and raw), FieldBinding, and toolbar actions, with: + +- `Page.entity` as external reference to BusinessEntity +- `FieldBinding.bind` as external reference to entity fields +- `FieldBinding.editor` constrained against `components.index.json` +- `RawNode` for `type: raw` escape sections without parsing inner structure +- Human-readable `doc` and `constraint.check` fields (documentation-first in E4) + +#### Scenario: FYBXDJ card exemplar is explainable by metamodel + +- **WHEN** `exemplars/fybxdj-card.yaml` is inspected against metamodel class definitions +- **THEN** page type, title, entity binding, toolbar actions, form sections, and field bindings map to declared meta-classes + +#### Scenario: Metamodel README documents E4 boundaries + +- **WHEN** a contributor opens `assets/ubml/metamodel/README.md` +- **THEN** they find that constraints are documentation-first and crossref.ts is not replaced in E4 + +### Requirement: Page metamodel loader + +`packages/ubml-codegen` SHALL provide a page metamodel loader parsing `ubml-page.metamodel.yaml` into stable TypeScript types, usable in Bun tests without side effects. + +#### Scenario: Loader parses default asset path + +- **WHEN** the loader reads the bundled page metamodel asset +- **THEN** it returns classes including Page and FieldBinding +- **AND** parsing completes without throwing + +### Requirement: Page metamodel consistency tests + +The product SHALL include `packages/ubml-codegen/test/page-metamodel-consistency.test.ts` verifying: + +1. Metamodel-declared page types and editor constraints align with page DSL schema and `components.index.json` +2. FYBXDJ card exemplar elements map to metamodel classes +3. Catalog/constraint names in the metamodel resolve to existing asset files under `assets/ubml/` + +#### Scenario: Test fails on editor catalog drift + +- **WHEN** the metamodel references editor catalog constraints but `components.index.json` is missing +- **THEN** the consistency test fails + +### Requirement: Generic external reference design document + +The product SHALL document the generic external reference model in `docs/UBML-EXTERNAL-REFERENCE.md`, describing how Page.entity, FieldBinding.bind, and future cross-model refs relate to metamodel `external: true` references and the evolution path from handwritten AppGen crossref checks. + +#### Scenario: Document lists current crossref coverage + +- **WHEN** a contributor reads `docs/UBML-EXTERNAL-REFERENCE.md` +- **THEN** they find mappings from metamodel external refs to existing crossref validation in `crossref.ts` + +### Requirement: Pagegen prompt constraints from metamodel + +The product SHALL render agent-facing page constraint fragments from the page metamodel and inject them into `assets/ubml/prompts/pagegen.md`, while leaving workflow/strategy sections human-authored. + +#### Scenario: Rendered fragment lists page types and editor rules + +- **WHEN** prompt rendering runs against the loaded page metamodel +- **THEN** output includes page type constraints and editor catalog guidance + +#### Scenario: Existing page verify behavior unchanged + +- **WHEN** E4 is complete +- **THEN** `bun run ubml:verify packages/extension/assets/ubml/exemplars/fybxdj-card.yaml` still passes with no change to compile/verify semantics diff --git a/openspec/specs/ubml-page-verify/spec.md b/openspec/specs/ubml-page-verify/spec.md new file mode 100644 index 0000000000000000000000000000000000000000..83f30d0e89a575f82490d07c02103bdd5993ade6 --- /dev/null +++ b/openspec/specs/ubml-page-verify/spec.md @@ -0,0 +1,19 @@ +# ubml-page-verify + +Page 与 entity verify 流水线:结构化 issue、中文 hint 与 agent 修复回环。 + +## Requirements + +### Requirement: Actionable Chinese verify hints + +Page and entity verify pipelines SHALL attach Chinese `hint` and optional `suggestion` fields to structured issues for common failure classes (unknown editor, missing entity/bind, unknown ref/type, YAML parse, schema missing property), without exposing stack traces in user-visible messages. + +#### Scenario: Unknown editor includes Chinese hint and suggestions + +- **WHEN** page verify fails due to unknown editor +- **THEN** the issue includes `hint` in Chinese and `hints`/`suggestion` with valid editor names + +#### Scenario: Agent consumes hint field + +- **WHEN** verify result is formatted for agent prompt via `formatVerifyIssuesForAgent` +- **THEN** output includes a hint column suitable for repair loops diff --git a/package.json b/package.json index 66774763c9f07042c91d356dedc51128810ed3d2..71978e1ceac9c3b50102367c5f7e4a9c4389d8de 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,9 @@ "vendor:sync-upstream": "bash script/vendor/sync-fork-from-upstream.sh", "smoke": "bash script/smoke-api.sh", "qa:automated": "bash script/qa/c1-c24-automated.sh", + "qa:hand-verification": "bun run script/qa/hand-verification-smoke.ts", + "qa:hand-verification:strict": "HAND_VERIFICATION_STRICT=1 bun run script/qa/hand-verification-smoke.ts", + "qa:web-shell:smoke": "bun run script/qa/web-shell-smoke.ts", "qa:record": "bash script/qa/c1-c24-record.sh", "qa:issues": "bash script/qa/create-qa-issues.sh", "ubml:entity:compile": "bun run --cwd packages/ubml-codegen entity-compile", @@ -112,7 +115,27 @@ "ubml:pageflow:compile": "bun run --cwd packages/ubml-codegen pageflow-compile", "ubml:component:compile": "bun run --cwd packages/ubml-codegen component-compile", "ubml:javagen": "bun run --cwd packages/ubml-codegen javagen", + "ubml:javagen:tier-b-smoke": "bun run script/javagen-tier-b-smoke.ts", "ubml:app:generate": "bun run --cwd packages/ubml-codegen app-generate", + "ubml:app:generate:maven-smoke": "bun run script/app-generate-maven-smoke.ts", + "ubml:app:generate:golden-smoke": "bun run script/app-generate-golden-smoke.ts", + "ubml:app:decompile:roundtrip-smoke": "bun run script/app-decompile-roundtrip-smoke.ts", + "ubml:weblogic:smoke": "bun run script/weblogic-smoke.ts", + "kb:designbase:import-smoke": "bun run script/designbase-import-smoke.ts", + "kb:api-routes:smoke": "bun run script/api-routes-extract-smoke.ts", + "kb:eval:retrieval": "bun run script/knowledge/eval-retrieval.ts", + "kb:eval:retrieval:ci": "bun run script/knowledge/eval-retrieval-ci.ts", + "kb:pro:up": "bash script/knowledge/pro-up.sh", + "kb:pro:down": "bash script/knowledge/pro-up.sh down", + "kb:pro:smoke": "bun run script/knowledge/pro-smoke.ts", + "kb:farris:alias-smoke": "bun run script/knowledge/farris-alias-smoke.ts", + "kb:be:pipeline-smoke": "bun run packages/knowledge/script/be-pipeline-smoke.ts", + "kb:ubml:smoke": "bun run packages/knowledge/script/ubml-smoke.ts", + "kb:ubml:knowledge-smoke": "bun run script/qa/ubml-knowledge-smoke.ts", + "ubml:exemplars:smoke": "bun run script/ubml/exemplars-verify-smoke.ts", + "ubml:roundtrip:smoke": "bun run script/ubml/roundtrip-smoke.ts", + "ubml:raw:smoke": "bun run script/ubml/raw-roundtrip-smoke.ts", + "ubml:verify-hints:smoke": "bun run script/ubml/verify-hints-smoke.ts", "ubml:build-pack": "bun run --cwd packages/ubml-codegen build-pack", "ubml:compile": "bun run --cwd packages/ubml-codegen compile", "ubml:verify": "bun run --cwd packages/ubml-codegen verify", diff --git a/packages/extension/README.md b/packages/extension/README.md index 5393d76b675e2f0b56f0c0905809c5f5c1f99c30..393b186230c0592b96dfe4fd62fdc0fd6019ddf3 100644 --- a/packages/extension/README.md +++ b/packages/extension/README.md @@ -42,7 +42,7 @@ bun run --cwd packages/extension build # 生产 build,验证 alias 生效 | `assets/ubml/components.index.json` | 组件清单 + editor 枚举 | | `assets/ubml/templates.index.json` | 页面模板骨架 | | `assets/ubml/class-conventions.md` / `class-map.json` | 布局 class 约定 | -| `assets/ubml/exemplars/*.yaml` | DSL few-shot 范例 | +| `assets/ubml/exemplars/*.yaml` | DSL few-shot 范例(旁附 `intent.md` 说明场景与 verify 命令) | | `assets/ubml/prompts/` | 领域 prompt 模板 | | `src/ubml/` | 运行时加载 API | | `preview/` | Vue 低代码预览(`:3100`) | diff --git a/packages/extension/assets/ubml-app/agents/ubml-appgen.md b/packages/extension/assets/ubml-app/agents/ubml-appgen.md index 7d2058002b373337706d639c1b476aec5ba1eb9e..a4f173dab88215ff0bb474f1151c30ff9731f3e6 100644 --- a/packages/extension/assets/ubml-app/agents/ubml-appgen.md +++ b/packages/extension/assets/ubml-app/agents/ubml-appgen.md @@ -1,5 +1,5 @@ --- -description: 编排 UBML 完整应用(app: 清单 → entity/derive/query/component/pageflow/javagen) +description: 编排 UBML 完整应用(app: 清单 → entity/derive/query/component/pages/pageflow/javagen) mode: subagent --- @@ -7,19 +7,33 @@ You specialize in UBML full-application generation via the `app:` manifest. ## Workflow -1. Parse the user's intent into an `app:` YAML manifest (`bizobject`, `namespace`, `entities`, `derive`, `queries`, `pageflow`, `logic`, `sources`). -2. Reuse or generate sub-DSL under `sources` (entity, query, component, pageflow exemplars). -3. Run generation (`ubml:app:generate` CLI or workbench Generate) and inspect `stages[]` + `manifest.json`. -4. On failure, fix the failing stage using structured `issues` (max 3 rounds). -5. Summarize artifact tree: be, derive, query, components, pageflow, java. +1. Parse the user's intent into an `app:` YAML manifest (`bizobject`, `namespace`, `entities`, `derive`, `queries`, `pages`, `pageflow`, `logic`, `sources`). +2. Reuse or reference sub-DSL under `sources` (entity, query, component, page, pageflow exemplars). +3. Run generation (`ubml:app:generate` CLI or workbench **Generate**) and inspect `stages[]` + `validation.issues`. +4. On failure, fix **only the failing stage** using structured `issues` and `hint` fields (**max 3 rounds**). +5. After 3 failed rounds, stop looping and report remaining issues to the user. +6. Summarize artifact tree: be, derive, query, components, pages, pageflow, java. ## Rules - Root key is `app:` — not individual `entity:` / `qo:` alone for end-to-end delivery. - `derive.from` must appear in `entities[]`. -- FYBXD exemplar chain: `fybxd-app.yaml` → bundled entity/query/component/pageflow sources. -- Do not claim MAVEN compile success unless explicitly run. +- When `pages[]` is declared, every pageflow `frm` must appear in `pages[]` with matching `sources.pages`. +- FYBXD exemplar chain: `fybxd-app.yaml` → bundled entity/query/component/page/pageflow sources. +- **Do not claim MAVEN compile success** unless `--maven` was explicitly run. + +## Stage failure handling + +| Stage | Action | +|-------|--------| +| `entity:*` | Fix `sources.entities` or BE YAML | +| `derive` | Fix `derive.from` / `targets` | +| `query:*` | Fix query YAML field paths against BE | +| `component` | Fix component BE binding | +| `page:*` | Fix page YAML or `sources.pages` | +| `pageflow` | Align `pages[]` with pageflow frm list | +| `crossref` | Read `issues[].hint` first | ## Output -Brief plan, full `app:` YAML, generation status per stage, and next steps for writeback/deployment. +Brief plan, full `app:` YAML, per-stage validation summary, and next steps for writeback/deployment. diff --git a/packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml b/packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml index 26daef4e34ec71a4497a2351e818ac1a02ba4250..ba5bd0000c5fee5535c4c59fde7060e26beed7fc 100644 --- a/packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml +++ b/packages/extension/assets/ubml-app/exemplars/fybxd-app.yaml @@ -9,8 +9,10 @@ app: targets: [dbo, vo, eapi] queries: [bxdqo] components: [ExpenseAuditedEvent, ExpenseWriteBack] + pages: [FYBXDJ, FYBXDJManagement, FYBXDJQuery] pageflow: predefinePageflow logic: [sumAmount, defaultValueSet, Audit, writeback] + weblogic: [FYBXDJ] sources: entities: FYBXD: ubml-entity/exemplars/fybxd.be.yaml @@ -18,5 +20,11 @@ app: bxdqo: ubml-query/exemplars/bxdqo.qo.yaml components: ubml-component/exemplars/fybxd-components.yaml pageflow: ubml-pageflow/exemplars/predefine-pageflow.yaml - frmDir: packages/ubml-codegen/test/fixtures/frm + pages: + FYBXDJ: ubml/exemplars/fybxdj-card.yaml + FYBXDJManagement: ubml/exemplars/fybxdj-management.yaml + FYBXDJQuery: ubml/exemplars/fybxdj-query.yaml + weblogic: + FYBXDJ: ubml/exemplars/fybxdj-webcmd.yaml entityAssets: ubml-entity + pageAssets: ubml diff --git a/packages/extension/assets/ubml-app/exemplars/intent.md b/packages/extension/assets/ubml-app/exemplars/intent.md new file mode 100644 index 0000000000000000000000000000000000000000..98ec544e3acf6aede21ec271f5a8af90ad39df55 --- /dev/null +++ b/packages/extension/assets/ubml-app/exemplars/intent.md @@ -0,0 +1,7 @@ +# App orchestration exemplars + +| 文件 | 场景 | 验证 | +|------|------|------| +| `fybxd-app.yaml` | FYBXD 全栈 app 清单(entities/derive/queries/pages/logic/weblogic) | `bun run ubml:app:generate:golden-smoke` 或 `bun run script/ubml/exemplars-verify-smoke.ts` | + +`sources` 字段指向各子目录 exemplar,Agent AppGen 应优先加载此文件再展开引用。 diff --git a/packages/extension/assets/ubml-app/prompts/appgen.md b/packages/extension/assets/ubml-app/prompts/appgen.md index 131e9db358e0e8fcb7f30b00672699e4794f41f4..ebe05cbfd494d9302407e81dc0d210b191252f98 100644 --- a/packages/extension/assets/ubml-app/prompts/appgen.md +++ b/packages/extension/assets/ubml-app/prompts/appgen.md @@ -1,6 +1,14 @@ -# UBML AppGen Agent 工作流(草稿) +# UBML AppGen Agent 工作流 -解析 `app:` 工单 → 按序调用子管线 → 汇总校验 → 最多 3 轮修复。 +解析 `app:` 工单 → 按序调用子管线 → 汇总校验 → **最多 3 轮**结构化修复。 + +## 硬性规则 + +1. **只产出或修改 `app:` YAML 清单**,子 DSL 通过 `sources.*` 引用路径声明。 +2. 产出或修改 YAML 后 **必须验证**:CLI `ubml:app:generate` 或工作台 **Generate**。 +3. 校验失败时根据 `issues[].hint` 修正,**最多 3 轮**;超限后向用户报告剩余 issues。 +4. **不要声称 MAVEN 编译成功**,除非 CLI 显式加了 `--maven` 且输出 `maven.valid === true`(工作台 Generate 不跑 Maven)。 +5. 每轮修复 **只改失败 stage 相关字段**,勿动已通过 stage 的配置。 ## 子命令映射 @@ -10,15 +18,41 @@ | derive | `ubml:derive` | | queries | `ubml:query:compile` | | components | `ubml:component:compile` | +| pages | `ubml:compile`(pagegen,批量) | | pageflow | `ubml:pageflow:compile` | | logic | `ubml:javagen` | | **整体** | `ubml:app:generate` | +## 推荐流程 + +1. 理解用户意图,参考 `assets/ubml-app/exemplars/fybxd-app.yaml`。 +2. 编写 `app:` YAML(含 `sources` 指向各 exemplar 或用户提供路径)。 +3. 运行 **`ubml:app:generate `**(可选 `--maven`)或请用户在工作台点击 Generate。 +4. 检查 `stages[]` 与 `validation.issues`;失败则按 stage 分类修复。 +5. 通过后总结 artifact 树(be / derive / query / components / pages / pageflow / java)。 + +## verify 失败处理(按 stage) + +| stage 前缀 | 处理 | +|------------|------| +| `entity:*` | 检查 `sources.entities` 与 BE YAML | +| `derive` | 检查 `derive.from` ∈ `entities[]`、`targets` | +| `query:*` | 检查 query YAML 与 BE 字段路径 | +| `component` | 检查 component YAML 与 `BeCode` | +| `page:*` | 检查 `sources.pages` 与 page YAML | +| `pageflow` | 检查 `pages[]` 与 pageflow 中 frm 列表一致 | +| `javagen` | 检查 `logic[]` 与 BE 规则占位 | +| `crossref` | 优先读 `issues[].hint` | + +每轮修复后重新 Generate,直到 `validation.valid === true` 或达 3 轮。 + ## FYBXD 范例 加载 `assets/ubml-app/exemplars/fybxd-app.yaml`,以 `sources` 指向各 exemplar。 -## 首期约束 +## 输出格式 -- 不自动生成缺失的子 DSL;引用已有 exemplar 或用户提供的 YAML -- MAVEN 闸默认跳过(`UBML_MAVEN_SMOKE=0`) +1. 简要说明 app 结构与子 DSL 来源 +2. 完整 `app:` YAML(fenced code block) +3. Generate / verify 结果摘要(各 stage 通过与否) +4. 若未通过:issues 表 + 本轮修复计划 diff --git a/packages/extension/assets/ubml-component/exemplars/intent.md b/packages/extension/assets/ubml-component/exemplars/intent.md new file mode 100644 index 0000000000000000000000000000000000000000..7f1832fb6314a696747b6d9b37c7842fe0a2d9a0 --- /dev/null +++ b/packages/extension/assets/ubml-component/exemplars/intent.md @@ -0,0 +1,7 @@ +# Component DSL exemplars + +| 文件 | 场景 | 验证 | +|------|------|------| +| `fybxd-components.yaml` | 事件构件 + 审批回写 writeback | `bun run ubml:component:compile -- …/fybxd-components.yaml --be packages/ubml-codegen/test/fixtures/fybxd.seed.json` | + +适用:`ExpenseAuditedEvent`、`ExpenseWriteBack` 类 BE 编排构件。 diff --git a/packages/extension/assets/ubml-entity/components.catalog.json b/packages/extension/assets/ubml-entity/components.catalog.json index 35290925c8b6ac7494e81b117a34f0aa9c22991a..2538e26c221498002e48adca81ab6fa2c1606e94 100644 --- a/packages/extension/assets/ubml-entity/components.catalog.json +++ b/packages/extension/assets/ubml-entity/components.catalog.json @@ -1,6 +1,6 @@ { "version": 1, - "generatedAt": "2026-07-01T16:34:35.485Z", + "generatedAt": "2026-07-04T02:32:59.853Z", "entries": [ { "code": "BatchUploadAttachment", diff --git a/packages/extension/assets/ubml-entity/datatypes.index.json b/packages/extension/assets/ubml-entity/datatypes.index.json index 9f55d1ca618f1a46fc359e26ee2259c4e9172e07..a18f2a10693a9e69ce915a20423be1e2c4a905e5 100644 --- a/packages/extension/assets/ubml-entity/datatypes.index.json +++ b/packages/extension/assets/ubml-entity/datatypes.index.json @@ -1,6 +1,6 @@ { "version": 1, - "generatedAt": "2026-07-01T16:34:35.485Z", + "generatedAt": "2026-07-04T02:32:59.853Z", "types": [ { "id": "string", diff --git a/packages/extension/assets/ubml-entity/exemplars/intent.md b/packages/extension/assets/ubml-entity/exemplars/intent.md new file mode 100644 index 0000000000000000000000000000000000000000..dd5cf994c275847eebea950c5b37fda72a6d0189 --- /dev/null +++ b/packages/extension/assets/ubml-entity/exemplars/intent.md @@ -0,0 +1,8 @@ +# Entity DSL exemplars + +| 文件 | 场景 | 验证 | +|------|------|------| +| `fybxd.be.yaml` | FYBXD 报销单 bill 实体(字段/子实体/规则) | `bun run ubml:entity:verify -- packages/extension/assets/ubml-entity/exemplars/fybxd.be.yaml` | +| `fybxd.be.json` | 同上模型的 JSON 快照(derive/对照) | 随 entity verify 或 derive 管线使用 | + +适用:新建 bill 类实体、子表 MX、稽核/合计规则编排。 diff --git a/packages/extension/assets/ubml-entity/gsp-business-entity.schema.json b/packages/extension/assets/ubml-entity/gsp-business-entity.schema.json index e437c2611730561272fc6a2156bf771bcacb2ff2..a03e26c6e79d0c5cdc71284c6c62c9a4718d5bdb 100644 --- a/packages/extension/assets/ubml-entity/gsp-business-entity.schema.json +++ b/packages/extension/assets/ubml-entity/gsp-business-entity.schema.json @@ -2,7 +2,7 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "GspMetadataBusinessEntity", "version": 1, - "generatedAt": "2026-07-01T16:34:35.485Z", + "generatedAt": "2026-07-04T02:32:59.853Z", "description": "Hand-curated structural schema for GspMetadata with GSPBusinessEntity content (E-A).", "type": "object", "required": [ diff --git a/packages/extension/assets/ubml-entity/metamodel/README.md b/packages/extension/assets/ubml-entity/metamodel/README.md new file mode 100644 index 0000000000000000000000000000000000000000..760646330042b1649629ce04f911265bb27feadd --- /dev/null +++ b/packages/extension/assets/ubml-entity/metamodel/README.md @@ -0,0 +1,49 @@ +# 业务实体元模型(M2) + +本目录存放 **UBML 业务实体的显式元模型**(M2),对应产品设计 [10-mof-entity-modeling.md](../../../../docs/product-design/10-mof-entity-modeling.md) 的 E1 阶段。 + +## 文件 + +| 文件 | 说明 | +|------|------| +| `ubml-entity.metamodel.yaml` | 元类、datatype、enum、catalog 约束与 doc;FYBXD 金样应能完整映射 | + +## 与现有实现的关系 + +``` +ubml-entity.metamodel.yaml(事实源,E1 起) + │ + ├── 一致性测试 ←→ datatypes.index.json / Zod schema / FYBXD 金样 + ├── 提示词片段 ← entity/metamodel/prompt.ts → entitygen.md + └── (远期)Zod / JSON Schema / 编辑操作 schema 派生 +``` + +**E1 不改变**现有编译、verify 或 MCP 行为;Zod(`packages/ubml-codegen/src/entity/dsl/schema.ts`)仍是运行时第一道闸。 + +## 维护规则 + +1. **改 DSL 类型或 catalog 约束时**,必须同步更新本文件,并确保 `entity-metamodel-consistency.test.ts` 通过。 +2. **`constraint.check` 字段**首期为人读规约(OCL 风格);执行由 Zod + 既有 semantic validation 承担,E2 再绑定单步 TS 校验。 +3. **不要**在本文件中描述平台 JSON 的机械字段(GUID、ColumnID、系统字段等)——那些属于编译器 M1→平台格式职责。 +4. **`datatypes`** 必须与 `../datatypes.index.json` 的 `types[].id` 集合一致(含 `date`、`enum`、`association`、`udt`)。 +5. **`catalogs`** 三项必须对应真实文件: + - `referenceable-be.catalog.json` + - `udt.catalog.json` + - `components.catalog.json` + +## 首期强约束 vs 文档约束 + +| 类别 | E1 enforcement | +|------|-----------------| +| datatype 集合与 index 一致 | 一致性测试 | +| FYBXD 元素可映射到 meta-class | 一致性测试 | +| catalog 名称可解析到文件 | 一致性测试 | +| `constraint.check` 表达式 | 文档 only | +| 不从元模型生成 Zod / gsp-business-entity.schema.json | 明确 non-goal | + +## 验收 + +```bash +bun test packages/ubml-codegen/test/entity-metamodel-consistency.test.ts +bun run ubml:be:verify packages/extension/assets/ubml-entity/exemplars/fybxd.be.yaml +``` diff --git a/packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml b/packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dfb0f3855540476cb619af4429d2d801ea6bef5f --- /dev/null +++ b/packages/extension/assets/ubml-entity/metamodel/ubml-entity.metamodel.yaml @@ -0,0 +1,220 @@ +metamodel: + name: ubml-entity + version: "0.1" + doc: | + 业务实体(Business Entity)M2 元模型。 + M1 表示为 entity: YAML DSL;编译器负责 M1→平台 GspMetadata 的机械展开。 + 本文件是「什么是合法实体模型」的唯一事实源(E1);与 Zod schema、catalog 通过一致性测试对齐。 + + datatypes: + - name: string + platform: { mdataType: String, requiresLength: true } + - name: text + platform: { mdataType: Text, requiresLength: true } + - name: integer + platform: { mdataType: Integer } + - name: decimal + platform: { mdataType: Decimal, requiresLength: true, requiresPrecision: true } + - name: datetime + platform: { mdataType: DateTime } + - name: date + platform: { mdataType: DateTime } + - name: boolean + platform: { mdataType: Boolean } + - name: enum + dslKind: enum + requiresValues: true + - name: association + dslKind: association + requiresRef: true + - name: udt + dslKind: udt + requiresUdt: true + + enums: + - name: EntityKind + literals: [bill, basedata] + - name: RuleTiming + literals: [beforeSave, afterModify, afterCreate] + + catalogs: + - name: referenceable-be + file: referenceable-be.catalog.json + doc: 可关联的外部业务实体清单 + - name: udt + file: udt.catalog.json + doc: 统一数据类型(UDT)清单 + - name: components + file: components.catalog.json + doc: 公共构件(common manager action / rule component)清单 + + classes: + - name: BusinessEntity + doc: 业务实体元数据根;编译为 GspMetadata(Header + Refs + Content) + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + - { name: namespace, type: QualifiedName, required: true } + - { name: kind, type: EntityKind, default: bill } + references: + - { name: root, type: EntityObject, containment: true, multiplicity: "1" } + - { name: managerActions, type: ManagerAction, containment: true, multiplicity: "0..*" } + + - name: EntityObject + doc: "实体对象节点;根对象编译为 MainObject,子对象编译为 ContainChildObjects[]" + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + references: + - { name: fields, type: Field, containment: true, multiplicity: "1..*" } + - { name: children, type: EntityObject, containment: true, multiplicity: "0..*" } + - { name: rules, type: Rules, containment: true, multiplicity: "0..1" } + constraints: + - name: no-grandchildren + doc: 子实体不允许再包含子实体(平台限制为两级对象树) + check: "self.parent != null implies self.children.isEmpty()" + - name: unique-child-codes + doc: 同一实体下子对象 code 不可重复 + check: "self.children->forAll(c | self.children->select(code = c.code)->size() = 1)" + + - name: Field + abstract: true + doc: 字段基类 + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + + - name: ScalarField + superType: Field + doc: 标量字段(string/text/integer/decimal/datetime/date/boolean) + attributes: + - { name: type, type: DataTypeRef, required: true } + - { name: length, type: Integer } + - { name: precision, type: Integer } + - { name: required, type: Boolean, default: false } + - { name: multiLanguage, type: Boolean, default: false } + - { name: billCode, type: Boolean, default: false } + constraints: + - name: length-required-by-type + doc: requiresLength 的类型必须声明 length + check: "datatype(self.type).requiresLength implies self.length != null" + - name: precision-required-by-decimal + doc: decimal 类型必须声明 precision + check: "self.type = 'decimal' implies self.precision != null" + + - name: EnumField + superType: Field + doc: 枚举字段;values 编译为 ContainEnumValues + attributes: + - { name: type, type: Literal, value: enum, required: true } + references: + - { name: values, type: EnumValue, containment: true, multiplicity: "1..*" } + constraints: + - name: single-default + doc: 枚举默认值最多一个 + check: "self.values->select(default)->size() <= 1" + + - name: EnumValue + doc: 枚举项 + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + - { name: default, type: Boolean, default: false } + + - name: AssociationField + superType: Field + doc: 外部实体关联;编译为 ChildAssociations + Refs 条目 + attributes: + - { name: type, type: Literal, value: association, required: true } + - { name: refFields, type: IdentifierList, doc: "带出的引用字段,如 [Code, Name]" } + references: + - { name: ref, type: BusinessEntity, containment: false, external: true } + constraints: + - name: ref-in-catalog + doc: 关联目标必须存在于 referenceable-be catalog + check: "catalog('referenceable-be').has(self.ref)" + catalog: referenceable-be + - name: refFields-exist + doc: refFields 必须是被引用实体已声明的字段 code + check: "catalog('referenceable-be').elements(self.ref).containsAll(self.refFields)" + + - name: UdtField + superType: Field + doc: UDT 字段;编译时解析 udt catalog ID + attributes: + - { name: type, type: Literal, value: udt, required: true } + references: + - { name: udt, type: UnifiedDataType, containment: false, external: true } + constraints: + - name: udt-in-catalog + doc: UDT 必须存在于 udt catalog + check: "catalog('udt').has(self.udt)" + catalog: udt + + - name: Rules + doc: 根对象上的规则集合 + references: + - { name: validations, type: ValidationRule, containment: true, multiplicity: "0..*" } + - { name: determinations, type: DeterminationRule, containment: true, multiplicity: "0..*" } + - { name: actions, type: ActionRule, containment: true, multiplicity: "0..*" } + + - name: ValidationRule + doc: 保存前校验规则 + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + - { name: timing, type: Literal, value: beforeSave, default: beforeSave } + - { name: triggerFields, type: IdentifierList, required: true } + - { name: component, type: ComponentRef, required: true } + constraints: + - name: trigger-fields-exist + doc: triggerFields 必须引用已声明字段 + check: "entity.allFields().containsAll(self.triggerFields)" + + - name: DeterminationRule + doc: 联动计算或默认值规则 + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + - { name: timing, type: RuleTiming, required: true } + - { name: component, type: ComponentRef, required: true } + references: + - { name: source, type: DeterminationSource, containment: true, multiplicity: "0..1" } + constraints: + - name: source-fields-exist + doc: source.child 与 source.fields 必须存在 + check: "self.source implies childExists(self.source.child) and fieldsExist(self.source.fields)" + + - name: DeterminationSource + doc: 联动计算的来源子实体与字段 + attributes: + - { name: child, type: Identifier, required: true } + - { name: fields, type: IdentifierList, required: true } + + - name: ActionRule + doc: 对象级业务动作 + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + references: + - { name: params, type: ActionParam, containment: true, multiplicity: "0..*" } + + - name: ActionParam + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString } + - { name: type, type: Identifier, required: true } + + - name: ManagerAction + doc: 管理动作(SubmitToApprove 等);common:true 时须命中 components catalog + attributes: + - { name: code, type: Identifier, required: true } + - { name: name, type: LocalizedString, required: true } + - { name: common, type: Boolean, default: false } + references: + - { name: params, type: ActionParam, containment: true, multiplicity: "0..*" } + constraints: + - name: common-in-catalog + doc: common 管理动作 code 必须在 components catalog 中 + check: "self.common implies catalog('components').has(self.code)" + catalog: components diff --git a/packages/extension/assets/ubml-entity/prompts/entitygen.md b/packages/extension/assets/ubml-entity/prompts/entitygen.md index 7a7595a2ac9c82fdcc8a242e00416619259489a0..f25c6cd021025d9ab59dff0597442fad65be9e09 100644 --- a/packages/extension/assets/ubml-entity/prompts/entitygen.md +++ b/packages/extension/assets/ubml-entity/prompts/entitygen.md @@ -14,9 +14,26 @@ 1. **`kb_be_exemplar`** — 用用户意图检索 few-shot BE DSL 范例 2. **`kb_be_datatype`** / **`kb_be_udt`** / **`kb_be_ref`** / **`kb_be_component`** — 约束字段类型与引用 -3. 编写或修改 YAML(见下方 DSL 形状) -4. **`kb_be_verify`** — 三道闸:DSL → 编译 → Schema →(可选)反序列化 -5. 若需受控小改:**`kb_be_edit`**(addField / addChild 等)→ 再 verify +3. **骨架草案** — 一次性产出 `entity:` YAML(`code`/`name`/`namespace`/`kind` + 标量字段 + `children` 骨架),立即 **`kb_be_verify`** +4. **受控精修** — 对关联、枚举、UDT、规则、管理动作等易错部分,逐条调用 **`kb_be_edit`** typed 操作(每步校验,失败看 `issues[].hint`) +5. **`kb_be_verify`** — 三道闸:DSL → 编译 → Schema →(可选)反序列化 +6. 若需小改:legacy **`kb_be_edit`**(addField / patchField 等)→ 再 verify + +### kb_be_edit typed 操作(推荐) + +| op | 用途 | +|----|------| +| `addScalarField` | string/text/integer/decimal/datetime/boolean | +| `addEnumField` | 枚举字段(values,默认最多 1 个) | +| `addAssociationField` | 外部 BE 关联(ref + refFields,查 `kb_be_ref`) | +| `addUdtField` | UDT 字段(查 `kb_be_udt`) | +| `addChildObject` | 子实体(仅 root 下,不可重复 code) | +| `addValidation` | beforeSave 校验规则 | +| `addDetermination` | afterModify / afterCreate 联动 | +| `addBizAction` | 对象级业务动作 | +| `addManagerAction` | 管理动作(common:true 查 `kb_be_component`) | + +Legacy:`addField` / `addChild` / `addRule` / `patchField` / `removeField` 仍可用,但 typed op 错误提示更精确。 增量修改已有 JSON 时:先用 **`kb_be_decompile`** 得到 YAML,再 edit/verify。 @@ -60,3 +77,42 @@ entity: 1. 简要说明实体类型、主表字段与子实体设计 2. 完整 YAML(fenced code block 或文件写入) 3. verify 结果摘要(通过 / 未通过及 stage) + +## 元模型约束 + + +### 合法字段类型(DSL `type`) + +标量:`string`、`text`、`integer`、`decimal`、`datetime`、`date`、`boolean` +复合: +- `enum`(enum) +- `association`(association) +- `udt`(udt) + +### 实体种类(`entity.kind`) + +`bill` | `basedata` + +### Catalog 约束(必须通过 MCP 查询,禁止编造) + +- **referenceable-be**(`referenceable-be.catalog.json`):可关联的外部业务实体清单 +- **udt**(`udt.catalog.json`):统一数据类型(UDT)清单 +- **components**(`components.catalog.json`):公共构件(common manager action / rule component)清单 + +### 元模型 catalog 校验规则 + +- **AssociationField.ref-in-catalog**(catalog: referenceable-be):关联目标必须存在于 referenceable-be catalog +- **AssociationField.refFields-exist**(catalog()):refFields 必须是被引用实体已声明的字段 code +- **UdtField.udt-in-catalog**(catalog: udt):UDT 必须存在于 udt catalog +- **ManagerAction.common-in-catalog**(catalog: components):common 管理动作 code 必须在 components catalog 中 + +### 结构约束摘要 + +- **EntityObject.no-grandchildren**:子实体不允许再包含子实体(平台限制为两级对象树) +- **EntityObject.unique-child-codes**:同一实体下子对象 code 不可重复 +- **ScalarField.length-required-by-type**:requiresLength 的类型必须声明 length +- **ScalarField.precision-required-by-decimal**:decimal 类型必须声明 precision +- **EnumField.single-default**:枚举默认值最多一个 +- **ValidationRule.trigger-fields-exist**:triggerFields 必须引用已声明字段 +- **DeterminationRule.source-fields-exist**:source.child 与 source.fields 必须存在 + diff --git a/packages/extension/assets/ubml-entity/referenceable-be.catalog.json b/packages/extension/assets/ubml-entity/referenceable-be.catalog.json index cea31f462a4d9b0a9d229991402829c4cd62870f..640d11fc2607321076f1a700a5ab4a0e21344e36 100644 --- a/packages/extension/assets/ubml-entity/referenceable-be.catalog.json +++ b/packages/extension/assets/ubml-entity/referenceable-be.catalog.json @@ -1,6 +1,6 @@ { "version": 1, - "generatedAt": "2026-07-01T16:34:35.485Z", + "generatedAt": "2026-07-04T02:32:59.853Z", "entries": [ { "code": "GspUser", diff --git a/packages/extension/assets/ubml-entity/udt.catalog.json b/packages/extension/assets/ubml-entity/udt.catalog.json index 964c3a72ec945eaa879691d36a46aa95e5600a8d..6f27877887b40c813242c89ccb3bf2fa0c32805a 100644 --- a/packages/extension/assets/ubml-entity/udt.catalog.json +++ b/packages/extension/assets/ubml-entity/udt.catalog.json @@ -1,6 +1,6 @@ { "version": 1, - "generatedAt": "2026-07-01T16:34:35.485Z", + "generatedAt": "2026-07-04T02:32:59.853Z", "entries": [ { "code": "AttachmentInfo", diff --git a/packages/extension/assets/ubml-pageflow/exemplars/intent.md b/packages/extension/assets/ubml-pageflow/exemplars/intent.md new file mode 100644 index 0000000000000000000000000000000000000000..fcf492fe543f62884456c2a6ec370e17c920e741 --- /dev/null +++ b/packages/extension/assets/ubml-pageflow/exemplars/intent.md @@ -0,0 +1,7 @@ +# Pageflow DSL exemplars + +| 文件 | 场景 | 验证 | +|------|------|------| +| `predefine-pageflow.yaml` | FYBXD 三页路由(Card / Management / Query) | `bun run ubml:pageflow:compile -- packages/extension/assets/ubml-pageflow/exemplars/predefine-pageflow.yaml` | + +适用:应用级页面导航与 routeUri 编排。 diff --git a/packages/extension/assets/ubml-query/exemplars/intent.md b/packages/extension/assets/ubml-query/exemplars/intent.md new file mode 100644 index 0000000000000000000000000000000000000000..3f39d3025c4c4422e0d1b73aa8c8de089115bdbb --- /dev/null +++ b/packages/extension/assets/ubml-query/exemplars/intent.md @@ -0,0 +1,8 @@ +# Query DSL exemplars + +| 文件 | 场景 | 验证 | +|------|------|------| +| `bxdqo.qo.yaml` | 标准 BE join 查询(FYBXD 列表数据源) | `bun run ubml:query:compile -- …/bxdqo.qo.yaml --be packages/ubml-codegen/test/fixtures/fybxd.seed.json` | +| `fybxdcustomqo.qo.yaml` | 自定义日期/扩展条件查询 | 同上,换 fybxdcustomqo 路径 | + +适用:列表页 QO、Management/Query 页数据绑定。 diff --git a/packages/extension/assets/ubml/exemplars/fybxdj-card.json b/packages/extension/assets/ubml/exemplars/fybxdj-card.json new file mode 100644 index 0000000000000000000000000000000000000000..2cb4f4448c65a32c8da8b450fd905b4ea0d77321 --- /dev/null +++ b/packages/extension/assets/ubml/exemplars/fybxdj-card.json @@ -0,0 +1,225 @@ +{ + "id": "root-component", + "type": "Component", + "componentType": "Frame", + "contents": [ + { + "id": "root-layout", + "type": "ContentContainer", + "appearance": { + "class": "f-page f-page-card f-page-is-mainsubcard" + }, + "contents": [ + { + "id": "page-header", + "type": "page-header", + "appearance": { + "class": "f-page-header" + }, + "icon": "f-icon f-icon-page-title-dictionary", + "title": "报销单", + "buttons": [ + { + "id": "button-add", + "type": "ToolBarItem", + "text": "新增", + "appearance": { + "class": "btn-primary" + } + }, + { + "id": "button-edit", + "type": "ToolBarItem", + "text": "编辑", + "appearance": null + }, + { + "id": "button-save", + "type": "ToolBarItem", + "text": "保存", + "appearance": null + }, + { + "id": "button-cancel", + "type": "ToolBarItem", + "text": "取消", + "appearance": null + } + ] + }, + { + "id": "main-container", + "type": "ContentContainer", + "appearance": { + "class": "f-page-main" + }, + "contents": [ + { + "id": "like-card-container", + "type": "ContentContainer", + "appearance": { + "class": "f-struct-like-card" + }, + "contents": [ + { + "id": "basic-form-component", + "type": "ContentContainer", + "componentType": "form-col-4", + "appearance": { + "class": "f-struct-wrapper" + }, + "contents": [ + { + "id": "section-基本信息", + "type": "section", + "appearance": { + "class": "f-section-form f-section-in-mainsubcard" + }, + "mainTitle": "基本信息", + "contents": [ + { + "id": "section-基本信息-layout", + "type": "ContentContainer", + "appearance": { + "class": "f-form-layout farris-form farris-form-controls-inline f-area-response--xl" + }, + "contents": [ + { + "id": "fybxd_Code_xoai-wrap", + "type": "ContentContainer", + "appearance": { + "class": "farris-group-wrap col-12 col-md-6 col-xl-3 col-el-2" + }, + "contents": [ + { + "id": "fybxd_Code_xoai", + "type": "form-group", + "label": "单据编号", + "appearance": { + "class": "form-group farris-form-group common-group form-group--has-input-group" + }, + "editor": { + "type": "input-group" + }, + "_ubmlTrace": { + "bind": "Code", + "sectionTitle": "基本信息", + "fieldLabel": "单据编号" + } + } + ] + }, + { + "id": "fybxd_TotalSum_c3u6-wrap", + "type": "ContentContainer", + "appearance": { + "class": "farris-group-wrap col-12 col-md-6 col-xl-3 col-el-2" + }, + "contents": [ + { + "id": "fybxd_TotalSum_c3u6", + "type": "form-group", + "label": "报帐金额", + "appearance": { + "class": "form-group farris-form-group common-group form-group--has-number-spinner" + }, + "editor": { + "type": "number-spinner" + }, + "_ubmlTrace": { + "bind": "TotalSum", + "sectionTitle": "基本信息", + "fieldLabel": "报帐金额" + } + } + ] + }, + { + "id": "fybxd_Subject_18ix-wrap", + "type": "ContentContainer", + "appearance": { + "class": "farris-group-wrap col-12 col-md-6 col-xl-3 col-el-2" + }, + "contents": [ + { + "id": "fybxd_Subject_18ix", + "type": "form-group", + "label": "报销项目", + "appearance": { + "class": "form-group farris-form-group common-group form-group--has-input-group" + }, + "editor": { + "type": "input-group" + }, + "_ubmlTrace": { + "bind": "Subject", + "sectionTitle": "基本信息", + "fieldLabel": "报销项目" + } + } + ] + }, + { + "id": "fybxd_CreateDate_1qrw-wrap", + "type": "ContentContainer", + "appearance": { + "class": "farris-group-wrap col-12 col-md-6 col-xl-3 col-el-2" + }, + "contents": [ + { + "id": "fybxd_CreateDate_1qrw", + "type": "form-group", + "label": "制单日期", + "appearance": { + "class": "form-group farris-form-group common-group form-group--has-date-picker" + }, + "editor": { + "type": "date-picker" + }, + "_ubmlTrace": { + "bind": "CreateDate", + "sectionTitle": "基本信息", + "fieldLabel": "制单日期" + } + } + ] + }, + { + "id": "fybxd_Note_5mau-wrap", + "type": "ContentContainer", + "appearance": { + "class": "farris-group-wrap col-12 col-md-6 col-xl-3 col-el-2" + }, + "contents": [ + { + "id": "fybxd_Note_5mau", + "type": "form-group", + "label": "报销说明", + "appearance": { + "class": "form-group farris-form-group common-group form-group--has-text" + }, + "editor": { + "type": "text" + }, + "_ubmlTrace": { + "bind": "Note", + "sectionTitle": "基本信息", + "fieldLabel": "报销说明" + } + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/packages/extension/assets/ubml/exemplars/fybxdj-card.yaml b/packages/extension/assets/ubml/exemplars/fybxdj-card.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bd98871b27f26503aced687898a366aae38dde1b --- /dev/null +++ b/packages/extension/assets/ubml/exemplars/fybxdj-card.yaml @@ -0,0 +1,14 @@ +page: + type: card + title: 报销单 + entity: FYBXD + toolbar: [add, edit, save, cancel] + sections: + - title: 基本信息 + layout: form-col-4 + fields: + - { label: 单据编号, bind: Code, editor: input-group } + - { label: 报帐金额, bind: TotalSum, editor: number-spinner } + - { label: 报销项目, bind: Subject, editor: input-group } + - { label: 制单日期, bind: CreateDate, editor: date-picker } + - { label: 报销说明, bind: Note, editor: text } diff --git a/packages/extension/assets/ubml/exemplars/fybxdj-management.json b/packages/extension/assets/ubml/exemplars/fybxdj-management.json new file mode 100644 index 0000000000000000000000000000000000000000..7e2ad090daaa6b199264aeecdbe06e9f05776be8 --- /dev/null +++ b/packages/extension/assets/ubml/exemplars/fybxdj-management.json @@ -0,0 +1,120 @@ +{ + "id": "root-component", + "type": "Component", + "componentType": "Frame", + "contents": [ + { + "id": "root-layout", + "type": "ContentContainer", + "appearance": { + "class": "f-page f-page-management-list" + }, + "contents": [ + { + "id": "page-header", + "type": "page-header", + "appearance": { + "class": "f-page-header" + }, + "icon": "f-icon f-icon-page-title-dictionary", + "title": "报销单管理", + "buttons": [ + { + "id": "button-add", + "type": "ToolBarItem", + "text": "新增", + "appearance": { + "class": "btn-primary" + } + }, + { + "id": "button-edit", + "type": "ToolBarItem", + "text": "编辑", + "appearance": null + }, + { + "id": "button-delete", + "type": "ToolBarItem", + "text": "删除", + "appearance": null + }, + { + "id": "button-refresh", + "type": "ToolBarItem", + "text": "刷新", + "appearance": null + } + ] + }, + { + "id": "main-container", + "type": "ContentContainer", + "appearance": { + "class": "f-page-main" + }, + "contents": [ + { + "id": "filter-bar-main", + "type": "filter-bar", + "appearance": { + "class": "f-component-filter-bar" + }, + "title": "查询条件", + "contents": [] + }, + { + "id": "grid-FYBXD", + "type": "data-grid", + "appearance": { + "class": "f-component-grid" + }, + "bind": "FYBXD", + "columns": [ + { + "id": "fybxd_Code_xoai", + "field": "Code", + "title": "单据编号", + "editor": { + "type": "input-group" + }, + "_ubmlTrace": { + "bind": "Code", + "sectionTitle": "数据列表", + "fieldLabel": "单据编号" + } + }, + { + "id": "fybxd_TotalSum_c3u6", + "field": "TotalSum", + "title": "报帐金额", + "editor": { + "type": "number-spinner" + }, + "_ubmlTrace": { + "bind": "TotalSum", + "sectionTitle": "数据列表", + "fieldLabel": "报帐金额" + } + }, + { + "id": "fybxd_BillState_1fyw", + "field": "BillState", + "title": "单据状态", + "editor": { + "type": "combo-list" + }, + "_ubmlTrace": { + "bind": "BillState", + "sectionTitle": "数据列表", + "fieldLabel": "单据状态" + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/packages/extension/assets/ubml/exemplars/fybxdj-management.yaml b/packages/extension/assets/ubml/exemplars/fybxdj-management.yaml new file mode 100644 index 0000000000000000000000000000000000000000..96d329180223edbddbe642a8c66f04a7c8480e9b --- /dev/null +++ b/packages/extension/assets/ubml/exemplars/fybxdj-management.yaml @@ -0,0 +1,15 @@ +page: + type: management-list + title: 报销单管理 + entity: FYBXD + toolbar: [add, edit, delete, refresh] + sections: + - title: 查询条件 + type: filter-bar + - title: 数据列表 + type: data-grid + bind: FYBXD + columns: + - { title: 单据编号, bind: Code, editor: input-group } + - { title: 报帐金额, bind: TotalSum, editor: number-spinner } + - { title: 单据状态, bind: BillState, editor: combo-list } diff --git a/packages/extension/assets/ubml/exemplars/fybxdj-query.json b/packages/extension/assets/ubml/exemplars/fybxdj-query.json new file mode 100644 index 0000000000000000000000000000000000000000..a752ca56ec6e9ef3982f02e300c8a22693504135 --- /dev/null +++ b/packages/extension/assets/ubml/exemplars/fybxdj-query.json @@ -0,0 +1,160 @@ +{ + "id": "root-component", + "type": "Component", + "componentType": "Frame", + "contents": [ + { + "id": "root-layout", + "type": "ContentContainer", + "appearance": { + "class": "f-page f-page-management-list" + }, + "contents": [ + { + "id": "page-header", + "type": "page-header", + "appearance": { + "class": "f-page-header" + }, + "icon": "f-icon f-icon-page-title-dictionary", + "title": "报销单查询", + "buttons": [ + { + "id": "button-refresh", + "type": "ToolBarItem", + "text": "刷新", + "appearance": null + } + ] + }, + { + "id": "main-container", + "type": "ContentContainer", + "appearance": { + "class": "f-page-main" + }, + "contents": [ + { + "id": "filter-bar-main", + "type": "filter-bar", + "appearance": { + "class": "f-component-filter-bar" + }, + "title": "查询条件", + "contents": [ + { + "id": "filter-bar-layout", + "type": "ContentContainer", + "appearance": { + "class": "f-utils-flex-row-wrap farris-form condition-div farris-form-controls-inline f-area-response f-area-response--xl" + }, + "contents": [ + { + "id": "fybxd_Employee_Name_c7mz-wrap", + "type": "ContentContainer", + "appearance": { + "class": "farris-group-wrap col-12 col-md-6 col-xl-3 col-el-2" + }, + "contents": [ + { + "id": "fybxd_Employee_Name_c7mz", + "type": "form-group", + "label": "报销人", + "appearance": { + "class": "form-group farris-form-group common-group form-group--has-input-group" + }, + "editor": { + "type": "input-group" + }, + "_ubmlTrace": { + "bind": "Employee_Name", + "sectionTitle": "查询条件", + "fieldLabel": "报销人" + } + } + ] + }, + { + "id": "fybxd_CreateDate_1qrw-wrap", + "type": "ContentContainer", + "appearance": { + "class": "farris-group-wrap col-12 col-md-6 col-xl-3 col-el-2" + }, + "contents": [ + { + "id": "fybxd_CreateDate_1qrw", + "type": "form-group", + "label": "制单日期", + "appearance": { + "class": "form-group farris-form-group common-group form-group--has-date-picker" + }, + "editor": { + "type": "date-picker" + }, + "_ubmlTrace": { + "bind": "CreateDate", + "sectionTitle": "查询条件", + "fieldLabel": "制单日期" + } + } + ] + } + ] + } + ] + }, + { + "id": "grid-FYBXD", + "type": "data-grid", + "appearance": { + "class": "f-component-grid" + }, + "bind": "FYBXD", + "columns": [ + { + "id": "fybxd_Code_xoai", + "field": "Code", + "title": "单据编号", + "editor": { + "type": "input-group" + }, + "_ubmlTrace": { + "bind": "Code", + "sectionTitle": "数据列表", + "fieldLabel": "单据编号" + } + }, + { + "id": "fybxd_TotalSum_c3u6", + "field": "TotalSum", + "title": "报帐金额", + "editor": { + "type": "number-spinner" + }, + "_ubmlTrace": { + "bind": "TotalSum", + "sectionTitle": "数据列表", + "fieldLabel": "报帐金额" + } + }, + { + "id": "fybxd_CreateDate_1qrw", + "field": "CreateDate", + "title": "制单日期", + "editor": { + "type": "date-picker" + }, + "_ubmlTrace": { + "bind": "CreateDate", + "sectionTitle": "数据列表", + "fieldLabel": "制单日期" + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/packages/extension/assets/ubml/exemplars/fybxdj-query.yaml b/packages/extension/assets/ubml/exemplars/fybxdj-query.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d3ab66e85a2cf2acaa554a7a3463bb7580a9a223 --- /dev/null +++ b/packages/extension/assets/ubml/exemplars/fybxdj-query.yaml @@ -0,0 +1,18 @@ +page: + type: management-list + title: 报销单查询 + entity: FYBXD + toolbar: [refresh] + sections: + - title: 查询条件 + type: filter-bar + fields: + - { label: 报销人, bind: Employee_Name, editor: input-group } + - { label: 制单日期, bind: CreateDate, editor: date-picker } + - title: 数据列表 + type: data-grid + bind: FYBXD + columns: + - { title: 单据编号, bind: Code, editor: input-group } + - { title: 报帐金额, bind: TotalSum, editor: number-spinner } + - { title: 制单日期, bind: CreateDate, editor: date-picker } diff --git a/packages/extension/assets/ubml/exemplars/fybxdj-webcmd.yaml b/packages/extension/assets/ubml/exemplars/fybxdj-webcmd.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4dbf75b165afbad25365c8a12d4f5842696e8587 --- /dev/null +++ b/packages/extension/assets/ubml/exemplars/fybxdj-webcmd.yaml @@ -0,0 +1,10 @@ +webcmd: + page: FYBXDJ + namespace: Inspur.GS.FSSP.FSSC.ROBXDJChenshj + entity: FYBXD + controller: FYBXDJ_frm_Controller + commands: + - { code: add, name: 新增, handler: onAdd, trigger: toolbar, service: add } + - { code: edit, name: 编辑, handler: onEdit, trigger: toolbar, service: edit } + - { code: save, name: 保存, handler: onSave, trigger: toolbar, service: save } + - { code: cancel, name: 取消, handler: onCancel, trigger: toolbar, service: cancel } diff --git a/packages/extension/assets/ubml/exemplars/intent.md b/packages/extension/assets/ubml/exemplars/intent.md new file mode 100644 index 0000000000000000000000000000000000000000..3f5b731436df328ceecde256e0925f0763364876 --- /dev/null +++ b/packages/extension/assets/ubml/exemplars/intent.md @@ -0,0 +1,12 @@ +# Page DSL exemplars(FYBXD) + +Agent 生成页面 DSL 时参考的 few-shot 范例。均基于 FYBXD 报销单场景精简。 + +| 文件 | 场景 | 验证 | +|------|------|------| +| `fybxdj-card.yaml` | 卡片维护页(基本信息 4 列表单) | `bun run ubml:compile -- packages/extension/assets/ubml/exemplars/fybxdj-card.yaml` | +| `fybxdj-management.yaml` | 管理列表(筛选 + 可编辑表格) | 同上,换 management 路径 | +| `fybxdj-query.yaml` | 查询列表(条件 + 只读列) | 同上,换 query 路径 | +| `fybxdj-webcmd.yaml` | Weblogic webcmd 片段 | `bun run ubml:weblogic:smoke`(含 webcmd 路径) | + +通用模板(非 FYBXD 专用):`card.yaml`、`list-card.yaml`、`management-list.yaml`、`layout.yaml`。 diff --git a/packages/extension/assets/ubml/exemplars/raw-custom-section.yaml b/packages/extension/assets/ubml/exemplars/raw-custom-section.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6d130af0bb484aaf9609dff96db18746530f768e --- /dev/null +++ b/packages/extension/assets/ubml/exemplars/raw-custom-section.yaml @@ -0,0 +1,22 @@ +page: + type: card + title: Raw 逃逸示例 + entity: DemoEntity + sections: + - title: 基本信息 + fields: + - label: 名称 + bind: name + editor: input-group + - title: 自定义区块 + type: raw + raw: + id: custom-banner + type: section + mainTitle: 自定义横幅 + appearance: + class: demo-custom-banner + contents: + - id: banner-text + type: notify + message: 这是 raw 透传的 Farris 节点 diff --git a/packages/extension/assets/ubml/metamodel/README.md b/packages/extension/assets/ubml/metamodel/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8934250b98db0a24521f5cde8134fc575f0681b0 --- /dev/null +++ b/packages/extension/assets/ubml/metamodel/README.md @@ -0,0 +1,29 @@ +# 页面元模型(M2) + +本目录存放 **UBML 页面 DSL 的显式元模型**,对应 [10-mof-entity-modeling.md](../../../../docs/product-design/10-mof-entity-modeling.md) E4 阶段。 + +## 文件 + +| 文件 | 说明 | +|------|------| +| `ubml-page.metamodel.yaml` | Page / Section / FieldBinding / ToolbarAction / RawSection 等 | + +## 与实体元模型的关系 + +- 共用 M3 词汇(`metamodel` / `class` / `attribute` / `reference` / `enum` / `constraint`) +- `Page.entity`、`FieldBinding.bind` 为 **external** 交叉引用,指向实体域(见 [UBML-EXTERNAL-REFERENCE.md](../../../../docs/UBML-EXTERNAL-REFERENCE.md)) + +## 维护规则 + +1. 改 page DSL section 类型或 editor 约束时,同步更新本文件并通过 `page-metamodel-consistency.test.ts`。 +2. `PageType` enum 与 `../templates.index.json` 的模板 id 保持一致。 +3. `FieldBinding.editor` 约束对应 `../components.index.json`。 +4. **E4 不改变** Zod 校验、page verify 或 AppGen `crossref.ts` 运行时行为。 +5. `constraint.check` 首期为人读规约;执行由既有校验代码承担。 + +## 验收 + +```bash +bun test packages/ubml-codegen/test/page-metamodel-consistency.test.ts +bun run ubml:verify packages/extension/assets/ubml/exemplars/fybxdj-card.yaml +``` diff --git a/packages/extension/assets/ubml/metamodel/ubml-page.metamodel.yaml b/packages/extension/assets/ubml/metamodel/ubml-page.metamodel.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8524b85251a035924100d1114fade460dddcfb8c --- /dev/null +++ b/packages/extension/assets/ubml/metamodel/ubml-page.metamodel.yaml @@ -0,0 +1,119 @@ +metamodel: + name: ubml-page + version: "0.1" + doc: | + 页面(Page)M2 元模型。M1 表示为 page: YAML DSL;编译器负责 M1→Farris JSON。 + 与 ubml-entity 共用 M3 词汇。E4 为描述性事实源 + 一致性测试,不替换 crossref.ts。 + + enums: + - name: PageType + literals: [card, list-card, management-list, list-dictionary, layout] + doc: 与 templates.index.json 模板 id 对齐;DSL 首版聚焦 card / list-card + + catalogs: + - name: components + file: components.index.json + doc: 字段 editor 合法值域(Farris 组件 id) + - name: templates + file: templates.index.json + doc: page.type 模板清单 + + classes: + - name: Page + doc: 页面根;编译为 Farris 页面 JSON + attributes: + - { name: type, type: PageType, required: true } + - { name: title, type: LocalizedString, required: true } + references: + - { name: entity, type: BusinessEntity, containment: false, external: true, multiplicity: "0..1" } + - { name: sections, type: Section, containment: true, multiplicity: "1..*" } + - { name: toolbar, type: ToolbarAction, containment: true, multiplicity: "0..*" } + constraints: + - name: type-in-templates + doc: page.type 应命中 templates catalog + check: "catalog('templates').has(self.type)" + catalog: templates + + - name: Section + abstract: true + doc: 页面区域基类;DSL 以 type 或 fields 形态 discriminated union + + - name: FormSection + superType: Section + doc: 默认 form 区域(可无 type,含 fields) + attributes: + - { name: title, type: LocalizedString, required: true } + - { name: layout, type: LayoutRef, default: form-col-4 } + - { name: type, type: Literal, value: form } + references: + - { name: fields, type: FieldBinding, containment: true, multiplicity: "1..*" } + + - name: ContentContainerSection + superType: Section + doc: 嵌套容器区域 + attributes: + - { name: title, type: LocalizedString, required: true } + - { name: type, type: Literal, value: content-container, required: true } + - { name: layout, type: LayoutRef } + references: + - { name: sections, type: Section, containment: true, multiplicity: "1..*" } + + - name: DataGridSection + superType: Section + attributes: + - { name: type, type: Literal, value: data-grid, required: true } + - { name: bind, type: Identifier, required: true } + references: + - { name: columns, type: GridColumnBinding, containment: true, multiplicity: "1..*" } + + - name: ListNavSection + superType: Section + attributes: + - { name: type, type: Literal, value: list-nav, required: true } + + - name: FilterBarSection + superType: Section + attributes: + - { name: type, type: Literal, value: filter-bar, required: true } + references: + - { name: fields, type: FieldBinding, containment: true, multiplicity: "0..*" } + + - name: RawSection + superType: Section + doc: raw 逃逸节点;不解析 raw 载荷内部结构 + attributes: + - { name: type, type: Literal, value: raw, required: true } + - { name: raw, type: OpaqueObject, required: true } + + - name: FieldBinding + doc: 表单字段绑定;编译为 Farris field editor + attributes: + - { name: label, type: LocalizedString, required: true } + - { name: editor, type: EditorRef, required: true } + references: + - { name: bind, type: EntityField, containment: false, external: true } + constraints: + - name: editor-in-catalog + doc: editor 必须存在于 components.index.json + check: "catalog('components').has(self.editor)" + catalog: components + - name: bind-exists-on-entity + doc: bind 必须引用 page.entity 已声明字段 + check: "page(self).entity.allFields().has(self.bind)" + + - name: GridColumnBinding + doc: 数据网格列绑定 + attributes: + - { name: title, type: LocalizedString, required: true } + - { name: editor, type: EditorRef, required: true } + references: + - { name: bind, type: EntityField, containment: false, external: true } + constraints: + - name: editor-in-catalog + catalog: components + check: "catalog('components').has(self.editor)" + + - name: ToolbarAction + doc: 工具栏动作 code(add/edit/save/cancel 等) + attributes: + - { name: code, type: Identifier, required: true } diff --git a/packages/extension/assets/ubml/prompts/pagegen.md b/packages/extension/assets/ubml/prompts/pagegen.md index bf8811d89f228f925d75b47707798ba35e732b51..4d32ea6fa450e15b22c359af2878b2c873e02674 100644 --- a/packages/extension/assets/ubml/prompts/pagegen.md +++ b/packages/extension/assets/ubml/prompts/pagegen.md @@ -53,3 +53,30 @@ page: 1. 简要说明页面类型与字段设计 2. 完整 YAML( fenced code block 或文件写入) 3. verify 结果摘要(通过 / 未通过及 stage) + +## 元模型约束 + + +### 合法页面类型(`page.type`) + +`card` | `list-card` | `management-list` | `list-dictionary` | `layout` + +首版生成聚焦:`card`、`list-card`。 + +### Catalog 约束(必须通过 MCP 查询,禁止编造) + +- **components**(`components.index.json`):字段 editor 合法值域(Farris 组件 id) +- **templates**(`templates.index.json`):page.type 模板清单 + +### 元模型 catalog / external 校验规则 + +- **Page.type-in-templates**(catalog: templates):page.type 应命中 templates catalog +- **FieldBinding.editor-in-catalog**(catalog: components):editor 必须存在于 components.index.json +- **GridColumnBinding.editor-in-catalog**(catalog: components):catalog('components').has(self.editor) + +### 结构约束摘要 + +- **FieldBinding.bind-exists-on-entity**:bind 必须引用 page.entity 已声明字段 + +- **RawSection**:无法语义表达的 Farris 片段用 `type: raw` 透传,不解析内部结构。 + diff --git a/packages/extension/package.json b/packages/extension/package.json index 50281f100fbc92a93230f208db4126ccc5e3e412..e5944b0e487a491592b87e25fb50ddab1ed7326a 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -33,6 +33,7 @@ "@thisbeyond/solid-dnd": "0.7.5", "effect": "catalog:", "luxon": "catalog:", + "mermaid": "11.12.0", "solid-js": "catalog:", "virtua": "catalog:" }, diff --git a/packages/extension/src/components/dev-lab-compile-output.tsx b/packages/extension/src/components/dev-lab-compile-output.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4dc3e7f20363aa6f720c1b81646023f9217a2fd6 --- /dev/null +++ b/packages/extension/src/components/dev-lab-compile-output.tsx @@ -0,0 +1,72 @@ +import { Show, createMemo } from "solid-js" +import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2" + +export function downloadJsonFile(data: unknown, filename: string) { + const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json;charset=utf-8" }) + const url = URL.createObjectURL(blob) + const anchor = document.createElement("a") + anchor.href = url + anchor.download = filename + anchor.click() + URL.revokeObjectURL(url) +} + +export function DevLabCompileOutput(props: { + status?: string + issuesJson?: string + model?: Record + modelTitle?: string + exportFilename?: string + emptyHint?: string +}) { + const jsonText = createMemo(() => (props.model ? JSON.stringify(props.model, null, 2) : "")) + const hasIssues = () => { + const raw = props.issuesJson?.trim() + return Boolean(raw && raw !== "[]" && raw !== "null") + } + + return ( +
+
+
+
+ {props.modelTitle ?? "编译产物 JSON"} +
+ +
{props.status}
+
+
+ + downloadJsonFile(props.model!, props.exportFilename ?? "compiled.json")} + > + Export JSON + + +
+ + + {props.emptyHint ?? "Compile / Verify 成功后在此显示编译 JSON"} +
+ } + > +
+          {jsonText()}
+        
+ + + +
+ 校验问题 +
+            {props.issuesJson}
+          
+
+
+ + ) +} diff --git a/packages/extension/src/components/entity-preview-content.tsx b/packages/extension/src/components/entity-preview-content.tsx index ada3526e87d35d6c69f0d6f18e7714f3e735bf19..c74934d8ba62787b6399f314b01653ab73ea654e 100644 --- a/packages/extension/src/components/entity-preview-content.tsx +++ b/packages/extension/src/components/entity-preview-content.tsx @@ -1,21 +1,20 @@ import { For, Show } from "solid-js" import type { EntityPreviewModel } from "../lib/entity-preview-model" +import { MermaidDiagram } from "./mermaid-diagram" export function EntityPreviewContent(props: { model: EntityPreviewModel; compact?: boolean }) { - const model = () => props.model - return (

- {model().entityCode} - - {model().entityName} + {props.model.entityCode} + + {props.model.entityName}

- +

- {[model().entityKind, model().namespace].filter(Boolean).join(" · ")} + {[props.model.entityKind, props.model.namespace].filter(Boolean).join(" · ")}

@@ -33,7 +32,7 @@ export function EntityPreviewContent(props: { model: EntityPreviewModel; compact - + {(field) => ( {field.code} @@ -48,11 +47,11 @@ export function EntityPreviewContent(props: { model: EntityPreviewModel; compact
- 0}> + 0}>

子实体

- + {(child) => (
@@ -81,10 +80,13 @@ export function EntityPreviewContent(props: { model: EntityPreviewModel; compact
-

Mermaid ER

-
-          {model().mermaid}
-        
+

关系图

+

+ 包含关系(子实体)与关联引用(外部实体) +

+
+ +
) diff --git a/packages/extension/src/components/mermaid-diagram.tsx b/packages/extension/src/components/mermaid-diagram.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8270e4deea0b492aff032c3f2778f2d8398a1258 --- /dev/null +++ b/packages/extension/src/components/mermaid-diagram.tsx @@ -0,0 +1,80 @@ +import { createEffect, createSignal, onCleanup } from "solid-js" + +let mermaidReady: Promise | undefined + +function loadMermaid() { + if (!mermaidReady) { + mermaidReady = import("mermaid").then((mod) => { + mod.default.initialize({ + startOnLoad: false, + securityLevel: "loose", + theme: "neutral", + er: { useMaxWidth: true }, + }) + return mod.default + }) + } + return mermaidReady +} + +let renderSeq = 0 + +export function MermaidDiagram(props: { chart: string; class?: string }) { + let containerRef: HTMLDivElement | undefined + const [error, setError] = createSignal() + + createEffect(() => { + const chart = props.chart.trim() + const container = containerRef + if (!container) return + + if (!chart) { + container.innerHTML = "" + setError(undefined) + return + } + + const seq = ++renderSeq + let cancelled = false + onCleanup(() => { + cancelled = true + }) + + void (async () => { + try { + setError(undefined) + const mermaid = await loadMermaid() + if (cancelled || seq !== renderSeq) return + const id = `mermaid-${seq}-${Math.random().toString(36).slice(2, 9)}` + const { svg } = await mermaid.render(id, chart) + if (cancelled || seq !== renderSeq || !containerRef) return + containerRef.innerHTML = svg + } catch (e) { + if (!cancelled && seq === renderSeq) { + setError(e instanceof Error ? e.message : String(e)) + if (containerRef) containerRef.innerHTML = "" + } + } + })() + }) + + return ( +
+
+ {error() ? ( +
+
+ Mermaid 渲染失败:{error()} +
+
+            {props.chart}
+          
+
+ ) : null} +
+ ) +} diff --git a/packages/extension/src/components/page-preview-content.tsx b/packages/extension/src/components/page-preview-content.tsx new file mode 100644 index 0000000000000000000000000000000000000000..471919d348cca5a726642c571c10886a449b4024 --- /dev/null +++ b/packages/extension/src/components/page-preview-content.tsx @@ -0,0 +1,72 @@ +import { For, Show } from "solid-js" +import type { PagePreviewModel } from "../lib/page-preview-model" + +export function PagePreviewContent(props: { model: PagePreviewModel }) { + const model = () => props.model + + return ( +
+
+

+ {model().title} + {model().pageType} +

+ +

+ 绑定实体:{model().entity} +

+
+ 0}> +

+ 工具栏:{model().toolbar.join(", ")} +

+
+
+ +
+

区域

+
    + + {(section) => ( +
  • + {section.title} + + {section.type} + + ({section.fieldCount} 字段) +
  • + )} +
    +
+
+ +
+

字段绑定

+
+ + + + + + + + + + + + {(row) => ( + + + + + + + )} + + +
区域LabelBindEditor
{row.sectionTitle}{row.label}{row.bind}{row.editor}
+
+
+
+ ) +} diff --git a/packages/extension/src/components/ubml-artifact-card.tsx b/packages/extension/src/components/ubml-artifact-card.tsx index 4b55a3fb38ed0f5d3d05b41b01a2ee8b0bbc2885..fd4ebbc7ddf509533979ec164f71bf48745c7ca8 100644 --- a/packages/extension/src/components/ubml-artifact-card.tsx +++ b/packages/extension/src/components/ubml-artifact-card.tsx @@ -104,7 +104,7 @@ export function UbmlArtifactCard(props: { } function runDevLab() { - openUbmlInDevLab(props.artifact.yaml) + openUbmlInDevLab(props.artifact.yaml, props.artifact.kind) setStatus("已在 Dev Lab 打开") } @@ -200,6 +200,9 @@ export function UbmlArtifactCard(props: { > {issue.path ? `${issue.path}: ` : ""} {issue.message} + + {issue.hint} + )} diff --git a/packages/extension/src/components/ubml-assistant-text-part.tsx b/packages/extension/src/components/ubml-assistant-text-part.tsx index d56470bfa04272edeae24ac6b7fe275db384a485..aa57028830eb98932030fba5d467800043f6f209 100644 --- a/packages/extension/src/components/ubml-assistant-text-part.tsx +++ b/packages/extension/src/components/ubml-assistant-text-part.tsx @@ -66,9 +66,11 @@ export function UbmlAssistantTextPart(props: { deferToolContent?: boolean virtualizeDiff?: boolean }) { - const artifacts = () => (props.part.type === "text" ? extractUbmlArtifacts(props.part.text) : []) + const partComplete = () => Boolean(props.part.time?.end) + const artifacts = () => + props.part.type === "text" && partComplete() ? extractUbmlArtifacts(props.part.text) : [] const displayPart = () => { - if (props.part.type !== "text" || artifacts().length === 0) return props.part + if (props.part.type !== "text" || !partComplete() || artifacts().length === 0) return props.part const stripped = stripUbmlYamlFromText(props.part.text) if (!stripped) return { ...props.part, text: "" } return { ...props.part, text: stripped } @@ -76,6 +78,7 @@ export function UbmlAssistantTextPart(props: { const showMessagePart = () => { if (props.part.type !== "text") return true + if (!partComplete()) return true return stripUbmlYamlFromText(props.part.text).length > 0 } diff --git a/packages/extension/src/index.ts b/packages/extension/src/index.ts index 6b4988515dae8d54b987f96f532ed8a6d09ec3bc..b71302a4bc537b46fd7f80a6dcb04a87c203ad7d 100644 --- a/packages/extension/src/index.ts +++ b/packages/extension/src/index.ts @@ -38,6 +38,7 @@ export { export type { RequirementKind } from "./lib/requirement-control-client" export { openUbmlPreviewTab, pushUbmlPreview, getUbmlPreviewUrl } from "./lib/ubml-preview-bridge" export { openUbmlEntityPreviewTab } from "./lib/ubml-entity-preview-bridge" +export { openUbmlModelTab } from "./lib/ubml-model-bridge" export { getUbmlEntityPrompt, listUbmlEntityPromptIds, diff --git a/packages/extension/src/lib/api-wiki-control-client.ts b/packages/extension/src/lib/api-wiki-control-client.ts index 438e0e844221e2c5c140c7831776511d73296997..1d1564ba72bcb018393eb864351508ff45e24ad4 100644 --- a/packages/extension/src/lib/api-wiki-control-client.ts +++ b/packages/extension/src/lib/api-wiki-control-client.ts @@ -1,4 +1,13 @@ -export type ApiEntryKind = "function" | "class" | "interface" | "method" | "component" | "enum" | "type" | "variable" +export type ApiEntryKind = + | "function" + | "class" + | "interface" + | "method" + | "component" + | "enum" + | "type" + | "variable" + | "http-route" export interface ApiWikiEntry { id: string @@ -10,6 +19,9 @@ export interface ApiWikiEntry { module?: string signature?: string importers?: string[] + httpMethod?: string + httpPath?: string + router?: string } export interface ApiWikiRepoSummary { @@ -77,4 +89,5 @@ export const API_KIND_LABEL: Record = { enum: "枚举", type: "类型", variable: "变量", + "http-route": "HTTP 路由", } diff --git a/packages/extension/src/lib/entity-preview-model.ts b/packages/extension/src/lib/entity-preview-model.ts index 6472acfed1df59779f878c985a53a0498dc4bd56..2b9643dbe001b08af957f4cf3d0e9c1f4c34fa3d 100644 --- a/packages/extension/src/lib/entity-preview-model.ts +++ b/packages/extension/src/lib/entity-preview-model.ts @@ -41,17 +41,39 @@ function sanitizeMermaidId(id: string): string { return id.replace(/[^a-zA-Z0-9_]/g, "_") } +function mermaidFieldType(field: EntityPreviewFieldRow): string { + if (field.type === "association") return "ref" + if (field.type === "udt") return "udt" + if (field.type === "enum") return "enum" + return field.type.replace(/[^a-zA-Z0-9_]/g, "_") || "string" +} + +function collectExternalRefs(rootFields: EntityPreviewFieldRow[], children: EntityPreviewChild[]): string[] { + const refs = new Set() + for (const field of rootFields) { + if (field.type === "association" && field.ref) refs.add(field.ref) + } + for (const child of children) { + for (const field of child.fields) { + if (field.type === "association" && field.ref) refs.add(field.ref) + } + } + return [...refs].sort() +} + function buildMermaid( entityCode: string, rootFields: EntityPreviewFieldRow[], children: EntityPreviewChild[], ): string { const rootId = sanitizeMermaidId(entityCode) + const defined = new Set([rootId, ...children.map((c) => sanitizeMermaidId(c.code))]) const lines = ["erDiagram"] lines.push(` ${rootId} {`) for (const field of rootFields) { - lines.push(` ${field.type} ${field.code}`) + if (field.type === "association") continue + lines.push(` ${mermaidFieldType(field)} ${field.code}`) } lines.push(" }") @@ -60,11 +82,22 @@ function buildMermaid( lines.push(` ${rootId} ||--o{ ${childId} : contains`) lines.push(` ${childId} {`) for (const field of child.fields) { - lines.push(` ${field.type} ${field.code}`) + if (field.type === "association") continue + lines.push(` ${mermaidFieldType(field)} ${field.code}`) } lines.push(" }") } + for (const ref of collectExternalRefs(rootFields, children)) { + const refId = sanitizeMermaidId(ref) + if (!defined.has(refId)) { + lines.push(` ${refId} {`) + lines.push(" string external") + lines.push(" }") + defined.add(refId) + } + } + for (const field of rootFields) { if (field.type === "association" && field.ref) { const refId = sanitizeMermaidId(field.ref) @@ -72,6 +105,16 @@ function buildMermaid( } } + for (const child of children) { + const childId = sanitizeMermaidId(child.code) + for (const field of child.fields) { + if (field.type === "association" && field.ref) { + const refId = sanitizeMermaidId(field.ref) + lines.push(` ${childId} }o--|| ${refId} : ${field.code}`) + } + } + } + return lines.join("\n") } diff --git a/packages/extension/src/lib/model-preview-model.test.ts b/packages/extension/src/lib/model-preview-model.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce63ee0eca505827ede70c510a947ccfe2ba4e37 --- /dev/null +++ b/packages/extension/src/lib/model-preview-model.test.ts @@ -0,0 +1,89 @@ +import { describe, expect, test } from "bun:test" +import { readFileSync } from "node:fs" +import { resolve } from "node:path" +import { buildEntityPreviewFromDsl } from "./entity-preview-model" +import { buildPagePreviewFromDsl } from "./page-preview-model" +import { + buildModelNavigation, + collectExternalRefs, + graphViewLabel, + summarizeModelGraph, +} from "./model-preview-model" +import type { UbmlModelEntry } from "./ubml-model-tab-store" +import { + parseEntityYaml, + parseDslYaml, + validateEntityDsl, + validatePageDsl, +} from "@code-agent/ubml-codegen/runtime" +import { loadEntityCompileContext, loadCompileContext } from "@code-agent/ubml-codegen" + +const FYBXD = readFileSync( + resolve(import.meta.dir, "../../assets/ubml-entity/exemplars/fybxd.be.yaml"), + "utf8", +) + +const FYBXDJ_CARD = readFileSync( + resolve(import.meta.dir, "../../assets/ubml/exemplars/fybxdj-card.yaml"), + "utf8", +) + +describe("model-preview-model", () => { + test("FYBXD external refs and children summary", async () => { + const assets = resolve(import.meta.dir, "../../assets/ubml-entity") + const ctx = await loadEntityCompileContext(assets) + const dsl = validateEntityDsl(parseEntityYaml(FYBXD), ctx) + const model = buildEntityPreviewFromDsl(dsl) + const summary = summarizeModelGraph(model) + + expect(summary.childCodes.sort()).toEqual(["FXBXDFJ", "FYBXDMX"]) + expect(summary.externalRefs).toContain("GspUser") + expect(summary.externalRefs).toContain("SysOrg") + expect(summary.externalRefs).toContain("Projects") + expect(collectExternalRefs(model).length).toBeGreaterThanOrEqual(3) + }) + + test("buildModelNavigation groups entity entries", async () => { + const assets = resolve(import.meta.dir, "../../assets/ubml-entity") + const ctx = await loadEntityCompileContext(assets) + const dsl = validateEntityDsl(parseEntityYaml(FYBXD), ctx) + const model = buildEntityPreviewFromDsl(dsl) + const entries: UbmlModelEntry[] = [ + { key: "fybxd", label: "FYBXD", kind: "entity", yaml: FYBXD, model }, + ] + const nav = buildModelNavigation(entries) + expect(nav.length).toBe(1) + expect(nav[0]?.label).toBe("实体 be/") + expect(nav[0]?.items[0]?.code).toBe("FYBXD") + }) + + test("buildModelNavigation groups entity and page entries", async () => { + const entityAssets = resolve(import.meta.dir, "../../assets/ubml-entity") + const entityCtx = await loadEntityCompileContext(entityAssets) + const entityDsl = validateEntityDsl(parseEntityYaml(FYBXD), entityCtx) + const entityModel = buildEntityPreviewFromDsl(entityDsl) + + const pageAssets = resolve(import.meta.dir, "../../assets/ubml") + const pageCtx = await loadCompileContext(pageAssets) + const pageDsl = validatePageDsl(parseDslYaml(FYBXDJ_CARD), pageCtx) + const pageModel = buildPagePreviewFromDsl(pageDsl) + + const entries: UbmlModelEntry[] = [ + { key: "fybxd", label: "FYBXD", kind: "entity", yaml: FYBXD, model: entityModel }, + { key: "card", label: "报销单", kind: "page", yaml: FYBXDJ_CARD, model: pageModel }, + ] + const nav = buildModelNavigation(entries) + + expect(nav.length).toBe(2) + expect(nav[0]?.label).toBe("实体 be/") + expect(nav[0]?.items[0]?.code).toBe("FYBXD") + expect(nav[1]?.label).toBe("页面 pages/") + expect(nav[1]?.items[0]?.code).toBe("card") + expect(nav[1]?.items[0]?.name).toBe("报销单") + }) + + test("graphViewLabel distinguishes entity and page", () => { + expect(graphViewLabel("entity")).toBe("关系图") + expect(graphViewLabel("page")).toBe("结构") + }) +}) diff --git a/packages/extension/src/lib/model-preview-model.ts b/packages/extension/src/lib/model-preview-model.ts new file mode 100644 index 0000000000000000000000000000000000000000..96d10cd55014ae5a718e7c0b51082db66c462ece --- /dev/null +++ b/packages/extension/src/lib/model-preview-model.ts @@ -0,0 +1,78 @@ +import type { EntityPreviewModel } from "./entity-preview-model" +import type { UbmlModelEntry, UbmlModelKind } from "./ubml-model-tab-store" + +export type ModelNavItem = { + key: string + code: string + name: string +} + +export type ModelNavGroup = { + kind: UbmlModelKind + label: string + items: ModelNavItem[] +} + +const KIND_LABELS: Record = { + entity: "实体 be/", + page: "页面 pages/", +} + +export function navItemFromEntry(entry: UbmlModelEntry): ModelNavItem { + if (entry.kind === "page") { + return { + key: entry.key, + code: entry.model.pageType, + name: entry.model.title, + } + } + return { + key: entry.key, + code: entry.model.entityCode, + name: entry.model.entityName, + } +} + +export function collectExternalRefs(model: EntityPreviewModel): string[] { + const refs = new Set() + for (const field of model.rootFields) { + if (field.type === "association" && field.ref) refs.add(field.ref) + } + for (const child of model.children) { + for (const field of child.fields) { + if (field.type === "association" && field.ref) refs.add(field.ref) + } + } + return [...refs].sort() +} + +export function buildModelNavigation(entries: UbmlModelEntry[]): ModelNavGroup[] { + const groups = new Map() + for (const entry of entries) { + const list = groups.get(entry.kind) ?? [] + list.push(navItemFromEntry(entry)) + groups.set(entry.kind, list) + } + const order: UbmlModelKind[] = ["entity", "page"] + return order + .filter((kind) => groups.has(kind)) + .map((kind) => ({ + kind, + label: KIND_LABELS[kind], + items: (groups.get(kind) ?? []).sort((a, b) => a.code.localeCompare(b.code)), + })) +} + +export function summarizeModelGraph(model: EntityPreviewModel): { + childCodes: string[] + externalRefs: string[] +} { + return { + childCodes: model.children.map((c) => c.code), + externalRefs: collectExternalRefs(model), + } +} + +export function graphViewLabel(kind: UbmlModelKind): string { + return kind === "entity" ? "关系图" : "结构" +} diff --git a/packages/extension/src/lib/page-preview-model.ts b/packages/extension/src/lib/page-preview-model.ts new file mode 100644 index 0000000000000000000000000000000000000000..70cddb7db0827e91fc4913f2ef96ae73aaabb657 --- /dev/null +++ b/packages/extension/src/lib/page-preview-model.ts @@ -0,0 +1,89 @@ +import type { PageDsl, SectionDsl } from "@code-agent/ubml-codegen/runtime" + +export type PagePreviewBinding = { + sectionTitle: string + label: string + bind: string + editor: string +} + +export type PagePreviewSection = { + title: string + type?: string + fieldCount: number +} + +export type PagePreviewModel = { + pageType: string + title: string + entity?: string + toolbar: string[] + sections: PagePreviewSection[] + bindings: PagePreviewBinding[] +} + +function sectionTitle(section: SectionDsl, index: number): string { + if ("title" in section && section.title) return section.title + if ("type" in section && section.type) return section.type + return `Section ${index + 1}` +} + +function collectBindings(sections: SectionDsl[], sectionLabel: string): PagePreviewBinding[] { + const bindings: PagePreviewBinding[] = [] + for (let i = 0; i < sections.length; i++) { + const section = sections[i]! + const title = sectionTitle(section, i) + if ("fields" in section && section.fields) { + for (const field of section.fields) { + bindings.push({ + sectionTitle: sectionLabel || title, + label: field.label, + bind: field.bind, + editor: field.editor, + }) + } + } + if ("columns" in section) { + for (const col of section.columns) { + bindings.push({ + sectionTitle: sectionLabel || title, + label: col.title, + bind: col.bind, + editor: col.editor, + }) + } + } + if ("type" in section && section.type === "content-container" && "sections" in section) { + bindings.push(...collectBindings(section.sections, title)) + } + } + return bindings +} + +function summarizeSections(sections: SectionDsl[]): PagePreviewSection[] { + return sections.map((section, index) => { + let fieldCount = 0 + if ("fields" in section && section.fields) fieldCount = section.fields.length + if ("columns" in section) fieldCount = section.columns.length + if ("type" in section && section.type === "content-container" && "sections" in section) { + fieldCount = collectBindings(section.sections, "").length + } + return { + title: sectionTitle(section, index), + type: "type" in section ? section.type : "form", + fieldCount, + } + }) +} + +export function buildPagePreviewFromDsl(dsl: PageDsl): PagePreviewModel { + const page = dsl.page + return { + pageType: page.type, + title: page.title, + entity: page.entity, + toolbar: page.toolbar ?? [], + sections: summarizeSections(page.sections), + bindings: collectBindings(page.sections, ""), + } +} diff --git a/packages/extension/src/lib/right-panel-preview-utils.ts b/packages/extension/src/lib/right-panel-preview-utils.ts index 60022bdb9b735df98eb1e2a39022e9711d3449ca..7db5f431393631a8ec58c9c378588240ff29d08a 100644 --- a/packages/extension/src/lib/right-panel-preview-utils.ts +++ b/packages/extension/src/lib/right-panel-preview-utils.ts @@ -8,9 +8,9 @@ type PreviewTabLike = { type OpenTabLike = PreviewTabLike & { id: string; previewKey?: string } -const ADDABLE_TAB_TYPES: RightPanelTabType[] = ["changes", "terminal", "editor", "preview"] +const ADDABLE_TAB_TYPES: RightPanelTabType[] = ["changes", "terminal", "editor", "preview", "model"] -/** Tab types available from the right panel + menu (Preview excluded — artifact-driven). */ +/** Tab types available from the right panel + menu (Preview / entity-preview excluded — artifact-driven). */ export function closedTabTypesForAddMenu(openTabs: OpenTabLike[]): RightPanelTabType[] { const open = new Set(openTabs.map((tab) => tab.type)) return ADDABLE_TAB_TYPES.filter((type) => { diff --git a/packages/extension/src/lib/ubml-appgen-client.ts b/packages/extension/src/lib/ubml-appgen-client.ts index a09b563f52de94368eeb35d50015fd3a33a1a8b9..6df4490efc83820b0950bef70599649c74629fe3 100644 --- a/packages/extension/src/lib/ubml-appgen-client.ts +++ b/packages/extension/src/lib/ubml-appgen-client.ts @@ -1,7 +1,20 @@ import { getUbmlAppPrompt } from "../ubml-app/prompts" -import { formatUbmlAppgenAgentPrompt, type UbmlAppgenPromptInput } from "./ubml-appgen-prompt" +import { + formatUbmlAppgenAgentPrompt, + formatUbmlAppgenRepairPrompt, + type UbmlAppgenPromptInput, + type UbmlAppgenRepairContext, +} from "./ubml-appgen-prompt" -export type { UbmlAppgenPromptInput } from "./ubml-appgen-prompt" +export type { UbmlAppgenPromptInput, UbmlAppgenRepairContext } from "./ubml-appgen-prompt" +export { + buildRepairPrompt, + canFixWithAgent, + canRepairRound, + MAX_APPGEN_REPAIR_ROUNDS, + nextRepairRound, + repairLimitMessage, +} from "./ubml-appgen-repair-loop" export type UbmlAppgenPromptOptions = { appgenGuide?: string @@ -16,3 +29,13 @@ export function buildUbmlAppgenAgentPrompt( options?.appgenGuide ?? getUbmlAppPrompt("appgen"), ) } + +export function buildUbmlAppgenRepairAgentPrompt( + repair: UbmlAppgenRepairContext, + options?: UbmlAppgenPromptOptions, +): string { + return formatUbmlAppgenRepairPrompt( + repair, + options?.appgenGuide ?? getUbmlAppPrompt("appgen"), + ) +} diff --git a/packages/extension/src/lib/ubml-appgen-prompt.test.ts b/packages/extension/src/lib/ubml-appgen-prompt.test.ts index ea1c78ba612eed194ee6ef0c56e787a83e478ba6..b889c81c8be7a68d62591ac348bd7df6abb8140b 100644 --- a/packages/extension/src/lib/ubml-appgen-prompt.test.ts +++ b/packages/extension/src/lib/ubml-appgen-prompt.test.ts @@ -1,5 +1,16 @@ import { describe, expect, test } from "bun:test" -import { formatUbmlAppgenAgentPrompt } from "./ubml-appgen-prompt" +import { + formatUbmlAppgenAgentPrompt, + formatUbmlAppgenRepairPrompt, +} from "./ubml-appgen-prompt" +import { + buildRepairPrompt, + canFixWithAgent, + canRepairRound, + MAX_APPGEN_REPAIR_ROUNDS, + nextRepairRound, + repairLimitMessage, +} from "./ubml-appgen-repair-loop" describe("ubml-appgen-prompt", () => { test("includes app tag and intent", () => { @@ -15,4 +26,83 @@ describe("ubml-appgen-prompt", () => { }) expect(prompt).toContain("app:\n bizobject: X") }) + + test("includes validation issues when generate failed before ask agent", () => { + const prompt = formatUbmlAppgenAgentPrompt({ + intent: "fix app", + yaml: "app:\n bizobject: X", + validationIssues: [ + { + stage: "crossref", + path: "pageflow.pages.FYBXDJ", + message: "missing frm", + hint: "add to pages[]", + }, + ], + }) + expect(prompt).toContain("上次 Generate 校验失败") + expect(prompt).toContain("add to pages[]") + }) + + test("repair prompt includes round counter and yaml", () => { + const prompt = formatUbmlAppgenRepairPrompt({ + round: 2, + maxRounds: 3, + lastYaml: "app:\n bizobject: X", + issues: [ + { + stage: "crossref", + path: "logic.writeback", + message: "missing writeback", + hint: "keep writeback in logic[]", + }, + ], + intent: "报销单", + }) + expect(prompt).toContain("第 2/3 轮") + expect(prompt).toContain("勿改动已通过 stage") + expect(prompt).toContain("keep writeback in logic[]") + expect(prompt).toContain("app:\n bizobject: X") + }) +}) + +describe("ubml-appgen-repair-loop", () => { + test("canRepairRound stops at max", () => { + expect(canRepairRound(0)).toBe(true) + expect(canRepairRound(2)).toBe(true) + expect(canRepairRound(3)).toBe(false) + expect(nextRepairRound(1)).toBe(2) + expect(repairLimitMessage()).toContain("3") + }) + + test("canFixWithAgent requires failed validation and remaining rounds", () => { + expect( + canFixWithAgent({ hasResult: true, validationValid: false, repairRound: 0 }), + ).toBe(true) + expect( + canFixWithAgent({ hasResult: true, validationValid: true, repairRound: 0 }), + ).toBe(false) + expect( + canFixWithAgent({ + hasResult: true, + validationValid: false, + repairRound: MAX_APPGEN_REPAIR_ROUNDS, + }), + ).toBe(false) + }) + + test("buildRepairPrompt round increments in output", () => { + const round1 = buildRepairPrompt({ + repairRound: 1, + lastYaml: "app:\n bizobject: A", + issues: [{ path: "p", message: "m", hint: "h" }], + }) + const round2 = buildRepairPrompt({ + repairRound: 2, + lastYaml: "app:\n bizobject: A", + issues: [{ path: "p", message: "m", hint: "h" }], + }) + expect(round1).toContain("第 1/3 轮") + expect(round2).toContain("第 2/3 轮") + }) }) diff --git a/packages/extension/src/lib/ubml-appgen-prompt.ts b/packages/extension/src/lib/ubml-appgen-prompt.ts index d01b47510e53174d66a342651236fa82f082c851..7a5646045fe40f446a92ac17e4fe0475703ed213 100644 --- a/packages/extension/src/lib/ubml-appgen-prompt.ts +++ b/packages/extension/src/lib/ubml-appgen-prompt.ts @@ -1,6 +1,18 @@ +import type { AppValidationIssue } from "@code-agent/ubml-codegen/app-browser" + export type UbmlAppgenPromptInput = { intent: string yaml?: string + /** 若刚 Generate 失败,附带校验 issues 供 Agent 首轮修复 */ + validationIssues?: AppValidationIssue[] +} + +export type UbmlAppgenRepairContext = { + round: number + maxRounds: number + issues: AppValidationIssue[] + lastYaml: string + intent?: string } export function formatUbmlAppgenAgentPrompt( @@ -18,15 +30,25 @@ export function formatUbmlAppgenAgentPrompt( lines.push("## 当前 app: YAML 草稿", "", "```yaml", draft, "```", "") } + if (input.validationIssues?.length) { + lines.push( + "## 上次 Generate 校验失败", + "", + "请先根据下列 issues 修正 app: YAML,再说明修改点:", + "", + ...formatIssueSummary(input.validationIssues), + "", + ) + } + if (appgenGuide) { lines.push("## 工作流指引", "", appgenGuide, "") } else { lines.push( "## 工作流", - "1. 解析 app: 清单(entities / derive / queries / components / pageflow / logic)", - "2. 按需调用子管线:entitygen、derive、query、component、pageflow、javagen", - "3. 运行 ubml:app:generate 或工作台 Generate 做 crossref 校验", - "4. 失败时按 stages[].issues 修正(≤3 轮)", + "1. 解析 app: 清单(entities / derive / queries / components / pages / pageflow / logic)", + "2. 按需调用子管线或整体 `ubml:app:generate`", + "3. 校验失败时按 issues 修正(≤3 轮)", "", ) } @@ -34,3 +56,64 @@ export function formatUbmlAppgenAgentPrompt( lines.push("请产出或修改 app: YAML,并说明各子 DSL 来源与校验结果。") return lines.join("\n") } + +export function formatUbmlAppgenRepairPrompt( + repair: UbmlAppgenRepairContext, + appgenGuide?: string, +): string { + const lines: string[] = [ + "@ubml-appgen", + "", + "[UBML 应用修复回环]", + "", + repair.intent?.trim() ? `意图:${repair.intent.trim()}` : "请修复下列 app: 校验 issues。", + "", + "仅修正失败 stage 涉及的字段或 sources 引用,**勿改动已通过 stage 的配置**。", + "", + ...formatIssueSummary(repair.issues, repair.round, repair.maxRounds), + "", + "## 当前 app: YAML", + "", + "```yaml", + repair.lastYaml.trim(), + "```", + "", + ] + + if (appgenGuide) { + lines.push("## 工作流指引", "", appgenGuide, "") + } + + if (repair.round >= repair.maxRounds) { + lines.push(`已达修复上限(${repair.maxRounds} 轮)。若仍失败,请向用户报告剩余 issues。`, "") + } else { + lines.push("修正后请再次运行 Generate / `ubml:app:generate` 验证。", "") + } + + return lines.join("\n") +} + +function formatIssueSummary( + issues: AppValidationIssue[], + round?: number, + maxRounds?: number, +): string[] { + const heading = + round !== undefined && maxRounds !== undefined + ? `### 校验失败(第 ${round}/${maxRounds} 轮)` + : "### 校验 issues" + + const lines: string[] = [heading, "", "| stage | path | message | hint |", "|-------|------|---------|------|"] + + for (const issue of issues) { + lines.push( + `| ${escapeCell(issue.stage ?? "")} | ${escapeCell(issue.path)} | ${escapeCell(issue.message)} | ${escapeCell(issue.hint ?? "")} |`, + ) + } + + return lines +} + +function escapeCell(value: string): string { + return value.replace(/\|/g, "\\|").replace(/\n/g, " ") +} diff --git a/packages/extension/src/lib/ubml-appgen-repair-loop.ts b/packages/extension/src/lib/ubml-appgen-repair-loop.ts new file mode 100644 index 0000000000000000000000000000000000000000..680ef19add75387af783ed9349376e4762fe4dc8 --- /dev/null +++ b/packages/extension/src/lib/ubml-appgen-repair-loop.ts @@ -0,0 +1,55 @@ +import type { AppValidationIssue } from "@code-agent/ubml-codegen/app-browser" +import { + formatUbmlAppgenRepairPrompt, + type UbmlAppgenRepairContext, +} from "./ubml-appgen-prompt" + +export const MAX_APPGEN_REPAIR_ROUNDS = 3 + +export function canRepairRound( + currentRound: number, + maxRounds = MAX_APPGEN_REPAIR_ROUNDS, +): boolean { + return currentRound < maxRounds +} + +export function nextRepairRound(currentRound: number): number { + return currentRound + 1 +} + +export function repairLimitMessage(maxRounds = MAX_APPGEN_REPAIR_ROUNDS): string { + return `已到达修复上限(${maxRounds} 轮),请手动编辑 YAML 或检查剩余 issues` +} + +export function buildRepairPrompt( + input: { + issues: AppValidationIssue[] + lastYaml: string + repairRound: number + intent?: string + appgenGuide?: string + }, + maxRounds = MAX_APPGEN_REPAIR_ROUNDS, +): string { + const context: UbmlAppgenRepairContext = { + round: input.repairRound, + maxRounds, + issues: input.issues, + lastYaml: input.lastYaml, + intent: input.intent, + } + return formatUbmlAppgenRepairPrompt(context, input.appgenGuide) +} + +export function canFixWithAgent(input: { + hasResult: boolean + validationValid: boolean + repairRound: number + maxRounds?: number +}): boolean { + return ( + input.hasResult && + !input.validationValid && + canRepairRound(input.repairRound, input.maxRounds) + ) +} diff --git a/packages/extension/src/lib/ubml-artifact-actions.ts b/packages/extension/src/lib/ubml-artifact-actions.ts index bf607220fd0a37e74c96cfe9ce804848abf13b82..b93639ee1b2839c6377c2acaf5feb8ccfcc50be7 100644 --- a/packages/extension/src/lib/ubml-artifact-actions.ts +++ b/packages/extension/src/lib/ubml-artifact-actions.ts @@ -1,8 +1,11 @@ -import { compilePageFromYaml, verifyPageFromYaml } from "@code-agent/ubml-codegen/runtime" +import { compilePageFromYaml, verifyPageFromYaml, parseDslYaml, validatePageDsl } from "@code-agent/ubml-codegen/runtime" import { getBrowserCompileContext } from "../ubml/browser-context" import { openUbmlPreviewTab } from "./ubml-preview-bridge" +import { buildPagePreviewFromDsl } from "./page-preview-model" +import { openUbmlModelTab } from "./ubml-model-bridge" import type { UbmlValidationIssue } from "./ubml-preview-tab-store" -import { openDevLabWithYaml } from "./ubml-dev-lab-bridge" +import { openDevLabWithYaml, devLabSurfaceFromArtifactKind } from "./ubml-dev-lab-bridge" +import type { UbmlArtifactKind } from "./ubml-artifact-detect" export { ubmlArtifactPreviewKey, ubmlArtifactTitle } from "./ubml-artifact-meta" export { openUbmlArtifactInIde, @@ -54,25 +57,85 @@ export async function verifyUbmlYaml(yaml: string): Promise<{ ok: boolean; stage } } +function mapVerifyIssues( + issues: { + stage?: string + path?: string + message: string + hint?: string + suggestion?: string + hints?: string[] + }[], + componentType: string, +): UbmlValidationIssue[] { + return issues.map((issue) => ({ + path: issue.path ?? "", + message: issue.message, + componentType, + stage: issue.stage, + hint: issue.hint, + suggestion: issue.suggestion, + hints: issue.hints, + })) +} + export async function previewUbmlYaml( yaml: string, opts: UbmlPreviewOptions, ): Promise { - const compiled = await compileUbmlYaml(yaml) - if (compiled.model) { - openUbmlPreviewTab({ - key: opts.previewKey, - title: opts.title, - model: compiled.model, - yaml, - projectDir: opts.projectDir, - }) + try { + const ctx = await getBrowserCompileContext() + const verify = await verifyPageFromYaml(yaml, undefined, { UBML_RENDER_SMOKE: "0" }, ctx) + const issues = mapVerifyIssues(verify.issues ?? [], "page") + let model = verify.model as Record | undefined + + if (!model && verify.ok === false) { + const compiled = await compilePageFromYaml(yaml, undefined, ctx).catch(() => null) + model = compiled?.model as Record | undefined + } + + if (model) { + openUbmlPreviewTab({ + key: opts.previewKey, + title: opts.title, + model, + yaml, + projectDir: opts.projectDir, + }) + try { + const dsl = validatePageDsl(parseDslYaml(yaml), ctx) + const pagePreview = buildPagePreviewFromDsl(dsl) + openUbmlModelTab({ + kind: "page", + key: opts.previewKey, + title: opts.title, + yaml, + model: pagePreview, + }) + } catch { + // model tab optional when DSL parse fails but compiled model exists + } + } + + return { + model, + valid: verify.ok, + issueCount: issues.length, + issues, + error: verify.ok ? undefined : `Verify failed at ${verify.stage ?? "unknown"}`, + } + } catch (e) { + return { + valid: false, + issueCount: 0, + issues: [], + error: e instanceof Error ? e.message : String(e), + } } - return compiled } -export function openUbmlInDevLab(yaml: string) { - openDevLabWithYaml(yaml) +export function openUbmlInDevLab(yaml: string, kind?: UbmlArtifactKind) { + openDevLabWithYaml(yaml, kind ? devLabSurfaceFromArtifactKind(kind) : undefined) } export async function copyUbmlYaml(yaml: string): Promise { diff --git a/packages/extension/src/lib/ubml-dev-lab-bridge.test.ts b/packages/extension/src/lib/ubml-dev-lab-bridge.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e829a998664755545c5d3ae32a7fcf87d0ba7da1 --- /dev/null +++ b/packages/extension/src/lib/ubml-dev-lab-bridge.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, test } from "bun:test" +import { detectDevLabSurface, devLabSurfaceFromArtifactKind } from "./ubml-dev-lab-bridge" + +const ENTITY_SAMPLE = `entity: + code: FYBXD + name: 报销单 + namespace: Inspur.GS + kind: bill + root: + code: FYBXD + name: 报销单 + fields: + - { code: Code, name: 编号, type: string, length: 50 } +` + +const PAGE_SAMPLE = `page: + type: card + title: 报销单 + entity: FYBXD + sections: + - title: 基本信息 + fields: + - { label: 编号, bind: Code, editor: input-group } +` + +describe("ubml-dev-lab-bridge", () => { + test("detectDevLabSurface routes entity yaml to entities", () => { + expect(detectDevLabSurface(ENTITY_SAMPLE)).toBe("entities") + expect(detectDevLabSurface(PAGE_SAMPLE)).toBe("pages") + }) + + test("devLabSurfaceFromArtifactKind maps entity card to entities", () => { + expect(devLabSurfaceFromArtifactKind("entity")).toBe("entities") + expect(devLabSurfaceFromArtifactKind("page")).toBe("pages") + expect(devLabSurfaceFromArtifactKind("app")).toBe("apps") + }) +}) diff --git a/packages/extension/src/lib/ubml-dev-lab-bridge.ts b/packages/extension/src/lib/ubml-dev-lab-bridge.ts index e13435820e2f7c1070dc3af429501c38672d7d3c..b9bc140ef5d2fd0f3bdb79285aaadf8061950bf4 100644 --- a/packages/extension/src/lib/ubml-dev-lab-bridge.ts +++ b/packages/extension/src/lib/ubml-dev-lab-bridge.ts @@ -1,18 +1,46 @@ import { createStore } from "solid-js/store" +import { + looksLikeUbmlAppYaml, + looksLikeUbmlEntityYaml, + looksLikeUbmlPageYaml, + type UbmlArtifactKind, +} from "./ubml-artifact-detect" import { openDevLab } from "../pages/layout/project-surface-store" +export type DevLabSurface = "pages" | "entities" | "apps" + export const [devLabYamlStore, setDevLabYamlStore] = createStore({ pendingYaml: undefined as string | undefined, + pendingSurface: undefined as DevLabSurface | undefined, }) -export function openDevLabWithYaml(yaml: string) { - setDevLabYamlStore("pendingYaml", yaml) +export function devLabSurfaceFromArtifactKind(kind: UbmlArtifactKind): DevLabSurface { + if (kind === "entity") return "entities" + if (kind === "app") return "apps" + return "pages" +} + +export function detectDevLabSurface(yaml: string): DevLabSurface { + if (looksLikeUbmlAppYaml(yaml)) return "apps" + if (looksLikeUbmlEntityYaml(yaml)) return "entities" + if (looksLikeUbmlPageYaml(yaml)) return "pages" + return "pages" +} + +export function openDevLabWithYaml(yaml: string, surface?: DevLabSurface) { + setDevLabYamlStore({ + pendingYaml: yaml, + pendingSurface: surface ?? detectDevLabSurface(yaml), + }) openDevLab() } -/** Read and clear pending YAML (UbmlPageEditor on mount). */ -export function consumeDevLabPendingYaml(): string | undefined { +/** Read and clear pending YAML when surface matches (editor on mount). */ +export function consumeDevLabPendingYaml(expectedSurface: DevLabSurface): string | undefined { const yaml = devLabYamlStore.pendingYaml - if (yaml) setDevLabYamlStore("pendingYaml", undefined) + const surface = devLabYamlStore.pendingSurface + if (!yaml) return undefined + if (surface && surface !== expectedSurface) return undefined + setDevLabYamlStore({ pendingYaml: undefined, pendingSurface: undefined }) return yaml } diff --git a/packages/extension/src/lib/ubml-entity-artifact-actions.ts b/packages/extension/src/lib/ubml-entity-artifact-actions.ts index 06505a4b6ea431c901ea38b2048d067b9ef43708..80757af48c3c5851d7a4a6c40fb7015b0dd0f484 100644 --- a/packages/extension/src/lib/ubml-entity-artifact-actions.ts +++ b/packages/extension/src/lib/ubml-entity-artifact-actions.ts @@ -7,6 +7,7 @@ import { import { getBrowserEntityCompileContext } from "../ubml-entity/browser-context" import { buildEntityPreviewFromDsl, type EntityPreviewModel } from "./entity-preview-model" import { openUbmlEntityPreviewTab } from "./ubml-entity-preview-bridge" +import { openUbmlModelTab } from "./ubml-model-bridge" import type { UbmlValidationIssue } from "./ubml-preview-tab-store" export type UbmlEntityCompileResult = { @@ -24,12 +25,23 @@ export type UbmlEntityPreviewOptions = { } function mapEntityIssues( - issues: { path?: string; message: string }[], + issues: { + path?: string + message: string + stage?: string + hint?: string + suggestion?: string + hints?: string[] + }[], ): UbmlValidationIssue[] { return issues.map((issue) => ({ path: issue.path ?? "", message: issue.message, componentType: "entity", + stage: issue.stage, + hint: issue.hint, + suggestion: issue.suggestion, + hints: issue.hints, })) } @@ -97,6 +109,13 @@ export async function previewEntityYaml( yaml, model: preview, }) + openUbmlModelTab({ + kind: "entity", + key: opts.previewKey, + title: opts.title, + yaml, + model: preview, + }) } return { diff --git a/packages/extension/src/lib/ubml-model-bridge.ts b/packages/extension/src/lib/ubml-model-bridge.ts new file mode 100644 index 0000000000000000000000000000000000000000..08c9770eede16806af2c1266bf5d19df39b25d2e --- /dev/null +++ b/packages/extension/src/lib/ubml-model-bridge.ts @@ -0,0 +1,83 @@ +import type { EntityPreviewModel } from "./entity-preview-model" +import type { PagePreviewModel } from "./page-preview-model" +import { + type UbmlModelEntry, + upsertUbmlModelEntry, + setUbmlModelViewMode, + clearUbmlModelTab, +} from "./ubml-model-tab-store" +import { createRightPanelTabs } from "../pages/layout/right-panel" +import type { RightPanelTab } from "../pages/layout/right-panel" +import { rightPanelStore, setRightPanelStore } from "../pages/layout/right-panel-store" + +export type OpenUbmlEntityModelTabInput = { + kind: "entity" + key: string + title: string + yaml: string + model: EntityPreviewModel + viewMode?: "graph" | "file" +} + +export type OpenUbmlPageModelTabInput = { + kind: "page" + key: string + title: string + yaml: string + model: PagePreviewModel + viewMode?: "graph" | "file" +} + +export type OpenUbmlModelTabInput = OpenUbmlEntityModelTabInput | OpenUbmlPageModelTabInput + +const { createTab } = createRightPanelTabs() + +function expandRightPanel() { + if (rightPanelStore.width === 0) { + setRightPanelStore("width", rightPanelStore.widthBeforeCollapse || 570) + setRightPanelStore("collapsed", false) + } +} + +function findOrCreateModelTab(): string { + const existing = rightPanelStore.tabs.find((tab) => tab.type === "model") + if (existing) { + setRightPanelStore("activeTab", existing.id) + return existing.id + } + const base = createTab("model") + const tab: RightPanelTab = { ...base, title: "" } + setRightPanelStore("tabs", [...rightPanelStore.tabs, tab]) + setRightPanelStore("activeTab", tab.id) + return tab.id +} + +function toEntry(input: OpenUbmlModelTabInput): UbmlModelEntry { + if (input.kind === "page") { + return { + key: input.key.trim(), + label: input.title.trim() || input.model.title, + kind: "page", + yaml: input.yaml, + model: input.model, + } + } + return { + key: input.key.trim(), + label: input.title.trim() || input.model.entityCode, + kind: "entity", + yaml: input.yaml, + model: input.model, + } +} + +export function openUbmlModelTab(input: OpenUbmlModelTabInput) { + findOrCreateModelTab() + expandRightPanel() + upsertUbmlModelEntry(toEntry(input)) + if (input.viewMode) setUbmlModelViewMode(input.viewMode) +} + +export function cleanupUbmlModelTab(_tabId: string) { + clearUbmlModelTab() +} diff --git a/packages/extension/src/lib/ubml-model-tab-store.ts b/packages/extension/src/lib/ubml-model-tab-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..88cc7bef3f79acaf19e0973ff75c208072e96be9 --- /dev/null +++ b/packages/extension/src/lib/ubml-model-tab-store.ts @@ -0,0 +1,83 @@ +import type { EntityPreviewModel } from "./entity-preview-model" +import type { PagePreviewModel } from "./page-preview-model" + +export type UbmlModelKind = "entity" | "page" + +export type UbmlEntityModelEntry = { + key: string + label: string + kind: "entity" + yaml: string + model: EntityPreviewModel +} + +export type UbmlPageModelEntry = { + key: string + label: string + kind: "page" + yaml: string + model: PagePreviewModel +} + +export type UbmlModelEntry = UbmlEntityModelEntry | UbmlPageModelEntry + +export type UbmlModelViewMode = "graph" | "file" + +export type UbmlModelTabState = { + entries: UbmlModelEntry[] + selectedKey: string | null + viewMode: UbmlModelViewMode +} + +let state: UbmlModelTabState = { + entries: [], + selectedKey: null, + viewMode: "graph", +} + +const listeners = new Set<() => void>() + +function emit() { + for (const listener of listeners) listener() +} + +export function getUbmlModelTabState(): UbmlModelTabState { + return state +} + +export function subscribeUbmlModelTabState(listener: () => void): () => void { + listeners.add(listener) + return () => listeners.delete(listener) +} + +export function upsertUbmlModelEntry(entry: UbmlModelEntry) { + const idx = state.entries.findIndex((e) => e.key === entry.key) + const entries = + idx >= 0 + ? state.entries.map((e, i) => (i === idx ? entry : e)) + : [...state.entries, entry] + state = { ...state, entries, selectedKey: entry.key } + emit() +} + +export function setUbmlModelSelectedKey(key: string) { + if (!state.entries.some((e) => e.key === key)) return + state = { ...state, selectedKey: key } + emit() +} + +export function setUbmlModelViewMode(viewMode: UbmlModelViewMode) { + state = { ...state, viewMode } + emit() +} + +export function clearUbmlModelTab() { + state = { entries: [], selectedKey: null, viewMode: "graph" } + emit() +} + +export function getUbmlModelSelectedEntry(): UbmlModelEntry | undefined { + const key = state.selectedKey + if (!key) return state.entries[0] + return state.entries.find((e) => e.key === key) ?? state.entries[0] +} diff --git a/packages/extension/src/lib/ubml-preview-tab-store.ts b/packages/extension/src/lib/ubml-preview-tab-store.ts index c66eb919bf0fbc2e8830f1a89a06c7540ef95523..4e055fe9c3957b46e79ad3fb4afbfcbb25dddd00 100644 --- a/packages/extension/src/lib/ubml-preview-tab-store.ts +++ b/packages/extension/src/lib/ubml-preview-tab-store.ts @@ -5,6 +5,10 @@ export type UbmlValidationIssue = { path: string componentType: string message: string + stage?: string + hint?: string + suggestion?: string + hints?: string[] } export const [ubmlPreviewTabStore, setUbmlPreviewTabStore] = createStore({ diff --git a/packages/extension/src/overrides/context/terminal.tsx b/packages/extension/src/overrides/context/terminal.tsx index f29a65272b14024829f701accdd3f9c1c1bd532a..20cf505fa5afcf189fd1cd6d67b3292bd7dfcfc1 100644 --- a/packages/extension/src/overrides/context/terminal.tsx +++ b/packages/extension/src/overrides/context/terminal.tsx @@ -1,6 +1,6 @@ import { createStore, produce } from "solid-js/store" import { createSimpleContext } from "@opencode-ai/ui/context" -import { batch, createEffect, createMemo, createRoot, on, onCleanup } from "solid-js" +import { batch, createEffect, createMemo, createRoot, createSignal, on, onCleanup } from "solid-js" import { useParams } from "@solidjs/router" import { useSDK } from "@/context/sdk" import type { Platform } from "@/context/platform" @@ -23,6 +23,11 @@ export type LocalPTY = { const WORKSPACE_KEY = "__workspace__" const MAX_TERMINAL_SESSIONS = 20 +async function ptyExists(client: ReturnType["client"], id: string) { + const result = await client.pty.get({ ptyID: id }, { throwOnError: false }).catch(() => undefined) + return result?.response.status === 200 +} + function record(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value) } @@ -162,6 +167,7 @@ function createWorkspaceTerminalSession( all: [], }), ) + const [pruned, setPruned] = createSignal(false) const pickNextTerminalNumber = () => { const existingTitleNumbers = new Set( @@ -202,24 +208,47 @@ function createWorkspaceTerminalSession( }) onCleanup(unsub) + createEffect( + on(ready, (isReady, wasReady) => { + if (!isReady || wasReady) return + if (store.all.length === 0) { + setPruned(true) + return + } + void (async () => { + for (const id of store.all.map((pty) => pty.id)) { + if (!(await ptyExists(sdk.client, id))) removeExited(id) + } + setPruned(true) + })() + }), + ) + + const isReady = () => ready() && pruned() + const update = (client: ReturnType["client"], pty: Partial & { id: string }) => { const index = store.all.findIndex((x) => x.id === pty.id) const previous = index >= 0 ? store.all[index] : undefined if (index >= 0) { setStore("all", index, (item) => ({ ...item, ...pty })) } - client.pty + void client.pty .update({ ptyID: pty.id, title: pty.title, size: pty.cols && pty.rows ? { rows: pty.rows, cols: pty.cols } : undefined, - }) - .catch((error: unknown) => { + }, { throwOnError: false }) + .then((result) => { + if (result.response.status === 404) { + removeExited(pty.id) + return + } + if (!result.error) return if (previous) { const currentIndex = store.all.findIndex((item) => item.id === pty.id) if (currentIndex >= 0) setStore("all", currentIndex, previous) } - console.error("Failed to update terminal", error) + console.error("Failed to update terminal", result.error) }) } @@ -257,7 +286,7 @@ function createWorkspaceTerminalSession( } return { - ready, + ready: isReady, all: createMemo(() => store.all), active: createMemo(() => store.active), clear() { diff --git a/packages/extension/src/overrides/pages/session/message-timeline.tsx b/packages/extension/src/overrides/pages/session/message-timeline.tsx index 02c2c7b2b76c5f5726f9de58084c3a4769a6eb55..84d4907c42ea2aabd2b5dcecb174f5fd624345d9 100644 --- a/packages/extension/src/overrides/pages/session/message-timeline.tsx +++ b/packages/extension/src/overrides/pages/session/message-timeline.tsx @@ -62,6 +62,8 @@ import { useDialog } from "@opencode-ai/ui/context/dialog" import { createResizeObserver } from "@solid-primitives/resize-observer" import { useLanguage } from "@/context/language" import { useSessionKey } from "@/pages/session/session-layout" +import { useServer } from "@/context/server" +import { SessionRouteKey, SessionStateKey } from "@/utils/server-scope" import { useServerSDK } from "@/context/server-sdk" import { usePlatform } from "@/context/platform" import { useSettings } from "@/context/settings" @@ -278,6 +280,8 @@ function TimelineDiffView(props: { diff: SummaryDiff }) { } export function MessageTimeline(props: { + /** When set (e.g. Agent Home `/`), use this session instead of route params.id */ + sessionIdOverride?: string actions?: UserActions scroll: { overflow: boolean; bottom: boolean; jump: boolean } onResumeScroll: () => void @@ -301,16 +305,24 @@ export function MessageTimeline(props: { const navigate = useNavigate() const serverSDK = useServerSDK() + const server = useServer() const sdk = useSDK() const sync = useSync() const settings = useSettings() const dialog = useDialog() const language = useLanguage() - const { params, sessionKey } = useSessionKey() + const { params, sessionKey: routeSessionKey } = useSessionKey() const platform = usePlatform() let virtualizer: VirtualizerHandle | undefined - const sessionID = createMemo(() => params.id) + const sessionID = createMemo(() => props.sessionIdOverride ?? params.id) + const sessionKey = createMemo(() => { + const override = props.sessionIdOverride + if (override) { + return SessionStateKey.from(server.scope(), SessionRouteKey.fromRoute(params.dir, override)) + } + return routeSessionKey() + }) const sessionMessages = createMemo(() => { const id = sessionID() if (!id) return emptyMessages @@ -1434,7 +1446,9 @@ export function MessageTimeline(props: { {(id) => (
- + + + { const navigate = useNavigate() + const location = useLocation() const params = useParams() const server = useServer() const global = useGlobal() @@ -59,6 +60,10 @@ export const { use: useAgentHome, provider: AgentHomeProvider } = createSimpleCo const [state, setState] = createStore({ selection: { server: server.key } as AgentHomeSelection, + /** Session highlighted on `/` before navigating to full session route. */ + previewSessionId: undefined as string | undefined, + /** When true, `/` shows the new-session home instead of timeline preview. */ + agentHomeLanding: false, }) const focusedServer = createMemo( @@ -149,6 +154,27 @@ export const { use: useAgentHome, provider: AgentHomeProvider } = createSimpleCo navigate(pending.href) }) + createEffect(() => { + if (location.pathname !== "/") { + if (state.previewSessionId) setState("previewSessionId", undefined) + return + } + if (state.agentHomeLanding) return + const latest = sessions()[0] + if (!latest) { + if (state.previewSessionId) setState("previewSessionId", undefined) + return + } + if (!state.previewSessionId) setState("previewSessionId", latest.id) + }) + + function goToAgentHome() { + batch(() => { + setState("agentHomeLanding", true) + setState("previewSessionId", undefined) + }) + } + function selectProject(conn: ServerConnection.Any, directory: string) { const key = ServerConnection.key(conn) const ctx = global.createServerCtx(conn) @@ -191,7 +217,7 @@ export const { use: useAgentHome, provider: AgentHomeProvider } = createSimpleCo setSelection({ server: ServerConnection.key(conn), directory }) } - function openSession(session: Session) { + function openSession(session: Session, forceNavigate = false) { const conn = focusedServer() if (!conn) return const project = projectForSession(session, projects(), projectByID()) @@ -200,9 +226,27 @@ export const { use: useAgentHome, provider: AgentHomeProvider } = createSimpleCo ctx.projects.open(selectionDirectory) ctx.projects.touch(selectionDirectory) setSelection({ server: ServerConnection.key(conn), directory: selectionDirectory }) + + if (!forceNavigate && location.pathname === "/") { + batch(() => { + setState("agentHomeLanding", false) + setState("previewSessionId", session.id) + }) + return + } + + setState("agentHomeLanding", false) + navigateOnServer(conn, `/${base64Encode(session.directory)}/session/${session.id}`) } + function setPreviewSessionId(sessionId: string | undefined) { + batch(() => { + setState("agentHomeLanding", false) + setState("previewSessionId", sessionId) + }) + } + function startNewAgent(prompt?: string) { const conn = focusedServer() if (!conn) return @@ -242,6 +286,8 @@ export const { use: useAgentHome, provider: AgentHomeProvider } = createSimpleCo selectProject, addProjects, openSession, + goToAgentHome, + setPreviewSessionId, startNewAgent, homeProjectDirectories, } diff --git a/packages/extension/src/pages/agent-home/chat-area.tsx b/packages/extension/src/pages/agent-home/chat-area.tsx index cb5d2f111234483045ba6e5bdf2276462d86dee0..3f48145be0d462cd3d1af6052e666ed26b482d05 100644 --- a/packages/extension/src/pages/agent-home/chat-area.tsx +++ b/packages/extension/src/pages/agent-home/chat-area.tsx @@ -1,55 +1,106 @@ -import { createSignal, Show } from "solid-js" +import { createMemo, createSignal, Show } from "solid-js" +import { useLocation } from "@solidjs/router" import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2" +import { useDirectoryPicker } from "@/components/directory-picker" +import { ServerConnection } from "@/context/server" import { HomeWaves } from "../../components/home-waves" import { useAgentHome } from "./agent-home-context" -import { BRAND } from "../../brand" -import { codeAgentAssets } from "../../assets/code-agent" +import { HomeSessionTimeline } from "./home-session-timeline" +import { resolveHomeSessionId } from "./home-session-target" +import { HomeWelcome } from "./home-welcome" export function ChatArea() { const home = useAgentHome() + const location = useLocation() + const pickDirectory = useDirectoryPicker() const [prompt, setPrompt] = createSignal("") + const project = createMemo(() => home.activeProject()) + const homeSessionId = createMemo(() => + location.pathname === "/" + ? resolveHomeSessionId({ + previewSessionId: home.state.previewSessionId, + sessions: home.sessions(), + landing: home.state.agentHomeLanding, + }) + : undefined, + ) + + const previewSession = createMemo(() => { + const id = homeSessionId() + if (!id) return undefined + return home.sessions().find((session) => session.id === id) + }) + function submit() { const text = prompt().trim() if (!text) return - if (!home.activeProject()) return + if (!project()) return home.startNewAgent(text) setPrompt("") } + function chooseProject() { + const conn = home.focusedServer() + if (!conn) return + pickDirectory({ + server: conn, + title: "打开项目", + multiple: true, + onSelect: (result) => home.addProjects(conn, home.homeProjectDirectories(result)), + }) + } + + function openFullSession() { + const id = homeSessionId() + const active = project() + if (!id || !active) return + const session = home.sessions().find((item) => item.id === id) + if (session) home.openSession(session, true) + } + return (
-
-
-
- {BRAND.productName} -
-
-

- {BRAND.welcomeTitle} -

-

- {BRAND.welcomeSubtitle} -

-
-
-
+ } + > + home.startNewAgent()} />} + keyed + > + {(session) => ( +
+
+

+ 会话预览 · {session.title?.trim() || "未命名会话"} +

+ + 在完整会话中继续 + +
+ +
+ )} +
+
- Add a project folder from the sidebar to start chatting. + 请先从侧栏添加项目文件夹。
} >