openyida 2026.9.11 → 2026.9.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +7 -5
  2. package/bin/yida.js +1 -0
  3. package/lib/ai/ai.js +2 -0
  4. package/lib/app/canvas-compile.js +29 -26
  5. package/lib/app/compile.js +39 -1
  6. package/lib/app/services/canvas-page-compiler.js +3 -3
  7. package/lib/app/update-app.js +17 -2
  8. package/lib/asset/ai-image.js +37 -65
  9. package/lib/asset/asset-cmd.js +151 -110
  10. package/lib/asset/asset-plan.js +105 -0
  11. package/lib/asset/asset-resolve.js +343 -225
  12. package/lib/asset/asset-status.js +20 -31
  13. package/lib/asset/attachment-upload.js +29 -0
  14. package/lib/asset/host-capabilities.js +91 -0
  15. package/lib/asset/image-metadata.js +104 -0
  16. package/lib/asset/url-verify.js +90 -130
  17. package/lib/core/agent-capabilities.js +11 -0
  18. package/lib/core/command-manifest.js +25 -11
  19. package/lib/core/locales/en.js +6 -2
  20. package/lib/core/locales/zh.js +6 -2
  21. package/lib/design-plan/materialize.js +7 -9
  22. package/lib/design-plan/normalize.js +11 -1
  23. package/lib/process/services/process-actions.js +145 -0
  24. package/lib/process/services/process-compiler.js +4 -30
  25. package/lib/process/services/process-view-verifier.js +31 -0
  26. package/package.json +1 -1
  27. package/scripts/postinstall.js +1 -1
  28. package/yida-skills/SKILL.md +3 -2
  29. package/yida-skills/skills/yida-app/SKILL.md +1 -1
  30. package/yida-skills/skills/yida-app/workflow/step-2-design.md +11 -1
  31. package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +21 -1
  32. package/yida-skills/skills/yida-app/workflow/step-5-seed-records.md +11 -7
  33. package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +4 -0
  34. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +5 -3
  35. package/yida-skills/skills/yida-canvas-custom-page/references/component-library-guide.md +1 -1
  36. package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +3 -27
  37. package/yida-skills/skills/yida-canvas-table-form/SKILL.md +1 -1
  38. package/yida-skills/skills/yida-create-app/SKILL.md +4 -4
  39. package/yida-skills/skills/yida-create-process/SKILL.md +2 -0
  40. package/yida-skills/skills/yida-custom-page/references/assets-guide.md +9 -12
  41. package/yida-skills/skills/yida-design/SKILL.md +1 -0
  42. package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +4 -4
  43. package/yida-skills/skills/yida-design/references/asset-workflow.md +14 -53
  44. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-compact-schema.md +1 -1
  45. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +21 -4
  46. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/render_build_plan.py +1 -2
  47. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/scripts/validate_design_themes.py +1 -1
  48. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/README.md +1 -1
  49. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-media-grid.md +1 -1
  50. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-modular-clarity.md +1 -1
  51. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/airy-structured-clarity.md +1 -1
  52. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ambient-halo-layered.md +1 -1
  53. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-focus-layered.md +1 -1
  54. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/dark-luminous-modular.md +1 -1
  55. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/deep-stage-duotone.md +1 -1
  56. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/hairline-runway-clarity.md +1 -1
  57. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/high-contrast-modular.md +1 -1
  58. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/interlocked-vivid-modules.md +1 -1
  59. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/matrix-grid-focus.md +1 -1
  60. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/media-rail-inspector.md +1 -1
  61. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mist-layered-signal.md +1 -1
  62. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/modular-rail-signal.md +1 -1
  63. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/mono-grid-signal.md +2 -2
  64. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/ribbon-ledger-lift.md +1 -1
  65. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/segmented-meter-clarity.md +1 -1
  66. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/split-rail-precision.md +1 -1
  67. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/templates/design-themes/status-framed-media-grid.md +1 -1
  68. package/yida-skills/skills/yida-design/workflow/output-design.md +4 -0
  69. package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +1 -1
  70. package/yida-skills/skills/yida-design/workflow/step-5-visual-states.md +2 -1
  71. package/yida-skills/skills/yida-image-assets/SKILL.md +91 -0
  72. package/yida-skills/skills/yida-image-assets/references/manifest-contract.md +87 -0
  73. package/yida-skills/skills/yida-image-assets/references/source-policy.md +19 -0
  74. package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +1 -1
  75. package/yida-skills/skills/yida-prd/workflow/output-prd.md +2 -2
  76. package/yida-skills/skills/yida-process-rule/SKILL.md +6 -1
  77. package/yida-skills/skills/yida-process-rule/references/approval-actions.md +48 -0
  78. package/yida-skills/skills/yida-publish-page/SKILL.md +5 -4
  79. package/yida-skills/skills/yida-rechart/SKILL.md +1 -1
  80. package/yida-skills/skills/yida-requirement-analysis/SKILL.md +1 -1
  81. package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +22 -13
  82. package/yida-skills/skills-index.json +18 -1
@@ -8,6 +8,7 @@ const { t } = require('./i18n');
8
8
  const { buildCommandManifest } = require('./command-manifest');
9
9
  const { buildEnvironmentSnapshot } = require('./env');
10
10
  const coreUtils = require('./utils');
11
+ const { buildHostAssetCapabilities } = require('../asset/host-capabilities');
11
12
  const { findProjectRoot, getAuthStatus } = coreUtils;
12
13
 
13
14
  function redactLogin(login) {
@@ -127,6 +128,10 @@ function buildRuntimeSnapshot(projectRoot) {
127
128
  };
128
129
  }
129
130
 
131
+ function buildAssetCapabilities(runtime, env = process.env) {
132
+ return buildHostAssetCapabilities({ runtime, env });
133
+ }
134
+
130
135
  const BUILDER_CORE_COMMAND_IDS = Object.freeze([
131
136
  'agent-capabilities',
132
137
  'commands',
@@ -461,6 +466,7 @@ function buildFullAppArtifactRoute(manifest) {
461
466
  orchestrator_skill_id: workflow.orchestrator_skill_id,
462
467
  requirement_analysis_skill_id: workflow.requirement_analysis_skill_id,
463
468
  requirement_brief_path: workflow.requirement_brief_path,
469
+ optional_asset_branch: workflow.optional_asset_branch,
464
470
  design_mode_policy: workflow.design_mode_policy,
465
471
  navigation_policy: workflow.navigation_policy,
466
472
  plan_command_ids: workflow.plan_command_ids,
@@ -572,6 +578,7 @@ function buildAgentCapabilitiesSummary() {
572
578
  const skillsDiagnostics = compactSkillsDiagnostics(buildSkillsDiagnosticsCompat(projectResolution));
573
579
  const loginStatus = getAuthStatus({ projectRoot, includeSecrets: false });
574
580
  const runtime = buildRuntimeSnapshot(projectRoot);
581
+ const assetCapabilities = buildAssetCapabilities(runtime);
575
582
  const builderPath = compactBuilderPath(
576
583
  buildBuilderPath(loginStatus, projectRoot, manifest, process.env, runtime)
577
584
  );
@@ -598,6 +605,7 @@ function buildAgentCapabilitiesSummary() {
598
605
  full_app_artifact_route: buildFullAppArtifactRoute(manifest),
599
606
  application_entry_policy: applicationEntryPolicy,
600
607
  runtime,
608
+ asset_capabilities: assetCapabilities,
601
609
  builder_path: builderPath,
602
610
  };
603
611
  }
@@ -615,6 +623,7 @@ function buildAgentCapabilities() {
615
623
  const skillsDiagnostics = buildSkillsDiagnosticsCompat(projectResolution);
616
624
  const loginStatus = redactLogin(getAuthStatus({ projectRoot, includeSecrets: false }));
617
625
  const runtime = buildRuntimeSnapshot(projectRoot);
626
+ const assetCapabilities = buildAssetCapabilities(runtime);
618
627
  const builderPath = buildBuilderPath(loginStatus, projectRoot, manifest, process.env, runtime);
619
628
  const applicationEntryPolicy = buildApplicationEntryPolicy(builderPath, process.env);
620
629
  const manifestDigest = buildCommandManifestDigest(manifest);
@@ -633,6 +642,7 @@ function buildAgentCapabilities() {
633
642
  system: envSnapshot.system,
634
643
  active: envSnapshot.active,
635
644
  runtime,
645
+ asset_capabilities: assetCapabilities,
636
646
  login: loginStatus,
637
647
  application_entry_policy: applicationEntryPolicy,
638
648
  recommended: {
@@ -710,6 +720,7 @@ module.exports = {
710
720
  buildAgentCapabilities,
711
721
  buildAgentCapabilitiesSummary,
712
722
  buildApplicationEntryPolicy,
723
+ buildAssetCapabilities,
713
724
  compactBuilderPath,
714
725
  buildCommandManifestDigest,
715
726
  buildIdentity,
@@ -422,10 +422,10 @@ const COMMAND_SIDE_EFFECTS = new Map([
422
422
  })],
423
423
 
424
424
  ['asset', sideEffect('mixed', {
425
- mutates_yida: false,
425
+ mutates_yida: true,
426
426
  mutates_local: true,
427
- read_actions: ['status', 'generate', 'resolve'],
428
- mutating_actions: ['resolve --upload-assets', 'resolve --mirror-external'],
427
+ read_actions: ['status', 'sources', 'resolve --offline'],
428
+ mutating_actions: ['resolve', 'resolve --manifest', 'resolve --upload-assets'],
429
429
  })],
430
430
 
431
431
  ...sideEffectEntries([
@@ -757,7 +757,7 @@ const COMMAND_PERMISSIONS = new Map([
757
757
  ...permissionEntries([
758
758
  'asset',
759
759
  ], actionDependentPermission({
760
- preauthorized_actions: ['generate', 'resolve --upload-assets', 'resolve --mirror-external'],
760
+ preauthorized_actions: ['resolve', 'resolve --manifest', 'resolve --upload-assets'],
761
761
  })),
762
762
 
763
763
  ...permissionEntries([
@@ -1251,7 +1251,7 @@ const COMMAND_GROUPS = [
1251
1251
  command('create-page', ['create-page'], 'create-page <appType> "<name>" [--mode dashboard] [--hide-nav] [--locale zh_CN|en_US|ja_JP] [--open|--no-open]', 'help.cmd_create_page'),
1252
1252
  command('build-page', ['build-page'], 'build-page <sourceFile> [--output file|--write]', 'help.cmd_build_page', { requiresLogin: false }),
1253
1253
  command('check-page', ['check-page'], 'check-page <src> [--compat]', 'help.cmd_check_page', { output: 'text|json' }),
1254
- command('compile', ['compile'], 'compile <src>', 'help.cmd_compile', { requiresLogin: false }),
1254
+ command('compile', ['compile'], 'compile <src> [--canvas] [--json]', 'help.cmd_compile', { requiresLogin: false, output: 'text|json' }),
1255
1255
  command('publish', ['publish'], 'publish <src> <appType> <formUuid> [--health-check] [--force] [--canvas] [--auto-nav-order] [--open|--no-open]', 'help.cmd_publish'),
1256
1256
  command('update-form-config', ['update-form-config'], 'update-form-config <appType> <formUuid> <true|false|keep> "<title>" [--locale zh_CN|en_US|ja_JP]', 'help.cmd_update_form_config'),
1257
1257
  command('get-form-config', ['get-form-config'], 'get-form-config <appType> <formUuid> [--json]', 'help.cmd_get_form_config', { output: 'json' }),
@@ -1292,8 +1292,15 @@ const COMMAND_GROUPS = [
1292
1292
  id: 'process',
1293
1293
  titleKey: 'help.group_process',
1294
1294
  commands: [
1295
- command('configure-process', ['configure-process'], 'configure-process <appType> <formUuid> <definition> [processCode] [--replace]', 'help.cmd_configure_process'),
1296
- command('create-process', ['create-process'], 'create-process <appType> ... [--replace]', 'help.cmd_create_process'),
1295
+ command('configure-process', ['configure-process'], 'configure-process <appType> <formUuid> <definition> [processCode] [--replace]', 'help.cmd_configure_process', {
1296
+ examples: ['openyida configure-process APP_XXX FORM_XXX .cache/openyida/process/process-with-actions.json'],
1297
+ }),
1298
+ command('create-process', ['create-process'], 'create-process <appType> ... [--replace]', 'help.cmd_create_process', {
1299
+ examples: [
1300
+ 'openyida create-process APP_XXX "Approval Form" .cache/openyida/process/fields.json .cache/openyida/process/process-with-actions.json',
1301
+ 'openyida create-process APP_XXX --formUuid FORM_XXX .cache/openyida/process/process-with-actions.json',
1302
+ ],
1303
+ }),
1297
1304
  command('ai-form-setting', ['ai-form-setting'], 'ai-form-setting <get|fields|models|enable|disable|save> <appType> ...', 'help.cmd_ai_form_setting', {
1298
1305
  output: 'json',
1299
1306
  aliases: ['ai-approve', 'aiFormSetting'],
@@ -1433,7 +1440,7 @@ const COMMAND_GROUPS = [
1433
1440
  command('ai', ['ai'], 'ai <text|image> [options]', 'help.cmd_ai', {
1434
1441
  output: 'text|json',
1435
1442
  }),
1436
- command('asset', ['asset'], 'asset <status|resolve|generate> [options]', 'help.cmd_asset', {
1443
+ command('asset', ['asset'], 'asset <status|resolve|sources> [options]', 'help.cmd_asset', {
1437
1444
  requiresLogin: false,
1438
1445
  output: 'text|json',
1439
1446
  }),
@@ -1539,6 +1546,13 @@ function summarizeLocalizedCommands(commands) {
1539
1546
  default_ui_guidance_skill_id: 'yida-design',
1540
1547
  requirement_analysis_skill_id: 'yida-requirement-analysis',
1541
1548
  requirement_brief_path: '.cache/openyida/<project>/requirement-brief.json',
1549
+ optional_asset_branch: {
1550
+ skill_id: 'yida-image-assets',
1551
+ trigger: 'Any page is imageNeed=required, or imageNeed=beneficial with declared image slots.',
1552
+ output_path: 'prd/<project>/asset-manifest.json',
1553
+ skip: 'All pages are imageNeed=none.',
1554
+ dependency_policy: 'Only blocks pages that consume the declared image slots.',
1555
+ },
1542
1556
  artifact_generation: {
1543
1557
  mode: 'parallel',
1544
1558
  audience: 'internal',
@@ -1555,10 +1569,10 @@ function summarizeLocalizedCommands(commands) {
1555
1569
  plan_command_ids: ['design-plan.init', 'design-plan.preview', 'design-plan.materialize', 'design-plan.patch'],
1556
1570
  theme_command_ids: ['sample', 'create-app', 'update-app'],
1557
1571
  navigation_command_ids: { platform: ['update-app', 'nav-group'], custom: ['update-app', 'update-form-config', 'get-form-config'] },
1558
- navigation_policy: 'Before PRD planning, reuse explicit navigation choices. During first-time intake, ask only when unknown: platform-l-shape, platform-top, platform-side, custom, with neutral descriptions and no preselection. For custom navigation also settle side/top/mixed/dock. Store type/source/reason/variant in the brief. Custom navigation requires app and per-page navigation hiding with readback; layout and navigation tone are separate choices.',
1559
- design_mode_policy: 'Analyze requirements first. First-time builds include new apps and existing apps without business pages. Reuse detailed supplied plans; if the mode is unspecified, ask whether to prepare a PRD for confirmation (Plan, more detailed and slower) or build from the supplied requirements (Fast). Without detailed requirements, offer Fast and Plan neutrally. Confirm unresolved navigation, custom navigation layout, visual style and page scope before planning or resource creation. Existing business apps only clarify the current change. A named resource-only request with explicitScope.allowInferredResources=false skips unrelated navigation and visual questions. Standard Plan uses design-plan init parallelTasks, then executes the returned materialize.command exactly once with business-file and visual-file; preview and --from-preview are only for explicitly incremental large plans. Confirm the displayed revision before building, and do not materialize or patch again after confirmation.',
1572
+ navigation_policy: 'Before PRD planning, reuse explicit navigation choices. During first-time intake, ask only when ownership is unknown and offer exactly two neutral options with no preselection: "宜搭原生导航" (使用宜搭自带的导航菜单。) and "自定义导航" (在自定义页面里定制菜单的样式和操作方式,替代宜搭自带的导航菜单。). Native means platform navigation. The agent chooses layout from module count, hierarchy, switching frequency, content width and target device; do not ask layout or presentation-style questions. Prefer top for few shallow modules and wide content, side for many frequently accessed modules, L-shaped/mixed for business domains with child modules, and custom dock for lightweight mobile entries. Preserve user-specified layouts. Store resolved type/source/reason/variant in the brief, distinguishing user ownership from agent layout reasoning. Platform navigation uses update-app --layout top|side|l_shape --show-app-nav, which writes layoutDirection=top|side|l_shape and hideAppNav=n. Preserve stored navType unchanged; do not derive or invent it and do not add a --nav-type option. Without --layout, normalize stored legacy layoutDirection and navType using the yc-utils application rules (hoz plus top_side means l_shape; missing layout uses top_fold/top_side for top/l_shape). Shell page-level top_fold/none overrides do not belong in app settings. Custom side/top/mixed/dock is a page layout and requires app and per-page navigation hiding with readback. Verify persisted layoutDirection and hideAppNav; updatedFields and themeVerification do not prove navigation persistence. Navigation tone is separate from layout.',
1573
+ design_mode_policy: 'Analyze requirements first. First-time builds include new apps and existing apps without business pages. Reuse detailed supplied plans; if the mode is unspecified, ask whether to prepare a PRD for confirmation (Plan, more detailed and slower) or build from the supplied requirements (Fast). Without detailed requirements, offer Fast and Plan neutrally. Confirm unresolved navigation ownership, visual style and page scope before planning or resource creation; the agent determines navigation layout from the business context without a layout question. Existing business apps only clarify the current change. A named resource-only request with explicitScope.allowInferredResources=false skips unrelated navigation and visual questions. Standard Plan uses design-plan init parallelTasks, then executes the returned materialize.command exactly once with business-file and visual-file; preview and --from-preview are only for explicitly incremental large plans. Confirm the displayed revision before building, and do not materialize or patch again after confirmation.',
1560
1574
  product_design_policy: 'yida-requirement-analysis owns shared facts and first-time intake. yida-prd owns business planning; yida-design owns visual design. Reuse the same confirmed brief and supplied details. Prepare business and base visuals concurrently, then bind visuals to settled page tasks. yida-app merges and checks the artifacts before creating resources. Plan uses the compact authoring contract and selected theme context; the CLI reads full templates and renders all artifacts.',
1561
- ui_guidance_policy: 'Page implementation consumes yida-prd prd.md for positioning, information architecture, page prototype, native form entry policy, material strategy, business-specific checks, resource creation order, page implementation delivery order, navigation order, and acceptance criteria; it consumes yida-design design.md for app custom theme CSS delivery, themeColor/navTheme, visual states, visualScaffold, surface material, rounded rules, density rules, components, and state styling. prd.md and design.md are the only design sources of truth. Page implementation may extract page-spec.json as a derived implementation handoff from prd.md + design.md; conflicts are resolved by sending business conflicts to yida-prd and visual conflicts to yida-design before regenerating the spec. Core normal forms default to 1-3 business sample records before page implementation, followed by query readback. An explicit opt-out, configuration dictionary, sensitive data, or lack of safely constructible values requires a recorded skip reason. Screenshots, public sharing, data-source deep binding, and fine navigation grouping are optional after explicit user request or PRD acceptance criteria.',
1575
+ ui_guidance_policy: 'Page implementation consumes yida-prd prd.md for positioning, information architecture, page prototype, native form entry policy, material strategy, business-specific checks, resource creation order, page implementation delivery order, navigation order, and acceptance criteria; it consumes yida-design design.md for app custom theme CSS delivery, themeColor/navTheme, visual states, visualScaffold, surface material, rounded rules, density rules, components, state styling, and page-level imageNeed. When a page is imageNeed=required, or beneficial with declared image slots, yida-image-assets prepares a traceable asset manifest before that page is implemented; imageNeed=none skips the branch. prd.md and design.md remain the only design sources of truth. Page implementation may extract page-spec.json as a derived implementation handoff from prd.md + design.md; conflicts are resolved by sending business conflicts to yida-prd and visual conflicts to yida-design before regenerating the spec. Core normal forms default to 1-3 business sample records before page implementation, followed by query readback. An explicit opt-out, configuration dictionary, sensitive data, or lack of safely constructible values requires a recorded skip reason. Screenshots, public sharing, data-source deep binding, and fine navigation grouping are optional after explicit user request or PRD acceptance criteria.',
1562
1576
  default_nav_order_policy: 'For custom navigation, implement PRD navigation order in the custom shell and verify app/page navigation hiding; skip platform nav-group ordering. For platform navigation: after the primary page is successfully published, perform exactly one navigation order operation. If the PRD names a navigation order, publish without --auto-nav-order and then call openyida nav-group order <appType> <items...>. If the PRD only gives broad groups or is missing navigation order, use openyida publish ... --auto-nav-order and do not call nav-group order or auto-order afterward. Explicit and automatic ordering are mutually exclusive; never generate per-item move loops. The fallback priority is portal/home/workbench entry > business handling > data management > business analytics > system configuration.',
1563
1577
  final_link_policy: 'Return exactly one user-visible application entry group, never one artifact or link card per form, process, report, schema, manifest, PRD, design file, or intermediate artifact. The group always contains the app workbench URL. Add the clean custom page URL only when PRD entryMode=standalone and get-form-config readback confirms isRenderNav=false. Add the admin URL only when agent-capabilities application_entry_policy.entries.admin=include. Summarize other business resources in prose by default, but every resource count, seed-record count, and completed/verified claim must come from that resource\'s own successful result or readback. When the user or caller explicitly requests a resource manifest, resource UUIDs/IDs, publish status, or test-data summary, include one concise verified delivery manifest in the final prose and do not substitute link cards for it.',
1564
1578
  application_entry_policy: {
@@ -64,8 +64,8 @@ module.exports = {
64
64
  cmd_corp_manager: 'Manage platform admins and address book permissions',
65
65
  cmd_agent_center: 'Manage process and departure delegation',
66
66
  group_process: 'Process',
67
- cmd_configure_process: 'Configure and publish process rules',
68
- cmd_create_process: 'Create process form (all-in-one)',
67
+ cmd_configure_process: 'Configure and publish process rules; Supports append/forward via JSON nodes[].actions.normalActions/appendActions',
68
+ cmd_create_process: 'Create process form (all-in-one); Supports append/forward via JSON nodes[].actions.normalActions/appendActions',
69
69
  cmd_ai_form_setting: 'Manage process form AI approval prompts',
70
70
  cmd_process_preview: 'Preview process instance (visual flowchart)',
71
71
  group_share: 'Page Config & Sharing',
@@ -2070,3 +2070,7 @@ module.exports.connector_e2e = {
2070
2070
  test_contract_unverified: 'Connector testing did not prove the controlled fixture, marker, ownership, and auth runtime contract.',
2071
2071
  action_mutated: 'Connector testing mutated the persisted action definition.',
2072
2072
  };
2073
+
2074
+ Object.assign(module.exports.process_errors || (module.exports.process_errors = {}), {
2075
+ action_config_invalid: 'Invalid approval action configuration for node {0}: {1}',
2076
+ });
@@ -64,8 +64,8 @@ module.exports = {
64
64
  cmd_corp_manager: '管理平台管理员与通讯录权限',
65
65
  cmd_agent_center: '管理流程代理和离职代理',
66
66
  group_process: '流程',
67
- cmd_configure_process: '配置并发布流程规则',
68
- cmd_create_process: '创建流程表单(一体化)',
67
+ cmd_configure_process: '配置并发布流程规则; 支持加签/转交,配置位于 JSON nodes[].actions.normalActions/appendActions',
68
+ cmd_create_process: '创建流程表单(一体化); 支持加签/转交,配置位于 JSON nodes[].actions.normalActions/appendActions',
69
69
  cmd_ai_form_setting: '管理流程表单 AI 审批提示',
70
70
  cmd_process_preview: '预览流程实例(可视化流程图)',
71
71
  group_share: '页面配置 & 分享',
@@ -2024,3 +2024,7 @@ module.exports.connector_e2e = {
2024
2024
  test_contract_unverified: '连接器测试未能证明受控 fixture、标记、归属和鉴权运行时契约。',
2025
2025
  action_mutated: '连接器测试改变了持久化动作定义。',
2026
2026
  };
2027
+
2028
+ Object.assign(module.exports.process_errors || (module.exports.process_errors = {}), {
2029
+ action_config_invalid: '节点 {0} 的审批动作配置无效:{1}',
2030
+ });
@@ -590,14 +590,11 @@ function renderPageApplications(applications) {
590
590
 
591
591
  function assetSummary(assetStrategy) {
592
592
  const strategy = assetStrategy || {};
593
- const values = [];
594
- for (const [key, value] of Object.entries(strategy)) {
595
- if (key === 'missingAssets' || key === 'notes' || value === undefined || value === null || value === '') {
596
- continue;
597
- }
598
- values.push(`${key}: ${text(value)}`);
599
- }
600
- return values.join(';') || '无现成品牌素材';
593
+ const pages = Array.isArray(strategy.pages) ? strategy.pages : [];
594
+ return pages.map((page) => {
595
+ const slotCount = Array.isArray(page.slots) ? page.slots.length : 0;
596
+ return `${page.pageId || '未命名页面'}: ${page.imageNeed || 'none'},${slotCount} 个槽位`;
597
+ }).join(';') || '无图片需求';
601
598
  }
602
599
 
603
600
  function componentReferences(markdown) {
@@ -653,7 +650,7 @@ function renderDesign(plan) {
653
650
  PRODUCT_TOPOLOGY_APPLICATION: text(forDesignMd.productTopologyApplication),
654
651
  PAGE_PATTERN_SUMMARY: renderPagePatterns(plan.pages || {}),
655
652
  PAGE_APPLICATIONS: renderPageApplications(forUser.pageApplications),
656
- BRAND_ASSETS: assetSummary(forUser.assetStrategy),
653
+ PAGE_IMAGE_NEEDS: assetSummary(forUser.assetStrategy),
657
654
  ASSET_GAPS: text((forUser.assetStrategy || {}).missingAssets, '无'),
658
655
  };
659
656
  let output = template.replace(/\{\{([A-Z_]+)\}\}/g, (match, key) => {
@@ -746,6 +743,7 @@ function renderDesign(plan) {
746
743
  }
747
744
  output = output.replace(/^---\n/, () => [
748
745
  '---',
746
+ `assetStrategy: ${JSON.stringify({ pages: [], ...forUser.assetStrategy })}`,
749
747
  `themeProfile: ${JSON.stringify({ themeColor: primaryColor, navTheme: navigationStyle.tone, ...navigationConfig(plan), logoSource: 'appIcon', themeDelivery: 'app-custom-theme-file' })}`,
750
748
  `sceneRecipes: ${JSON.stringify(scenes)}`,
751
749
  `components: ${JSON.stringify(references.components)}`,
@@ -302,7 +302,17 @@ function ensureVisual(plan) {
302
302
  forUser.stateSummary = forUser.stateSummary || '默认、悬停、按下、聚焦、禁用、加载、空态、错误、无权限和选中状态保持一致。';
303
303
  forUser.responsiveSummary = forUser.responsiveSummary || '响应式断点、重排和触控规则保持统一,并维持页面主任务顺序。';
304
304
  forUser.iconSummary = forUser.iconSummary || '图标风格、尺寸和语义保持一致。';
305
- forUser.assetStrategy = forUser.assetStrategy || { materialStatus: 'none', missingAssets: [], notes: '不得编造素材 URL。' };
305
+ forUser.assetStrategy = forUser.assetStrategy || {
306
+ materialStatus: 'none',
307
+ pages: list((plan.pages || {}).customPageDetails).map(page => ({
308
+ pageId: page.pageId,
309
+ imageNeed: 'none',
310
+ reason: '无图片槽位。',
311
+ slots: [],
312
+ })),
313
+ missingAssets: [],
314
+ notes: '需要图片时使用 yida-image-assets。',
315
+ };
306
316
  forUser.designMdReady = forUser.designMdReady || 'materialize 将从所选完整主题模板生成 design.md。';
307
317
 
308
318
  const forDesignMd = visual.forDesignMd = visual.forDesignMd || {};
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+
3
+ const { buildYidaI18n } = require('../../core/yida-i18n');
4
+ const { t } = require('../../core/i18n');
5
+
6
+ function buildActions() {
7
+ const actionDefs = [
8
+ { action: 'agree', zh: '同意', en: 'Agree', ja: '同意', hidden: false },
9
+ { action: 'disagree', zh: '拒绝', en: 'Disagree', ja: '拒否', hidden: false },
10
+ { action: 'save', zh: '保存', en: 'Save', ja: '保存', hidden: true },
11
+ { action: 'forward', zh: '转交', en: 'Forward', ja: '転送', hidden: true },
12
+ { action: 'append', zh: '加签', en: 'Append', ja: '承認者を追加', hidden: true },
13
+ { action: 'return', zh: '退回', en: 'Return', ja: '差し戻し', hidden: true },
14
+ ];
15
+
16
+ return actionDefs.map(function (def) {
17
+ return {
18
+ hidden: def.hidden,
19
+ name: buildYidaI18n(def.zh, { en_US: def.en, ja_JP: def.ja }),
20
+ action: def.action,
21
+ text: buildYidaI18n(def.zh, { en_US: def.en, ja_JP: def.ja }),
22
+ alias: buildYidaI18n(def.zh, { en_US: def.en, ja_JP: def.ja }),
23
+ };
24
+ });
25
+ }
26
+
27
+ function invalid(nodeName, field) {
28
+ const error = new TypeError(t('process_errors.action_config_invalid', nodeName, field));
29
+ error.code = 'PROCESS_COMPILE_ACTION_CONFIG_INVALID';
30
+ error.details = { nodeName, field };
31
+ throw error;
32
+ }
33
+
34
+ function isObject(value) {
35
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
36
+ }
37
+
38
+ function clone(value) {
39
+ return JSON.parse(JSON.stringify(value));
40
+ }
41
+
42
+ /** Merge partial button settings without changing unspecified permissions. */
43
+ function normalizeActions(value, field, nodeName, complete = false) {
44
+ const defaults = buildActions();
45
+ if (value === undefined) { return defaults; }
46
+ if (!Array.isArray(value)) { invalid(nodeName, field); }
47
+ const seen = new Set();
48
+ const overrides = value.map(action => {
49
+ if (!isObject(action) || typeof action.action !== 'string' || !action.action.trim()
50
+ || (!complete && !['agree', 'disagree', 'save', 'forward', 'append', 'return', 'recall'].includes(action.action))
51
+ || seen.has(action.action) || (action.hidden !== undefined && typeof action.hidden !== 'boolean')) {
52
+ invalid(nodeName, field);
53
+ }
54
+ seen.add(action.action);
55
+ return clone(action);
56
+ });
57
+ const merge = (base, override) => {
58
+ const result = { ...base, ...override };
59
+ if (override && override.name !== undefined && override.alias === undefined) { result.alias = clone(override.name); }
60
+ if (override && override.alias !== undefined && override.name === undefined) { result.name = clone(override.alias); }
61
+ return result;
62
+ };
63
+ // Raw processProps arrays are complete lists, not partial overrides. Do not add
64
+ // agree/disagree permissions to a legacy list that intentionally omitted them.
65
+ if (complete) {
66
+ // NewProcessAdapter treats an omitted hidden field in a raw action as visible.
67
+ return overrides.map(item => merge({ ...defaults.find(action => action.action === item.action), hidden: false }, item));
68
+ }
69
+ const result = defaults.map(action => merge(action, overrides.find(item => item.action === action.action)));
70
+ // Native versions may include the additional recall button.
71
+ overrides.filter(item => item.action === 'recall').forEach(item => result.push({ hidden: true, ...item }));
72
+ return result;
73
+ }
74
+
75
+ /**
76
+ * Compile one shared action contract into designer and engine representations.
77
+ * Native node.actions takes precedence; legacy processProps lists remain accepted.
78
+ * Missing append settings use the same defaults as ActionSetter; invalid explicit
79
+ * settings fail before publishing. No config retains historical hidden defaults.
80
+ * @param {object|undefined} config native normalActions / appendActions overrides
81
+ * @param {object} rawProps legacy approver process properties
82
+ * @param {string} nodeName diagnostic node name
83
+ * @returns {{viewActions: object, processProps: object}} independent JSON values
84
+ */
85
+ function buildNodeActions(config, rawProps = {}, nodeName = '') {
86
+ if (config !== undefined && (!isObject(config)
87
+ || Object.keys(config).some(key => !['normalActions', 'appendActions'].includes(key)))) {
88
+ invalid(nodeName, 'actions');
89
+ }
90
+ const input = config || {};
91
+ const normalActions = normalizeActions(input.normalActions === undefined ? rawProps.actions : input.normalActions,
92
+ 'actions.normalActions', nodeName, input.normalActions === undefined);
93
+ const appendActions = normalizeActions(input.appendActions === undefined ? rawProps.appendActions : input.appendActions,
94
+ 'actions.appendActions', nodeName, input.appendActions === undefined);
95
+ const append = normalActions.find(item => item.action === 'append') || { hidden: true };
96
+ const appendedAppend = appendActions.find(item => item.action === 'append') || { hidden: true };
97
+ const processProps = {};
98
+
99
+ // These engine fields are node-wide: the designer derives them from normalActions.
100
+ if (!appendedAppend.hidden && append.hidden) {
101
+ invalid(nodeName, 'actions.normalActions.append.hidden');
102
+ }
103
+ if (!append.hidden) {
104
+ if (append.appendPosition === undefined) {
105
+ append.appendPosition = rawProps.moldList === undefined ? ['BEFORE_APPEND'] : clone(rawProps.moldList);
106
+ }
107
+ if (append.appendResult === undefined) {
108
+ if (rawProps.isConsiderAppendedAction !== undefined && typeof rawProps.isConsiderAppendedAction !== 'boolean') {
109
+ invalid(nodeName, 'processProps.isConsiderAppendedAction');
110
+ }
111
+ append.appendResult = rawProps.isConsiderAppendedAction === undefined
112
+ ? 'valid' : (rawProps.isConsiderAppendedAction === true ? 'valid' : 'invalid');
113
+ }
114
+ if (!Array.isArray(append.appendPosition) || append.appendPosition.length === 0
115
+ || append.appendPosition.some(position => !['BEFORE_APPEND', 'AFTER_APPEND'].includes(position))
116
+ || new Set(append.appendPosition).size !== append.appendPosition.length) {
117
+ invalid(nodeName, 'actions.normalActions.append.appendPosition');
118
+ }
119
+ if (!['valid', 'invalid'].includes(append.appendResult)) {
120
+ invalid(nodeName, 'actions.normalActions.append.appendResult');
121
+ }
122
+ Object.assign(processProps, {
123
+ allowTaskAppend: true,
124
+ moldList: clone(append.appendPosition),
125
+ isConsiderAppendedAction: append.appendResult === 'valid',
126
+ isNeedEndTaskGroupChain: append.appendResult === 'valid',
127
+ });
128
+ } else if (['allowTaskAppend', 'moldList', 'isConsiderAppendedAction', 'isNeedEndTaskGroupChain']
129
+ .some(key => rawProps[key] !== undefined)) {
130
+ // Clear stale engine flags when the action is explicitly disabled.
131
+ Object.assign(processProps, {
132
+ allowTaskAppend: false, moldList: [], isConsiderAppendedAction: false, isNeedEndTaskGroupChain: false,
133
+ });
134
+ }
135
+ const viewActions = { normalActions, appendActions };
136
+ // Retain existing return settings in both representations.
137
+ ['realBackOriginator', 'triggerRule', 'backScope'].forEach(key => {
138
+ if (rawProps[key] !== undefined) { viewActions[key] = clone(rawProps[key]); }
139
+ });
140
+ processProps.actions = clone(normalActions);
141
+ processProps.appendActions = clone(appendActions);
142
+ return { viewActions, processProps };
143
+ }
144
+
145
+ module.exports = { buildNodeActions };
@@ -8,6 +8,7 @@
8
8
 
9
9
  const { buildYidaI18n } = require('../../core/yida-i18n');
10
10
  const { t } = require('../../core/i18n');
11
+ const { buildNodeActions } = require('./process-actions');
11
12
 
12
13
  const KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*$/;
13
14
 
@@ -649,29 +650,6 @@ function buildFriendlyApproverConfig(node, approver) {
649
650
  return null;
650
651
  }
651
652
 
652
- // ── 构建审批/抄送动作列表 ────────────────────────────
653
-
654
- function buildActions() {
655
- const actionDefs = [
656
- { action: 'agree', zh: '同意', en: 'Agree', ja: '同意', hidden: false },
657
- { action: 'disagree', zh: '拒绝', en: 'Disagree', ja: '拒否', hidden: false },
658
- { action: 'save', zh: '保存', en: 'Save', ja: '保存', hidden: true },
659
- { action: 'forward', zh: '转交', en: 'Forward', ja: '転送', hidden: true },
660
- { action: 'append', zh: '加签', en: 'Append', ja: '承認者を追加', hidden: true },
661
- { action: 'return', zh: '退回', en: 'Return', ja: '差し戻し', hidden: true },
662
- ];
663
-
664
- return actionDefs.map(function (def) {
665
- return {
666
- hidden: def.hidden,
667
- name: i18n(def.zh, def.en, def.ja),
668
- action: def.action,
669
- text: i18n(def.zh, def.en, def.ja),
670
- alias: i18n(def.zh, def.en, def.ja),
671
- };
672
- });
673
- }
674
-
675
653
  // ── 构建条件规则(严格匹配宜搭真实格式)─────────────
676
654
 
677
655
  function buildConditionRules(rules, logic) {
@@ -1162,10 +1140,9 @@ function buildApprovalNode(node, nextNodeId, nameToIdMap, nodeType) {
1162
1140
  formConfig = { behaviorList: behaviorList };
1163
1141
  }
1164
1142
 
1143
+ const nodeActions = buildNodeActions(node.actions, approverConfig.processProps, displayName);
1165
1144
  const processNodeProps = {
1166
1145
  conditionalMode: 'conditional',
1167
- actions: buildActions(),
1168
- appendActions: buildActions(),
1169
1146
  openDigitalSign: false,
1170
1147
  noActionersType: 'stopProcess',
1171
1148
  routeRule: routeRule,
@@ -1173,7 +1150,7 @@ function buildApprovalNode(node, nextNodeId, nameToIdMap, nodeType) {
1173
1150
  if (approverConfig.approvals !== undefined) {
1174
1151
  processNodeProps.approvals = approverConfig.approvals;
1175
1152
  }
1176
- Object.assign(processNodeProps, approverConfig.processProps);
1153
+ Object.assign(processNodeProps, approverConfig.processProps, nodeActions.processProps);
1177
1154
  let multiApproverRules;
1178
1155
  if (approvalNodeType === 'multiApproval') {
1179
1156
  const multiMode = node.approvalMode
@@ -1220,10 +1197,7 @@ function buildApprovalNode(node, nextNodeId, nameToIdMap, nodeType) {
1220
1197
  nodeName: componentName,
1221
1198
  name: i18n(displayName, titleText[1], displayName),
1222
1199
  description: nodeDescription,
1223
- actions: {
1224
- normalActions: buildActions(),
1225
- appendActions: buildActions(),
1226
- },
1200
+ actions: nodeActions.viewActions,
1227
1201
  routeRule: routeRule,
1228
1202
  };
1229
1203
  if (multiApproverRules) {
@@ -125,6 +125,7 @@ function verifyPlatformView(response, expectedViewJson, expectedFormUuid) {
125
125
  actual: actualNodes,
126
126
  });
127
127
  }
128
+ verifyActionSettings(expectedViewJson.schema.children, actualView.schema.children, errors);
128
129
  return {
129
130
  verificationLevel: errors.length === 0 ? 'PLATFORM_VIEW_VERIFIED' : 'PUBLISHED_UNVERIFIED',
130
131
  valid: errors.length === 0,
@@ -133,6 +134,36 @@ function verifyPlatformView(response, expectedViewJson, expectedFormUuid) {
133
134
  };
134
135
  }
135
136
 
137
+ /** Compare permissions and append semantics, tolerating platform-added metadata. */
138
+ function verifyActionSettings(expectedNodes, actualNodes, errors, parentPath = '') {
139
+ (expectedNodes || []).forEach((node, index) => {
140
+ const actual = (actualNodes || [])[index];
141
+ const path = parentPath + '/' + index;
142
+ const expectedActions = node.props && node.props.actions;
143
+ const actualActions = actual && actual.props && actual.props.actions;
144
+ if (isPlainObject(expectedActions)) {
145
+ ['normalActions', 'appendActions'].forEach(group => {
146
+ if (!Array.isArray(expectedActions[group])) { return; }
147
+ expectedActions[group].forEach(action => {
148
+ const matches = actualActions && Array.isArray(actualActions[group])
149
+ ? actualActions[group].filter(item => item && item.action === action.action) : [];
150
+ const found = matches[0];
151
+ const keys = ['hidden'];
152
+ if (group === 'normalActions' && action.action === 'append' && !action.hidden) {
153
+ keys.push('appendPosition', 'appendResult');
154
+ }
155
+ if (matches.length !== 1 || keys.some(key => JSON.stringify(action[key]) !== JSON.stringify(found[key]))) {
156
+ errors.push({ code: 'PROCESS_PLATFORM_VIEW_ACTION_MISMATCH', path, group, action: action.action });
157
+ }
158
+ });
159
+ });
160
+ }
161
+ if (Array.isArray(node.children)) {
162
+ verifyActionSettings(node.children, actual && actual.children, errors, path);
163
+ }
164
+ });
165
+ }
166
+
136
167
  module.exports = {
137
168
  extractPlatformView,
138
169
  verifyPlatformView,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "2026.9.11",
3
+ "version": "2026.9.13",
4
4
  "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
6
  "openyida": "bin/yida.js",
@@ -311,7 +311,7 @@ openyida copy
311
311
  | --- | --- | --- |
312
312
  | \`yida-skills/context\` | 登录、退出、组织信息、Schema、fieldId、只读预检 | \`yida-login\`, \`yida-logout\`, \`yida-basic-info\`, \`yida-get-schema\`, \`yida-corp-efficiency\` |
313
313
  | \`yida-skills/app\` | 从零搭应用、导航、多语言 | \`yida-app\`, \`yida-create-app\`, \`yida-nav-group\`, \`yida-i18n\` |
314
- | \`yida-skills/design\` | 完整应用需求分析、产品 PRD、单页 UI 改造、主页面视觉设计、应用主题色、全局换肤 | \`yida-requirement-analysis\`, \`yida-prd\`, \`yida-design\` |
314
+ | \`yida-skills/design\` | 完整应用需求分析、产品 PRD、单页 UI 改造、主页面视觉设计、应用主题色、全局换肤、图片素材 | \`yida-requirement-analysis\`, \`yida-prd\`, \`yida-design\`, \`yida-image-assets\` |
315
315
  | \`yida-skills/form\` | 表单字段、公式、校验、业务规则、批量录入、数据记录 | \`yida-create-form-page\`, \`yida-formula\`, \`yida-formula-evaluate\`, \`yida-business-rule\`, \`yida-canvas-table-form\`, \`yida-table-form\`, \`yida-data-management\` |
316
316
  | \`yida-skills/process\` | 审批、流程表单、流程规则、代理人 | \`yida-create-process\`, \`yida-process-rule\`, \`yida-agent-center\` |
317
317
  | \`yida-skills/page\` | 自定义展示页、YidaCodeCanvas 组件、历史平台 JSX 组件页面维护、发布、导航壳、PPT | \`yida-create-page\`, \`yida-canvas-custom-page\`, \`yida-custom-page\`, \`yida-canvas-data-binding\`, \`yida-canvas-upgrade\`, \`yida-publish-page\`, \`yida-openyida-publish-guard\`, \`yida-density\`, \`yida-nav-shell\`, \`yida-ppt-slider\` |
@@ -101,7 +101,7 @@ description: >
101
101
  | --- | --- | --- |
102
102
  | `yida-skills/context` | 登录、退出、切换组织、组织版本/容量、Schema、fieldId、执行前检查 | `yida-login`、`yida-logout`、`yida-basic-info`、`yida-get-schema`、`yida-corp-efficiency` |
103
103
  | `yida-skills/app` | 从零搭应用、完整系统、应用启停、应用导航、多语言 | `yida-app`、`yida-create-app`、`yida-app-lifecycle`、`yida-nav-group`、`yida-i18n` |
104
- | `yida-skills/design` | 完整应用需求分析、PRD、视觉设计、单页 UI 改造、应用主题色、全局换肤 | `yida-requirement-analysis`、`yida-prd`、`yida-design` |
104
+ | `yida-skills/design` | 完整应用需求分析、PRD、视觉设计、单页 UI 改造、应用主题色、全局换肤、图片素材 | `yida-requirement-analysis`、`yida-prd`、`yida-design`、`yida-image-assets` |
105
105
  | `yida-skills/form` | 表单字段、公式、校验、业务关联规则、批量录入、数据记录 | `yida-create-form-page`、`yida-formula`、`yida-formula-evaluate`、`yida-business-rule`、`yida-canvas-table-form`、`yida-table-form`、`yida-data-management` |
106
106
  | `yida-skills/process` | 审批、流程表单、流程规则、节点/分支/字段权限、流程代理 | `yida-create-process`、`yida-process-rule`、`yida-agent-center` |
107
107
  | `yida-skills/page` | 自定义展示页、页面源码开发、平台 JSX 组件页面维护、页面发布、页面内导航、PPT 页面 | `yida-create-page`、`yida-canvas-custom-page`、`yida-custom-page`、`yida-canvas-data-binding`、`yida-canvas-upgrade`、`yida-publish-page`、`yida-openyida-publish-guard`、`yida-density`、`yida-nav-shell`、`yida-ppt-slider` |
@@ -128,7 +128,7 @@ description: >
128
128
  | 创建自定义展示页资源 | `yida-create-page`,之后交给 `yida-canvas-custom-page` 编写页面源码,再交给 `yida-publish-page` 发布 |
129
129
  | 开发表单字段结构 / 增删改字段 | 使用 `yida-create-form-page` 落地字段结构 |
130
130
  | 创建带审批的流程表单 | `yida-create-process` |
131
- | 修改已有流程节点/分支/字段权限 | `yida-process-rule` |
131
+ | 修改已有流程节点/分支/字段权限,配置加签/转交操作权限 | `yida-process-rule`;加签/转交先读其 `references/approval-actions.md` |
132
132
  | 查字段 ID / 保存 Schema 证据 | `yida-get-schema`;凡涉及 fieldId 的数据、流程、公式、页面代码先取证 |
133
133
  | 改表单数据记录 | `yida-data-management`,不是 `yida-create-form-page` |
134
134
  | 配字段默认值、计算、校验 | `yida-formula`;静态检查用 `yida-formula-evaluate` |
@@ -145,6 +145,7 @@ description: >
145
145
  | 批量录入、表格填写、多行编辑 | 默认 `yida-canvas-table-form`;已检测到平台 JSX 组件页面、native 页面或存量源码使用 `this.utils.yida.saveFormData` 时用 `yida-table-form` |
146
146
  | 页面视觉方向、页面美化、去 AI 味 | `yida-design` 只产出或更新 `prd/<项目名>/design.md`;若业务/页面契约也变化,由 `yida-prd` 更新 `prd.md`;实现阶段默认交给 `yida-canvas-custom-page` |
147
147
  | 应用级主题、品牌色、全局换肤 | `yida-design` |
148
+ | 图片需求、搜图、生图 | `yida-image-assets`;完整应用按 `design.md.assetStrategy` 加载 |
148
149
  | 平台左侧导航树分组/排序 | `yida-nav-group` |
149
150
  | 应用导航隐藏后自绘导航壳 | `yida-nav-shell` |
150
151
  | 普通报表/统计 | `yida-report` |
@@ -64,7 +64,7 @@ Plan 模式只读取 `workflow/step-1-resource-context.md`、`workflow/step-2-de
64
64
  | 4 | [创建或更新业务资源](workflow/step-4-forms-processes.md) | 执行 `use_skill("yida-create-form-page")`;按 PRD 执行 `use_skill("yida-create-process")`、`use_skill("yida-get-schema")`、`use_skill("yida-report")` 和 `use_skill("yida-integration")` | 真实 `formUuid`、`processCode`、必要 `fieldId/reportId` |
65
65
  | 5 | [写入初始表单数据](workflow/step-5-seed-records.md) | 执行 `use_skill("yida-data-management")`,为核心普通表单写入 1-3 条业务化 seed records 并 query 抽查 | 真实表单记录或明确跳过原因 |
66
66
  | 6 | [创建或复用主页面](workflow/step-6-main-page.md) | 已有 display 页面直接复用;缺少主页面且允许创建时执行 `use_skill("yida-create-page")` | 真实主页面 `formUuid` |
67
- | 7 | [编写或更新页面](workflow/step-7-page-code.md) | 执行 `use_skill("yida-canvas-custom-page")`;看板/工作台/驾驶舱必须再执行 `use_skill("yida-dashboard")`,读取表单数据时必须执行 `use_skill("yida-canvas-data-binding")` | 页面源码和真实 dataBinding 通过校验 |
67
+ | 7 | [编写或更新页面](workflow/step-7-page-code.md) | 按 `design.md.assetStrategy` 加载素材、页面、看板和数据绑定技能 | 素材状态明确;页面源码和 dataBinding 通过校验 |
68
68
  | 8 | [发布页面并排序导航](workflow/step-8-publish-navigation.md) | 执行 `use_skill("yida-publish-page")`,发布本轮源码到主页面并执行轻量导航排序 | 已发布主页面 URL |
69
69
  | 9 | [输出与收尾](workflow/step-9-output-finish.md) | 核对完成条件,按业务语言输出结果 | 2-3 句业务总结 + 一组应用访问入口 |
70
70
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## 2.0 先分析并确认需求
10
10
 
11
- 调用 `yida-requirement-analysis`,按 [需求分析与首次搭建确认](../../yida-requirement-analysis/workflow/prepare-brief.md) 整理来源、复用资源及用户已有计划,在同一轮一次性确认尚未明确的 Fast / Plan、业务模块、页面与表单、导航归属与布局及风格。导航选择直接包含平台或自定义及具体布局,并按 [导航选项说明](../../yida-requirement-analysis/workflow/prepare-brief.md#导航选项说明) 备注平台原生布局与自定义页面实现的区别,“自定义导航”大类不标记“推荐”;已选自定义顶部时,呈现样式默认推荐浮导,用户明确的其他样式优先;后续阶段复用答案。首次搭建在必要回答写回、`intake.confirmed=true` 后继续。
11
+ 调用 `yida-requirement-analysis`,按 [需求分析与首次搭建确认](../../yida-requirement-analysis/workflow/prepare-brief.md) 整理来源、复用资源及用户已有计划,在同一轮一次性确认尚未明确的 Fast / Plan、业务模块、页面与表单、导航归属及风格。导航 `ask_human` 仅提供“宜搭原生导航”和“自定义导航”两个选项,原生导航就是平台导航,并按 [导航选项说明](../../yida-requirement-analysis/workflow/prepare-brief.md#导航选项说明) 备注实现方式,不标记“推荐”。顶部、侧边、L 型等布局在用户所选归属内根据场景确定,不询问布局或样式;用户已明确的要求优先,后续阶段复用归属选择和布局依据。首次搭建在必要回答写回、`intake.confirmed=true` 后继续。
12
12
 
13
13
  回答齐全后直接保存内部需求记录并进入 2.1;不把“生成需求简报”列为独立任务,不再扩写或展示简报请用户确认。已有确认记录且需求未变化时直接复用。记录粒度、保存和校验规则统一遵守上述需求分析流程。
14
14
 
@@ -50,6 +50,16 @@ Plan 分支从已加载 `yida-app` 的 Available Files 读取精确路径 `workf
50
50
 
51
51
  校验未通过时 Step 2 未完成,不得进入资源创建。校验通过后,后续页面实现以 `prd.md` 和 `design.md` 为准;`page-spec.json` 只用于把要求传给页面实现阶段。
52
52
 
53
+ ## 2.4 条件式素材分支
54
+
55
+ `design.md.assetStrategy` 按页面分级:
56
+
57
+ - 商品目录、品牌、营销和媒体展示页通常为 `required`;
58
+ - 门户、工作台、档案、知识库和引导页有图片槽位时为 `beneficial`;
59
+ - 表单、审批、台账、设置和库存流水默认 `none`。
60
+
61
+ 存在 `required` 或带槽位的 `beneficial` 时启动 `yida-image-assets`,先选图并保存 `prd/<项目名>/manifest-draft.json`。宜搭附件上传需要真实 `appType`;已有应用可直接上传,否则等 Step 3 创建应用后再落地,不因缺少 appType 阻塞应用创建。素材只阻塞使用它的页面;Step 7 通过 `--design design.md --app-type <真实appType>` 上传并校验,以输出清单的页面级状态判断能否继续。
62
+
53
63
  ## 主题文件实现指令
54
64
 
55
65
  在设计中确定配色、导航明暗和布局。未禁止 `theme-file` 时,Plan 使用 CLI 返回的 `outputs.theme`,Fast 按 [主题文件生成与更新](../../yida-design/workflow/output-design.md#cli-token-契约fast--plan-共用) 准备主题 CSS;用户确认计划或主题后即可启动 CSS 生成,不等待表单或页面开发。Plan 已生成当前版本的 CSS 时直接复用。拿到真实 appType 后,在应用级配置同一份主题文件,与表单创建和页面开发并行;页面组件按已确认契约消费主题 token。若 `theme-file` 被禁止,跳过生成、复制、修改和上传,沿用现有平台主题并在交付中说明未更改主题。详见 [主题与业务资源的依赖](parallel-work.md#主题与业务资源的依赖)。