<!-- Generated by scripts/generate-ai-task-packets.mjs; edit ai/tasks/correct-published-chart.json. -->

# Correct a chart after its source changes

Apply a source correction, replace affected claims explicitly, and preserve the context a later reader needs.

Source package: semiotic@3.12.1. Channel: source.
Source revision: sha256:5408356821eb385c5982f16109541d3a7beb09541707e8b1ece3ed8e338f9864. Verified source checkout guidance; npm package, hosted site and MCP deployment identities require separate checks.

## The job

A source value changed. Update the chart and identify conclusions that still need review.

Data: Synthetic regional totals: North 12, South 30, West 18. Correct West to 36; the new total is 78 and West becomes largest.

## Fit and constraints

- Use when published chart claims and source identity must remain inspectable across revisions.
- Artifact sidecars are optional; ordinary chart rendering remains available without project notes.

- A matching hash does not authenticate the source or prove arbitrary prose true.
- A generated revision or transfer report does not grant publication approval or record a human review.

## Smallest working path

Component: BarChart from `semiotic/ordinal`. Usage: static. Data prop required in this mode: true.

### Correction and handoff controls

React browser; source: docs/src/pages/tasks/examples/SourceCorrectionExample.tsx.

```tsx
import { useMemo, useState } from "react"
import { BarChart } from "semiotic/ordinal"
import {
  correctedRows,
  inspectSourceHandoff,
  originalRows,
  originalSourceArtifact,
  reviseSourceArtifact,
  sourceChartProps,
  sourceCorrectionNote,
  sourceCorrectionSidecar,
} from "./source-correction"
import "./source-correction.css"

function downloadText(filename: string, content: string, type = "application/json") {
  const url = URL.createObjectURL(new Blob([content], { type }))
  const link = document.createElement("a")
  link.href = url
  link.download = filename
  link.click()
  setTimeout(() => URL.revokeObjectURL(url), 0)
}

export default function SourceCorrectionExample() {
  const original = useMemo(originalSourceArtifact, [])
  const [revised, setRevised] = useState<ReturnType<typeof reviseSourceArtifact>>()
  const [message, setMessage] = useState("Original revision: total 60; South leads with 30 units.")
  const [includeSidecar, setIncludeSidecar] = useState(true)
  const [includeNote, setIncludeNote] = useState(false)
  const current = revised ?? original
  const props = current.props as ReturnType<typeof sourceChartProps>
  const sidecar = useMemo(() => sourceCorrectionSidecar(current.contract), [current.contract])
  const handoff = useMemo(
    () => inspectSourceHandoff(props, includeSidecar ? JSON.parse(sidecar.content) : undefined),
    [props, includeSidecar, sidecar.content],
  )

  function attemptCorrection(reassess: boolean) {
    try {
      const result = reviseSourceArtifact(reassess)
      setRevised(result)
      setMessage(
        "Correction prepared: total 78; West leads with 36 units. Two previous claims were superseded. Human editorial review remains pending.",
      )
    } catch (error) {
      setMessage(`Correction refused: ${error instanceof Error ? error.message : String(error)}`)
    }
  }

  return (
    <section className="source-correction-example" aria-label="Source correction demonstration">
      <p>
        The source corrects West from 18 to 36 units. That changes the total and the leading region.
        Updating the bars requires reviewing both conclusions. These are synthetic teaching values.
      </p>
      <table>
        <caption>The source correction, in units</caption>
        <thead>
          <tr>
            <th scope="col">Region</th>
            <th scope="col">Original</th>
            <th scope="col">Corrected</th>
          </tr>
        </thead>
        <tbody>
          {originalRows.map((row, index) => (
            <tr key={row.region}>
              <th scope="row">{row.region}</th>
              <td>{row.total}</td>
              <td>{correctedRows[index].total}</td>
            </tr>
          ))}
          <tr>
            <th scope="row">Total</th>
            <td>60</td>
            <td>78</td>
          </tr>
        </tbody>
      </table>
      <div className="source-correction-actions">
        <button type="button" disabled={Boolean(revised)} onClick={() => attemptCorrection(false)}>
          Try changing data only
        </button>
        <button type="button" disabled={Boolean(revised)} onClick={() => attemptCorrection(true)}>
          Correct data and both claims
        </button>
        <button
          type="button"
          onClick={() => {
            setRevised(undefined)
            setMessage("Original revision restored: total 60; South leads with 30 units.")
          }}
        >
          Reset example
        </button>
      </div>
      <p role="status" data-testid="source-correction-status">
        {message}
      </p>
      <BarChart {...props} />
      <p>
        <strong>Displayed revision {current.contract.artifact.revision}.</strong> {props.summary}
      </p>
      <ul aria-label="Claim history">
        {current.contract.claims.map((claim) => (
          <li key={claim.id}>
            <strong>{claim.status}</strong>: {claim.text}
            {claim.supersedes?.length ? ` Replaces ${claim.supersedes.join(", ")}.` : ""}
          </li>
        ))}
      </ul>
      <p>
        Supported here means an authored claim about this fixture, with arithmetic checked in its
        tests. The contract does not prove prose true. No human editorial approval is recorded.
        {revised
          ? ` The revision evaluation is ${revised.evaluation.status}; publishable is ${String(revised.publishable)}.`
          : ""}
      </p>
      {revised && (
        <details>
          <summary>Inspect correction identities and open checks</summary>
          <p>
            Original data identity: <code>{original.contract.artifact.dataFingerprint}</code>
          </p>
          <p>
            Revised data identity: <code>{revised.contract.artifact.dataFingerprint}</code>
          </p>
          <ul>
            {revised.evaluation.manualChecks.map((check) => (
              <li key={check}>{check}</li>
            ))}
          </ul>
          <pre>{JSON.stringify(revised.contract.contestability?.corrections, null, 2)}</pre>
        </details>
      )}
      <details>
        <summary>Optional handoff: see what another reader receives</summary>
        <p>
          The chart configuration includes its values. The separate sidecar carries claims, evidence
          identifiers and correction history. The optional note records project context. You can
          keep the chart without either file.
        </p>
        <label>
          <input
            type="checkbox"
            checked={includeSidecar}
            onChange={(event) => setIncludeSidecar(event.target.checked)}
          />{" "}
          Include correction sidecar
        </label>
        <label>
          <input
            type="checkbox"
            checked={includeNote}
            onChange={(event) => setIncludeNote(event.target.checked)}
          />{" "}
          Include project maintenance note
        </label>
        <p role="status" data-testid="source-handoff-status">
          {handoff.message}
        </p>
        <p>
          An image alone cannot establish the source values, correction history or review status.
        </p>
        <div className="source-correction-actions">
          <button
            type="button"
            onClick={() =>
              downloadText(
                "regional-totals.config.json",
                JSON.stringify({ component: "BarChart", props }, null, 2),
              )
            }
          >
            Download chart configuration
          </button>
          {includeSidecar && (
            <button
              type="button"
              onClick={() => downloadText(sidecar.sidecarPath, sidecar.content)}
            >
              Download correction sidecar
            </button>
          )}
          {includeNote && (
            <button
              type="button"
              onClick={() =>
                downloadText(
                  "regional-totals-maintenance.md",
                  sourceCorrectionNote,
                  "text/markdown",
                )
              }
            >
              Download maintenance note
            </button>
          )}
        </div>
        {includeNote && <pre>{sourceCorrectionNote}</pre>}
      </details>
    </section>
  )
}
```

### Source correction and portable context

Host authoring and transfer; source: docs/src/pages/tasks/examples/source-correction.ts.

```ts
import {
  buildArtifactContract,
  createAdjacentArtifactSidecar,
  createArtifactPacket,
  evaluateArtifact,
  fingerprintValue,
  prepareArtifactRevision,
  validateArtifactPacket,
  type ArtifactContract,
  type Claim,
  type EvidenceRef,
  type PrepareArtifactRevisionOptions,
} from "semiotic/artifact"
import type { BarChartProps } from "semiotic/ordinal"

export const originalRows = [
  { region: "North", total: 12 },
  { region: "South", total: 30 },
  { region: "West", total: 18 },
]

export const correctedRows = [
  { region: "North", total: 12 },
  { region: "South", total: 30 },
  { region: "West", total: 36 },
]

// Fixed teaching-fixture times describe the example, not a verification run.
const originalAt = "2026-08-01T12:00:00Z"
export const correctionAt = "2026-08-02T12:00:00Z"
const reason = "The source corrected West from 18 to 36 units."

export function sourceChartProps(data = originalRows) {
  const sum = data.reduce((total, row) => total + row.total, 0)
  const largest = data.reduce((winner, row) => (row.total > winner.total ? row : winner))
  return {
    data: data.map((row) => ({ ...row })),
    categoryAccessor: "region",
    valueAccessor: "total",
    title: "Regional totals",
    description: "Three synthetic regional totals, measured in the same unit.",
    summary: `${largest.region} has the largest total: ${largest.total} units. All regions total ${sum} units.`,
    accessibleTable: true,
    height: 280,
    responsiveWidth: true,
  } satisfies BarChartProps<(typeof originalRows)[number]>
}

function sourceEvidence(revision: "1" | "2"): EvidenceRef {
  return {
    id: `regional-source-v${revision}`,
    role: "source-data",
    label: `Synthetic regional totals, revision ${revision}`,
    source: { name: "Authored teaching fixture", version: revision },
    dataVersion: revision,
    fingerprint: fingerprintValue(revision === "1" ? originalRows : correctedRows).fingerprint,
    observedAt: revision === "1" ? originalAt : correctionAt,
  }
}

function claims(revision: "1" | "2"): Claim[] {
  return [
    {
      id: `regional-total-v${revision}`,
      text: revision === "1" ? "The total is 60 units." : "The total is 78 units.",
      kind: "aggregation",
      status: "supported",
      evidenceIds: [`regional-source-v${revision}`],
      scope: { unit: "units", coverage: "three synthetic regions" },
      authoredBy: { kind: "system", name: "Deterministic teaching fixture" },
      review: { status: "proposed" },
    },
    {
      id: `regional-largest-v${revision}`,
      text:
        revision === "1"
          ? "South has the largest total: 30 units."
          : "West has the largest total: 36 units.",
      kind: "observation",
      status: "supported",
      evidenceIds: [`regional-source-v${revision}`],
      scope: { unit: "units", coverage: "three synthetic regions" },
      authoredBy: { kind: "system", name: "Deterministic teaching fixture" },
      review: { status: "proposed" },
    },
  ]
}

export function originalSourceArtifact() {
  const props = sourceChartProps()
  const contract = buildArtifactContract("BarChart", props, {
    id: "regional-totals",
    revision: "1",
    intents: ["comparison"],
    purpose: { stakes: "informational" },
    claims: claims("1"),
    evidence: [sourceEvidence("1")],
    time: {
      observedAt: originalAt,
      presentation: { state: "historical", label: "Original synthetic source" },
      revision: { status: "original" },
    },
    reception: {
      channels: [{ channel: "visual" }, { channel: "agent" }],
      description: props.description,
      dataFallback: true,
    },
    form: {
      chartFamily: "ordinal",
      whyThisForm: "Bar lengths compare totals in the same unit.",
    },
    contestability: { sourceRequestsAllowed: true },
    accountability: {
      authors: [{ kind: "system", name: "Semiotic documentation fixture" }],
      reviews: [{ id: "editorial-review", status: "pending" }],
    },
    inheritance: { privacy: "public", rawDataDefault: "exclude" },
    fieldStatus: {
      "accountability.reviews": {
        status: "manual",
        reason: "This teaching example records no human editorial approval.",
        suppliedBy: "system",
      },
    },
  })
  return { props, contract }
}

export function sourceCorrectionOptions(current: ArtifactContract): PrepareArtifactRevisionOptions {
  return {
    revision: "2",
    data: correctedRows.map((row) => ({ ...row })),
    evidence: [...current.evidence, sourceEvidence("2")],
    presentation: {
      summary: sourceChartProps(correctedRows).summary,
      description: "Corrected synthetic regional totals; West changed from 18 to 36 units.",
    },
    time: {
      observedAt: correctionAt,
      presentation: { state: "historical", label: "Corrected synthetic source" },
      revision: { status: "corrected", reason, correctionId: "regional-total-correction" },
    },
    claimTransitions: claims("2").map((replacement, index) => ({
      action: "supersede",
      previousClaimId: claims("1")[index].id,
      replacement,
      correction: {
        id: index === 0 ? "regional-total-correction" : "regional-largest-correction",
        reason,
        createdAt: correctionAt,
        createdBy: { kind: "system", name: "Deterministic teaching fixture" },
      },
    })),
    policy: "exploratory",
    now: correctionAt,
    recommendRepresentation: false,
    groundingChannels: ["agent"],
  }
}

export function reviseSourceArtifact(reassessClaims = true) {
  const original = originalSourceArtifact()
  const options = sourceCorrectionOptions(original.contract)
  return prepareArtifactRevision("BarChart", original.props, original.contract, {
    ...options,
    claimTransitions: reassessClaims ? options.claimTransitions : [],
  })
}

export function sourceCorrectionSidecar(contract: ArtifactContract) {
  return createAdjacentArtifactSidecar(
    createArtifactPacket(contract, {
      format: "static-package",
      includeEvidenceSamples: false,
    }),
    "regional-totals",
  )
}

export const sourceCorrectionNote = `Regional totals: maintenance context

Purpose: compare three synthetic totals in the same unit.
Component: BarChart from semiotic/ordinal.
Choice rationale: React charting with exact values and explicit correction history.
Constraint: preserve the project's dependency policy; changed requirements may justify another library.
Version: inspect the installed package and lockfile before editing.
Verification: this optional note stores no execution result or human approval.
Recheck: category/value mapping, arithmetic, summary, claim transitions and accessible route.
Limits: a matching fingerprint does not authenticate the source or prove arbitrary prose.
Files: retain chart configuration/data separately from the artifact-contract sidecar.
Documentation: /tasks/correct-published-chart (check its build identity against your installation).
`

/** A missing sidecar carries no evidence of source identity or prior review. */
export function inspectSourceHandoff(props: ReturnType<typeof sourceChartProps>, packet?: unknown) {
  if (packet === undefined) {
    return {
      status: "unavailable" as const,
      message:
        "Correction context unavailable. Chart values remain available, but source identity, previous claims and review history were not transferred.",
    }
  }
  const validation = validateArtifactPacket(packet)
  if (!validation.valid || !validation.packet) {
    return {
      status: "refused" as const,
      message: `Sidecar refused: ${validation.errors.join("; ")}`,
    }
  }
  const contract = validation.packet.contract
  const evaluation = evaluateArtifact("BarChart", props, contract, {
    policy: "exploratory",
    now: correctionAt,
    recommendRepresentation: false,
  })
  if (
    evaluation.obligations.some(({ id, status }) => id.startsWith("identity.") && status !== "pass")
  ) {
    return {
      status: "refused" as const,
      message:
        "Sidecar refused: its identity does not establish a match with this chart configuration and data.",
    }
  }
  return {
    status: "available" as const,
    message: `Revision ${contract.artifact.revision} correction context matches this chart. Human editorial approval remains unrecorded.`,
    contract,
  }
}
```

### Example styles

Browser presentation; source: docs/src/pages/tasks/examples/source-correction.css.

```css
.source-correction-example {
  min-width: 0;
}

.source-correction-example table {
  border-collapse: collapse;
  margin-block: 1rem;
  width: 100%;
}

.source-correction-example th,
.source-correction-example td {
  border-bottom: 1px solid var(--border-color, #ccc);
  padding: 0.5rem;
  text-align: left;
}

.source-correction-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-block: 1rem;
}

.source-correction-actions button {
  font: inherit;
  padding: 0.45rem 0.7rem;
}

.source-correction-example label {
  display: block;
  margin-block: 0.6rem;
}

.source-correction-example pre {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.source-correction-example code {
  overflow-wrap: anywhere;
}
```

## Check the result

Evidence state: stale. Only the content digests and environment recorded here; not a release range or an adoption result.

- Changing data without revisiting active claims is refused.
- The original total 60 and South-largest claims are superseded; replacements describe total 78 and West 36.
- The original chart and evidence remain intact while corrected identities change.
- Exported context retains the revision history; image-only delivery reports missing context.
- Publication and human-review obligations remain visible.

Run:

```sh
npx vitest run docs/src/pages/tasks/examples/source-correction.test.ts
npx playwright test --config playwright.docs-examples.config.ts integration-tests/docs-examples-tasks.spec.ts
```

No current execution result is recorded for these source contents.

Not assessed:

- Authenticity of upstream data and the truth of arbitrary editorial statements.
- Independent human review, publication policy and reader reception.
- Benefits to fresh-agent maintenance: the offline study defines cases but measures no adoption outcome.

## Repair and recheck

### prepareArtifactRevision refuses a changed source.

Supply explicit transitions for every affected active claim. Retain prior evidence and give the correction a distinct revision and new source identity.

Recheck: Inspect superseded claims, replacement IDs, correction history and remaining manual obligations.

### A recipient has the image but cannot inspect source or review context.

Provide chart config/data and an optional artifact packet or adjacent sidecar. The sidecar contains the contract and transfer report, not a complete dataset.

Recheck: Check the transfer's omissions. Treat unavailable context as unknown; do not manufacture approval.

## Exact API and mode guidance

Selected properties copied from ai/schema.json; retrieve the component resource for the full schema. React push mode follows the included behavior contracts.

Full component resource: semiotic://schema/BarChart. Machine packet: /tasks/correct-published-chart.json.

- **accessibility.description-props:** 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. 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.

- **props.data-required-by-usage-mode:** 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. 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.

- **rendering.renderchart-static-props:** 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. 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.

## Maintenance context

A published regional comparison needs source and claim continuity through a correction.

- Affected arithmetic, ranking, source identity, claim transitions and review obligations after a change.

Removing a project note does not break the chart. Image-only transfer loses inspectable source/review context; retain config/data separately from the artifact sidecar.

Read the installed package and lockfile before applying source guidance. A matching version string alone does not establish deployed parity. These notes are removable project context, subordinate to the current task and dependency policy.

## Behavioral update

Status: source-only. Released introduction: not established.

Assumption to revisit: Updating source rows or matching an identity automatically refreshes all conclusions and approves publication.

Use explicit claim transitions for the changed source, preserve old evidence and inspect correction history, transfer losses and outstanding human review.

- Both total and ranking claims must change in this fixture.
- A matching version string does not establish deployed parity or source authenticity.

This is current source guidance with no recorded release introduction or editorial approval.
