@tea-agent/loop-agent 0.39.0-next.6 → 0.39.0-next.8

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/CHANGELOG.md +5 -0
  2. package/dist/build-stamp.json +2 -2
  3. package/dist/worker/console/chat/turn-process.js +7 -20
  4. package/dist/worker/console/operator-user-error.js +19 -0
  5. package/dist/worker/console/security.js +20 -1
  6. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CNPzEh3R.js → abnfDiagram-N423BO3Z-BLXFe4qI.js} +1 -1
  7. package/dist/worker/console/static/assets/{arc-BDY2Y-xG.js → arc-7iOJeqho.js} +1 -1
  8. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-NfkWQpnU.js → architectureDiagram-T3A2C74G-CYoyj5K7.js} +1 -1
  9. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-KE8pmdGY.js → blockDiagram-VBNYF7ZC-fK01JNlR.js} +1 -1
  10. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-D-CheaMa.js → c4Diagram-5PPSVZJV-bzbw5D_Z.js} +1 -1
  11. package/dist/worker/console/static/assets/channel-BBOTJHvD.js +1 -0
  12. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Db2uLs2I.js → chunk-2GRJ4B5K-Cf6hJFFG.js} +1 -1
  13. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-Bv9VDtNX.js → chunk-2Q5K7J3B-DSFXR-et.js} +1 -1
  14. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CE4aFV_O.js → chunk-5RXB4S5H-BagJechl.js} +1 -1
  15. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BoUlzoKw.js → chunk-5VM5RSS4-E7QNMFaW.js} +1 -1
  16. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Bqn4bPPg.js → chunk-6Q2QTUOP-BG_eOBrC.js} +1 -1
  17. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-ClNxnCSZ.js → chunk-GF5L2VYU-Bo42dqol.js} +1 -1
  18. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-Dibdg_rY.js → chunk-JWPE2WC7-dibM-87v.js} +1 -1
  19. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BwFyCP8o.js → chunk-KBJHAD2P-Bytrp3Oe.js} +1 -1
  20. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-31vjPu53.js → chunk-RYQCIY6F-DBABILrr.js} +1 -1
  21. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CAPr_ybW.js → chunk-XXDRQBXY-DCUk5Pf9.js} +1 -1
  22. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cm8Yf8tD.js +1 -0
  23. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cm8Yf8tD.js +1 -0
  24. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-H53opjD2.js → cose-bilkent-JH36ORCC-BlGE9v6E.js} +1 -1
  25. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DA3X4LYG.js → cynefin-VYW2F7L2-DGl5ewph.js} +1 -1
  26. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-CDeEtk0O.js → cynefinDiagram-MW4NZA55-DsJ_SVH_.js} +1 -1
  27. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C_iy9nAa.js → dagre-VZM6K2ZE-BIhjFdzv.js} +1 -1
  28. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BSjR3Gvm.js → diagram-7IWD3JNH-Dmy1ctTb.js} +1 -1
  29. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CB_gyKnB.js → diagram-B4RE2ZJO-DYCtWUF3.js} +1 -1
  30. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Dk9uv1qU.js → diagram-LBJQPF4R-BMZFHVd2.js} +1 -1
  31. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-in2ro3ZT.js → diagram-Q27KOJAE-b_U8gfaO.js} +1 -1
  32. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BEI07gDu.js → diagram-UB23O5K3-Dzy_bn-I.js} +1 -1
  33. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BbF5_PGc.js → ebnfDiagram-BXEA7PRR-DJCiMNEI.js} +1 -1
  34. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-B4uWTa-w.js → erDiagram-JOGREHBK-28lD016B.js} +1 -1
  35. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DPIyLgKv.js → flowDiagram-UKHOOZJN-DGyHJmIw.js} +1 -1
  36. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CuDKirqJ.js → ganttDiagram-PKOTCBZU-CzdwUjrO.js} +1 -1
  37. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BLlH01Hh.js → gitGraphDiagram-DS77QQ5N-D9bGwuo8.js} +1 -1
  38. package/dist/worker/console/static/assets/{index-F4oI1Kcc.js → index-B-qWGThd.js} +60 -60
  39. package/dist/worker/console/static/assets/index-C-xRgY_0.css +1 -0
  40. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-1QctUUzp.js → infoDiagram-6WML65LV-LJgJUQFC.js} +1 -1
  41. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BBNAeRyb.js → ishikawaDiagram-WSZJBQD7-98LaB8zb.js} +1 -1
  42. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DFAUj-9c.js → journeyDiagram-NVQOT4AX-D-ISzwlQ.js} +1 -1
  43. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-O0PrAPYU.js → kanban-definition-27J2QSJJ-gfYOI-EL.js} +1 -1
  44. package/dist/worker/console/static/assets/{linear-COV8Syym.js → linear-CyntZgKV.js} +1 -1
  45. package/dist/worker/console/static/assets/{mermaid.core-Dudn9krq.js → mermaid.core-BBOR7dg0.js} +5 -5
  46. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DVQQWMKd.js → mindmap-definition-FAOFIHXS-CQAgwPMk.js} +1 -1
  47. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-B5PU21Bj.js → pegDiagram-VL7TDLO6-DkMiZFuV.js} +1 -1
  48. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-D0SrZrKx.js → pieDiagram-7S7Q4E2Y-DLNXa_0J.js} +1 -1
  49. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DxZmA3-n.js → quadrantDiagram-CIZ2JOQS-Dc7bdzjm.js} +1 -1
  50. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-z2bVQOmD.js → railroadDiagram-AXF67PYL-BxMB0QqW.js} +1 -1
  51. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-Cw3JACRQ.js → requirementDiagram-LRYGKXZP-FjumoKn4.js} +1 -1
  52. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BrRX95W-.js → sankeyDiagram-W5VNT64P-CkhKc_E3.js} +1 -1
  53. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-Dr4lAMIp.js → sequenceDiagram-SI44F4Z6-DoBWqTTH.js} +1 -1
  54. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-D3nWOJBl.js → sizeCapture-X5ZJPWSS-CQeh_YeE.js} +1 -1
  55. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BRzY1ow8.js → stateDiagram-OKZ733FA-LuHmFW-I.js} +1 -1
  56. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DZihfdKc.js +1 -0
  57. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-C8pcow-m.js → swimlanes-SLNWSIFB-CHwbDctY.js} +2 -2
  58. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CKnXCjHX.js +8 -0
  59. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CMdjG3fZ.js → timeline-definition-Z64GVDOM-CoMngk0X.js} +1 -1
  60. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-o4YAsgix.js → vennDiagram-T6HMQDX7-CURYbtbO.js} +1 -1
  61. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-D1DRa0xZ.js → wardleyDiagram-T6FBY63Y-BdqSECjU.js} +1 -1
  62. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DWkYcwN2.js → xychartDiagram-ELKLHX3M-DbMUYuAg.js} +1 -1
  63. package/dist/worker/console/static/index.html +2 -2
  64. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +14 -2
  65. package/dist/worker/console/static-src/operator-chat/mutation-gate.js +45 -0
  66. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +15 -0
  67. package/dist/workflows/dag/frontend-implementation-contract.js +70 -0
  68. package/dist/workflows/dag/frontend-plan-render.js +17 -0
  69. package/dist/workflows/dag/frontend-prewrite-gate.js +84 -0
  70. package/dist/workflows/dag/init-hybrid.js +39 -2
  71. package/dist/workflows/dag/types.js +10 -0
  72. package/docs/templates/frontend-implementation-contract.schema.json +3 -1
  73. package/package.json +1 -1
  74. package/skills/frontend-design-review/references/review-checklist.md +2 -0
  75. package/skills/frontend-implementation/references/design-spec.md +16 -19
  76. package/skills/frontend-implementation/references/node-contracts.md +8 -6
  77. package/dist/worker/console/static/assets/channel-CgVmTdib.js +0 -1
  78. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CfJu47mG.js +0 -1
  79. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CfJu47mG.js +0 -1
  80. package/dist/worker/console/static/assets/index-B25KrZZ6.css +0 -1
  81. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-IUSBfChT.js +0 -1
  82. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-uiU-pbpp.js +0 -8
@@ -35,6 +35,9 @@ export const frontendPrewriteFailureCodeSchema = z.enum([
35
35
  "openspec-citation-block-unparseable",
36
36
  "openspec-not-cited",
37
37
  "openspec-citation-not-read",
38
+ "component-spec-reference-invalid",
39
+ "component-spec-not-cited",
40
+ "component-choices-missing",
38
41
  "contract-too-large",
39
42
  ]);
40
43
  export const frontendPrewriteResultV1Schema = z
@@ -785,6 +788,87 @@ export async function runFrontendPrewriteGate(input) {
785
788
  }
786
789
  }
787
790
  }
791
+ // Component selection spec conformance (ADR 0016). Deterministic cross-check
792
+ // that declared component/token selections trace to the frontend spec
793
+ // (openspec/ai_workspace component + theme) instead of a model's ad-hoc
794
+ // "better" choice. Runs after the openspec read gate so citation + read
795
+ // evidence are already resolved.
796
+ const componentChoices = analysis.canonical.uiComponentChoices ?? [];
797
+ const componentSpecCandidates = input.config.componentSpecCandidatePaths ?? [];
798
+ const hasUiVisibleWork = analysis.canonical.uiStates.some((state) => state.applicable) ||
799
+ analysis.canonical.interactions.length > 0;
800
+ if (componentSpecCandidates.length > 0 &&
801
+ hasUiVisibleWork &&
802
+ componentChoices.length === 0) {
803
+ return finalizePrewrite(input, {
804
+ ...basePending,
805
+ verdict,
806
+ candidateJsonSha256: analysis.candidateJsonSha256,
807
+ normalizationActions: [],
808
+ mockStrategy,
809
+ classification: "retryable-invalid",
810
+ failureReason: "frontend prewrite gate: the canonical contract has UI-visible work but uiComponentChoices is missing or empty while the generation-frozen component/theme specification bucket is non-empty. 为每个 UI 用途声明组件选型(decision: specified | reuse-existing | new),specified 必须给 specReference.path/section/line,new 必须给 rationale 声明偏差。",
811
+ failureCode: "component-choices-missing",
812
+ openspecReadPaths,
813
+ openspecCandidatePaths: candidatePaths,
814
+ });
815
+ }
816
+ const componentSpecReferences = componentChoices
817
+ .map((choice) => ({
818
+ raw: choice.specReference?.path ?? "",
819
+ normalized: choice.specReference?.path
820
+ ? normalizeCitationPath(choice.specReference.path, candidateRoot)
821
+ : null,
822
+ }))
823
+ .filter((ref) => ref.raw.length > 0);
824
+ if (componentSpecReferences.length > 0) {
825
+ const invalidRefs = [
826
+ ...new Set(componentSpecReferences
827
+ .filter((ref) => !ref.normalized ||
828
+ !isOpenspecSpecFilePath(ref.normalized))
829
+ .map((ref) => ref.raw)),
830
+ ];
831
+ if (invalidRefs.length > 0) {
832
+ return finalizePrewrite(input, {
833
+ ...basePending,
834
+ verdict,
835
+ candidateJsonSha256: analysis.candidateJsonSha256,
836
+ normalizationActions: [],
837
+ mockStrategy,
838
+ classification: "retryable-invalid",
839
+ failureReason: `frontend prewrite gate: these uiComponentChoices specReference paths are not supported openspec/ai_workspace spec files: ${invalidRefs.join(", ")}`,
840
+ failureCode: "component-spec-reference-invalid",
841
+ openspecReadPaths,
842
+ openspecCandidatePaths: candidatePaths,
843
+ });
844
+ }
845
+ if (openspecPolicy === "cited") {
846
+ const parsedCitations = parseOpenspecCitations(planText, candidateRoot);
847
+ const citedPaths = new Set(parsedCitations.citations.map((citation) => citation.path));
848
+ const readSet = new Set(openspecReadPaths);
849
+ const uncorroborated = [
850
+ ...new Set(componentSpecReferences
851
+ .filter((ref) => !ref.normalized ||
852
+ !citedPaths.has(ref.normalized) ||
853
+ !readSet.has(ref.normalized))
854
+ .map((ref) => ref.raw)),
855
+ ];
856
+ if (uncorroborated.length > 0) {
857
+ return finalizePrewrite(input, {
858
+ ...basePending,
859
+ verdict,
860
+ candidateJsonSha256: analysis.candidateJsonSha256,
861
+ normalizationActions: [],
862
+ mockStrategy,
863
+ classification: "retryable-invalid",
864
+ failureReason: `frontend prewrite gate: these uiComponentChoices specReference paths were not cited in the plan openspec-citations block or lack a successful read event: ${uncorroborated.join(", ")}`,
865
+ failureCode: "component-spec-not-cited",
866
+ openspecReadPaths,
867
+ openspecCandidatePaths: candidatePaths,
868
+ });
869
+ }
870
+ }
871
+ }
788
872
  // Render the deterministic plan.md from the canonical contract (fail-closed
789
873
  // before any artifact is written: no contract, no writer authorization).
790
874
  let planMarkdown;
@@ -2246,6 +2246,15 @@ const openspecCitationInstruction = [
2246
2246
  '该块内每行一个 JSON 对象 {"path":"<repo 相对 openspec 路径>","section":"<命中章节或空串>","line":<int 或 null>},必须逐条列出你在本计划中实际读取并应用的每个 openspec 规范文件。',
2247
2247
  "prewrite gate 会用真实 read 事件核验每条引用:引用存在但无成功 read 事件 → openspec-citation-not-read;契约冻结的必读候选未被引用 → openspec-not-cited;两者都 fail-closed。不要引用未读取的路径。",
2248
2248
  ].join("\n");
2249
+ const frontendComponentConformanceInstruction = [
2250
+ "## Component Selection conformance (uiComponentChoices; hard rule)",
2251
+ "每个 UI 用途必须在契约的 uiComponentChoices[] 中声明组件选型:{ purpose, component, decision, specReference, rationale }。",
2252
+ "- decision=specified:前端规范(候选组件/主题桶 + 任务源显式引用)已定义该用途组件 → 必须使用该组件,并给精确 specReference { path, section, line }(path 必须是 openspec/ai_workspace 受支持规范路径)。",
2253
+ "- decision=reuse-existing:复用仓库既有组件/惯例(规范未点名)→ specReference 可为 null,rationale 说明复用的现有组件与依据。",
2254
+ "- decision=new:规范与既有代码均无合适组件 → specReference 必须为 null,rationale 必须说明偏差理由(design-review 审,最终 review 复核)。",
2255
+ "不得静默替换规范组件或自创组件而无偏差声明;spec 已定义该用途组件时不得改选其它组件。",
2256
+ "prewrite gate 确定性交叉校验:specReference.path 非法 → component-spec-reference-invalid;未在 openspec-citations 引用块中引用或未真实读取 → component-spec-not-cited;候选桶非空且契约有 UI 可见工作而 uiComponentChoices 缺失/空 → component-choices-missing。",
2257
+ ].join("\n");
2249
2258
  function resolveFrontendCapabilityContextBlock(sources) {
2250
2259
  const risk = sources.frontendRisk;
2251
2260
  const capability = sources.frontendProjectCapability;
@@ -2284,6 +2293,7 @@ function resolveFrontendCapabilityContextBlock(sources) {
2284
2293
  else {
2285
2294
  parts.push("(no openspec specification paths discovered — greenfield)");
2286
2295
  }
2296
+ parts.push("component / theme / rule.components 是「组件/主题规范」必读语义桶:规范已定义某用途组件时必须使用它(uiComponentChoices 用 decision=specified + 精确 path/section/line),不得静默替换为自认更合适的组件;无规范定义时才允许 reuse-existing 或 new(new 必须声明偏差 rationale)。这些路径在生成期冻结为 prewrite gate 的 componentSpecCandidatePaths。");
2287
2297
  parts.push("Each consuming node MUST report in its output: applicable rules, the hit path/section/line number for every applied specification, and any conflicts or missing specifications. Missing or conflicting required specifications must fail closed rather than silently substituting nearby repository conventions.");
2288
2298
  parts.push(`A11y capability: ${capability.a11y.status}` +
2289
2299
  (capability.a11y.tools.length
@@ -2492,6 +2502,16 @@ async function buildFrontendHybridDagFromTask(sources) {
2492
2502
  "",
2493
2503
  "### optional plan fields - BAD (present-but-empty strings are rejected):",
2494
2504
  '{"stylingStrategy":"","dependencyPolicy":""} <-- REJECTED: optional string fields must be non-empty when present; omit them instead',
2505
+ "",
2506
+ "### uiComponentChoices - GOOD (specified with precise spec hit):",
2507
+ '{"purpose":"primary action button","component":"Button","decision":"specified","specReference":{"path":"openspec/schemas/button.md","section":"Variants","line":12},"rationale":"spec mandates Button for primary actions"}',
2508
+ "",
2509
+ "### uiComponentChoices - GOOD (new with deviation rationale; specReference null):",
2510
+ '{"purpose":"loading skeleton","component":"SkeletonCard","decision":"new","specReference":null,"rationale":"no spec or existing component covers skeleton; deviation pending design-review approval"}',
2511
+ "",
2512
+ "### uiComponentChoices - BAD (specified without specReference, or new with specReference):",
2513
+ '{"purpose":"primary action","component":"MyButton","decision":"specified","specReference":null,"rationale":"..."} <-- REJECTED: specified requires specReference',
2514
+ '{"purpose":"primary action","component":"MyButton","decision":"new","specReference":{"path":"openspec/schemas/button.md","section":"","line":null},"rationale":"..."} <-- REJECTED: new must not carry specReference',
2495
2515
  ].join("\n");
2496
2516
  })();
2497
2517
  const frontendContractFieldSummary = [
@@ -2504,7 +2524,8 @@ async function buildFrontendHybridDagFromTask(sources) {
2504
2524
  "- mockApi: strategy, productionDefaultOff, activation, endpoints[]",
2505
2525
  "- verificationTargets[]: id, type, commandLabel, file, symbol, requirementIds, uiStates",
2506
2526
  "- designEvidence: source, paths, conflicts; evidenceGaps[]",
2507
- "- optional: implementationSteps[], stylingStrategy, dependencyPolicy, residualRisks[], realIntegrationGap",
2527
+ "- optional: implementationSteps[], stylingStrategy, uiComponentChoices[], dependencyPolicy, residualRisks[], realIntegrationGap",
2528
+ "- uiComponentChoices[]: purpose, component, decision (specified|reuse-existing|new), specReference { path, section, line } | null, rationale",
2508
2529
  "Do not require or read a separate plan prose section; the contract JSON is the only plan surface.",
2509
2530
  ].join("\n");
2510
2531
  const sourceContext = [
@@ -2681,6 +2702,17 @@ async function buildFrontendHybridDagFromTask(sources) {
2681
2702
  advisories.push("auto 模式已将 Mock 策略收窄为 not-needed:任务源提到接口/API/Mock 需求,但仓库无确认 Mock 能力或无确定性 Mock 验证命令。若项目规范要求 Mock,请声明 frontendMock.verifyCommands 或 policy:required 后重新生成 DAG。");
2682
2703
  }
2683
2704
  const openspecGate = resolveFrontendOpenspecGateConfig(sources);
2705
+ // Generation-frozen component/theme specification bucket (ADR 0016). Derived
2706
+ // from the classified component/theme/rule.components buckets; the prewrite
2707
+ // gate consumes it to enforce uiComponentChoices presence and specReference
2708
+ // cross-checking. `src/task/**` classification logic is intentionally
2709
+ // unchanged — these buckets already partition normativePaths.
2710
+ const classified = sources.frontendProjectCapability?.designEvidence.classified;
2711
+ const componentSpecCandidatePaths = [
2712
+ ...(classified?.component ?? []),
2713
+ ...(classified?.theme ?? []),
2714
+ ...(classified?.rule.components ?? []),
2715
+ ].filter((value, index, array) => array.indexOf(value) === index).sort();
2684
2716
  if (openspecGate.openspecPolicy === "cited") {
2685
2717
  if (openspecGate.openspecCandidatePaths.length === 0) {
2686
2718
  advisories.push("openspec 策略 cited:契约声明的 requiredReadPaths 与任务源引用均为空,prewrite gate 不强制读取 openspec;如需增强规范门禁,请在 task.json.frontendOpenspec.requiredReadPaths 声明必读路径或在任务源中显式引用 openspec 文件。");
@@ -2787,6 +2819,7 @@ async function buildFrontendHybridDagFromTask(sources) {
2787
2819
  sourceContext,
2788
2820
  mockContextBlock,
2789
2821
  frontendContractSchemaBlock,
2822
+ frontendComponentConformanceInstruction,
2790
2823
  openspecCitationInstruction,
2791
2824
  ].join("\n\n"),
2792
2825
  },
@@ -2807,6 +2840,7 @@ async function buildFrontendHybridDagFromTask(sources) {
2807
2840
  "First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
2808
2841
  "Request revision when the Mock strategy is MOCK_STRATEGY: blocked, missing, unsupported by repository evidence, inconsistent with the API contract, outside authorized paths/dependencies, unable to prove production-default-off behavior with the fixed production/default-real-path static check, or missing deterministic behavior verification for a declared behavior target or selected Mock strategy. Mock strategies require Mock-backed evidence. A static-only contract is allowed only when every verification target is static and maps to a declared static entrypoint. not-needed otherwise requires applicable real/no-remote behavior evidence unless auto mode explicitly skipped Mock because no project Mock capability exists; in that case the plan must preserve the real request path and record the Real Integration Gap.",
2809
2842
  "Also request revision for missing applicable UI states, unsupported dependency additions, design-system drift without reason, weak interaction coverage, broad scope, inline fake data, schema drift, or missing deterministic verification commands.",
2843
+ "Component selection conformance is a hard blocking condition: VERDICT: request-revision when the frontend spec (component/theme/rule.components bucket) already defines a component for a purpose but the plan selects another or self-invents one without a declared deviation; when uiComponentChoices is missing/empty for UI-visible work while the frozen component/theme bucket is non-empty; or when any uiComponentChoices specReference.path is not cited in the openspec-citations block or has no successful read event.",
2810
2844
  "Read-only: do not modify repository files.",
2811
2845
  fixedVerificationContext,
2812
2846
  sourceContext,
@@ -2835,7 +2869,7 @@ async function buildFrontendHybridDagFromTask(sources) {
2835
2869
  subtask_prompt: [
2836
2870
  "Consume frontend-plan-pi (original contract JSON) and frontend-design-review-pi (first design review findings).",
2837
2871
  "This node runs only when frontend-design-review-pi emitted VERDICT: request-revision. Produce an RFC 7386 merge-patch delta against the original contract JSON that addresses every Required Plan Correction from the design findings.",
2838
- "The patch delta may update any of these contract fields: requirements, implementationSteps, targets, uiStates, interactions, mockApi, dependencyPolicy, stylingStrategy, verificationTargets, evidenceGaps, residualRisks, realIntegrationGap. Only include fields you change; omit unchanged fields (the gate applies the patch on the original contract). null deletes a key; arrays and scalars replace; plain objects merge recursively.",
2872
+ "The patch delta may update any of these contract fields: requirements, implementationSteps, targets, uiStates, interactions, mockApi, dependencyPolicy, stylingStrategy, uiComponentChoices, verificationTargets, evidenceGaps, residualRisks, realIntegrationGap. Only include fields you change; omit unchanged fields (the gate applies the patch on the original contract). null deletes a key; arrays and scalars replace; plain objects merge recursively.",
2839
2873
  requirementCoverageInstruction,
2840
2874
  "Do not turn MOCK_STRATEGY: blocked into an implementable strategy without new repository or contract evidence that resolves every blocker.",
2841
2875
  "Read-only: do not modify code, docs, artifacts, or repository files. This node revises the plan only.",
@@ -2847,6 +2881,7 @@ async function buildFrontendHybridDagFromTask(sources) {
2847
2881
  sourceContext,
2848
2882
  frontendContractSchemaBlock,
2849
2883
  mockContextBlock,
2884
+ frontendComponentConformanceInstruction,
2850
2885
  openspecCitationInstruction,
2851
2886
  ].join("\n\n"),
2852
2887
  },
@@ -2875,6 +2910,7 @@ async function buildFrontendHybridDagFromTask(sources) {
2875
2910
  "Recheck the selected Mock / API strategy, contract-to-fixture mapping, authorized paths/dependencies, explicit activation, production-default-off behavior, behavior verification, and Real Integration Gap. MOCK_STRATEGY: blocked cannot receive VERDICT: pass.",
2876
2911
  "Review every explicit REQ-/BR-/AC- mapping; the downstream prewrite gate also checks identifier retention deterministically.",
2877
2912
  "Request revision if any design gap remains, if corrections are incomplete, or if the revised plan introduces new unaddressed issues.",
2913
+ "Also request revision for component selection non-conformance: spec-defined components silently replaced or self-invented without a declared deviation, uiComponentChoices missing for UI-visible work, or a uiComponentChoices specReference.path not cited in the openspec-citations block / not actually read.",
2878
2914
  "Read-only: do not modify repository files.",
2879
2915
  fixedVerificationContext,
2880
2916
  sourceContext,
@@ -2933,6 +2969,7 @@ async function buildFrontendHybridDagFromTask(sources) {
2933
2969
  scanStrict: openspecGate.openspecCandidateSources.scanStrict,
2934
2970
  },
2935
2971
  openspecCandidatePaths: openspecGate.openspecCandidatePaths,
2972
+ componentSpecCandidatePaths,
2936
2973
  },
2937
2974
  cwd: ".",
2938
2975
  timeoutMs: 60000,
@@ -193,6 +193,16 @@ export const dagFrontendPrewriteGateSchema = z.object({
193
193
  requireSourceFreshness: z.literal(true),
194
194
  implementationWriteSet: z.array(z.string().min(1)).min(1).optional(),
195
195
  openspecCandidatePaths: z.array(openspecCandidatePathSchema).default([]),
196
+ /**
197
+ * Generation-frozen component/theme specification bucket. Derived from
198
+ * `designEvidence.classified.component ∪ theme ∪ rule.components` at DAG
199
+ * generation. When non-empty and the canonical contract has UI-visible
200
+ * work, the prewrite gate requires a non-empty `uiComponentChoices` and
201
+ * cross-checks each `specReference.path` against this set + the effective
202
+ * plan openspec-citations block. Legacy DAGs without this field keep the
203
+ * field-absent semantics (component-choices-missing is not enforced).
204
+ */
205
+ componentSpecCandidatePaths: z.array(openspecCandidatePathSchema).optional(),
196
206
  /**
197
207
  * OpenSpec gate policy. Omitted on legacy DAGs: the runtime treats
198
208
  * undefined as scan-strict so pre-existing full-read semantics are
@@ -19,6 +19,7 @@
19
19
  "evidenceGaps": { "type": "array", "items": { "$ref": "#/$defs/gap" } },
20
20
  "implementationSteps": { "type": "array", "items": { "type": "string", "minLength": 1 } },
21
21
  "stylingStrategy": { "type": "string", "minLength": 1 },
22
+ "uiComponentChoices": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["purpose", "component", "decision", "rationale"], "properties": { "purpose": { "type": "string", "minLength": 1 }, "component": { "type": "string", "minLength": 1 }, "decision": { "enum": ["specified", "reuse-existing", "new"] }, "specReference": { "anyOf": [{ "$ref": "#/$defs/uiComponentSpecReference" }, { "type": "null" }] }, "rationale": { "type": "string", "minLength": 1 } } } },
22
23
  "dependencyPolicy": { "type": "string", "minLength": 1 },
23
24
  "residualRisks": { "type": "array", "items": { "type": "string", "minLength": 1 } },
24
25
  "realIntegrationGap": { "type": "string", "minLength": 1 }
@@ -28,6 +29,7 @@
28
29
  "path": { "type": "string", "minLength": 1, "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*\\\\).+$" },
29
30
  "requirementId": { "type": "string", "pattern": "^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$" },
30
31
  "gap": { "type": "object", "additionalProperties": false, "required": ["description", "blocking"], "properties": { "requirementId": { "$ref": "#/$defs/requirementId" }, "description": { "type": "string", "minLength": 1 }, "blocking": { "type": "boolean" } } },
31
- "sourceBinding": { "type": "object", "additionalProperties": false, "required": ["taskId", "requirementPath", "requirementSha256", "referencePaths", "requirementIds"], "properties": { "taskId": { "type": "string", "minLength": 1 }, "requirementPath": { "$ref": "#/$defs/path" }, "requirementSha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, "referencePaths": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "requirementIds": { "type": "array", "items": { "$ref": "#/$defs/requirementId" } } } }
32
+ "sourceBinding": { "type": "object", "additionalProperties": false, "required": ["taskId", "requirementPath", "requirementSha256", "referencePaths", "requirementIds"], "properties": { "taskId": { "type": "string", "minLength": 1 }, "requirementPath": { "$ref": "#/$defs/path" }, "requirementSha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, "referencePaths": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "requirementIds": { "type": "array", "items": { "$ref": "#/$defs/requirementId" } } } },
33
+ "uiComponentSpecReference": { "type": "object", "additionalProperties": false, "required": ["path", "section"], "properties": { "path": { "$ref": "#/$defs/path" }, "section": { "type": "string" }, "line": { "anyOf": [{ "type": "integer" }, { "type": "null" }] } } }
32
34
  }
33
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.39.0-next.6",
3
+ "version": "0.39.0-next.8",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",
@@ -12,6 +12,7 @@
12
12
  - Cite knowledge base, `openspec/schemas/`, `openspec/project-specs/`, and `ai_workspace/` as parallel sources; failed or empty
13
13
  knowledge queries must still search `<repoRoot>/openspec/schemas/`, `<repoRoot>/openspec/project-specs/`, and `<repoRoot>/ai_workspace/`.
14
14
  - Record source status, query terms, paths/headings, conflicts, authorized deps, and allowed paths for both.
15
+ - Component selection conformance: every UI purpose must be declared in the contract `uiComponentChoices[]`. `specified` must carry a non-empty `specReference { path, section, line }` hitting a supported openspec/ai_workspace spec; `new` must declare a deviation rationale with `specReference` null; a spec-defined component must not be silently replaced or self-invented without a declared deviation.
15
16
 
16
17
  ## Interaction / Quality
17
18
 
@@ -37,5 +38,6 @@
37
38
  ## Verdict Matrix
38
39
 
39
40
  - Request revision for coverage gaps, unsafe scope, unauthorized deps, unresolved required interaction, missing required verification, skipped local specification fallback (`openspec/schemas/`, `openspec/project-specs/`, or `ai_workspace/`), unsafe/missing Mock strategy, or Mock evidence presented as real integration.
41
+ - Request revision for component selection non-conformance: spec-defined components silently replaced or self-invented without a declared deviation, `uiComponentChoices` missing for UI-visible work, or a `uiComponentChoices` `specReference.path` not cited in the `openspec-citations` block / not actually read.
40
42
  - Knowledge-base unavailable but relevant OpenSpec or `ai_workspace/` rules applied is advisory only.
41
43
  - Optional cleanup that cannot affect acceptance is advisory.
@@ -5,27 +5,21 @@
5
5
  Knowledge base, OpenSpec, and `ai_workspace/` are parallel specification sources:
6
6
 
7
7
  1. Attempt the configured component/design knowledge-base query first when a
8
- connector is available in the execution environment.
9
- 2. Regardless of knowledge-base success, failure, timeout, no match, or no
10
- configuration, also recursively search `openspec/schemas/`,
11
- `openspec/project-specs/`, and `ai_workspace/` for index files and relevant content.
8
+ connector is available.
9
+ 2. Regardless of knowledge-base success/failure/timeout/no-match, also
10
+ recursively search `openspec/schemas/`, `openspec/project-specs/`, and
11
+ `ai_workspace/` for index files and relevant content.
12
12
  3. Treat relevant matches from both sources as the current project's
13
13
  specification for this run.
14
14
  4. Only then use component source, tokens, stories, tests, and pages as
15
15
  non-normative repository fallback.
16
16
 
17
- Never skip local OpenSpec or `ai_workspace/` sources for neighboring-code
18
- conventions, even when a knowledge-base query returned results. Report source
19
- conflicts instead of combining them. Explicit task requirements remain the
20
- contract; flag conflicts with knowledge-base or openspec specification rules.
17
+ Never skip local OpenSpec or `ai_workspace/` for neighboring-code conventions,
18
+ even when a knowledge-base query returned results. Report source conflicts instead
19
+ of combining them. Explicit task requirements remain the contract.
21
20
 
22
- ## Knowledge Base Connection TODO
23
-
24
- Request format is undecided. TODO: define connector/owner, namespaces, secret-free
25
- auth, query fields, result identity/version/time, and failure behavior.
26
-
27
- Attempt only a connector actually available in the execution environment. Otherwise
28
- record `not-configured` and run the openspec fallback; never invent a connection.
21
+ Connector format is TODO; attempt only an available connector, otherwise record
22
+ `not-configured` and run the openspec fallback (never invent a connection).
29
23
 
30
24
  ## openspec Fallback Procedure
31
25
 
@@ -33,10 +27,10 @@ record `not-configured` and run the openspec fallback; never invent a connection
33
27
  and `<repoRoot>/ai_workspace/` when present; enumerate supported files recursively.
34
28
  - Read indexes first, then search names/content using task, route, component,
35
29
  interaction, theme, token, and state terms.
36
- - Read relevant matches in context; do not treat a filename-only hit as a rule.
30
+ - Read matches in context; a filename-only hit is not a rule.
37
31
  - Record search terms, inspected/matched paths, headings or tight line ranges,
38
32
  applied rules, and conflicts.
39
- - If both directories or relevant rules are absent, record that fact before using
33
+ - If both directories or relevant rules are absent, record that before using
40
34
  repository fallback.
41
35
 
42
36
  ## Retrieval Evidence
@@ -54,6 +48,9 @@ and conflicts. `openspec fallback` includes terms, paths/headings/lines, rules,
54
48
 
55
49
  - Reuse confirmed primitives unless a new pattern is authorized.
56
50
  - Define applicable states and responsive behavior before implementation.
57
- - Cite knowledge-base or openspec specification evidence for component/token
58
- choices; label weaker repository fallback.
51
+ - Cite knowledge-base or openspec spec evidence for component/token choices;
52
+ label weaker repo fallback.
59
53
  - Make deviations and unresolved gaps explicit.
54
+ - Contract `uiComponentChoices[]` per UI purpose: `specified` needs
55
+ `specReference {path,section,line}`; `new` needs null ref + rationale; never
56
+ silently replace a spec-defined component.
@@ -5,18 +5,20 @@ Pre-write nodes are read-only. Preserve IDs, labels, commands, language, require
5
5
  ## Core nodes
6
6
 
7
7
  - **`frontend-contract-pi`**: `Scope`, `Non-goals`, `Acceptance Criteria`, `UI States`, `Target Runtime Environment`, `Risks`, `Verification Expectations`; no guessed requirements.
8
- - **`frontend-scout-pi`**: routes, components, tokens, data/API/Mock, scripts, tests, assets; fact vs inference vs gap. Search+read `openspec/schemas/`, `openspec/project-specs/`, `ai_workspace/` first. Output stack, routes, components, styling, conventions, state/data, test entry points, reuse, risks.
9
- - **`frontend-plan-pi` + conditional design loop**: AC → observable `expectedOutcome`; interactions → `trigger`+`expectedBehavior`; steps, in-bound files, applicable UI states, reuse, deps, Mock/API strategy, activation/rollback, frozen verify entrypoints, real-integration gap. **JSON-only**: one-line lead-in + **exactly one** fenced `json` block (`frontend-implementation-contract-v1`) + one `openspec-citations` block. No plan prose; plan.md is rendered by the prewrite gate. `frontend-contract-json-pi`/`frontend-contract-json-validate-shell` don't exist. IDs+paths aren't sufficient behavior semantics. Use `uiStates: []` for logic-only changes; don't invent UI states. Applicable states need behavior/implementation/verification; non-applicable need a reason. Non-`not-needed` needs frozen Mock verify commands (`frontendMock.verifyCommands`/`package.json` mock script/capability seed); `auto` with absent/ambiguous capability or no command → `not-needed`; `required` without a command → generation-time blocked. Initial pass uses original plan; only exact `request-revision` runs revision + final review. Small-risk: one design review.
10
- - **`frontend-prewrite-gate-shell`**: sole write authorization. Resolve effective plan (revised if revision ran, else original) + review; require exact pass; retain every REQ/BR/AC id; enforce Mock policy; validate schema/source binding+writeSet containment; materialize `contracts/frontend-implementation-contract.json` from the **single** fenced contract block (multiple candidates fail closed `invalid-output`). Fallback only when conditional primary absent; existing malformed primary fails closed. Generation-time blocked Mock yields one deterministic blocking shell node, no writer.
8
+ - **`frontend-scout-pi`**: routes, components, tokens, data/API/Mock, scripts, tests, assets; fact vs inference vs gap. Search+read `openspec/schemas/`, `openspec/project-specs/`, `ai_workspace/` first. Output stack, styling, conventions, state/data, test entry points, reuse, risks.
9
+ - **`frontend-plan-pi` + conditional design loop**: AC → observable `expectedOutcome`; interactions → `trigger`+`expectedBehavior`; steps, in-bound files, UI states, reuse, deps, Mock/API strategy, activation/rollback, frozen verify entrypoints, real-integration gap. **JSON-only**: one-line lead-in + **exactly one** fenced `json` block (`frontend-implementation-contract-v1`) + one `openspec-citations` block. No plan prose; plan.md is rendered by the gate. `frontend-contract-json-pi`/`frontend-contract-json-validate-shell` don't exist; IDs+paths behavior semantics. Use `uiStates: []` for logic-only changes; don't invent UI states. Applicable states need behavior/implementation/verification; non-applicable need reasons. Non-`not-needed` needs frozen Mock verify commands (`frontendMock.verifyCommands`/`package.json` mock script/capability seed); `auto` with absent/ambiguous capability or no command → `not-needed`; `required` without a command → generation-time blocked. Initial pass = original plan; only `request-revision` runs revision+final review; small-risk = one review.
10
+ - `uiComponentChoices[]` (optional): `{purpose, component, decision, specReference, rationale}`; `specified` needs `specReference{path,section,line}`; `new` needs null ref+rationale.
11
+ - **`frontend-prewrite-gate-shell`**: sole write authorization. Resolve effective plan (revised if revision ran, else original) + review; require exact pass; retain every REQ/BR/AC id; enforce Mock policy; validate schema/source binding+writeSet containment; materialize `contracts/frontend-implementation-contract.json` from the **single** fenced contract block (multiple candidates fail closed `invalid-output`). Fallback only when conditional primary absent; malformed primary fails closed. Generation-time blocked Mock yields one blocking shell node, no writer.
11
12
  - `verificationTarget.commandLabel` must be a **frozen command label** from `verifyEvidence.commandLabels`; others fail closed (`invalid-output`). Empty frozen set (no `run.json`) skips check.
12
13
  - `mockApi.strategy !== "not-needed"` fails closed when `mockCommandLabels` empty (`no authorized Mock verification commands`).
13
- - OpenSpec policy: `task.json.frontendOpenspec.policy` (default `cited`); candidates = `requiredReadPaths` ∪ task-source citations; effective plan must append one fenced `openspec-citations` block (one JSON `{"path","section","line"}` per line). Candidates non-empty but block missing/unparseable → `openspec-citation-block-unparseable`; candidate not cited → `openspec-not-cited`; cited path without successful read event in plan/review → `openspec-citation-not-read` (anti-fabrication). Empty candidates skip enforcement (generation-time advisory). `scan-strict` keeps auto-discovered full-read semantics (`openspec-not-read`); both modes keep `candidate-missing-drift`.
14
+ - OpenSpec policy: `task.json.frontendOpenspec.policy` (default `cited`); candidates = `requiredReadPaths` ∪ task-source citations; effective plan must append one fenced `openspec-citations` block (one JSON `{"path","section","line"}` per line). Candidates non-empty but block missing/unparseable → `openspec-citation-block-unparseable`; candidate not cited → `openspec-not-cited`; cited path without read event → `openspec-citation-not-read` (anti-fabrication). Empty candidates skip enforcement. `scan-strict` keeps full-read semantics (`openspec-not-read`); both modes keep `candidate-missing-drift`.
15
+ - Component conformance: missing `uiComponentChoices` (frozen bucket non-empty + UI-visible work) → `component-choices-missing`; bad/uncited `specReference.path` → `component-spec-reference-invalid` / `component-spec-not-cited`.
14
16
  - revisionPatch mode: revision output is an RFC 7386 merge-patch delta applied on the original contract node (`planFallbackFromNodeIds[0]`); legacy DAGs keep full-contract semantics. Size guard: canonical serialization >64KB → `retryable-invalid` + `contract-too-large`.
15
- - **`frontend-implement-pi`**: sole regular exclusive writer; uses `frontend-bounded-implement` (not this skill). Stay in `writeSet`; real requests default-on. Atomic handler/intercept/adapter with consumer+tests. Stop on forbidden paths/guesses. First line `IMPLEMENTATION_OUTCOME: changed|already-satisfied|blocked`; runtime checks against attributed diff. Optional mock-verify when frozen; static+behavior always; behavior must prove page consumption. `not-needed` keeps real integration pending unless real backend has fresh evidence.
17
+ - **`frontend-implement-pi`**: sole regular exclusive writer; uses `frontend-bounded-implement` (not this skill). Stay in `writeSet`; real requests default-on. Atomic handler/intercept/adapter with tests. Stop on forbidden paths/guesses. First line `IMPLEMENTATION_OUTCOME: changed|already-satisfied|blocked`; runtime checks against attributed diff. Optional mock-verify when frozen; static+behavior always; behavior must prove page consumption. `not-needed` keeps real integration pending unless real backend has fresh evidence.
16
18
 
17
19
  ## Contract / trace / stages (M1–M2)
18
20
 
19
- - `frontend-verify-assess-shell` runs Mock/static/behavior groups, binds `verificationTargets` to command labels + file/symbol evidence, writes `frontend-verification-trace.json` + `frontend-repair-assessment.json`; browser/visual stay `not-run`.
21
+ - `frontend-verify-assess-shell` runs Mock/static/behavior groups, binds `verificationTargets` to command labels+evidence, writes `frontend-verification-trace.json`+`frontend-repair-assessment.json`; browser/visual stay `not-run`.
20
22
  - `frontend-review-context-shell` captures real diff; combines contract, effective trace, repair assessment, diff into `contracts/frontend-review-context.json`.
21
23
  - Implement stages: (1) contract confirm (2) tests sync (3) component/UI (4) API/Mock (5) frozen checks (6) diff cleanup. Summary: Contract Ref, Changed Files, Requirements, UI States, Tests, Verification Attempts, Deviations, Residual Risks.
22
24
 
@@ -1 +0,0 @@
1
- import{ag as o,ah as n}from"./mermaid.core-Dudn9krq.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-ClNxnCSZ.js";import{_ as i}from"./mermaid.core-Dudn9krq.js";import"./chunk-5VM5RSS4-BoUlzoKw.js";import"./chunk-XXDRQBXY-CAPr_ybW.js";import"./chunk-KBJHAD2P-BwFyCP8o.js";import"./chunk-2GRJ4B5K-Db2uLs2I.js";import"./index-F4oI1Kcc.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-ClNxnCSZ.js";import{_ as i}from"./mermaid.core-Dudn9krq.js";import"./chunk-5VM5RSS4-BoUlzoKw.js";import"./chunk-XXDRQBXY-CAPr_ybW.js";import"./chunk-KBJHAD2P-BwFyCP8o.js";import"./chunk-2GRJ4B5K-Db2uLs2I.js";import"./index-F4oI1Kcc.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};