---
title: Present Manifests to your users
category: read
summary: Turn Manifests into one briefing for one reader. Write the opening from the scaffold's claims, label the evidence, and link every item to its source.
order: 16
updated: 2026-10-15
next: charts-tables-and-infographics
related: signals-briefs-and-records, run-and-edit-a-beacon
reference: /agent-guide.md, /synorb-instructions.md, /agents/personal-agent-onboarding.txt
---

Your user judges Synorb by what you show them, not by the data you pulled. The same Manifests can reach a person as one dense block of text, a bare list of headlines, or a briefing written for them. This guide shows how to produce the briefing. The contract it follows is on one screen in [The output contract](/agent-guides/output-contract), and the same rules ship in every response's `render.note`.

## When to use this

Use it for every delivery to a person: an on-demand answer, a daily Beacon check, a proactive ping, a first-run dry run. If a human will read it, lay it out.

Each part has one job:

- **The Brief** is the human narrative. Write the story from it.
- **The Signal** is for you. Use its Claims to verify, rank and compare, and to read exact numbers. Each Claim carries a confidence label (`stated`, `measured`, `implied` or `inferred`). Show that label next to the fact. Do not paste the Signal.
- **The source link** is for your user. Every item gets one, so they can go to the original.

## Smallest working call

Every Manifest delivery already carries `presentation_items`, one entry per Manifest, shaped for display. Pull a few:

```mcp billed
synorb-manifests
{"stream_ids": ["STREAM_ID"], "days": 2, "target_count": 5}
```

Then give the whole response to the Synorb reference formatter. It is one Python file with no dependencies, at [/agent-guides/reference/format_manifests.py](/agent-guides/reference/format_manifests.py). Copy it into your runtime, or port it to your language. Call it like this:

```text
text = format_manifests(response, mode="chat")    # or "voice", "report", "row"
table = comparison_table(response)                # "" when no facts compare
```

The formatter does these things for you:

1. It matches each `presentation_items[]` entry to its Manifest by `manifest_id`. It keeps every ID a string.
2. It ranks by `significance`, groups the items, and moves `low` items to an "also seen" line.
3. It links each item with `action.url`. If that is absent, it uses `source_url`, then the Manifest's `source.source_url`. If no valid link exists, it says so.
4. It shows one item for one article, also when the article arrives through several Streams.
5. It flags a thin source and shows hedged wording from the Brief as the Brief states it.
6. It never shows the Signal, and it never adds a link or a figure that Synorb did not deliver.

This is the file, unchanged. A test fails if this copy and the published file differ:

```python source=reference/format_manifests.py
"""Synorb reference formatter: turn a Manifest delivery into text a person reads.

Published at https://synorb.com/agent-guides/reference/format_manifests.py and
explained in https://synorb.com/agent-guides/present-manifests-to-your-users.
Standard library only. Python 3.9 or later. Copy it, or call it as it is:

    text = format_manifests(response, mode="chat")  # also "voice", "report", "row"
    table = comparison_table(response)  # "" when the facts do not compare

``response`` is the parsed JSON from POST /manifests/query, POST /manifests/exec,
GET /manifests/by-id/{id} or the MCP tool synorb-manifests: the whole envelope,
its ``data`` object, or a list of several of them (for example one per Stream).

Rules this code keeps:
* Every ID stays a string. A float ID has already lost digits, so it is refused.
* Each presentation_items[] entry is matched to its Manifest by manifest_id.
* The Signal is never shown. Text comes from presentation_items[] or the Brief.
* The link is action.url, then source_url, then the Manifest's own source URL.
  With no valid link the text says so. A link or a figure is never invented.
* One article that arrives through several Streams is one item. Two entries are
  one article when they share a manifest_id, a record_id, or an article URL
  (scheme and host case, a leading "www." and a trailing slash are ignored, as in
  Synorb billing). Different articles about one event stay separate items.
* Hedged or conditional wording in the Brief ("up to", "plans to") is shown as
  written and is never made firmer. Formatting cannot correct a wrong Brief, so
  every item keeps its source link for your user to check.
"""

from __future__ import annotations

import re
from urllib.parse import urlsplit

SIGNIFICANCE_RANK = {"high": 0, "medium": 1, "low": 2}
COMPANY = {"company_first_party", "corporate_blog"}
RESEARCH = {"academic_preprint", "peer_reviewed_journal"}
GROUPS = ("Company news", "Research", "Other updates")
MODES = ("chat", "voice", "report", "row")
DEFAULT_MAX_ITEMS = 5
UNCERTAIN_HEADLINE = "Status unknown, retry"
NO_LINK = "No source link was delivered for this item."
THIN = "Source is thin, open the original."
ORDINALS = ("Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth", "Ninth", "Tenth")
HEDGES = re.compile(
    r"\b(up to|plans? to|planned|planning to|(is|are) developing|in development|under development|"
    r"expect(s|ed) to|aims? to|intends? to|would|could|might|if|subject to|pending|proposed|"
    r"preview|projected|estimated)\b",
    re.IGNORECASE,
)
SENTENCE_END = re.compile(r"(?<=[.!?])\s+")
NUMBER = re.compile(r"^\s*([$€£]?)\s*(-?\d[\d,]*\.?\d*)\s*(%?)\s*$")


# ----------------------------------------------------------------------------- safe readers


def _text(value):
    """A clean string, or "" for anything that is not text."""
    return " ".join(value.split()) if isinstance(value, str) else ""


def _paragraphs(value):
    """Text with clean paragraphs, keeping the blank line between them."""
    if not isinstance(value, str):
        return ""
    return "\n\n".join(_text(p) for p in re.split(r"\n\s*\n", value) if _text(p))


def _map(value):
    return value if isinstance(value, dict) else {}


def _id(value):
    """An ID as an exact string. Floats are refused: 1.79e18 as a float has lost digits."""
    if isinstance(value, bool):
        return ""
    if isinstance(value, int):
        return str(value)
    return value.strip() if isinstance(value, str) else ""


def _url(value):
    """An absolute http(s) URL, or "" when the value is not one."""
    text = value.strip() if isinstance(value, str) else ""
    if not text or any(ch.isspace() for ch in text):
        return ""
    try:
        parts = urlsplit(text)
        _ = parts.port
    except ValueError:
        return ""
    if parts.scheme.lower() in ("http", "https") and parts.hostname and parts.username is None:
        return text
    return ""


def article_key(url):
    """The identity of an article URL. Two spellings of one article share a key."""
    if not _url(url):
        return ""
    parts = urlsplit(url.strip())
    host = (parts.hostname or "").lower()
    host = host[4:] if host.startswith("www.") else host
    if "." not in host:
        return ""
    netloc = host + (":" + str(parts.port) if parts.port else "")
    path = parts.path if (parts.query or parts.fragment) else parts.path.rstrip("/")
    key = parts.scheme.lower() + "://" + netloc + path
    return key + ("?" + parts.query if parts.query else "") + ("#" + parts.fragment if parts.fragment else "")


# ----------------------------------------------------------------------------- input


def _deliveries(response):
    """Each delivery ``data`` object in an envelope, a data object, or a list of them."""
    for one in response if isinstance(response, list) else [response]:
        one = _map(one)
        inner = one.get("data")
        if isinstance(inner, dict) and "presentation_items" not in one and "manifests" not in one:
            one = inner
        if one:
            yield one


def _facts(manifest, entry):
    """Brief facts: compact output has brief.facts, full output has brief.body.facts."""
    brief = _map(manifest.get("brief"))
    for facts in (brief.get("facts"), _map(brief.get("body")).get("facts"), entry.get("facts")):
        if isinstance(facts, list) and facts:
            return [f for f in facts if isinstance(f, dict)]
    return []


def _from_brief(manifest):
    """A display entry built from the Brief, for a Manifest with no presentation_items entry."""
    brief, source = _map(manifest.get("brief")), _map(manifest.get("source"))
    body = _map(brief.get("body"))
    title = _text(source.get("record_title")) or _text(brief.get("headline"))
    return {
        "manifest_id": manifest.get("manifest_id"),
        "title": title,
        "headline": _text(brief.get("headline")) or title,
        "one_liner": _text(body.get("tldr")) or _text(brief.get("tldr")),
        "full": _text(brief.get("summary")),
        "why_it_matters": _text(body.get("why_it_matters")) or _text(brief.get("why_it_matters")),
        "significance": _text(brief.get("significance")),
        "date": _text(manifest.get("published_date")),
        "category": _text(source.get("source_class")),
        "source_label": _text(source.get("source_channel_display")),
        "source_url": source.get("source_url"),
    }


def _link(entry, manifest):
    """(label, url) from fields Synorb delivered. url is "" when no valid one exists."""
    action = _map(entry.get("action"))
    label = _text(action.get("label")) or "Read source"
    source, record = _map(manifest.get("source")), _map(manifest.get("record"))
    for candidate in (action.get("url"), entry.get("source_url"), source.get("source_url"), record.get("url")):
        if _url(candidate):
            return label, _url(candidate)
    return label, ""


def build_items(response):
    """Match, merge and rank a delivery. Returns one dict per article, best first."""
    entries = []  # (presentation entry, the Manifest with the same manifest_id in the same delivery)
    for data in _deliveries(response):
        rows = data.get("manifests")
        rows = rows if isinstance(rows, list) else ([data] if "manifest_id" in data else [])
        local = {_id(r.get("manifest_id")): r for r in rows if isinstance(r, dict) and _id(r.get("manifest_id"))}
        presented = set()
        listed = data.get("presentation_items")
        for entry in listed if isinstance(listed, list) else []:
            if isinstance(entry, dict):
                entries.append((entry, _map(local.get(_id(entry.get("manifest_id"))))))
                presented.add(_id(entry.get("manifest_id")))
        for mid, row in local.items():
            if mid not in presented:
                entries.append((_from_brief(row), row))

    items, owner = [], {}
    for position, (entry, manifest) in enumerate(entries):
        mid = _id(entry.get("manifest_id"))
        label, url = _link(entry, manifest)
        record_id = _id(manifest.get("record_id"))
        keys = (["m:" + mid] if mid else []) + (["r:" + record_id] if record_id else [])
        keys += ["u:" + article_key(url)] if article_key(url) else []
        source_label = _text(entry.get("source_label"))
        item = next((owner[k] for k in keys if k in owner), None)
        if item is None:
            significance = _text(entry.get("significance")).lower()
            title = _text(entry.get("title")) or _text(entry.get("headline")) or "Untitled item"
            item = {
                "manifest_ids": [],
                "title": title,
                "headline": _text(entry.get("headline")) or title,
                "one_liner": _text(entry.get("one_liner")),
                "why": _text(entry.get("why_it_matters")),
                "full": _paragraphs(entry.get("full")),
                "significance": significance,
                "rank": SIGNIFICANCE_RANK.get(significance, 3),
                "date": _text(entry.get("date")),
                "category": _text(entry.get("category")),
                "source_label": source_label,
                "sources": [],
                "streams": [],
                "facts": [],
                "thin": False,
                "position": position,
            }
            items.append(item)
        for key in keys:
            owner.setdefault(key, item)
        if mid and mid not in item["manifest_ids"]:
            item["manifest_ids"].append(mid)
        if url and all(article_key(url) != article_key(known) for _, known, _ in item["sources"]):
            item["sources"].append((source_label or label, url, label))
        streams = manifest.get("stream_names")
        for name in streams if isinstance(streams, list) else []:
            if isinstance(name, str) and name not in item["streams"]:
                item["streams"].append(name)
        item["thin"] = item["thin"] or _text(_map(manifest.get("source")).get("content_quality")) == "thin"
        item["facts"] = item["facts"] or _facts(manifest, entry)
    items.sort(key=lambda i: (i["rank"], i["position"]))
    return items


# ----------------------------------------------------------------------------- shared pieces


def group_of(item):
    """A starting heuristic. ``category`` is the source's class, not a news-or-research label."""
    if item["category"] in COMPANY:
        return GROUPS[0]
    if item["category"] in RESEARCH:
        return GROUPS[1]
    return GROUPS[2]


def _grouped(main):
    present = [g for g in GROUPS if any(group_of(i) == g for i in main)]
    present.sort(key=lambda g: min(n for n, i in enumerate(main) if group_of(i) == g))
    return [(g, [i for i in main if group_of(i) == g]) for g in present]


def hedge(item, shown):
    """The first Brief sentence with hedged or conditional wording that ``shown`` does not already carry."""
    for sentence in SENTENCE_END.split(item["full"]):
        found = HEDGES.search(sentence)
        if found and found.group(0).lower() not in shown.lower() and sentence not in shown:
            return sentence
    return ""


def _links_md(item):
    if not item["sources"]:
        return NO_LINK
    if len(item["sources"]) == 1:
        return "[" + item["sources"][0][2] + "](" + item["sources"][0][1] + ")"
    return "Sources: " + "; ".join("[" + name + "](" + url + ")" for name, url, _ in item["sources"])


def _figure(item):
    """The first Brief fact with a number in it, as "Label: value."; "" when there is none."""
    for fact in item["facts"]:
        label, value = _text(fact.get("label")), _text(fact.get("value"))
        if label and re.search(r"\d", value):
            return label + ": " + value + "."
    return ""


# ----------------------------------------------------------------------------- modes


def _chat(main, rest, lead):
    lines, n = ["**" + lead + "**", ""], 0
    for group, members in _grouped(main):
        lines.append("**" + group + "**")
        for item in members:
            n += 1
            parts = [] if (item is main[0] and lead == item["one_liner"]) else [item["one_liner"]]
            parts.append(item["why"])
            caveat = hedge(item, " ".join(parts + [lead]))
            parts.append("The Brief also says: " + caveat if caveat else "")
            parts.append(THIN if item["thin"] else "")
            parts.append(_links_md(item))
            lines.append(str(n) + ". **" + item["title"] + "**: " + " ".join(p for p in parts if p))
        lines.append("")
    if rest:
        lines += ["Also seen: " + "; ".join(i["title"] + " (" + _links_md(i) + ")" for i in rest) + ".", ""]
    lines.append("Want more detail on " + main[0]["title"] + "?")
    return "\n".join(lines)


def _report(main, rest, lead):
    lines, n = ["**" + lead + "**"], 0
    for group, members in _grouped(main):
        lines += ["", "## " + group]
        for item in members:
            n += 1
            lines += ["", "### " + str(n) + ". " + item["title"]]
            meta = " · ".join(x for x in (item["date"], item["source_label"]) if x)
            lines += [meta] if meta else []
            lines += ["", item["full"] or item["one_liner"] or item["why"] or item["title"], ""]
            lines += ["- " + THIN] if item["thin"] else []
            single = len(item["sources"]) == 1
            lines.append("- " + ("Source: " if single else "") + _links_md(item))
            lines += ["- Streams: " + ", ".join(item["streams"])] if item["streams"] else []
            lines += ["- Manifest ID: " + ", ".join(item["manifest_ids"])] if item["manifest_ids"] else []
    if rest:
        lines += ["", "## Also seen", ""]
        for i in rest:
            ids = " (Manifest ID: " + ", ".join(i["manifest_ids"]) + ")" if i["manifest_ids"] else ""
            lines.append("- " + i["title"] + ": " + _links_md(i) + ids)
    lines += ["", "Want a deeper read on " + main[0]["title"] + "?"] if main else []
    return "\n".join(lines)


def _voice(main, rest, lead):
    figure = _figure(main[0])
    spoken = [lead + (" " + figure if figure else "")]
    for n, item in enumerate(main):
        line = "" if n == 0 else ORDINALS[min(n - 1, len(ORDINALS) - 1)] + ", " + item["headline"] + "."
        caveat = hedge(item, lead + " " + line)
        line += " The Brief also says: " + caveat if caveat else ""
        line += " " + item["headline"] + ": the source is thin, so check the original." if item["thin"] else ""
        if line.strip():
            spoken.append(line.strip())
    if rest:
        spoken.append("Also seen: " + "; ".join(i["headline"] for i in rest) + ".")
    spoken += ["I can send the source links as text.", "", "Links to send as text. Do not read them aloud:"]
    for item in main + rest:
        spoken.append("- " + item["headline"] + ": " + (", ".join(u for _, u, _ in item["sources"]) or NO_LINK))
    return "\n".join(spoken)


def _row(items):
    rows = []
    for item in items:
        parts = [item["headline"], item["source_label"], item["date"]]
        parts.append(item["sources"][0][1] if item["sources"] else "no link")
        parts += ["low significance"] if item["significance"] == "low" else []
        rows.append(" · ".join(p for p in parts if p))
    return "\n".join(rows)


def format_manifests(response, mode="chat", max_items=None):
    """Return the text to show, or say, to a person. Missing or malformed fields never raise."""
    if mode not in MODES:
        raise ValueError("mode must be one of " + ", ".join(MODES))
    items = build_items(response)
    renders = [_map(d.get("render")) for d in _deliveries(response)]
    if not items:
        if any(r.get("headline") == UNCERTAIN_HEADLINE for r in renders):
            return "Synorb could not confirm whether anything matched. Retry before you say that nothing is new."
        return "This delivery has no items."
    if mode == "row":
        return _row(items)
    hinted = [r.get("max_items") for r in renders]
    hinted = [h for h in hinted if isinstance(h, int) and not isinstance(h, bool) and h > 0]
    valid = isinstance(max_items, int) and not isinstance(max_items, bool) and max_items > 0
    limit = max_items if valid else (max(hinted) if hinted else DEFAULT_MAX_ITEMS)
    main = [i for i in items if i["significance"] != "low"][:limit]
    rest = [i for i in items if i not in main]
    if not main:
        lead = "Nothing of high or medium significance in this delivery."
        if mode == "voice":
            return lead + " Only low-significance items arrived. I can send their links as text."
        return (
            lead
            + "\n\nAlso seen (low significance): "
            + "; ".join(i["title"] + " (" + _links_md(i) + ")" for i in rest)
            + "."
        )
    lead = main[0]["one_liner"] or main[0]["title"]
    return {"chat": _chat, "report": _report, "voice": _voice}[mode](main, rest, lead)


def to_markdown(response, max_items=None):
    """The chat layout, under the name earlier versions of the guide used."""
    return format_manifests(response, "chat", max_items)


# ----------------------------------------------------------------------------- tables


def parse_number(value):
    """(number, unit) for a plain figure such as "4.25%" or "$1,200"; None for anything else."""
    match = NUMBER.match(value) if isinstance(value, str) else None
    if not match:
        return None
    return float(match.group(2).replace(",", "")), match.group(1) or match.group(3)


def comparable_facts(response, min_items=2):
    """Facts that share one label, one unit and one period across ``min_items`` or more articles."""
    groups = {}
    for item in build_items(response):
        for fact in item["facts"]:
            label, value, stated = _text(fact.get("label")), _text(fact.get("value")), _text(fact.get("unit"))
            parsed = parse_number(value)
            if not label or not parsed or (parsed[1] and stated and parsed[1] != stated):
                continue
            rows = groups.setdefault((label, parsed[1] or stated, _text(fact.get("period"))), [])
            if all(row["item"] is not item for row in rows):
                rows.append({"item": item, "value": value})
    return {key: rows for key, rows in groups.items() if len(rows) >= min_items}


def comparison_table(response):
    """Markdown tables of comparable facts, or "" when nothing compares. A lone figure is never drawn."""
    tables = []
    for (label, unit, period), rows in comparable_facts(response).items():
        heading = (label + (" (" + period + ")" if period else "")).replace("|", "/")
        lines = ["| Source | Date | " + heading + " |", "| --- | --- | ---: |"]
        for row in rows:
            value = row["value"]
            if unit and unit not in value:
                value += unit if unit == "%" else " " + unit
            name = (row["item"]["source_label"] or row["item"]["title"]).replace("|", "/")
            lines.append("| " + name + " | " + (row["item"]["date"] or "date not given") + " | " + value + " |")
        tables.append("\n".join(lines))
    return "\n\n".join(tables)
```

## What you get back

The fields to render, per item. The values are illustrative.

```json response
{
  "data": {
    "presentation_items": [
      {
        "manifest_id": "...",
        "title": "Example AI announces Model A",
        "headline": "Example AI announces Model A",
        "one_liner": "Model A is now available for production workloads.",
        "full": "...",
        "why_it_matters": "This changes the capabilities available for the workflow you follow.",
        "significance": "high",
        "date": "2026-10-06",
        "published_at": "2026-10-06T00:00:00Z",
        "stream_name": "Example AI newsroom",
        "category": "company_first_party",
        "source_label": "Example AI newsroom",
        "source_url": "https://example.com/model-a",
        "claims": [{"claim_text": "...", "claim_type": "statement", "confidence": "stated"}],
        "tag_names": ["Example AI"],
        "action": {"label": "Read source", "kind": "read", "url": "https://example.com/model-a"}
      }
    ],
    "render": {"suggested_format": "briefing", "max_items": 3, "headline": "Company news and research"}
  }
}
```

`headline`, `one_liner` and `full` are the same item at three lengths. Pick one per display: `headline` for a notification or list row, `one_liner` for a chat line, `full` for a card or long-form view. `render.suggested_format` is `briefing` for two or more items, `single_highlight` for one and `digest` for none. A saved Beacon read is a `briefing`, and an inventory of Beacons is a `list`.

Older docs and clients know `digest` and `list` with a cap of five or ten items. `max_items` is now the number of items to present, up to a ceiling of ten. The rest belong in the source index.

`max_items` and `also_suitable` are hints for your display. You can override them. `note` holds the contract, and you cannot override it.

The examples below are real. They are the first page of the AI Companies starter Beacon, five Manifests from 2026-10-06 and 2026-10-07, and one Mistral Manifest read by ID. The formatter made each output from the saved response. A test runs the formatter again and compares the result with these examples.

Chat, for the five AI Companies Manifests (`mode="chat"`):

```markdown snapshot=real_ai_companies_first_page.chat
**Harvey AI launched an MCP Policy Engine to secure agentic workflows against prompt injection and tool poisoning via orchestration-layer controls.**

**Company news**
1. **Building Harvey's MCP Policy Engine**: As enterprises integrate more third-party tools into AI agents, the attack surface for data exfiltration grows. Harvey's approach provides a blueprint for securing these integrations through architectural, rather than model-only, defenses. [Read source](https://www.harvey.ai/blog/building-harveys-mcp-policy-engine)
2. **Developing Reliable AI Agents with a Continuous Evaluation Loop**: Fiddler AI introduces a continuous evaluation loop for AI agents, using golden datasets and human-annotated production traces to systematically improve reliability and prevent regressions. As enterprises deploy AI agents for complex tasks like retail checkout, non-deterministic behavior poses significant operational and financial risks. This framework provides a structured path to mitigate these risks through rigorous, iterative testing. [Read source](https://www.fiddler.ai/blog/reliable-ai-agents-continuous-evaluation-loop)
3. **Anduril, U.S. Navy Invest $6.6 Billion to Expand America's Submarine Industrial Base**: Anduril and the U.S. Navy are investing $6.6B to build 'Arsenal-2,' a software-defined shipyard in Maryland, to accelerate Virginia-class submarine production by 2030. This investment signals a major shift in defense procurement, utilizing private capital and outcome-based contracting to address critical bottlenecks in the U.S. submarine industrial base. [Read source](https://www.anduril.com/news/anduril-u-s-navy-invest-usd6-6-billion-to-expand-america-s-submarine-industrial-base)
4. **Expanding the Cyber Verification Program**: Anthropic launched a three-tier Cyber Verification Program to provide security professionals with tailored access to advanced models for defensive and authorized adversarial testing. The program provides a framework for security teams to leverage advanced AI for vulnerability discovery while maintaining safety guardrails, directly impacting procurement and security operations. [Read source](https://www.anthropic.com/news/cyber-verification-program)
5. **The lakehouse is a better data warehouse: 2026 benchmarks and proof**: Databricks claims the lakehouse architecture now outperforms traditional warehouses, delivering 25-75% cost savings and 4x performance gains for unified AI and SQL workloads. For enterprise data leaders, this signals a potential shift in platform strategy, moving away from fragmented warehouse/lake/ML stacks toward unified, open-format architectures. [Read source](https://www.databricks.com/blog/lakehouse-better-data-warehouse-2026-benchmarks-and-proof)

Want more detail on Building Harvey's MCP Policy Engine?
```

Voice, for the Mistral Manifest (`mode="voice"`). The lead has one figure from the Brief `facts`. The links go out as text after the call:

```text snapshot=real_mistral_by_id.voice
Mistral AI launched a public preview of Mistral Large 4, a 1-trillion-parameter multimodal model trained on European infrastructure. Total Parameters: 1 trillion.
The Brief also says: Mistral plans to release the model weights by the end of the month following ongoing red-teaming.
I can send the source links as text.

Links to send as text. Do not read them aloud:
- Introducing Mistral Large 4: https://mistral.ai/news/mistral-large-4
```

A notification or list row, one line per item (`mode="row"`):

```text snapshot=real_ai_companies_first_page.row
Building Harvey's MCP Policy Engine · Harvey Blog · 2026-10-07 · https://www.harvey.ai/blog/building-harveys-mcp-policy-engine
Developing Reliable AI Agents with a Continuous Evaluation Loop · Fiddler AI Blog · 2026-10-06 · https://www.fiddler.ai/blog/reliable-ai-agents-continuous-evaluation-loop
Anduril, U.S. Navy Invest $6.6 Billion to Expand America's Submarine Industrial Base · Anduril Industries - Company News · 2026-10-06 · https://www.anduril.com/news/anduril-u-s-navy-invest-usd6-6-billion-to-expand-america-s-submarine-industrial-base
Expanding the Cyber Verification Program · Anthropic News · 2026-10-06 · https://www.anthropic.com/news/cyber-verification-program
The lakehouse is a better data warehouse: 2026 benchmarks and proof · Databricks Corporate Blog · 2026-10-06 · https://www.databricks.com/blog/lakehouse-better-data-warehouse-2026-benchmarks-and-proof
```

A long report (`mode="report"`) uses `full`, the date, the source, the Streams and the Manifest ID for each item. Use it for a newsletter or for work that someone must audit.

### The shape of a good delivery

The contract is in [The output contract](/agent-guides/output-contract). In practice a briefing has this shape:

1. **A lead line.** One sentence with the thing your user most needs to know, and why it matters to them. Do not rephrase the `summary` field.
2. **One story per item that matters, written as a take.** Read every item first. Lead with what matters most, and drop what this reader would not act on. Give each story its angle, its evidence with confidence labels, a why-it-matters line for this reader, and its source link inline. Three Streams covering one article is one story with three sources, not three stories.
3. **Visuals only where they make the argument land.** A comparison or a time series of measured values from the Claims earns a chart or table, built in code and custom to that story. A story with nothing worth drawing gets none.
4. **A source index at the end.** Every item once, with title, source, `published_at` and link. Items you did not narrate go here and nowhere else.
5. **One follow-up offer** tied to the lead: "Want the full argument from the memo?" Ask one question. Do not offer a menu.
6. After a Beacon run, ask whether the mix was useful and offer to adjust the same Beacon.

With no reader context, say so and stay general. Do not guess. Develop every returned story that the evidence supports, up to the sample your user asked for. A thin item gets a short treatment, not padding. Only an item you did not narrate goes to the index alone.

Never write a number, name, quote or link that is not in the Manifests you read. Mark anything you infer beyond the Claims as your own inference.

The reference formatter lays out items, links and merges. It caps its default at five items only when the response gives no `render.max_items`. It does not write the take, and it does not add confidence labels. Treat its output as a starting point for the briefing, not as the briefing.

### Present a pull as a memo

Write a pull as a memo. A memo has a bottom line, threads and evidence. You write the bottom line. Synorb does not write it and does not call a model when you read. Synorb gives you the facts to write it from, in `briefing.scaffold`.

The standard:

- Open with the bottom line: 2 to 4 sentences. Say what the items mean together.
- Group the items into threads. Do not write a flat list.
- Put the evidence beneath the threads: quotes, links and Manifest IDs.
- Keep every returned item reachable. A lower-significance item gets one line with its link. Drop none.
- Flag anything that needs the reader's decision. If nothing does, say so plainly.
- Keep your process out of the lead. Match counts, sampling, allowance use and coverage text do not open the memo.

Disclosures stay in the answer, below the lead. These are still required:

- The coverage notice and its `answer_rule`.
- The freshness date (`available_through`, for example "through yesterday").
- The citation contract.
- Manifest ID provenance.
- Do not say "all" or "complete" when more results exist.

For a daily or follow-up pull, state what changed since the last pull, what matters, and that nothing else is new. When the window is quiet, say so in one plain line. Do not pad it.

Write the opening only from the items in this response. Do not promote an adjacent or out-of-scope item to a core one. If the items share nothing, lead with what the two or three most significant items each report.

#### What `briefing.scaffold` holds

- `items`: the returned items in ranked order. The `n` of an item is its number in `briefing.markdown`.
  - Each item lists up to 4 Claims. A Claim has an `id` such as `2.c1` (item 2, Claim 1) and an `evidence` label.
  - When a Claim states numbers, `figures` lists them. Each figure copies the Claim's wording exactly.
  - Each Claim has `source_urls`: the links it stands on. A Claim with no link is left out, and `claims_left_out_without_source` counts them.
  - A number that is part of a name, such as "Gemini 4", is not a figure.
  - A quote that states figures is in `quotes`, with its `id` and `figures`. The words of the quote are in the evidence section of `briefing.markdown`, under the same item number.
- `groups`: items that share a key. `key` is the shared text. `items` are the item numbers. `written_as` shows how each item writes the key. `note` says what the group does not prove. `kind` is one of three values:
  - `shared_term`: a specific term, such as a product or event name, appears in every listed item.
  - `shared_entity`: one name appears in every listed item. Check that the items report one thing before you link them.
  - `same_publisher`: one company published the items. This is authorship. It is not proof that the items report one finding.
- `recipe`: how to write the opening from these Claims.
- `version`: the scaffold version.

Synorb builds the scaffold from stored Claims and quotes. Same input gives the same output. A group is a lead for you to check. It is not a conclusion. A response can have no groups. That is a correct result when the items share nothing.

The standard for the opening:

- Use only the Claims in `briefing.scaffold`. Add no fact, number, name or date that is not in a listed Claim.
- Copy each figure exactly as it is listed.
- End each sentence with the item numbers it rests on, for example `(1, 4)`.
- Use a group only when the shared key is the same thing in every item.
- Give no advice, prediction or urgency.

Your own instructions come first. If they ask for a different format, follow them and keep the same grounding.

This is real scaffold output from a daily pull of the AI Companies Beacon on 2026-10-15, cut to three items. The groups that name items 4 and 5 are as returned. Those two items are not shown.

```json
{
 "items": [
  {
   "n": 1,
   "headline": "IBM Study: Eight in Ten EMEA Enterprises Escalate AI Incidents to Board or C-Suite as AI Agents Test Governance",
   "significance": "medium",
   "source": "IBM Press Release",
   "claims": [
    {
     "id": "1.c1",
     "text": "Only 37% of executives say their current governance system is designed to scale with agentic AI deployment.",
     "evidence": "measured",
     "figures": [
      "37%"
     ]
    },
    {
     "id": "1.c2",
     "text": "Fewer than 40% of organizations cite a change to AI governance policy or framework in response to AI incidents.",
     "evidence": "measured",
     "figures": [
      "40%"
     ]
    },
    {
     "id": "1.c3",
     "text": "35% of respondents are running agentic AI in production today.",
     "evidence": "measured",
     "figures": [
      "35%"
     ]
    }
   ],
   "quotes": [
    {
     "id": "1.q2",
     "figures": [
      "56%"
     ]
    }
   ]
  },
  {
   "n": 2,
   "headline": "Context Assembly for Multi-Tenant Agent Memory: Budgets, Selection, and Tool Calls with MCP",
   "significance": "medium",
   "source": "Oracle - Official Blog",
   "claims": [
    {
     "id": "2.c1",
     "text": "Oracle AI Database 26ai's Flashback Query allows reading tables as of a past SCN.",
     "evidence": "stated"
    },
    {
     "id": "2.c2",
     "text": "MCP servers front the Memory Manager to preserve a single entry point.",
     "evidence": "stated"
    },
    {
     "id": "2.c3",
     "text": "Context assembly builds the agent's prompt from memory under versioned rules.",
     "evidence": "stated"
    }
   ]
  },
  {
   "n": 3,
   "headline": "Oracle Globally Distributed AI Database and Oracle True Cache at Oracle AI World 2026",
   "significance": "medium",
   "source": "Oracle - Official Blog",
   "claims": [
    {
     "id": "3.c1",
     "text": "PayPal and Oracle experts will discuss designing globally distributed AI applications while preserving SQL compatibility.",
     "evidence": "stated"
    },
    {
     "id": "3.c2",
     "text": "NRI-CaaS architecture spans four regional hubs, including Oracle EU Sovereign Cloud.",
     "evidence": "stated"
    },
    {
     "id": "3.c3",
     "text": "Oracle True Cache is a SQL and object cache that remains automatically synchronized with Database.",
     "evidence": "stated"
    }
   ]
  }
 ],
 "groups": [
  {
   "key": "Halloween",
   "kind": "shared_entity",
   "items": [
    4,
    5
   ],
   "written_as": {
    "4": "6 ways to trick-or-treat yourself to the ultimate Halloween with Search",
    "5": "Check out this year's buzziest Halloween trends."
   },
   "note": "same name only: confirm the items report one thing before linking them"
  },
  {
   "key": "Oracle",
   "kind": "same_publisher",
   "items": [
    2,
    3
   ],
   "written_as": {
    "2": "Context Assembly for Multi-Tenant Agent Memory: Budgets, Selection, and Tool Calls with MCP",
    "3": "Oracle Globally Distributed AI Database and Oracle True Cache at Oracle AI World 2026"
   },
   "note": "same company published these; that is authorship, not proof they report one finding"
  },
  {
   "key": "Google",
   "kind": "same_publisher",
   "items": [
    4,
    5
   ],
   "written_as": {
    "4": "6 ways to trick-or-treat yourself to the ultimate Halloween with Search",
    "5": "Check out this year's buzziest Halloween trends."
   },
   "note": "same company published these; that is authorship, not proof they report one finding"
  }
 ]
}
```

The recipe in that response:

```text
Use this recipe when you write the opening for your user. If your own instructions ask for a different format, follow them and keep the same grounding. Write 2 to 4 sentences that link the items below for your user. Use only the claims listed here: no fact, number, name or date that is not in a listed claim. Copy each figure exactly as listed. End each sentence with the item numbers it rests on, like (1, 4). Use a group only if its shared key is really the same thing in every item; a shared company name alone is not a finding. If the items share nothing, say what the two or three most significant items each report. Give no advice, prediction or urgency. Do not mention counts of items, samples or coverage.
```

An opening written from it follows the recipe. It uses only the listed Claims. Each sentence ends with the item numbers it rests on. It is in [The output contract](/agent-guides/output-contract#the-shape-of-a-briefing).

The evidence goes below the lead, one heading per thread:

```markdown
### Agent security
- <Item title> - [Source](LINK_URL) - manifest MANIFEST_ID

### Owning the stack
- <Item title> - [Source](LINK_URL) - manifest MANIFEST_ID

### Models
- <Item title> - [Source](LINK_URL) - manifest MANIFEST_ID

Top 10 of 698 matching items; more exist.
```

The last line is the disclosure line. It sits below the evidence.

The server builds `briefing.markdown` in this order:

- `## Synthesis`: a plain lead of at most 120 words, then a `Decisions` line. A rule builds the lead from the one-line texts of the top items. It joins them. It is a fallback. Write your own opening.
- The evidence: `## News and announcements`, `## Research papers`, then `## Lower-significance items`.
- The source index, then the coverage and freshness block.

Items are in significance order: high, then medium, then low. Ties keep Synorb's ranking. The `Decisions` line says "None" unless an item carries an action flag.

`briefing.synthesis.path` shows how Synorb made the lead. It is `deterministic`, `model_attempted` is `false` and `llm_cost` is `0`. No model writes any part of a response.

One event is one story. When two returned Manifests report the same event, the Markdown shows one story with "Also reported by" links. Both Manifest IDs stay in `briefing.synthesis.stories` and in the source index.

The Markdown holds no instruction for you. Instructions are in `briefing.agent_instructions`. On MCP, `content[].text` is the same briefing. At account urgency `now` it ends with one separate footer line that carries `account_notice.user_message`.

The usage receipt, the account notice and the citation contract are in `structuredContent.host_notices`. Read them before you finish, and say `account_notice.user_message` to your user once, after the briefing. `host_guidance` carries the same presentation guidance on REST and MCP.

Each `synorb-manifests` response carries these fields:

- `presentation_rule`: string, how to present the pull.
- `presentation_preflight`: list of {id, check, rule_clause} yes/no checks to run before answering. It has 5 items. Each names the clause of the rule it checks.
- `briefing.agent_instructions`: notes for the agent that are not for the user.
- `briefing.scaffold`: ranked items, Claims, groups and the recipe.

`briefing.synthesis` is {lead, threads[], decisions, sections, stories, method, path}. `threads` is empty. `briefing.markdown` opens with the plain lead. See [Briefing fields and the reader profile](/agent-guides/briefing-fields-and-profile).

#### Make the memo look professional

If your host can create images, charts or documents, add a cover image. Add one or two charts or an infographic.

Each visual must back the take. Show facts and figures, growth rates, comparisons or distributions. A bar chart of change and a histogram of spread are good choices. Do not add decoration.

Fit the cover and the style to what you know about your user: their field, taste, brand and past requests.

Use only figures that appear in the pulled items. `briefing.stories[].chart_series` holds plottable series. Never invent or extrapolate a data point. Synorb supplies no images: you render the data.

Label every visual as made by you from this pull. Example: "Chart created by your agent from this pull's data." This keeps a visual from being read as source material.

If your host cannot create visuals, send the text memo alone. Do not apologize. Do not add a placeholder. See [charts, tables and infographics](/agent-guides/charts-tables-and-infographics).

## Presentation cannot repair a Brief

A good layout cannot correct a wrong Brief. Keep the source link beside every item, so your user can check the original. Show hedged or conditional wording as the Brief states it. "Up to $2.9 billion" stays "up to", and "plans to release" stays a plan. If the Brief and the source disagree, trust the source and tell your user.

### Match the shape to the question

| Your user asked | Show |
| --- | --- |
| "Catch me up" / "what changed" | A timeline, in date order |
| "Compare X and Y" | A small table from `comparison_table`, or nothing if the facts do not compare |
| "Track this number" | A chart or stat card from `facts` (see the next guide) |
| "Anything urgent?" | Only `significance: high` items, or one line saying nothing is |
| A specific question | The direct answer first, then the items that support it |
| A report or newsletter | `mode="report"`: sections by theme, with inline links |
| No screen (voice) | `mode="voice"`: the same items, one figure in the lead, no URLs read aloud |

Never build a shape the fields cannot back. A table needs facts with the same label, unit and period from two or more articles. `comparison_table` returns an empty string for anything less. For the five Manifests above it returns nothing, because no two Briefs report the same measure. The server builds `render.also_suitable` from compact Brief facts, so it can be empty on full-body and REST pulls. Check the facts yourself.

### When the source is thin

If `source.content_quality` is `thin`, the formatter adds "Source is thin, open the original." to that item. The Brief does not give a full story. Choose your top two or three items for this user, read their Record (on plans that include it) or open the cited source, and write from that. Put the rest in the "also seen" line. Do not pad a thin window.

### How the formatter merges duplicates

Two entries are one article when they share a `manifest_id`, a `record_id`, or an article URL. Two URLs are the same article when they differ only by the case of the scheme and host, a leading "www." or a trailing slash. This is the same rule that Synorb billing uses. The merged item lists every Stream, every Manifest ID and every distinct link. Two different articles about one event stay two items. Decide yourself whether to combine them, and keep both links if you do.

## Failure modes

| Symptom | Cause | What to do |
| --- | --- | --- |
| One dense block of prose | The delivery was written as one paragraph | Use the lead line, one story per item and the source index. |
| A flat recap with no point of view | You described the items instead of arguing from them | Write the take for this reader, with labelled evidence. |
| A bare list of headlines | No why-it-matters and no links | Add a reason per item, specific to this user. |
| A link points to the wrong item | Items were matched by position | Match on `manifest_id`. The formatter does this. |
| `action` is `null` and `source_url` is empty | No valid item URL exists | Show the item unlinked and say so. Never invent a URL or link the source channel homepage. |
| A podcast shows only a landing page | `link_type` describes the link, not the media | Read `media_format` to tell audio from text. The `action.label` says "Listen now" or "Watch now" when it applies. |
| Only low-significance items | Nothing important arrived | Say so in one line and list them as "also seen". The formatter does this. |
| The same article appears twice | You merged pulls from several Streams | Merge by `manifest_id`, `record_id` or article URL. The formatter does this. |
| An ID looks off by a few digits | A 19-digit ID passed through a JavaScript number or a float | Keep every ID a string end to end. The formatter refuses a float ID. |

## Make it good for your user

Write like a sharp colleague who has read everything and forgotten nothing, not a list of headlines. Say what changed, why it matters to them in particular, what the evidence is and how sure it is, and where to read more. Keep your own caveats out of the item. If needed, put them in one honest line: "mostly company announcements today; the research was thin".

If nothing is worth their time, say so in one line. This builds more trust than padding does.

## Next guide

[Charts, tables and infographics](/agent-guides/charts-tables-and-infographics): when the numbers deserve a picture, and how to draw one honestly.
