openyida 2026.9.2-1 → 2026.9.2-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/lib/app/nav-group.js +221 -53
  2. package/lib/app/publish.js +13 -1
  3. package/lib/core/agent-capabilities.js +38 -1
  4. package/lib/core/cli-error.js +6 -1
  5. package/lib/core/command-manifest.js +33 -5
  6. package/lib/core/locales/en.js +3 -0
  7. package/lib/core/locales/zh.js +4 -1
  8. package/lib/core/query-data.js +126 -8
  9. package/package.json +1 -1
  10. package/scripts/postinstall.js +3 -3
  11. package/yida-skills/SKILL.md +8 -8
  12. package/yida-skills/skills/yida-app/SKILL.md +8 -7
  13. package/yida-skills/skills/yida-app/references/common-issues.md +1 -1
  14. package/yida-skills/skills/yida-app/workflow/step-2-design.md +31 -14
  15. package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +2 -1
  16. package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +2 -2
  17. package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +26 -11
  18. package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +21 -10
  19. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +5 -5
  20. package/yida-skills/skills/yida-canvas-custom-page/references/canvas-style-implementation-guide.md +1 -1
  21. package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +1 -1
  22. package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +4 -4
  23. package/yida-skills/skills/yida-create-app/SKILL.md +4 -4
  24. package/yida-skills/skills/yida-create-form-page/SKILL.md +2 -2
  25. package/yida-skills/skills/yida-create-form-page/references/form-field-properties.md +1 -1
  26. package/yida-skills/skills/yida-custom-page/SKILL.md +4 -4
  27. package/yida-skills/skills/yida-custom-page/references/coding-guide.md +1 -1
  28. package/yida-skills/skills/yida-custom-page/references/design-system.md +1 -1
  29. package/yida-skills/skills/yida-data-management/SKILL.md +6 -4
  30. package/yida-skills/skills/yida-data-management/references/data-format-guide.md +13 -5
  31. package/yida-skills/skills/yida-design/SKILL.md +34 -37
  32. package/yida-skills/skills/yida-design/references/page-quality-gates.md +2 -2
  33. package/yida-skills/skills/yida-design/references/style-design-selection.md +2 -2
  34. package/yida-skills/skills/yida-design/references/theme/theme-token-presets.md +1 -1
  35. package/yida-skills/skills/yida-design/references/visual-scaffold-recipes.md +1 -1
  36. package/yida-skills/skills/yida-design/sub_skill/page-design/SKILL.md +4 -5
  37. package/yida-skills/skills/yida-design/workflow/output-design.md +22 -2
  38. package/yida-skills/skills/yida-design/workflow/output-prd.md +3 -1
  39. package/yida-skills/skills/yida-design/workflow/step-1-read-brief.md +14 -0
  40. package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +8 -8
  41. package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +3 -3
  42. package/yida-skills/skills/yida-design/workflow/step-5-visual-states.md +4 -4
  43. package/yida-skills/skills/yida-design/workflow/step-6-handoff.md +22 -55
  44. package/yida-skills/skills/yida-nav-group/SKILL.md +3 -1
  45. package/yida-skills/skills/yida-page-config/SKILL.md +3 -1
  46. package/yida-skills/skills/yida-prd/SKILL.md +48 -0
  47. package/yida-skills/skills/yida-prd/references/app/blueprint.md +73 -0
  48. package/yida-skills/skills/yida-prd/references/app/navigation-patterns.md +43 -0
  49. package/yida-skills/skills/yida-prd/references/app/role-journey.md +30 -0
  50. package/yida-skills/skills/yida-prd/workflow/output-prd.md +190 -0
  51. package/yida-skills/skills/yida-prd/workflow/step-1-read-brief.md +15 -0
  52. package/yida-skills/skills/{yida-design/workflow/step-3-information-architecture.md → yida-prd/workflow/step-2-information-architecture.md} +2 -2
  53. package/yida-skills/skills/yida-publish-page/SKILL.md +1 -1
  54. package/yida-skills/skills/yida-requirement-analysis/SKILL.md +49 -0
  55. package/yida-skills/skills-index.json +32 -9
  56. package/yida-skills/skills/yida-design/workflow/step-1-positioning.md +0 -58
@@ -7,6 +7,7 @@ const {
7
7
  requestWithAutoLogin,
8
8
  } = require('../core/utils');
9
9
  const { createAuthRef, isAuthRefReady } = require('../core/yida-client');
10
+ const { CliError } = require('../core/cli-error');
10
11
  const { parseYidaI18n } = require('../core/yida-i18n');
11
12
 
12
13
  const ROOT_NAV_UUID = 'NAV-SYSTEM-PARENT-UUID';
@@ -422,6 +423,92 @@ function autoOrderRootNodes(list) {
422
423
  };
423
424
  }
424
425
 
426
+ function snapshotNavigationRoots(roots) {
427
+ const snapshot = [];
428
+
429
+ const visit = (nodes, parentNavUuid) => {
430
+ (nodes || []).forEach((node, siblingIndex) => {
431
+ snapshot.push({
432
+ navUuid: node.navUuid,
433
+ parentNavUuid,
434
+ siblingIndex,
435
+ navType: node.navType || '',
436
+ });
437
+ visit(node.children || [], node.navUuid);
438
+ });
439
+ };
440
+
441
+ visit(roots || [], ROOT_NAV_UUID);
442
+ return snapshot;
443
+ }
444
+
445
+ function buildNavigationSnapshot(list) {
446
+ return snapshotNavigationRoots(buildMutableForest(list).roots);
447
+ }
448
+
449
+ function navigationSnapshotsEqual(left, right) {
450
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
451
+ return false;
452
+ }
453
+
454
+ return left.every((item, index) => {
455
+ const other = right[index];
456
+ return other &&
457
+ item.navUuid === other.navUuid &&
458
+ item.parentNavUuid === other.parentNavUuid &&
459
+ item.siblingIndex === other.siblingIndex &&
460
+ item.navType === other.navType;
461
+ });
462
+ }
463
+
464
+ function findFirstNavigationDifference(actual, expected) {
465
+ const actualItems = Array.isArray(actual) ? actual : [];
466
+ const expectedItems = Array.isArray(expected) ? expected : [];
467
+ const length = Math.max(actualItems.length, expectedItems.length);
468
+
469
+ for (let index = 0; index < length; index++) {
470
+ const actualItem = actualItems[index] || null;
471
+ const expectedItem = expectedItems[index] || null;
472
+ if (!actualItem || !expectedItem ||
473
+ actualItem.navUuid !== expectedItem.navUuid ||
474
+ actualItem.parentNavUuid !== expectedItem.parentNavUuid ||
475
+ actualItem.siblingIndex !== expectedItem.siblingIndex ||
476
+ actualItem.navType !== expectedItem.navType) {
477
+ return {
478
+ index,
479
+ actual: actualItem,
480
+ expected: expectedItem,
481
+ };
482
+ }
483
+ }
484
+
485
+ return null;
486
+ }
487
+
488
+ function summarizeNavigationOrderDiff(code, beforeOrder, expectedOrder, observedOrder) {
489
+ const hasObservedOrder = Array.isArray(observedOrder);
490
+ return {
491
+ beforeCount: Array.isArray(beforeOrder) ? beforeOrder.length : 0,
492
+ expectedCount: Array.isArray(expectedOrder) ? expectedOrder.length : 0,
493
+ observedCount: hasObservedOrder ? observedOrder.length : null,
494
+ firstPlannedChange: findFirstNavigationDifference(beforeOrder, expectedOrder),
495
+ ...(code === 'NAV_ORDER_READBACK_MISMATCH' && hasObservedOrder
496
+ ? { firstReadbackMismatch: findFirstNavigationDifference(observedOrder, expectedOrder) }
497
+ : {}),
498
+ };
499
+ }
500
+
501
+ function buildNavigationOrderPlan(list, ordered) {
502
+ const beforeOrder = buildNavigationSnapshot(list);
503
+ const expectedOrder = snapshotNavigationRoots(ordered.roots);
504
+ return {
505
+ beforeOrder,
506
+ expectedOrder,
507
+ targetNavUuids: ordered.orderedNavUuids.slice(),
508
+ changed: !navigationSnapshotsEqual(beforeOrder, expectedOrder),
509
+ };
510
+ }
511
+
425
512
  function countChildren(list, navUuid) {
426
513
  return (list || []).filter((node) => node.parentNavUuid === navUuid).length;
427
514
  }
@@ -455,7 +542,7 @@ async function fetchNavigationList(appType, authRef) {
455
542
  return Array.isArray(result.content) ? result.content : [];
456
543
  }
457
544
 
458
- async function postNavAction(appType, action, payload, authRef) {
545
+ async function postNavAction(appType, action, payload, authRef, options = {}) {
459
546
  const endpoints = {
460
547
  save: 'saveFormNavigation.json',
461
548
  update: 'updateFormNavigation.json',
@@ -475,21 +562,143 @@ async function postNavAction(appType, action, payload, authRef) {
475
562
  throw new Error(`Unknown nav action: ${action}`);
476
563
  }
477
564
 
478
- const result = await requestWithAutoLogin((auth) => {
565
+ const requestFn = (auth) => {
479
566
  const path = `/dingtalk/web/${appType}/query/formnav/${endpoint}` +
480
567
  `?_api=${encodeURIComponent(apiNames[action])}` +
481
568
  '&_mock=false' +
482
569
  `&_stamp=${Date.now()}`;
483
570
  return httpPost(auth.baseUrl, path, querystring.stringify(payload));
484
- }, authRef);
571
+ };
572
+ const result = options.oneShot === true
573
+ ? await requestFn(authRef)
574
+ : await requestWithAutoLogin(requestFn, authRef);
485
575
 
486
- if (!result || result.success === false) {
576
+ if (!result || result.success === false || result.__needLogin || result.__csrfExpired) {
487
577
  throw new Error(result ? result.errorMsg || `Nav action failed: ${action}` : `Nav action failed: ${action}`);
488
578
  }
489
579
 
490
580
  return result;
491
581
  }
492
582
 
583
+ function buildNavigationOrderSuccess(appType, action, ordered, observedList, plan, recoveredByReadback) {
584
+ const orderedNodes = ordered.orderedNavUuids.map((navUuid) => normalizeNavNode(
585
+ resolveNode(observedList, navUuid, { excludeSystem: true })
586
+ ));
587
+ return {
588
+ success: true,
589
+ appType,
590
+ action,
591
+ targetParentNavUuid: ROOT_NAV_UUID,
592
+ changed: plan.changed,
593
+ alreadyApplied: !plan.changed,
594
+ mutationPerformed: plan.changed,
595
+ readbackVerified: true,
596
+ recoveredByReadback: recoveredByReadback === true,
597
+ sideEffectState: plan.changed ? 'committed' : 'none',
598
+ orderedNavUuids: ordered.orderedNavUuids,
599
+ orderedNodes,
600
+ skipped: !plan.changed,
601
+ };
602
+ }
603
+
604
+ function buildNavigationOrderError(code, appType, action, plan, observedOrder, cause) {
605
+ const sideEffectState = code === 'NAV_ORDER_NOT_APPLIED' ? 'none' : 'unknown';
606
+ const retrySafe = code === 'NAV_ORDER_NOT_APPLIED';
607
+ const readbackVerified = code === 'NAV_ORDER_NOT_APPLIED';
608
+ const status = code === 'NAV_ORDER_NOT_APPLIED'
609
+ ? 'NOT_APPLIED'
610
+ : code === 'NAV_ORDER_READBACK_MISMATCH'
611
+ ? 'SEMANTIC_FAILURE'
612
+ : 'RESULT_UNKNOWN';
613
+ return new CliError(code, {
614
+ code,
615
+ details: {
616
+ target: {
617
+ type: 'navigationOrder',
618
+ appType,
619
+ action,
620
+ navUuids: plan.targetNavUuids,
621
+ },
622
+ changed: plan.changed,
623
+ ...(code === 'NAV_ORDER_NOT_APPLIED' ? { mutationPerformed: false } : {}),
624
+ readbackVerified,
625
+ retryable: retrySafe,
626
+ retrySafe,
627
+ sideEffectState,
628
+ status,
629
+ readbackAllowed: true,
630
+ orderDiff: summarizeNavigationOrderDiff(
631
+ code,
632
+ plan.beforeOrder,
633
+ plan.expectedOrder,
634
+ observedOrder
635
+ ),
636
+ cause: cause && cause.message ? cause.message : undefined,
637
+ nextStep: `openyida nav-group list ${appType} --flat`,
638
+ },
639
+ });
640
+ }
641
+
642
+ async function applyNavigationOrder(appType, action, list, ordered, authRef, dependencies = {}) {
643
+ const fetchList = dependencies.fetchNavigationList || fetchNavigationList;
644
+ const postAction = dependencies.postNavAction || postNavAction;
645
+ const plan = buildNavigationOrderPlan(list, ordered);
646
+
647
+ if (!plan.changed) {
648
+ return buildNavigationOrderSuccess(appType, action, ordered, list, plan, false);
649
+ }
650
+
651
+ const firstNode = resolveNode(list, ordered.orderedNavUuids[0], { excludeSystem: true });
652
+ let mutationError = null;
653
+ try {
654
+ await postAction(appType, 'updateOrderNew', {
655
+ currentId: firstNode.id,
656
+ parentNavUuid: ROOT_NAV_UUID,
657
+ navType: firstNode.navType,
658
+ ids: ordered.ids.join(','),
659
+ }, authRef, { oneShot: true });
660
+ } catch (error) {
661
+ mutationError = error;
662
+ }
663
+
664
+ let updatedList;
665
+ try {
666
+ updatedList = await fetchList(appType, authRef);
667
+ } catch (readbackError) {
668
+ throw buildNavigationOrderError(
669
+ 'NAV_ORDER_RESULT_UNKNOWN',
670
+ appType,
671
+ action,
672
+ plan,
673
+ null,
674
+ readbackError
675
+ );
676
+ }
677
+
678
+ const observedOrder = buildNavigationSnapshot(updatedList);
679
+ if (navigationSnapshotsEqual(observedOrder, plan.expectedOrder)) {
680
+ return buildNavigationOrderSuccess(appType, action, ordered, updatedList, plan, !!mutationError);
681
+ }
682
+ if (navigationSnapshotsEqual(observedOrder, plan.beforeOrder)) {
683
+ throw buildNavigationOrderError(
684
+ 'NAV_ORDER_NOT_APPLIED',
685
+ appType,
686
+ action,
687
+ plan,
688
+ observedOrder,
689
+ mutationError
690
+ );
691
+ }
692
+ throw buildNavigationOrderError(
693
+ 'NAV_ORDER_READBACK_MISMATCH',
694
+ appType,
695
+ action,
696
+ plan,
697
+ observedOrder,
698
+ mutationError
699
+ );
700
+ }
701
+
493
702
  function normalizeListOutput(list, options = {}) {
494
703
  const includeSystem = options.includeSystem === true;
495
704
  return (list || [])
@@ -647,62 +856,17 @@ async function orderRootNodes(appType, values, authRef) {
647
856
 
648
857
  const list = await fetchNavigationList(appType, authRef);
649
858
  const ordered = reorderRootNodes(list, values);
650
- const firstNode = resolveNode(list, ordered.orderedNavUuids[0], { excludeSystem: true });
651
-
652
- await postNavAction(appType, 'updateOrderNew', {
653
- currentId: firstNode.id,
654
- parentNavUuid: ROOT_NAV_UUID,
655
- navType: firstNode.navType,
656
- ids: ordered.ids.join(','),
657
- }, authRef);
658
-
659
- const updatedList = await fetchNavigationList(appType, authRef);
660
- const orderedNodes = ordered.orderedNavUuids.map((navUuid) => normalizeNavNode(
661
- resolveNode(updatedList, navUuid, { excludeSystem: true })
662
- ));
663
- return {
664
- success: true,
665
- appType,
666
- action: 'order',
667
- targetParentNavUuid: ROOT_NAV_UUID,
668
- orderedNavUuids: ordered.orderedNavUuids,
669
- orderedNodes,
670
- };
859
+ return applyNavigationOrder(appType, 'order', list, ordered, authRef);
671
860
  }
672
861
 
673
862
  async function autoOrderNavigation(appType, authRef) {
674
863
  const list = await fetchNavigationList(appType, authRef);
675
864
  const ordered = autoOrderRootNodes(list);
676
865
  if (!ordered.orderedNavUuids.length) {
677
- return {
678
- success: true,
679
- appType,
680
- action: 'auto-order',
681
- targetParentNavUuid: ROOT_NAV_UUID,
682
- orderedNavUuids: [],
683
- orderedNodes: [],
684
- skipped: true,
685
- };
866
+ const plan = buildNavigationOrderPlan(list, ordered);
867
+ return buildNavigationOrderSuccess(appType, 'auto-order', ordered, list, plan, false);
686
868
  }
687
-
688
- const firstNode = resolveNode(list, ordered.orderedNavUuids[0], { excludeSystem: true });
689
- await postNavAction(appType, 'updateOrderNew', {
690
- currentId: firstNode.id,
691
- parentNavUuid: ROOT_NAV_UUID,
692
- navType: firstNode.navType,
693
- ids: ordered.ids.join(','),
694
- }, authRef);
695
-
696
- const updatedList = await fetchNavigationList(appType, authRef);
697
- const updatedOrdered = autoOrderRootNodes(updatedList);
698
- return {
699
- success: true,
700
- appType,
701
- action: 'auto-order',
702
- targetParentNavUuid: ROOT_NAV_UUID,
703
- orderedNavUuids: updatedOrdered.orderedNavUuids,
704
- orderedNodes: updatedOrdered.orderedNodes,
705
- };
869
+ return applyNavigationOrder(appType, 'auto-order', list, ordered, authRef);
706
870
  }
707
871
 
708
872
  async function setVisibility(appType, value, hidden, authRef) {
@@ -790,8 +954,11 @@ async function run(args) {
790
954
 
791
955
  module.exports = {
792
956
  ROOT_NAV_UUID,
957
+ applyNavigationOrder,
793
958
  autoOrderNavigation,
794
959
  autoOrderRootNodes,
960
+ buildNavigationOrderPlan,
961
+ buildNavigationSnapshot,
795
962
  buildI18nTitle,
796
963
  buildNavigationTree,
797
964
  compareNavigationNodes,
@@ -800,6 +967,7 @@ module.exports = {
800
967
  getNavigationPriority,
801
968
  moveNodeInTree,
802
969
  normalizeNavNode,
970
+ navigationSnapshotsEqual,
803
971
  orderRootNodes,
804
972
  parseArgs,
805
973
  reorderRootNodes,
@@ -28,7 +28,7 @@ const { createAuthRef, isTokenAuthRef } = require('../core/yida-client');
28
28
  const { requireSchemaServerRevision } = require('../core/server-revision');
29
29
  const { t } = require('../core/i18n');
30
30
  const { banner, step, label, success, fail, warn, info, error, result, usage, hint } = require('../core/chalk');
31
- const { CliError } = require('../core/cli-error');
31
+ const { CliError, isCliError, toErrorPayload } = require('../core/cli-error');
32
32
  const { compileSource } = require('./page-compiler');
33
33
  const { compileCanvas } = require('./canvas-compile');
34
34
  const { runLintCheck } = require('./page-linter');
@@ -723,6 +723,18 @@ async function runMain(argv) {
723
723
  success(t('publish.nav_order_ok', orderedNames || '-'));
724
724
  } catch (navOrderError) {
725
725
  navOrderWarning = navOrderError && navOrderError.message ? navOrderError.message : t('common.unknown_error');
726
+ navOrderResult = isCliError(navOrderError)
727
+ ? toErrorPayload(navOrderError)
728
+ : {
729
+ success: false,
730
+ errorCode: 'NAV_ORDER_RESULT_UNKNOWN',
731
+ errorMsg: navOrderWarning,
732
+ retryable: false,
733
+ retrySafe: false,
734
+ sideEffectState: 'unknown',
735
+ readbackVerified: false,
736
+ nextStep: `openyida nav-group list ${appType} --flat`,
737
+ };
726
738
  warn(t('publish.nav_order_failed', navOrderWarning));
727
739
  }
728
740
  }
@@ -446,6 +446,37 @@ function compactBuilderPath(builderPath) {
446
446
  };
447
447
  }
448
448
 
449
+ function buildFullAppArtifactRoute(manifest) {
450
+ const workflow = manifest.summary.core_workflows.full_app_build;
451
+ return {
452
+ orchestrator_skill_id: workflow.orchestrator_skill_id,
453
+ requirement_analysis_skill_id: workflow.requirement_analysis_skill_id,
454
+ requirement_brief_path: workflow.requirement_brief_path,
455
+ artifact_generation: workflow.artifact_generation,
456
+ };
457
+ }
458
+
459
+ function buildApplicationEntryPolicy(builderPath, env = process.env) {
460
+ const runtime = builderPath.runtime || {};
461
+ const managedRuntime = String(env.OPENYIDA_MANAGED_RUNTIME || '').trim().toLowerCase();
462
+ const managedCloudAgent = managedRuntime === 'cloud' ||
463
+ runtime.runtime === 'web_sandbox' ||
464
+ runtime.tool === 'mulerun';
465
+
466
+ return {
467
+ schema_version: 1,
468
+ environment: managedCloudAgent ? 'managed_cloud_agent' : 'non_cloud_agent',
469
+ delivery_unit: 'single_application_entry_group',
470
+ resource_delivery: 'summary_only',
471
+ internal_artifact_delivery: 'never',
472
+ entries: {
473
+ workbench: 'always',
474
+ custom: 'when_entry_mode_standalone_and_is_render_nav_false_readback',
475
+ admin: managedCloudAgent ? 'omit' : 'include',
476
+ },
477
+ };
478
+ }
479
+
449
480
  function canonicalize(value) {
450
481
  if (Array.isArray(value)) {
451
482
  return value.map(canonicalize);
@@ -514,6 +545,7 @@ function buildAgentCapabilitiesSummary() {
514
545
  const builderPath = compactBuilderPath(
515
546
  buildBuilderPath(loginStatus, projectRoot, manifest, process.env, runtime)
516
547
  );
548
+ const applicationEntryPolicy = buildApplicationEntryPolicy(builderPath, process.env);
517
549
 
518
550
  return {
519
551
  schema_version: 1,
@@ -532,6 +564,8 @@ function buildAgentCapabilitiesSummary() {
532
564
  command_manifest_digest_algorithm: 'sha256',
533
565
  command_count: manifest.summary.command_count,
534
566
  full_capabilities_command: 'openyida agent-capabilities --json',
567
+ full_app_artifact_route: buildFullAppArtifactRoute(manifest),
568
+ application_entry_policy: applicationEntryPolicy,
535
569
  runtime,
536
570
  builder_path: builderPath,
537
571
  };
@@ -551,6 +585,7 @@ function buildAgentCapabilities() {
551
585
  const loginStatus = redactLogin(getAuthStatus({ projectRoot, includeSecrets: false }));
552
586
  const runtime = buildRuntimeSnapshot(projectRoot);
553
587
  const builderPath = buildBuilderPath(loginStatus, projectRoot, manifest, process.env, runtime);
588
+ const applicationEntryPolicy = buildApplicationEntryPolicy(builderPath, process.env);
554
589
 
555
590
  return {
556
591
  schema_version: 1,
@@ -564,6 +599,7 @@ function buildAgentCapabilities() {
564
599
  active: envSnapshot.active,
565
600
  runtime,
566
601
  login: loginStatus,
602
+ application_entry_policy: applicationEntryPolicy,
567
603
  recommended: {
568
604
  preflight_command: 'openyida agent-capabilities --summary-json',
569
605
  full_capabilities_command: 'openyida agent-capabilities --json',
@@ -607,7 +643,7 @@ function buildAgentCapabilities() {
607
643
  ],
608
644
  retry_policy: 'Do not repeat the same failed command without changing login state, organization, parameters, files, or field IDs.',
609
645
  completion_contracts: {
610
- full_app: 'Default full-app build is complete after creating or reusing the app, core forms, primary page, publishing it, and returning access URLs. If three or more resources/links are returned, present them in a Markdown table with type, purpose/name, URL, and status.',
646
+ full_app: 'Default full-app build is complete after creating or reusing the app, core forms, primary page, publishing it, and returning one named application entry group. Summarize business resources in prose; do not deliver one artifact or link card per resource.',
611
647
  },
612
648
  full_app_data_contract: 'Default full-app page code must not call this.dataSourceMap.* unless the same run created and bound a designer data source; use this.utils.yida.* or an entry-only page by default.',
613
649
  },
@@ -638,6 +674,7 @@ async function run(args = []) {
638
674
  module.exports = {
639
675
  buildAgentCapabilities,
640
676
  buildAgentCapabilitiesSummary,
677
+ buildApplicationEntryPolicy,
641
678
  compactBuilderPath,
642
679
  buildCommandManifestDigest,
643
680
  run,
@@ -50,6 +50,10 @@ function toErrorPayload(error) {
50
50
  'deleted',
51
51
  'alreadyAbsent',
52
52
  'mutationAccepted',
53
+ 'mutationPerformed',
54
+ 'changed',
55
+ 'alreadyApplied',
56
+ 'recoveredByReadback',
53
57
  'readbackVerified',
54
58
  'status',
55
59
  ].forEach((key) => {
@@ -86,7 +90,8 @@ function shouldUseStructuredErrorOutput(error, args = []) {
86
90
  && details.retrySafe === false
87
91
  && details.sideEffectState === 'unknown'
88
92
  );
89
- return ownedPartial || mutationOutcomeUnknown;
93
+ const navigationOrderFailure = /^NAV_ORDER_/.test(error.code || '');
94
+ return ownedPartial || mutationOutcomeUnknown || navigationOrderFailure;
90
95
  }
91
96
 
92
97
  module.exports = {
@@ -1456,17 +1456,45 @@ function summarizeLocalizedCommands(commands) {
1456
1456
  core_workflows: {
1457
1457
  full_app_build: {
1458
1458
  mode: 'unified_build',
1459
+ orchestrator_skill_id: 'yida-app',
1459
1460
  trigger_phrases: ['默认方案', '不要追问', '直接创建', '尽快搭建'],
1460
1461
  default_form_visual_guidance_skill_id: 'yida-form-detail',
1461
1462
  default_page_skill_id: 'yida-canvas-custom-page',
1462
1463
  default_ui_guidance_skill_id: 'yida-design',
1464
+ requirement_analysis_skill_id: 'yida-requirement-analysis',
1465
+ requirement_brief_path: '.cache/openyida/<project>/requirement-brief.json',
1466
+ artifact_generation: {
1467
+ mode: 'parallel',
1468
+ audience: 'internal',
1469
+ user_visible_delivery: false,
1470
+ tasks: [
1471
+ { skill_id: 'yida-prd', output_path: 'prd/<project>/prd.md' },
1472
+ { skill_id: 'yida-design', output_path: 'prd/<project>/design.md' },
1473
+ ],
1474
+ join_owner_skill_id: 'yida-app',
1475
+ },
1463
1476
  ordinary_jsx_skill_id: 'yida-custom-page',
1464
1477
  form_visual_guidance_policy: 'Default form page development loads yida-form-detail as form visual guidance and merges Divider semantic grouping into field JSON. The runtime loads the same application custom theme CSS file in custom pages, forms, submissions, formDetail pages, and FormOpenContainer iframes so all contexts use aligned theme variables.',
1465
1478
  page_skill_policy: 'Use YidaCodeCanvas by default for new primary custom pages. Choose yida-custom-page only when the current target has been identified as existing .oyd.jsx/.oyb.jsx/renderJsx/platform Jsx component maintenance; do not switch to yida-custom-page merely because a page needs data, forms, uploads, this.utils.yida.*, or dataSourceMap-like capabilities.',
1466
- product_design_policy: 'Full app creation first resolves resource context, then uses yida-design for requirement analysis and product design before creating or filling missing app/form/process/page resources. yida-design outputs prd/<project>/prd.md with app information, app configuration, data structures, page functions, business rules, interaction states, resource blueprint, resource creation order, page implementation delivery order, navigation order, and acceptance criteria; it outputs prd/<project>/design.md with theme tokens, app custom theme CSS delivery, themeColor/navTheme, visual DNA, layout density, rounded rules, scene recipes, component rules, state rules, and visual acceptance checks.',
1467
- ui_guidance_policy: 'Default full-app primary page creation consumes yida-design prd.md for positioning, information architecture, page prototype, native form entry policy, material strategy, and business-specific checks; it consumes design.md for theme system, 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 regenerating the spec from prd.md/design.md. Demo data, screenshots, public sharing, data-source deep binding, and fine navigation grouping are optional after explicit user request or PRD acceptance criteria.',
1468
- default_nav_order_policy: 'After the primary page is successfully published, immediately run a lightweight navigation order update. If the PRD names a navigation order, 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 or openyida nav-group auto-order <appType> as a fallback with portal/home/workbench entry > business handling > data management > business analytics > system configuration.',
1469
- final_link_policy: 'Return exactly one primary user-facing link. For single-page create/update/publish intent, return the current page URL. For full app, form, process, permission, theme, navigation, or batch-resource work, return the app workbench URL: {base_url}/{appType}/workbench. Summarize other resources by type/name/ID/status only; do not output static asset, CDN build, locale JSON, admin, config, schema, or intermediate-file URLs unless explicitly requested.',
1479
+ product_design_policy: 'Full app creation first resolves resource context. yida-requirement-analysis writes one shared requirement brief. yida-prd and yida-design start in parallel from that brief and own prd/<project>/prd.md and prd/<project>/design.md independently; neither waits for or overwrites the other. yida-app joins both artifacts, validates their references, and only then creates or fills missing app/form/process/page resources.',
1480
+ 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. Demo data, screenshots, public sharing, data-source deep binding, and fine navigation grouping are optional after explicit user request or PRD acceptance criteria.',
1481
+ default_nav_order_policy: '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.',
1482
+ 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 unless the user explicitly asks for technical IDs.',
1483
+ application_entry_policy: {
1484
+ delivery_unit: 'single_application_entry_group',
1485
+ workbench: { include: 'always', url: '{base_url}/{appType}/workbench' },
1486
+ custom: {
1487
+ include: 'when_entry_mode_standalone_and_is_render_nav_false_readback',
1488
+ url: '{base_url}/{appType}/custom/{formUuid}',
1489
+ },
1490
+ admin: {
1491
+ include: 'follow_agent_capabilities_application_entry_policy',
1492
+ url: '{base_url}/{appType}/admin',
1493
+ },
1494
+ internal_artifacts: 'never_user_visible',
1495
+ business_resources: 'summary_only',
1496
+ },
1497
+ conditional_entry_command_ids: ['update-form-config', 'get-form-config'],
1470
1498
  required_command_ids: [
1471
1499
  'agent-capabilities',
1472
1500
  'create-app',
@@ -1496,7 +1524,7 @@ function summarizeLocalizedCommands(commands) {
1496
1524
  'yida-data-management',
1497
1525
  'yida-dashboard',
1498
1526
  ],
1499
- completion_contract: 'Resolve context, write the yida-design PRD, create or reuse app, core forms/processes, and primary page in PRD resource order, publish it, apply PRD navigation order or lightweight fallback navigation order, and return access URLs. If three or more resources/links are returned, present them in a Markdown table with type, purpose/name, URL, and status.',
1527
+ completion_contract: 'Resolve context, write the shared requirement brief, complete and join the internal parallel yida-prd and yida-design artifacts, create or reuse app, core forms/processes, and primary page in PRD resource order, publish it, apply PRD navigation order or lightweight fallback navigation order, and return one named application entry group. The group contains workbench, conditionally a verified standalone custom entry, and admin only for non-cloud agents. Business resources are summarized instead of emitted as individual delivery artifacts.',
1500
1528
  },
1501
1529
  },
1502
1530
  };
@@ -1849,6 +1849,8 @@ Options:
1849
1849
  };
1850
1850
 
1851
1851
  Object.assign(module.exports.query_data || (module.exports.query_data = {}), {
1852
+ association_derived_field_read_only: 'Derived association-form query field {0} is read-only and cannot be saved or updated. Write the source association field without the _id suffix.',
1853
+ association_value_invalid: 'The write value for association-form field {0} is invalid. Use an array of objects where every item includes appType, formUuid, formType, instanceId, and title.',
1852
1854
  command_unsupported: 'Unsupported data command: {0} {1}. Run openyida commands --json to inspect the actual capability.',
1853
1855
  delete_confirmation_required: 'Before deleting a form instance, query it, show the target summary to the user, and add --confirm only after explicit approval. No delete was performed.',
1854
1856
  delete_preflight_failed: 'Could not read form instance {0} for delete preflight. No delete was performed.',
@@ -1857,6 +1859,7 @@ Object.assign(module.exports.query_data || (module.exports.query_data = {}), {
1857
1859
  delete_result_unknown: 'The delete outcome for form instance {0} is unknown. Perform read-only inspection and do not retry the delete automatically.',
1858
1860
  delete_target_mismatch: 'Form instance {0} does not belong to target form {1}. The operation stopped before delete.',
1859
1861
  delete_target_unverified: 'Could not verify the identity and ownership of form instance {0}. The operation stopped before delete.',
1862
+ field_reference_unknown: 'Field reference {0} is not a real fieldId or component alias in the target form. The operation stopped before mutation.',
1860
1863
  form_mode_unverified: 'Could not verify the type of form {0}. Creation stopped before any data write.',
1861
1864
  instance_target_mismatch: 'Instance {0} does not belong to expected business resource {1}. The operation stopped before mutation.',
1862
1865
  instance_target_unverified: 'Could not verify the business resource ownership of instance {0}. The operation stopped before mutation.',
@@ -137,7 +137,7 @@ openyida - 宜搭命令行工具
137
137
  copy [--force] 复制 project 工作目录到当前 AI 工具环境
138
138
  login OAuth token 登录态管理
139
139
  logout 退出登录 / 切换账号
140
- create-app --name "<名称>" [--theme-file <css> 及导航布局选项] 创建应用;推荐导入新版主题,输出 appType
140
+ create-app --name "<名称>" [--theme-file <css> 及导航布局选项] 创建应用;推荐导入应用主题文件,输出 appType
141
141
  create-page <appType> "<页面名>" [--mode dashboard] [--hide-nav] 创建自定义页面,输出 pageId
142
142
  update-app <appType> [--name "..."] [--hide-app-nav|--show-app-nav] 更新应用信息
143
143
  create-form create <appType> "<表单名>" <字段JSON> [--layout <布局>] [--theme <主题>] [--label-align <对齐>] 创建表单页面
@@ -1803,6 +1803,8 @@ openyida - 宜搭命令行工具
1803
1803
  };
1804
1804
 
1805
1805
  Object.assign(module.exports.query_data || (module.exports.query_data = {}), {
1806
+ association_derived_field_read_only: '关联表单查询派生字段 {0} 只读,不能用于保存或更新。请写入不带 _id 后缀的原始关联表单字段。',
1807
+ association_value_invalid: '关联表单字段 {0} 的写入值无效。必须使用对象数组,每项完整包含 appType、formUuid、formType、instanceId 和 title。',
1806
1808
  command_unsupported: '不支持的数据命令:{0} {1}。请使用 openyida commands --json 查询真实能力。',
1807
1809
  delete_confirmation_required: '删除表单实例前必须先查询并向用户展示目标摘要,获得明确确认后追加 --confirm。未执行删除。',
1808
1810
  delete_preflight_failed: '无法读取表单实例 {0} 完成删除前校验,未执行删除。',
@@ -1811,6 +1813,7 @@ Object.assign(module.exports.query_data || (module.exports.query_data = {}), {
1811
1813
  delete_result_unknown: '表单实例 {0} 的删除结果未知。请只读回查目标,不要自动重试删除。',
1812
1814
  delete_target_mismatch: '表单实例 {0} 不属于目标表单 {1},已停止且未执行删除。',
1813
1815
  delete_target_unverified: '无法验证表单实例 {0} 的身份和归属,已停止且未执行删除。',
1816
+ field_reference_unknown: '字段引用 {0} 不是当前表单的真实 fieldId 或组件别名,已在写入前停止。',
1814
1817
  form_mode_unverified: '无法验证表单 {0} 的类型,已停止创建;未执行任何数据写入。',
1815
1818
  instance_target_mismatch: '实例 {0} 不属于期望的业务资源 {1},已在写入前停止。',
1816
1819
  instance_target_unverified: '无法验证实例 {0} 的业务资源归属,已在写入前停止。',