{
  "schemaVersion": 1,
  "generatedBy": "scripts/generate-ai-task-packets.mjs",
  "id": "compare-category-totals",
  "title": "Compare category totals you can check",
  "summary": "Compare three totals in React, inspect their exact values, and verify the server-rendered bar geometry.",
  "route": "/tasks/compare-category-totals",
  "identity": {
    "packageName": "semiotic",
    "packageVersion": "3.13.0",
    "channel": "source",
    "sourceRevision": "sha256:448fb56451832b74b618d93edc7fd486743fe7e9cd61667c99cc8dadd2ce0e59",
    "revisionKind": "content-digest",
    "runtime": {
      "digest": "sha256:031af3a116e306d86454a64eeaa702b39226262d38a422051add21190bcd45e7",
      "fileCount": 1464
    },
    "taskSources": {
      "digest": "sha256:72ed056490edf2a366f83200c74c6eda3bbbe3f7b285827479740d7208456bdc",
      "fileCount": 36
    },
    "availability": "Verified source checkout guidance; npm package, hosted site and MCP deployment identities require separate checks."
  },
  "job": "Compare categories and keep a usable route to the values when the picture is unavailable.",
  "dataShape": "One already aggregated, nonnegative total per category, all in the same unit. The synthetic fixture is North 12, South 30, West 18.",
  "fit": [
    "Use in an existing Semiotic React application or when structured chart access and schema-guided validation meet an explicit project requirement."
  ],
  "exclusions": [
    "A small chart change alone need not justify replacing a working visualization library.",
    "This fixture does not establish that an external source is accurate or that every reader can use the result."
  ],
  "api": {
    "name": "BarChart",
    "category": "ordinal",
    "importPath": "semiotic/ordinal",
    "schemaResourceUri": "semiotic://schema/BarChart",
    "renderable": true,
    "requiresLiveData": false,
    "description": "Vertical or horizontal bars for categorical comparisons.",
    "component": "BarChart",
    "usageMode": "static",
    "dataRequired": true,
    "schema": {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "description": "Array of data objects"
        },
        "categoryAccessor": {
          "type": "string",
          "description": "Key for category labels",
          "default": "category",
          "x-semiotic-runtime-types": [
            "string",
            "function"
          ]
        },
        "valueAccessor": {
          "type": "string",
          "description": "Key for bar values",
          "default": "value",
          "x-semiotic-runtime-types": [
            "string",
            "function"
          ]
        },
        "sort": {
          "type": [
            "boolean",
            "string"
          ],
          "description": "Sort bars: false, true, 'asc', 'desc', or comparator function",
          "default": false,
          "x-semiotic-runtime-types": [
            "boolean",
            "string",
            "function"
          ]
        },
        "orientation": {
          "type": "string",
          "enum": [
            "vertical",
            "horizontal"
          ],
          "default": "vertical"
        },
        "title": {
          "type": "string",
          "description": "Visible chart title and the chart's accessible name."
        },
        "description": {
          "type": "string",
          "description": "Concise accessible description that overrides the chart's generated aria-label."
        },
        "summary": {
          "type": "string",
          "description": "Screen-reader-only summary of the chart's key takeaway; include keyboard interaction guidance when relevant."
        },
        "accessibleTable": {
          "type": [
            "boolean",
            "object"
          ],
          "description": "Expose the chart data through Semiotic's screen-reader data table. Object form `{ portalTarget: string }` relocates its interactive UI to the DOM element with that ID.",
          "default": true,
          "oneOf": [
            {
              "type": "boolean"
            },
            {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "portalTarget"
              ],
              "properties": {
                "portalTarget": {
                  "type": "string",
                  "description": "ID of a DOM element outside any consumer-owned role=img wrapper. React callers may also pass an Element or a callback through the typed API."
                }
              }
            }
          ]
        },
        "height": {
          "type": "number",
          "default": 400
        },
        "responsiveWidth": {
          "type": "boolean"
        },
        "valueLabel": {
          "type": "string"
        }
      },
      "requiredForStatic": [
        "data"
      ]
    },
    "schemaScope": "Selected properties copied from ai/schema.json; retrieve the component resource for the full schema. React push mode follows the included behavior contracts.",
    "contracts": [
      {
        "id": "accessibility.description-props",
        "title": "Accessible chart text uses direct chart props",
        "summary": "High-level charts expose title for the visible name, description for a concise accessible description, summary for a screen-reader-only takeaway and interaction guidance, and accessibleTable for the data-table fallback.",
        "action": "Put title, description, summary, and accessibleTable directly on the chart component when they appear in its schema. If a consumer-owned role=img wraps the chart, use accessibleTable: { portalTarget: \"element-id\" } and render that target outside the image. For generated L1–L3 description or a navigable chart tree, use ChartContainer with chartConfig plus describe and/or navigable; do not invent frameProps fields."
      },
      {
        "id": "props.data-required-by-usage-mode",
        "title": "Data required by usage mode",
        "summary": "Static usage (`renderChart`, MCP previews, SSR snapshots, and copy/paste examples with immediate data) requires data in props. React push mode selects live ingestion by omitting data and mutating through a ref.",
        "action": "Pass usageMode=\"push\" to `semiotic-ai --doctor` when validating ref-based JSX with no data prop. Keep usageMode=\"static\" or omit it for renderChart/MCP/static configs where data must be present."
      },
      {
        "id": "rendering.renderchart-static-props",
        "title": "renderChart uses static props only",
        "summary": "MCP renderChart and semiotic/server renderChart render a single static SVG/PNG snapshot, never future ref pushes. semiotic/server accepts bounded data for realtime charts; MCP rendering excludes the five live Realtime components but includes TemporalHistogram.",
        "action": "Use complete static data for snapshots. For a Realtime component snapshot, use semiotic/server directly; for live behavior, return React code with a ref and do not promise MCP-rendered output."
      },
      {
        "id": "serialization.formatters-are-react-callbacks",
        "title": "Axis formatters are React callbacks",
        "summary": "xFormat, yFormat, categoryFormat, and valueFormat are callback props, not d3 format strings or axis-title strings. They are intentionally absent from JSON/MCP schemas and string values fail validation.",
        "action": "In serialized props, omit formatter callbacks and use xLabel, yLabel, categoryLabel, or valueLabel for axis titles. In React JSX, pass a function such as xFormat={value => formatAxis(value)}."
      }
    ]
  },
  "examples": [
    {
      "title": "React component",
      "environment": "React browser",
      "path": "docs/src/pages/tasks/examples/CategoryComparisonExample.tsx",
      "source": "import { useState } from \"react\"\nimport { BarChart } from \"semiotic/ordinal\"\nimport { categoryComparisonProps } from \"./category-comparison\"\n\nexport default function CategoryComparisonExample() {\n  const [orientation, setOrientation] = useState<\"vertical\" | \"horizontal\">(\"vertical\")\n  return (\n    <section aria-label=\"Category comparison example\" className=\"task-example\">\n      <p>Synthetic fixture: three already aggregated regional totals, all measured in units.</p>\n      <label>\n        Bar direction{\" \"}\n        <select\n          value={orientation}\n          onChange={(event) => setOrientation(event.target.value as typeof orientation)}\n        >\n          <option value=\"vertical\">Vertical</option>\n          <option value=\"horizontal\">Horizontal</option>\n        </select>\n      </label>\n      <BarChart {...categoryComparisonProps} orientation={orientation} responsiveWidth />\n      <p>\n        South accounts for 30 of the 60 units. Changing direction preserves the values and category\n        order.\n      </p>\n    </section>\n  )\n}\n",
      "digest": "sha256:1cbc3f7dc27a4114e0b1b208c4eab532230ef5fda623116f5c734a51582f764b"
    },
    {
      "title": "Shared fixture and chart props",
      "environment": "Browser and server",
      "path": "docs/src/pages/tasks/examples/category-comparison.ts",
      "source": "import type { BarChartProps } from \"semiotic/ordinal\"\n\nexport const regionalTotals = [\n  { region: \"North\", total: 12 },\n  { region: \"South\", total: 30 },\n  { region: \"West\", total: 18 },\n]\n\nexport const categoryComparisonProps = {\n  data: regionalTotals,\n  categoryAccessor: \"region\",\n  valueAccessor: \"total\",\n  sort: false,\n  title: \"Regional totals\",\n  description: \"Synthetic regional totals in the same unit: North 12, South 30, West 18.\",\n  summary:\n    \"South has the largest total, 30. The three regions total 60 units. Open the data table for exact values.\",\n  accessibleTable: true,\n  valueLabel: \"Units\",\n  height: 300,\n} satisfies BarChartProps<(typeof regionalTotals)[number]>\n",
      "digest": "sha256:385eb02b937bced7c4a7d6bbf88232ace90c56a7e06bb7935fdba37f0ee30c53"
    },
    {
      "title": "Optional server verification",
      "environment": "Node authoring only",
      "path": "docs/src/pages/tasks/examples/check-category-comparison.ts",
      "source": "// Server-side authoring only: keep validation and SVG rendering out of the React route.\nimport { prepareChart } from \"semiotic/ai/core\"\nimport { renderChartWithEvidence } from \"semiotic/server\"\nimport { categoryComparisonProps } from \"./category-comparison\"\n\nexport function checkCategoryComparison() {\n  const result = prepareChart(\n    { component: \"BarChart\", props: categoryComparisonProps },\n    { render: renderChartWithEvidence },\n  )\n  if (!result.ok || !result.evidence || !result.svg) {\n    throw new Error(result.reasons.join(\"; \") || \"Render evidence is unavailable\")\n  }\n  return result\n}\n",
      "digest": "sha256:a0e2cfe29c1985899f360b447b9bc223026d9954e26790adf30a5bf4473da56b"
    }
  ],
  "evidence": {
    "claimId": "task.compare-category-totals",
    "statement": "Compare three totals in React, inspect their exact values, and verify the server-rendered bar geometry.",
    "owner": "Semiotic repository maintainers",
    "status": "stale",
    "observed": null,
    "validity": "Only the content digests and environment recorded here; not a release range or an adoption result.",
    "invalidation": "Changes to runtime source, schemas, manifests, task examples, tests, verification tooling or this task definition invalidate the recorded result.",
    "commands": [
      "npx vitest run docs/src/pages/tasks/examples/category-comparison.test.ts",
      "npx playwright test --config playwright.docs-examples.config.ts integration-tests/docs-examples-tasks.spec.ts"
    ],
    "checks": {
      "unit": "docs/src/pages/tasks/examples/category-comparison.test.ts",
      "browser": "integration-tests/docs-examples-tasks.spec.ts"
    },
    "expected": [
      "North 12, South 30, West 18; total 60, with South largest.",
      "Three SVG bars retain 12:30:18 proportions and insertion order in both orientations.",
      "The browser data table retains the exact category/value mapping.",
      "An invalid value accessor is refused; the repaired configuration preserves the rows."
    ],
    "unassessed": [
      "Underlying source accuracy: this is a synthetic fixture.",
      "Assistive-technology reception and usability with actual readers.",
      "Organic discovery, model preference, and published-package parity."
    ]
  },
  "recovery": [
    {
      "symptom": "The chart has no useful values after a field was renamed.",
      "contractId": "props.data-required-by-usage-mode",
      "action": "Set valueAccessor to the actual numeric field, total, while preserving data. Run the server check and inspect the exact-value table.",
      "recheck": "The bars must encode 12, 30 and 18; a nonempty scene alone is insufficient."
    },
    {
      "symptom": "prepareChart returned ok, but there is no SVG.",
      "contractId": "rendering.renderchart-static-props",
      "action": "Inject renderChartWithEvidence from semiotic/server in Node authoring code when rendered evidence is required.",
      "recheck": "Inspect evidence and svg explicitly. Without a renderer, ok covers a different set of checks."
    }
  ],
  "continuation": {
    "rationale": "This project needs React rendering with inspectable category values.",
    "recheck": [
      "Category/value mapping, units, order, summary and the accessible table after source or dependency changes."
    ],
    "loss": "An image alone omits the source rows and verification context. Keep the config and fixture when the next reader needs exact values."
  },
  "update": {
    "status": "source-only",
    "introducedIn": null,
    "previousAssumption": "A valid configuration or a nonempty render establishes that the values are mapped correctly.",
    "action": "Run configuration checks, inspect the rendered geometry and compare the category/value table with independent fixture facts.",
    "recheck": [
      "Rendering evidence must be present when a render is claimed.",
      "An installed package version different from the packet's packageVersion needs matching guidance."
    ],
    "remaining": "Source truth and usability with actual readers remain separate obligations."
  },
  "definition": "ai/tasks/compare-category-totals.json"
}
