@taylorwong/ichartjs 2.0.20 → 2.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.21 - 2026-10-01
4
+
5
+ - Completed Iteration 17 production trust and Agent reliability hardening with unknown-option diagnostics, effective Spec explanations, unsupported-renderer planning diagnostics, and stable normalization records.
6
+ - Added `agentReliability` capability metadata, npm package-content checks, bilingual production-trust guidance, and release-gate coverage for the ESM, TypeScript, headless, SVG, and Canvas consumer paths.
7
+
3
8
  ## 2.0.20 - 2026-09-30
4
9
 
5
10
  - Completed Iteration 16 preference precedence, Agent source resolution, conversational workflow guidance, capability discovery, and Gallery acceptance coverage.
package/README.md CHANGED
@@ -45,7 +45,7 @@ getCapabilities
45
45
  npm install @taylorwong/ichartjs@^2
46
46
  ```
47
47
 
48
- As a fallback for environments without npm access, install directly from GitHub: `npm install github:wanghetommy/ichartjs#v2.0.20`.
48
+ As a fallback for environments without npm access, install directly from GitHub: `npm install github:wanghetommy/ichartjs#v2.0.21`.
49
49
 
50
50
  ### Optional Agent Skill
51
51
 
@@ -67,7 +67,7 @@ For a non-interactive global Codex installation:
67
67
  npx skills add wanghetommy/ichartjs --skill ichartjs --agent codex --global --yes
68
68
  ```
69
69
 
70
- For a release-pinned installation, use `npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.20/skills/ichartjs --agent codex --global --yes`. WorkBuddy users can import the same tagged `skills/ichartjs` URL through the host's Skill interface; do not assume a `--agent workbuddy` adapter unless the installed CLI declares it. Package consumers can still copy `node_modules/@taylorwong/ichartjs/skills/ichartjs` as a manual fallback. After installation, invoke `$ichartjs` when named Skill invocation is supported, or select `ichartjs` in the host UI.
70
+ For a release-pinned installation, use `npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs --agent codex --global --yes`. WorkBuddy users can import the same tagged `skills/ichartjs` URL through the host's Skill interface; do not assume a `--agent workbuddy` adapter unless the installed CLI declares it. Package consumers can still copy `node_modules/@taylorwong/ichartjs/skills/ichartjs` as a manual fallback. After installation, invoke `$ichartjs` when named Skill invocation is supported, or select `ichartjs` in the host UI.
71
71
 
72
72
  ### Agent workflow
73
73
 
@@ -33,7 +33,7 @@ Install the official Skill with the standard Agent Skills CLI:
33
33
  npx skills add wanghetommy/ichartjs --skill ichartjs
34
34
  ```
35
35
 
36
- For global non-interactive Codex setup, append `--agent codex --global --yes`. To pin the released workflow, install `https://github.com/wanghetommy/ichartjs/tree/v2.0.20/skills/ichartjs`. WorkBuddy can import that tagged directory through its Skill interface; only use a host-specific `--agent` value when the installed CLI declares it.
36
+ For global non-interactive Codex setup, append `--agent codex --global --yes`. To pin the released workflow, install `https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs`. WorkBuddy can import that tagged directory through its Skill interface; only use a host-specific `--agent` value when the installed CLI declares it.
37
37
 
38
38
  Verify discovery with `npx skills add wanghetommy/ichartjs --list`; the result should include `ichartjs`.
39
39
 
@@ -40,7 +40,7 @@ iChart.js 2.0 is a new Agent-first product line. Compatibility with 1.x and a 1.
40
40
 
41
41
  ## Iteration 13 Addendum — 2026-09-23
42
42
 
43
- The historical 2.0.0 release decision remains unchanged. Release `v2.0.20` includes Iteration 13 contract hardening, Iteration 14 lightweight Flow semantics, Iteration 15 text-readability hardening, and Iteration 16 Agent presentation and acceptance hardening without adding a chart type: 18 chart profiles, 25 commands, 11 schemas, contract version `1.1`, atomic mutation validation, Mindmap edge discovery, strict TypeScript consumer compilation, no active source cycles, Node tests, Chromium browser acceptance, package dry-run verification, the 13F axis-free layout strategy, Timeline/Milestone layout hardening, renderer-parity Flow semantic shapes, native Flow ellipse primitives, adaptive diagram labels, diagram-only edge-label plates, polar label layout diagnostics for Pie, Donut, and Gauge, preference source resolution, conversational workflow guidance, and Gallery browser acceptance.
43
+ The historical 2.0.0 release decision remains unchanged. Release `v2.0.21` includes Iteration 13 contract hardening, Iteration 14 lightweight Flow semantics, Iteration 15 text-readability hardening, Iteration 16 Agent presentation and acceptance hardening, and Iteration 17 production trust and Agent reliability hardening without adding a chart type: 18 chart profiles, 25 commands, 11 schemas, contract version `1.1`, atomic mutation validation, Mindmap edge discovery, strict TypeScript consumer compilation, no active source cycles, Node tests, Chromium browser acceptance, package dry-run verification, the 13F axis-free layout strategy, Timeline/Milestone layout hardening, renderer-parity Flow semantic shapes, native Flow ellipse primitives, adaptive diagram labels, diagram-only edge-label plates, polar label layout diagnostics for Pie, Donut, and Gauge, preference source resolution, conversational workflow guidance, effective-option explanations, stable normalization diagnostics, unsupported-renderer planning diagnostics, package-content checks, and Gallery browser acceptance.
44
44
 
45
45
  ## Deferred npm Publication
46
46
 
@@ -0,0 +1,55 @@
1
+ # Iteration 17 — Production Trust and Agent Reliability
2
+
3
+ Iteration 17 hardens the existing chart runtime for production Agent use. It adds no chart type, no CLI, no MCP server, and no HTTP service.
4
+
5
+ ## 17A — Contract Reliability
6
+
7
+ - Diagnose unknown top-level Spec options instead of silently ignoring them.
8
+ - Keep `validateSpec()`, normalization, `createChart()`, capabilities, TypeScript, and manifests aligned.
9
+ - Preserve stable diagnostic codes, JSON paths, expected values, and actionable suggestions.
10
+
11
+ ## 17B — Agent Self-check
12
+
13
+ - Expose effective rendering options and normalization results through `chart.explain()`.
14
+ - Report unsupported renderer requests during `planChart()` instead of returning only a fallback.
15
+ - Publish `agentReliability` in `getCapabilities()` and `capabilities.json`.
16
+ - Keep navigation, editing, and motion safe by default.
17
+
18
+ ## 17C — Package and Integration Confidence
19
+
20
+ - Add a package dry-run gate for required runtime, TypeScript, Skill, recipes, and capability files.
21
+ - Reject accidental publication of `.trae`, `.github`, tests, and Playground development paths.
22
+ - Keep the consumer TypeScript fixture and ESM/headless examples in the release gate.
23
+
24
+ ## 17D — Rendering and Accessibility Acceptance
25
+
26
+ - Continue SVG/Canvas parity checks for labels, diagrams, exports, and responsive layouts.
27
+ - Keep browser acceptance separate from headless checks and require explicit local-server evidence.
28
+ - Preserve static, safe defaults while testing opt-in navigation and editing.
29
+
30
+ ## 17E — Open Source Maintenance
31
+
32
+ - Keep `CONTRIBUTING.md`, `SECURITY.md`, CI, release SOP, Skill, and Agent guides aligned.
33
+ - Record package and contract checks as mandatory contribution and release gates.
34
+
35
+ ## Acceptance
36
+
37
+ ```text
38
+ inspect data
39
+ → plan chart
40
+ → validate Spec
41
+ → create chart
42
+ → inspect effective options and health
43
+ → preview/export
44
+ ```
45
+
46
+ - Invalid or ignored configuration is diagnosable.
47
+ - An Agent can verify what actually rendered without reading renderer internals.
48
+ - The npm tarball contains only supported consumer artifacts.
49
+ - `npm run agent:check`, browser acceptance, and `git diff --check` pass.
50
+
51
+ ## Out of Scope
52
+
53
+ - New chart types.
54
+ - NLP parsing inside the runtime.
55
+ - CLI, MCP, HTTP, Python, or 1.x compatibility layers.
@@ -1,6 +1,6 @@
1
1
  # iChart.js 2.0 Roadmap
2
2
 
3
- > Roadmap baseline: 2026-09-14. Current release status: `v2.0.20` includes the completed Iteration 12 structured-diagram work, Iteration 13 contract hardening, Iteration 14 lightweight Flow semantics, Iteration 15 text-readability hardening, Iteration 16 Agent presentation and acceptance hardening, native Flow ellipse primitives, sector-aware Pie/Donut labels, radius-aware Gauge metrics, and the preceding layout and diagnostic improvements. Geographic charts and 3D rendering remain out of scope until explicitly reintroduced.
3
+ > Roadmap baseline: 2026-09-14. Current release status: `v2.0.21` includes the completed Iteration 12 structured-diagram work, Iteration 13 contract hardening, Iteration 14 lightweight Flow semantics, Iteration 15 text-readability hardening, Iteration 16 Agent presentation and acceptance hardening, Iteration 17 production trust and Agent reliability hardening, native Flow ellipse primitives, sector-aware Pie/Donut labels, radius-aware Gauge metrics, and the preceding layout and diagnostic improvements. Geographic charts and 3D rendering remain out of scope until explicitly reintroduced.
4
4
 
5
5
  ## Current Status
6
6
 
@@ -20,6 +20,7 @@
20
20
  - Iteration 15A–15D is complete and included in `v2.0.19`: adaptive diagram node labels, semantic inline-first edge labels, diagram edge-label background plates, plate-free generic chart labels, shared SVG/Canvas fitting, unified label diagnostics, Agent/diagram documentation, and polar label layout hardening for Pie, Donut, and Gauge.
21
21
  - Iteration 16A–16E is complete and included in `v2.0.20`: Gallery preference precedence, preference source resolution, conversational Agent routing, bilingual workflow guidance, manifest discovery, lifecycle regression coverage, and browser acceptance for Gallery theme controls.
22
22
  - Flow start/end nodes now use native ellipse geometry across Scene Graph, SVG, Canvas, export, scaling, and hit testing.
23
+ - Iteration 17A–17E is complete and included in `v2.0.21`: unknown-option diagnostics, effective Agent explanations, unsupported renderer planning diagnostics, `agentReliability` discovery, npm package-content checks, and production-trust documentation. See `docs/agent/development/iteration-17.md`.
23
24
  - The original `2.0.0` readiness record is historical and superseded by the published `2.0.x` releases. Current release checks are defined by `docs/agent/development/release-sop.md`.
24
25
 
25
26
  ## Iteration 4 — Agent Data Contract and Business Editing
@@ -10,7 +10,7 @@ This is the production component path. For one-off files or Agent-led repository
10
10
  npm install @taylorwong/ichartjs@^2
11
11
  ```
12
12
 
13
- For environments without npm registry access, install from GitHub as a fallback: `npm install github:wanghetommy/ichartjs#v2.0.20`.
13
+ For environments without npm registry access, install from GitHub as a fallback: `npm install github:wanghetommy/ichartjs#v2.0.21`.
14
14
 
15
15
  Use the package through a bundler or another environment that resolves npm ESM imports:
16
16
 
@@ -129,7 +129,7 @@ chart.downloadSVG()
129
129
  chart.downloadJSON()
130
130
  ```
131
131
 
132
- Validation results contain separate `errors`, `warnings`, and `normalizations`. Diagnostics use stable codes, JSON-oriented paths, expected values where useful, and actionable suggestions. `chart.explain()` returns encodings, transforms, interactions, assumptions, warnings, stable record lineage, and an accessibility summary.
132
+ Validation results contain separate `errors`, `warnings`, and `normalizations`. Diagnostics use stable codes, JSON-oriented paths, expected values where useful, and actionable suggestions. Unknown top-level options return `UNKNOWN_SPEC_OPTION` instead of being silently treated as valid configuration. `chart.explain()` returns encodings, transforms, interactions, effective options, normalizations, assumptions, warnings, stable record lineage, and an accessibility summary.
133
133
 
134
134
  For lineage checks and linked updates, provide stable string `id` values on input rows. Without one, the runtime uses deterministic positional IDs such as `record-0`; these are suitable for a local self-check but not for durable business identity.
135
135
 
@@ -125,7 +125,7 @@ npx skills add wanghetommy/ichartjs --skill ichartjs --agent codex --global --ye
125
125
  For reproducible installation, pin the released Skill directory:
126
126
 
127
127
  ```bash
128
- npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.20/skills/ichartjs \
128
+ npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs \
129
129
  --agent codex --global --yes
130
130
  ```
131
131
 
@@ -22,7 +22,7 @@ npm Registry 中无作用域的 `ichartjs` 是安全占位包,并非本项目
22
22
  npx skills add wanghetommy/ichartjs --skill ichartjs
23
23
  ```
24
24
 
25
- Codex 全局无交互安装可追加 `--agent codex --global --yes`。需要固定发布版本时,安装 `https://github.com/wanghetommy/ichartjs/tree/v2.0.20/skills/ichartjs`。WorkBuddy 可通过自身 Skill 界面导入该带 Tag 的目录;只有当前 CLI 明确声明对应适配器时才使用宿主专用 `--agent` 参数。
25
+ Codex 全局无交互安装可追加 `--agent codex --global --yes`。需要固定发布版本时,安装 `https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs`。WorkBuddy 可通过自身 Skill 界面导入该带 Tag 的目录;只有当前 CLI 明确声明对应适配器时才使用宿主专用 `--agent` 参数。
26
26
 
27
27
  使用 `npx skills add wanghetommy/ichartjs --list` 验证发现结果,其中应包含 `ichartjs`。
28
28
 
@@ -6,7 +6,7 @@ iChart.js 应作为普通 JavaScript UI 组件运行在浏览器应用中。数
6
6
  npm install @taylorwong/ichartjs@^2
7
7
  ```
8
8
 
9
- 无法访问 npm Registry 的环境请用 GitHub 源作为后备:`npm install github:wanghetommy/ichartjs#v2.0.20`。
9
+ 无法访问 npm Registry 的环境请用 GitHub 源作为后备:`npm install github:wanghetommy/ichartjs#v2.0.21`。
10
10
 
11
11
  ```js
12
12
  import { createChart } from '@taylorwong/ichartjs';
@@ -0,0 +1,50 @@
1
+ # Iteration 17 — 生产可信度与 Agent 可靠性
2
+
3
+ Iteration 17 面向生产环境强化现有 Runtime 的 Agent 使用体验。不新增图表类型,也不新增 CLI、MCP 或 HTTP 服务。
4
+
5
+ ## 17A — 契约可靠性
6
+
7
+ - 对未知顶层 Spec 配置提供诊断,不再静默忽略。
8
+ - 保持 `validateSpec()`、规范化、`createChart()`、Capabilities、TypeScript 和 Manifest 一致。
9
+ - 保留稳定诊断编码、JSON 路径、期望值和可执行建议。
10
+
11
+ ## 17B — Agent 自检
12
+
13
+ - 通过 `chart.explain()` 暴露实际生效的渲染选项和规范化结果。
14
+ - `planChart()` 遇到不支持的 Renderer 时直接报告风险,而不是只返回隐式 fallback。
15
+ - 在 `getCapabilities()` 和 `capabilities.json` 中发布 `agentReliability`。
16
+ - 保持导航、编辑和动效默认安全关闭。
17
+
18
+ ## 17C — 包与集成可信度
19
+
20
+ - 增加 npm 包 dry-run 检查,确认 Runtime、TypeScript、Skill、recipes 和能力清单均存在。
21
+ - 禁止 `.trae`、`.github`、测试和 Playground 开发文件进入发布包。
22
+ - 将 TypeScript consumer fixture 与 ESM/headless 示例纳入发布检查。
23
+
24
+ ## 17D — 渲染与无障碍验收
25
+
26
+ - 持续验证 SVG/Canvas 在标签、图表、导出和响应式布局上的一致性。
27
+ - 将浏览器验收与 headless 检查分开,并保留明确的本地服务证据。
28
+ - 在测试显式开启导航和编辑的同时,保持默认静态安全行为。
29
+
30
+ ## 验收标准
31
+
32
+ ```text
33
+ 检查数据
34
+ → 规划图表
35
+ → 校验 Spec
36
+ → 创建图表
37
+ → 检查生效配置和健康状态
38
+ → 预览或导出
39
+ ```
40
+
41
+ - 无效或被忽略的配置都可诊断。
42
+ - Agent 无需阅读 Renderer 内部代码即可确认实际渲染结果。
43
+ - npm 包只包含支持用户使用的产物。
44
+ - `npm run agent:check`、浏览器验收和 `git diff --check` 通过。
45
+
46
+ ## 不纳入本轮
47
+
48
+ - 新图表类型。
49
+ - Runtime 内置 NLP。
50
+ - CLI、MCP、HTTP、Python 或 1.x 兼容层。
@@ -20,7 +20,7 @@ Iteration 8 通过 `getChartCapability(type)` 提供逐图表能力档案,包
20
20
 
21
21
  未知 intent 还会返回 `intentKnown`、`intentSuggestions` 和 `fallbackUsed`。Spec 的通道按图表类型约束:笛卡尔图表是 `x`/`y`,Pie/Funnel 是 `category`/`value`,Gauge 只使用 `value`,Heatmap 是 `x`/`y`/`color`,Radar 字段位于 `indicators`。`validateSpec()` 会拒绝不支持的通道和缺失字段。Gauge 必须显式声明 `domain`,超出范围时会报告 `VALUE_CLAMPED`;Pie 的负值报告 `NEGATIVE_VALUE_DROPPED`,没有正数占比时 `ZERO_TOTAL` 会使校验失败。
22
22
 
23
- `validateSpec()` 分开返回 `errors`、`warnings` 和 `normalizations`;诊断包含稳定代码、JSON 路径、期望值和修复建议。`chart.explain()` 返回编码、转换、交互、假设、警告、稳定记录血缘和无障碍摘要。
23
+ `validateSpec()` 分开返回 `errors`、`warnings` 和 `normalizations`;诊断包含稳定代码、JSON 路径、期望值和修复建议。未知顶层选项会返回 `UNKNOWN_SPEC_OPTION`,不会静默当作有效配置。`chart.explain()` 返回编码、转换、交互、实际生效选项、规范化结果、假设、警告、稳定记录血缘和无障碍摘要。
24
24
 
25
25
  ## 关键规则
26
26
 
@@ -120,7 +120,7 @@ npx skills add wanghetommy/ichartjs --skill ichartjs --agent codex --global --ye
120
120
  需要固定发布版本时,直接安装已发布的 Skill 目录:
121
121
 
122
122
  ```bash
123
- npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.20/skills/ichartjs \
123
+ npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs \
124
124
  --agent codex --global --yes
125
125
  ```
126
126
 
@@ -282,8 +282,8 @@
282
282
  "Short form `branding: false` and object form `branding: { enabled: false }` are both supported. Custom text, link, position, or font are intentionally NOT exposed in the current iteration."
283
283
  ]
284
284
  },
285
- "packageVersion": "2.0.20",
286
- "runtimeVersion": "2.0.20",
285
+ "packageVersion": "2.0.21",
286
+ "runtimeVersion": "2.0.21",
287
287
  "chartTypes": [
288
288
  "line",
289
289
  "area",
@@ -1438,5 +1438,32 @@
1438
1438
  "chart.getState()",
1439
1439
  "chart.getState().health"
1440
1440
  ]
1441
+ },
1442
+ "agentReliability": {
1443
+ "contract": {
1444
+ "validationBeforeRender": true,
1445
+ "normalizedSpecReturned": true,
1446
+ "unknownOptionsDiagnosed": true,
1447
+ "stableDiagnosticCodes": true
1448
+ },
1449
+ "selfCheck": [
1450
+ "chart.explain().effective",
1451
+ "chart.explain().normalizations",
1452
+ "chart.getState().health",
1453
+ "chart.getState().warnings"
1454
+ ],
1455
+ "integration": {
1456
+ "esm": true,
1457
+ "typescript": true,
1458
+ "headless": true,
1459
+ "svg": true,
1460
+ "canvas": true,
1461
+ "packageTarball": true
1462
+ },
1463
+ "defaults": {
1464
+ "navigation": false,
1465
+ "editing": false,
1466
+ "motion": "auto"
1467
+ }
1441
1468
  }
1442
1469
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taylorwong/ichartjs",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "description": "Agent-first, renderer-independent charting and project visualization runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -43,9 +43,10 @@
43
43
  "deps:check": "node scripts/check-cycles.mjs",
44
44
  "docs:examples": "node examples/agent-workflow.mjs",
45
45
  "docs:check": "node scripts/check-agent-docs.mjs",
46
+ "package:check": "node scripts/check-package.mjs",
46
47
  "example:agent": "node examples/agent-workflow.mjs",
47
48
  "playground": "node scripts/serve-playground.mjs",
48
- "agent:check": "npm run contracts:check && npm run types:check && npm run deps:check && npm run docs:check && npm run docs:examples && npm run check && npm test",
49
+ "agent:check": "npm run contracts:check && npm run types:check && npm run deps:check && npm run docs:check && npm run package:check && npm run docs:examples && npm run check && npm test",
49
50
  "rc:check": "npm test && npm run check && git diff --check"
50
51
  },
51
52
  "devDependencies": {
@@ -22,7 +22,7 @@ Recommended installation:
22
22
  npx skills add wanghetommy/ichartjs --skill ichartjs
23
23
  ```
24
24
 
25
- Use `--agent codex --global --yes` for global non-interactive Codex installation. Use the tagged directory `https://github.com/wanghetommy/ichartjs/tree/v2.0.20/skills/ichartjs` when reproducibility matters. WorkBuddy can import the same directory through its Skill interface; do not assume a `--agent workbuddy` adapter unless the installed CLI declares it.
25
+ Use `--agent codex --global --yes` for global non-interactive Codex installation. Use the tagged directory `https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs` when reproducibility matters. WorkBuddy can import the same directory through its Skill interface; do not assume a `--agent workbuddy` adapter unless the installed CLI declares it.
26
26
 
27
27
  The Skill is a workflow adapter, not the chart runtime. If the current JavaScript or TypeScript project does not already depend on iChart.js, install the matching runtime from npm:
28
28
 
@@ -121,6 +121,11 @@ export function planChart(input, options = {}) {
121
121
  const confidence = !report.rows || requiredFields.length ? 0.35 : warnings.some(item => item.code !== 'MISSING_VALUE') ? 0.65 : 0.9;
122
122
  const styleRecommendation = planStyle({ type: primary, intent: requestedIntent, data: Array.isArray(input) ? input : input?.values || input, encoding: { x: report.dimensions[0] ? { field: report.dimensions[0] } : undefined, y: report.measures.map(field => ({ field })) } }, options);
123
123
  warnings.push(...styleRecommendation.warnings);
124
+ const unsupportedRequests = options.renderer && options.renderer !== 'auto' && !profile.renderers.includes(options.renderer) ? [`renderer:${options.renderer}`] : [];
125
+ if (unsupportedRequests.length) {
126
+ warnings.push(warning('UNSUPPORTED_RENDERER', 'renderer', `${primary} does not support renderer ${options.renderer}.`, `Use one of: ${profile.renderers.join(', ')}.`));
127
+ nextActions.push(`Change renderer to ${profile.renderers[0]}.`);
128
+ }
124
129
  return {
125
130
  version: '1.0',
126
131
  intent: requestedIntent,
@@ -135,7 +140,7 @@ export function planChart(input, options = {}) {
135
140
  suggestedEncodings: { dimension: report.dimensions[0] || null, measure: report.measures[0] || null, secondaryMeasure: report.measures[1] || null },
136
141
  assumptions: ['Field roles are inferred from provided values; business meaning and units are not inferred.'],
137
142
  warnings,
138
- unsupportedRequests: options.renderer && !profile.renderers.includes(options.renderer) ? [`renderer:${options.renderer}`] : [],
143
+ unsupportedRequests,
139
144
  nextActions,
140
145
  capability: getChartCapability(primary),
141
146
  styleRecommendation,
@@ -157,7 +162,9 @@ export function explainChart(spec, model = {}) {
157
162
  encodings,
158
163
  transforms: spec.transform ? (Array.isArray(spec.transform) ? spec.transform : [spec.transform]).map(item => item.type) : [],
159
164
  interactions: Object.keys(spec.interaction || {}).filter(key => spec.interaction[key]),
165
+ effective: { renderer: spec.renderer, title: spec.title, legend: spec.legend, grid: spec.grid, labels: spec.labels, interaction: spec.interaction, branding: spec.branding },
160
166
  assumptions: [...(model.data?.assumptions || []), ...(model.state?.projectAnalytics?.assumptions || [])],
167
+ normalizations: model.normalizations || [],
161
168
  warnings,
162
169
  axes: model.state?.axes || null,
163
170
  style: spec.theme && typeof spec.theme === 'object' ? { name: spec.theme.name, preset: spec.theme.preset, mode: spec.theme.mode, resolvedMode: spec.theme.resolvedMode, palette: spec.theme.palette, reasons: spec.theme.reasons || [], warnings: spec.theme.warnings || [] } : planStyle(spec),
@@ -237,6 +244,12 @@ export function getCapabilities() {
237
244
  confirmation: ['deletion', 'structural-change', 'bulk-edit'],
238
245
  selfCheck: ['chart.explain()', 'chart.getState()', 'chart.getState().health']
239
246
  },
247
+ agentReliability: {
248
+ contract: { validationBeforeRender: true, normalizedSpecReturned: true, unknownOptionsDiagnosed: true, stableDiagnosticCodes: true },
249
+ selfCheck: ['chart.explain().effective', 'chart.explain().normalizations', 'chart.getState().health', 'chart.getState().warnings'],
250
+ integration: { esm: true, typescript: true, headless: true, svg: true, canvas: true, packageTarball: true },
251
+ defaults: { navigation: false, editing: false, motion: 'auto' }
252
+ },
240
253
  preferences: {
241
254
  version: '1.0',
242
255
  scopes: preferenceCapabilities.scopes,
package/src/index.mjs CHANGED
@@ -465,7 +465,7 @@ export class Chart {
465
465
  setLinkedFilters(filters = {}) { this.spec.project = { ...(this.spec.project || {}), linked: { ...(this.spec.project?.linked || {}), filters: normalizeLinkedFilters(filters) } }; this.emit('linkedstatechange', { chart: this, linked: this.spec.project.linked }); return this.render(); }
466
466
  setLinkedSelection(selection = []) { this.spec.project = { ...(this.spec.project || {}), linked: { ...(this.spec.project?.linked || {}), selection: normalizeLinkedSelection(selection) } }; this.emit('linkedstatechange', { chart: this, linked: this.spec.project.linked }); return this.render(); }
467
467
  describe() { return { type: this.spec.type, renderer: this.renderer.constructor.name, dimensions: [this.spec.encoding.x?.field || this.spec.encoding.category?.field], measures: (Array.isArray(this.spec.encoding.y) ? this.spec.encoding.y : [this.spec.encoding.y || this.spec.encoding.value]).filter(Boolean).map(encoding => encoding.field), dataCount: this.model.data.rows.length, theme: this.spec.theme?.name || 'custom', interactions: Object.keys(this.spec.interaction || {}).filter(key => this.spec.interaction[key]) }; }
468
- explain() { const explanation = explainChart(this.spec, this.model), state = this.getState(); return { ...explanation, layout: state.layout, timeAxis: state.timeAxis, warnings: state.warnings, health: state.health }; }
468
+ explain() { const explanation = explainChart(this.spec, { ...this.model, normalizations: this._specDiagnostics?.normalizations || [] }), state = this.getState(); return { ...explanation, layout: state.layout, timeAxis: state.timeAxis, warnings: state.warnings, health: state.health }; }
469
469
  getAccessibleDescription() { const description = this.spec.accessibility?.description || this.spec.title?.text || `${this.spec.type} chart`; return `${description}; ${this.model.data.rows.length} data items.`; }
470
470
  inspectDataSchema() { return inspectDataSchema(this.spec.data.schema || this.spec.schema); }
471
471
  validateData() { return validateData(this.toDataTable(), this.spec.data.schema || this.spec.schema, this.spec.validationOptions); }
@@ -775,4 +775,4 @@ export { normalizeLinkedFilters, normalizeLinkedSelection, filterProjectRows, cr
775
775
 
776
776
  export { contrastRatio, planStyle, resolveTheme, styleCapabilities, themeModes, themePalettes, themePresets, validateThemeContrast, annotationPlugin, dataZoomPlugin, dataLabelsPlugin, accessibilityPlugin };
777
777
  export { applyPreferencesToSpec, createPreferencesStore, defaultPreferences, mergePreferences, mergeThemePreference, mountChartSettings, normalizePreferences, validatePreferences };
778
- export const iChart = { version: '2.0.20', createChart, ChartValidationError, inspectData, normalizeData, binData, applyTransforms, data, getCapabilities, getChartCapability, getPreferenceCapabilities, planChart, recommend, explainChart, contrastRatio, planStyle, resolveTheme, styleCapabilities, themeModes, themePalettes, themePresets, validateThemeContrast, createPreferencesStore, defaultPreferences, normalizePreferences, mergePreferences, validatePreferences, applyPreferencesToSpec, mountChartSettings, annotationPlugin, dataZoomPlugin, dataLabelsPlugin, accessibilityPlugin, getBusinessSchema, inspectDataSchema, validateData, getEditCapabilities, validateEdit, previewEdit, commitPreview, validateRecipe, normalizeProjectCalendar, applyWorkingCalendar, normalizeDependencies, analyzeSchedule, analyzeBurndownSeries, analyzeCapacity, buildCapacityView, buildCumulativeFlowSeries, buildVelocitySeries, buildReleaseForecast, buildRiskMatrixSeries, buildIssueAgingSeries, normalizeLinkedFilters, normalizeLinkedSelection, filterProjectRows, createLinkedProjectState, linkedRecordId };
778
+ export const iChart = { version: '2.0.21', createChart, ChartValidationError, inspectData, normalizeData, binData, applyTransforms, data, getCapabilities, getChartCapability, getPreferenceCapabilities, planChart, recommend, explainChart, contrastRatio, planStyle, resolveTheme, styleCapabilities, themeModes, themePalettes, themePresets, validateThemeContrast, createPreferencesStore, defaultPreferences, normalizePreferences, mergePreferences, validatePreferences, applyPreferencesToSpec, mountChartSettings, annotationPlugin, dataZoomPlugin, dataLabelsPlugin, accessibilityPlugin, getBusinessSchema, inspectDataSchema, validateData, getEditCapabilities, validateEdit, previewEdit, commitPreview, validateRecipe, normalizeProjectCalendar, applyWorkingCalendar, normalizeDependencies, analyzeSchedule, analyzeBurndownSeries, analyzeCapacity, buildCapacityView, buildCumulativeFlowSeries, buildVelocitySeries, buildReleaseForecast, buildRiskMatrixSeries, buildIssueAgingSeries, normalizeLinkedFilters, normalizeLinkedSelection, filterProjectRows, createLinkedProjectState, linkedRecordId };
package/src/spec.mjs CHANGED
@@ -118,6 +118,7 @@ const presentationEncodingKeys = new Set(['title', 'format', 'labels', 'legend']
118
118
  const diagramTypes = new Set(['flow', 'swimlane', 'architecture', 'mindmap']);
119
119
  const diagramFields = ['nodes', 'edges', 'lanes', 'groups', 'layers', 'boundaries'];
120
120
  const nonCartesianAnalysisTypes = new Set(['pie', 'funnel', 'gauge', 'heatmap', 'radar']);
121
+ const knownSpecKeys = new Set(['version', 'type', 'renderer', 'container', 'chartId', 'width', 'height', 'padding', 'colors', 'background', 'locale', 'data', 'encoding', 'title', 'legend', 'grid', 'labels', 'xAxis', 'yAxis', 'domain', 'colorScale', 'indicators', 'innerRadius', 'stack', 'transform', 'criticalPath', 'nodes', 'edges', 'lanes', 'layers', 'boundaries', 'groups', 'diagram', 'project', 'interaction', 'editing', 'accessibility', 'branding', 'theme', 'preferences', 'preferencesStore', 'plugins', 'schema', 'validationOptions', 'emptyText', 'responsive', 'view', 'context', 'intent', 'tasks', 'events']);
121
122
 
122
123
  function finiteDomain(domain) {
123
124
  return Array.isArray(domain) && domain.length === 2 && domain.every(value => Number.isFinite(Number(value))) && Number(domain[1]) > Number(domain[0]);
@@ -179,11 +180,12 @@ export function normalizeSpec(input = {}) {
179
180
  export function validateSpec(input = {}) {
180
181
  const spec = normalizeSpec(input);
181
182
  const errors = [], warnings = [], normalizations = [];
183
+ Object.keys(input || {}).filter(key => !knownSpecKeys.has(key)).forEach(key => warnings.push({ code: 'UNKNOWN_SPEC_OPTION', path: key, message: `Top-level option ${key} is not part of the iChart.js Spec contract and will be ignored.`, expected: [...knownSpecKeys].sort(), suggestion: 'Remove the option or place host metadata outside the chart Spec.' }));
182
184
  if (typeof input.title === 'string') {
183
- normalizations.push({ path: 'title', from: 'string', to: 'title.text' });
185
+ normalizations.push({ code: 'NORMALIZED_TITLE', path: 'title', from: 'string', to: 'title.text' });
184
186
  warnings.push({ code: 'NORMALIZED_TITLE', path: 'title', message: 'String titles are normalized to title.text.', suggestion: 'Prefer title: { text: "..." } for an explicit title contract.' });
185
187
  } else if (input.title && typeof input.title === 'object' && !Array.isArray(input.title) && input.title.text === undefined && typeof input.title.label === 'string') {
186
- normalizations.push({ path: 'title.label', from: 'title.label', to: 'title.text' });
188
+ normalizations.push({ code: 'NORMALIZED_TITLE', path: 'title.label', from: 'title.label', to: 'title.text' });
187
189
  warnings.push({ code: 'NORMALIZED_TITLE', path: 'title.label', message: 'title.label is normalized to title.text.', suggestion: 'Use title: { text: "..." }.' });
188
190
  } else if (input.title !== undefined && (!input.title || typeof input.title !== 'object' || Array.isArray(input.title) || (input.title.text === undefined && input.title.subtitle === undefined))) {
189
191
  warnings.push({ code: 'INVALID_TITLE', path: 'title', message: 'Title must be a string or an object with text and/or subtitle.', suggestion: 'Use title: { text: "...", subtitle: "..." }.' });
@@ -33,7 +33,7 @@ export function validateData(input, schema, options = {}) {
33
33
  if (!Object.hasOwn(object, name)) {
34
34
  if (Object.hasOwn(field, 'default')) {
35
35
  object[name] = copyJSON(field.default);
36
- normalizations.push({ path, beforePresent: false, after: copyJSON(object[name]), reason: 'default' });
36
+ normalizations.push({ code: 'DEFAULT_VALUE', path, beforePresent: false, after: copyJSON(object[name]), reason: 'default' });
37
37
  } else { if (field.required) errors.push(issue('REQUIRED', path, 'Required field is missing.')); return; }
38
38
  }
39
39
  const value = object[name];
@@ -67,7 +67,7 @@ export function validateData(input, schema, options = {}) {
67
67
  if (!isRecord(row)) { errors.push(issue('INVALID_RECORD', path, 'Each row must be an object.')); return; }
68
68
  if (options.progressUnit === 'ratio' && schema.name === 'project-task' && Object.hasOwn(row, 'progress')) {
69
69
  if (typeof row.progress !== 'number' || row.progress < 0 || row.progress > 1) errors.push(issue('PROGRESS_UNIT', `${path}.progress`, 'Ratio progress must be between 0 and 1.'));
70
- else { const before = row.progress; row.progress *= 100; normalizations.push({ path: `${path}.progress`, beforePresent: true, before, after: row.progress, reason: 'ratio-to-percent' }); }
70
+ else { const before = row.progress; row.progress *= 100; normalizations.push({ code: 'PROGRESS_UNIT_CONVERSION', path: `${path}.progress`, beforePresent: true, before, after: row.progress, reason: 'ratio-to-percent' }); }
71
71
  }
72
72
  Object.keys(row).filter(key => !Object.hasOwn(schema.fields, key)).forEach(key => errors.push(issue('UNKNOWN_FIELD', `${path}.${key}`, 'Field is not declared in the business schema.')));
73
73
  Object.entries(schema.fields).forEach(([name, field]) => normalizeField(row, name, field, `${path}.${name}`));
package/types/index.d.ts CHANGED
@@ -27,12 +27,12 @@ export class ChartValidationError extends Error { name: 'ChartValidationError';
27
27
  export interface DataFieldInfo { name: string; type: 'quantitative' | 'temporal' | 'category' | 'unknown'; role: 'identifier' | 'measure' | 'temporal-dimension' | 'dimension'; unit: string | null; cardinality: number; validCount: number; nullCount: number; min?: number; max?: number; temporalMin?: string; temporalMax?: string; }
28
28
  export interface DataInspection { version: '1.0'; rows: number; fields: DataFieldInfo[]; dimensions: string[]; measures: string[]; temporalFields: string[]; missingValueCount: number; warnings: Diagnostic[]; }
29
29
  export interface ChartCapability { type: ChartType; family: string; intents: string[]; required: string[]; optional: string[]; dataShapes: string[]; interactions: string[]; features: Record<string, 'supported' | 'not-applicable' | 'degraded'>; renderers: Array<'canvas' | 'svg'>; exports: string[]; limits: Record<string, number>; }
30
- export interface RuntimeCapabilities { version: '2.0'; contractVersion: '1.1'; chartTypes: ChartType[]; charts: Record<ChartType, ChartCapability>; intents: string[]; locale?: { default: string; recommended: string[]; appliesTo: string[]; inputDates: string }; renderers: Array<'canvas' | 'svg'>; interactions: string[]; exports: Array<'png' | 'svg' | 'json' | 'jpeg'>; chartModes?: Record<string, unknown>; conversationalWorkflow?: { version: '1.0'; steps: string[]; routing: Record<string, string>; confirmation: string[]; selfCheck: string[] }; diagram?: { flowNodeKinds?: FlowNodeKind[]; flowBranchEdges?: { label: string; minimumOutgoing: number }; flowLoops?: string; flowConnectors?: { kind: FlowNodeKind; linking: string }; [key: string]: unknown }; styleSystem: { modes: ThemeMode[]; presets: ThemePreset[]; palettes: ThemePalette[]; switchable: boolean; automatic: boolean; [key: string]: unknown }; preferences?: { version: '1.0'; scopes: string[]; persistence: string[]; fields: string[]; agentAdjustable: boolean; interactiveSettingsUI: boolean; precedence: string[]; schema: PreferenceCapabilities }; headless: { preview?: boolean; json: boolean; svg: boolean; png: boolean | string }; export: { types: string[]; mime: Record<string, string>; browser: Record<string, boolean>; headless: Record<string, string | boolean>; methods: string[]; options: Record<string, unknown>; branding: Record<string, unknown> }; branding: { defaultEnabled: boolean; signature: string; options: Record<string, unknown> }; [key: string]: unknown; }
30
+ export interface RuntimeCapabilities { version: '2.0'; contractVersion: '1.1'; chartTypes: ChartType[]; charts: Record<ChartType, ChartCapability>; intents: string[]; locale?: { default: string; recommended: string[]; appliesTo: string[]; inputDates: string }; renderers: Array<'canvas' | 'svg'>; interactions: string[]; exports: Array<'png' | 'svg' | 'json' | 'jpeg'>; chartModes?: Record<string, unknown>; conversationalWorkflow?: { version: '1.0'; steps: string[]; routing: Record<string, string>; confirmation: string[]; selfCheck: string[] }; agentReliability?: Record<string, unknown>; diagram?: { flowNodeKinds?: FlowNodeKind[]; flowBranchEdges?: { label: string; minimumOutgoing: number }; flowLoops?: string; flowConnectors?: { kind: FlowNodeKind; linking: string }; [key: string]: unknown }; styleSystem: { modes: ThemeMode[]; presets: ThemePreset[]; palettes: ThemePalette[]; switchable: boolean; automatic: boolean; [key: string]: unknown }; preferences?: { version: '1.0'; scopes: string[]; persistence: string[]; fields: string[]; agentAdjustable: boolean; interactiveSettingsUI: boolean; precedence: string[]; schema: PreferenceCapabilities }; headless: { preview?: boolean; json: boolean; svg: boolean; png: boolean | string }; export: { types: string[]; mime: Record<string, string>; browser: Record<string, boolean>; headless: Record<string, string | boolean>; methods: string[]; options: Record<string, unknown>; branding: Record<string, unknown> }; branding: { defaultEnabled: boolean; signature: string; options: Record<string, unknown> }; [key: string]: unknown; }
31
31
  export interface ChartPlan { version: '1.0'; intent: string; intentKnown?: boolean; intentSuggestions?: string[]; fallbackUsed?: boolean; primary: ChartType; alternatives: ChartType[]; confidence: number; reasons: string[]; requiredFields: string[]; suggestedEncodings: { dimension: string | null; measure: string | null; secondaryMeasure: string | null }; assumptions: string[]; warnings: Diagnostic[]; unsupportedRequests: string[]; nextActions: string[]; capability: ChartCapability; styleRecommendation: StyleRecommendation; data: DataInspection; }
32
32
  export interface AxisState { rawDomain: [number, number]; domain: [number, number]; ticks: number[]; step: number | null; policy: 'nice' | 'raw' | 'explicit'; }
33
33
  export interface TimeAxisState { orientation: 'horizontal'; field: 'date'; coordinate: 'x'; rowCoordinate: 'y'; domain: [number, number]; domainISO: [string, string]; }
34
34
  export interface ChartHealth { version: '1.0'; status: 'ready' | 'degraded' | 'empty'; renderable: boolean; issues: string[]; metrics: { warnings: number; suppressedLabels: number; clampedValues: number; renderedMarks: number }; }
35
- export interface ChartExplanation { version: '1.0'; type: ChartType; family: string; purpose: string; renderer: Renderer; dataCount: number; encodings: Record<string, string | string[]>; transforms: string[]; interactions: string[]; assumptions: string[]; warnings: Diagnostic[]; axes?: { y?: AxisState; right?: AxisState } | null; timeAxis?: TimeAxisState | null; health?: ChartHealth; style: Partial<StyleRecommendation> & { name?: string }; lineage: { recordIds: string[]; sourcePreserved: boolean }; accessibility: { enabled: boolean; summary: string }; }
35
+ export interface ChartExplanation { version: '1.0'; type: ChartType; family: string; purpose: string; renderer: Renderer; dataCount: number; encodings: Record<string, string | string[]>; transforms: string[]; interactions: string[]; effective?: { renderer?: Renderer; title?: ChartTitle; legend?: ChartLegend; grid?: Record<string, unknown>; labels?: ChartLabels; interaction?: ChartInteraction; branding?: ChartSpec['branding'] }; assumptions: string[]; normalizations?: Diagnostic[]; warnings: Diagnostic[]; axes?: { y?: AxisState; right?: AxisState } | null; timeAxis?: TimeAxisState | null; health?: ChartHealth; style: Partial<StyleRecommendation> & { name?: string }; lineage: { recordIds: string[]; sourcePreserved: boolean }; accessibility: { enabled: boolean; summary: string }; }
36
36
  export interface ChartInteraction { tooltip?: boolean; hover?: boolean; click?: boolean; crosshair?: boolean; zoom?: boolean; pan?: boolean; brush?: boolean; drag?: boolean; edgeDrag?: boolean; portConnect?: boolean; keyboard?: boolean; [key: string]: boolean | undefined; }
37
37
  export interface ChartEditing { enabled?: boolean; mode?: 'command' | string; requireConfirmation?: boolean; allowDelete?: boolean; allowStructuralChanges?: boolean; }
38
38
  export interface ChartTitle { text?: string; subtitle?: string; label?: string; }