> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siftstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Annotations reference

> Status definitions and file attachment limits for Data Review Annotations, and creation, Rule, and Family behavior for Phase Annotations

export const MintTable = ({columns = [], rows = [], columnWidths = []}) => {
  const pushTextWithLineBreaks = (parts, text, keyBase) => {
    const segments = String(text).split(/\\n|\n/);
    segments.forEach((segment, idx) => {
      if (segment) {
        parts.push(<span key={`${keyBase}-text-${idx}`}>{segment}</span>);
      }
      if (idx < segments.length - 1) {
        parts.push(<br key={`${keyBase}-br-${idx}`} />);
      }
    });
  };
  const parseMarkdown = text => {
    if (text === null || text === undefined) return "";
    const str = String(text);
    const parts = [];
    let lastIndex = 0;
    const pattern = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*]+\*|\[([^\]]+)\]\(([^)]+)\))/g;
    let match;
    while (true) {
      match = pattern.exec(str);
      if (match === null) {
        break;
      }
      if (match.index > lastIndex) {
        pushTextWithLineBreaks(parts, str.substring(lastIndex, match.index), `before-${lastIndex}`);
      }
      const fullMatch = match[0];
      if (fullMatch.startsWith("`") && fullMatch.endsWith("`")) {
        parts.push(<code key={match.index}>{fullMatch.slice(1, -1)}</code>);
      } else if (fullMatch.startsWith("**") && fullMatch.endsWith("**")) {
        parts.push(<strong key={match.index}>{fullMatch.slice(2, -2)}</strong>);
      } else if (fullMatch.startsWith("*") && fullMatch.endsWith("*")) {
        parts.push(<em key={match.index}>{fullMatch.slice(1, -1)}</em>);
      } else if (fullMatch.startsWith("[")) {
        const linkText = match[2];
        const linkUrl = match[3];
        parts.push(<a key={match.index} href={linkUrl} className="text-black-600 dark:text-black-400">
            {linkText}
          </a>);
      }
      lastIndex = pattern.lastIndex;
    }
    if (lastIndex < str.length) {
      pushTextWithLineBreaks(parts, str.substring(lastIndex), `tail-${lastIndex}`);
    }
    if (parts.length > 0) {
      return parts;
    }
    const plainParts = [];
    pushTextWithLineBreaks(plainParts, str, "plain");
    return plainParts.length ? plainParts : str;
  };
  const safeColumns = Array.isArray(columns) ? columns : [];
  const safeRows = Array.isArray(rows) ? rows : [];
  const safeColumnWidths = Array.isArray(columnWidths) ? columnWidths : [];
  const hasColumnWidths = safeColumnWidths.some(w => w !== null && w !== undefined && w !== "");
  const toCssWidth = width => typeof width === "number" ? `${width}px` : String(width);
  const getColumnStyle = idx => {
    const rawWidth = safeColumnWidths[idx];
    if (rawWidth === null || rawWidth === undefined || rawWidth === "") {
      return undefined;
    }
    const width = toCssWidth(rawWidth);
    return {
      width,
      minWidth: width
    };
  };
  const containerStyle = hasColumnWidths ? undefined : {
    overflowX: "auto"
  };
  const tableStyle = hasColumnWidths ? {
    tableLayout: "fixed",
    width: "100%"
  } : {
    width: "max-content",
    minWidth: "100%"
  };
  if (!Array.isArray(columns) || !Array.isArray(rows) || !Array.isArray(columnWidths)) {
    console.warn("MintTable received invalid props:", {
      columns,
      rows,
      columnWidths
    });
  }
  if (!safeColumns.length && !safeRows.length) {
    return null;
  }
  return <div className="mint-table-container" style={containerStyle}>
      <table style={tableStyle}>
        {hasColumnWidths && <colgroup>
            {safeColumns.map((_, idx) => {
    const style = getColumnStyle(idx);
    return <col key={idx} style={style} />;
  })}
          </colgroup>}
        <thead>
          <tr>
            {safeColumns.map((col, idx) => <th key={idx} className="text-left" style={getColumnStyle(idx)}>
                <b>{parseMarkdown(col)}</b>
              </th>)}
          </tr>
        </thead>
        <tbody>
          {safeRows.map((row, rIdx) => {
    const safeRow = Array.isArray(row) ? row : [];
    return <tr key={rIdx}>
                {safeRow.map((cell, cIdx) => <td key={cIdx} style={getColumnStyle(cIdx)}>
                    {parseMarkdown(cell)}
                  </td>)}
              </tr>;
  })}
        </tbody>
      </table>
    </div>;
};

This page covers the status workflow and file attachment behavior for Data Review Annotations, and the creation, Rule, and Family behavior of Phase Annotations, described in [Phase Annotations](#phase-annotations) below. See [Annotation fields](/documentation/reference/explore-settings#annotation-fields) for the fields available when creating either type.

## Statuses

The following statuses apply to Data Review Annotations only. Phase Annotations have no status.

<MintTable
  columns={['State', 'Meaning', 'When to use it']}
  rows={[
['Open', 'The Annotation has not been reviewed. This is the default state when an Annotation is created.', 'Before any review has taken place.'],
['Failed', 'The Annotation was reviewed and the underlying telemetry represents a real problem that must be addressed.', 'When the finding requires follow-up action; for example, a sensor anomaly or a rule violation that cannot be accepted.'],
['Accepted', 'The Annotation was reviewed and determined to be valid or not requiring action.', 'When the finding is understood, expected, or otherwise does not require corrective action.']
]}
/>

## Transitioning between states

Use the status control in the Annotation details panel to transition an Annotation between **Open**, **Failed**, and **Accepted**. All state transitions are recorded in the Annotation's activity log with a timestamp and the user who made the change.

## How Annotation states appear in Reports

When a Rule is evaluated in a Report, the status displayed for that Rule is derived from the states of its Annotations:

* If all Annotations are **Open**: the Rule shows as Open.
* If at least one Annotation is **Failed**: the Rule shows as Failed, regardless of other Annotation states.
* If all Annotations are **Accepted**: the Rule shows as Accepted.
* If the Rule generated no Annotations: the Rule shows as **Passed**. Passed Rules are not shown in the Report by default; filter for Passed Rules to see them.

Changing an Annotation's state immediately updates the Rule's displayed status in the Report.

A Rule that generated a Phase Annotation is hidden from the Report's Rules panel by default, since Phase Annotations carry no Open, Failed, or Accepted state. Select **All** in the Rules panel filter to view the Rule and its Phase Annotation.

## File attachments

Files can be attached to Annotations and to individual comments on Annotations.

### Supported file types

<MintTable
  columns={['Type', 'Formats', 'In-platform viewing']}
  rows={[
['Image', '`.png`, `.jpg`, `.jpeg`', 'Yes; viewable directly in Sift'],
['Video', '`.mp4`, `.webm`', 'Yes; viewable directly in Sift'],
['All other file types', 'Any format', 'No; files can be uploaded and downloaded but must be opened externally']
]}
/>

### Size limits

<MintTable
  columns={['Limit', 'Value']}
  rows={[
['Maximum file size per upload', '30 GB'],
['Maximum number of files per Annotation', 'No enforced limit']
]}
/>

## Phase Annotations

A Phase Annotation marks a period or stage in a Run, such as "Engine Ignition" or "Max-Q." Unlike a Data Review Annotation, a Phase Annotation has no status, cannot be assigned to a user, and does not support file attachments.

### Create a Phase Annotation

Create a Phase Annotation manually in Explore using the **Annotations** tab or the **Select X** tool. See [Investigate a telemetry anomaly](/documentation/analyze/investigate-telemetry-anomaly#investigate-an-anomaly) for the steps, and [Annotation fields](/documentation/reference/explore-settings#annotation-fields) for the available fields.

A Rule can also generate a Phase Annotation instead of a Data Review Annotation when its condition evaluates to true, marking a period rather than flagging an issue for triage. In the Rule editor's **Annotation Details** section, select **Phase** instead of **Data Review** to configure this.

### Use in Families

An Annotation alignment on a Family resolves T-0 to the timestamp of a matching Annotation on each Run. Phase Annotations are the type used for this purpose, since they mark a named event without requiring the review status fields a Data Review Annotation carries. See [Configure alignments](/documentation/analyze/families/group-runs-into-a-family#step-4-configure-alignments).

### API behavior

Phase Annotations are created, retrieved, and updated through the same [Annotation API](/api/reference/protocol-buffers/annotations) as Data Review Annotations, distinguished by the `annotation_type` field. A Phase Annotation's `state` field must be left unset; setting it returns an error.

## Behavior

<MintTable
  columns={['Area', 'Details']}
  rows={[
['Archiving', 'Archiving an Annotation removes it from the active view but does not change its state. Archived Annotations can be restored.'],
['Attachments and archiving', 'Attachments are stored alongside their Annotation or comment and remain associated with it even if the Annotation is archived.'],
['Attachments and behavior', 'Attachments do not alter the content or behavior of the Annotation or comment they are added to.'],
['File size limit', 'Files that exceed 30 GB cannot be uploaded; the upload will fail.'],
['Rules excluded from evaluation', 'A Rule that was not included in the Report evaluation does not appear in the Report at all; not even as Passed.'],
['Live Rules and Reports', 'Live Rules that did not generate an Annotation during the original evaluation are not linked to the Report and will not be included if the Report is rerun. To evaluate them, generate a new Report.'],
['Re-running Rule evaluation', 'Re-running Rule evaluation against a Run creates new Annotations rather than updating or deduplicating the ones from a prior evaluation. When comparing Annotation output across repeated evaluations, deduplicate the results yourself.']
]}
/>

## Link to an Annotation

To open a specific Annotation directly, use its ID in the URL: `https://app.siftstack.com/annotation/<annotation-id>`.

## Related workflows

* [Detect and review issues in a Run](/documentation/review/detect-and-review-issues-in-a-run)
* [Triage and close out flagged issues](/documentation/review/triage-and-close-out-flagged-issues)
* [Group Runs into a Family](/documentation/analyze/families/group-runs-into-a-family)
