---
slug: task-payload-provenance-article
title: "Task provenance: every payload of a piece of work, on one page the worker could not have written"
kind: architecture
status: published
hash: 11665afc6c8f837c5b1bf0d96c3ed7dd801aa8a10630bba009e2e14d53ec603c
created: 2026-10-09T00:19:02.849Z
updated: 2026-10-09T04:17:39.689Z
summary: "When an agent says \"done\", the only witness is usually the agent. This system binds a scoped token to a task, so every call made for that task is filed under it by the runtime, with its exact request and answer. The task's page is assembled from those records, not from the worker's report. This article walks the mechanism, a recorded run, a full lead-to-inbox example, and the specified projection from a task to a shareable article with hashed sources."
tags: ["architecture","ai-agents","provenance","proof-of-work","verification","evaluation"]
above: ["[[unified-dispatch-macaroon-atoms]]"]
below: []
adjacent: ["[[directory-ledger-pattern]]", "[[atomic-tooling]]", "[[scoped-access]]", "[[authority-outside-the-model]]"]
nested: []
---
# Task provenance: every payload of a piece of work, on one page the worker could not have written

*When an agent says "done", the only witness is usually the agent. This system binds a scoped token to a task, so every call made for that task is filed under it by the runtime, with its exact request and answer. The task's page is assembled from those records, not from the worker's report. This article walks the mechanism, a recorded run, a full lead-to-inbox example, and the specified projection from a task to a shareable article with hashed sources.*

## "Done" is a claim. The calls are the evidence.

You assign a task. An agent works it: it searches, filters, drafts, sends. Then it says "done" and writes a paragraph about what it did. The paragraph may be accurate. But the paragraph is the agent's testimony about itself, written after the fact, by the one party with a reason to round up. When a client, a reviewer or a regulator asks *show me how you got here*, a paragraph is not an answer.

This system gives a different answer. Every call an agent makes for a task is **filed under that task by the runtime**, with the exact request and the exact answer, at the moment it happens. The task's page is then assembled from those records. The worker [could not leave a call out, because it could not act without the token, and it could not add one, because the build writes the record](https://miscsubjects.com/a/scoped-access).

That is task provenance: a recorded connection between an assignment and every action taken for it, produced by the infrastructure rather than reported by the agent. It is the third piece of a design whose other two parts are the [[directory-ledger-pattern|Directory and Ledger]] (what could run, and what was recorded) and [[unified-dispatch-macaroon-atoms|unified dispatch with scoped tokens]] (one runner, and limits that can be lent out). This article is about the one caveat that ties them to a piece of work.

## The task becomes part of the permission

A scoped token can carry the line `task = T-0207`, where `T-0207` names an assignment. The line is a caveat like any other: it is signed into the token's chain, so a holder can add restrictions beneath it but can never remove it. Three things follow.

1. **Every call is grouped under the task.** When the token is minted, it is added to the task's index. On every use the checker writes the call to the token's log and submits the Ledger events under the session `task:T-0207`. A single filter on that session recovers every byte the task ever touched.
2. **Closing the task closes its permission.** The checker reads the task's state on every use. Once the task is marked done, any further execution is refused with the reason "its task is done". The proof door stays open, within the token's other limits, so the record remains readable after the work stops.
3. **Delegating never detaches the work.** A helper handed a narrower copy still carries `task = T-0207`. Its calls land on the same page. There is no way to do work for a task off the task's record while using the task's authority.

The binding is small and the consequence is large: the same string that identifies the work being authorised identifies the record that will be inspected afterwards.

## What a reviewer sees at `/w/<TASK>`

The proof page is a plain, read-only document generated from the records:

- **The task, as it was written.** Not a summary; the text of the assignment, with its state.
- **What was done, counted.** "3 tool runs (2 answered, 0 failed, 1 refused by a limit), 0 chat messages, under 2 tokens."
- **The tokens it was worked with,** each with its limits in plain words.
- **Every call, in order.** Time, tool, door, duration, and the result: answered, failed, or refused and by which caveat. Each call expands to its arguments and the exact request and answer, with credential values shown by name only.
- **Every chat turn,** if the task was worked through the chat door.

The owner opens the page with the build key. Anyone else needs a token for that task with `ways = proof`: a credential that opens the page [read-only and runs nothing](https://miscsubjects.com/a/scoped-access), with its own expiry, and which can be turned off. Showing a task to a client is minting one line and sending a link. Opening the page [is not a use](https://miscsubjects.com/a/scoped-access).

## A recorded run

On 3 October 2026, while the token system was being built, a task was created to check it on the live build. Its completion condition was written into the task: two runs on the proof page, a refused run refused, and the token stopping when the task is done.

```widget
{
  "type": "json",
  "title": "T-0229, as its proof page records it",
  "ref": "#facts.recorded_check",
  "open_depth": 2
}
```

Two calls answered within five seconds of each other. The task was marked done. Twenty-one minutes and eighteen seconds after the second call, a third was attempted. It never left the build. The checker refused it on `task = T-0229` with the reason "its task T-0229 is done", and the page shows the refusal with that caveat beside it. The tokens have since expired and been turned off; the page preserves the calls.

This proves what it proves: that the task condition is enforced before execution, and that the page is drawn from the checker's records. It does not prove that an agent completed an arbitrary assignment well. It is one hand-made task with three calls. What it demonstrates is the mechanism, which is what it was written to demonstrate.

## The example this was built for: a cold lead, start to finish

The sentence that motivated the design, from the owner on 3 October 2026: *if I assign a task to the models I can now have all of the associated payloads from the ledger show up from that task onto an article.* The example below shows what that means for the person at the far end of the work: someone who just received an outreach letter and wants to know how they were chosen. **The names and figures are illustrative; this is the mechanism, not a recorded run.**

**The task.** Find three plumbers in Marin County with forty or more reviews and a working website. Draft one personalised note each. Queue the sends. Record the provider receipts. Budget: twenty tool uses, one week.

**The token.** Minted with `task = T-XXXX`, `tools = LEADS_*,OUTREACH_*,EMAIL_*`, `ways = rest,mcp`, `uses <= 20`, and an expiry one week out. The mint response carries the address of the task's proof page. The page is empty. The work will fill it.

**Step 1: search.** `LEADS_SEARCH_GOOGLE_PLACES` with a query and a limit. The request and the whole answer, eight candidates with names, addresses, ratings and review counts, are recorded as one exchange under one call id. The call log entry names the tool, the door, the arguments and the receipt.

**Step 2: filter.** `LEADS_LIST` with the task's rules. Three remain. The answer is recorded whole.

**Step 3: draft.** `OUTREACH_DRAFT` once per lead. The draft **as first emitted** is the receipt. A person may edit it before sending; the record keeps both the model's version and the sent version, each as its own event.

**Step 4: send.** `EMAIL_SEND` once per draft. The outbound bytes and the provider's answer (message id, accepted, or the bounce reason) are recorded.

**Step 5: delivery.** The tracking sweep records delivered, opened, clicked, replied, each as an event with the bytes exactly as they arrived.

At that point the proof page shows the task as written, the token's limits, and five calls in order, each one tap from its exact exchange. The recipient of the letter can be given a proof link and see: the criteria they were selected by, the search that found them, the filter that kept them, the draft that became their letter, and the receipt that it was sent. No screenshot. No deck. No retelling.

## From a task to an article: the specified projection

The proof page is private until a proof token exists for it, and it lives on this system's domain. The next step is to turn a task into something that travels: an **article**, one JSON object with facts, a body and hashed sources, published at one address. The projection below is specified and **not built**; it is about one module and one route, and its write path is the one the `ARTICLE` tool already uses.

```
POST /api/articles/from-task   {"task": "T-0207", "publish_when": "all_quotes_found"}

1. read the task as written
2. read the task's token index and every token's call log
3. read the Ledger rows for each call id
4. write one article:
     facts.task       the task as written
     facts.tokens     each token's limits
     facts.events     one entry per call: tool, door, time, call id, result
     facts.payloads   the structured answers (candidates, filtered list, drafts, receipts)
     sources          one per event, pointing at the record, with its quote hashed
     body             the narrative, plus a JSON widget over facts.events
5. verify every source, and publish only if every quote was found
```

Two properties make this a projection rather than a second store:

- **Each event points back to its record.** An entry in `facts.events` names a call id that resolves on the Ledger. An agent that fabricated an event would have invented a call id that resolves to nothing, which is detectable by anyone with access to compare.
- **Deleting the article loses nothing.** The task, the call logs and the Ledger rows stay where they are. The article is for readability and sharing; the Ledger is for durability.

Three audiences get three views of one substrate. The operator already uses the proof page and the Ledger. The recipient of a letter gets the article: why me, why now, with receipts rather than assertions. A reviewer doing diligence gets the answer to whether "provenance" is a feature or a system property: every task can be shown this way, because the records exist whether or not anyone asks.

## What a hashed source actually proves

An article's sources are part of its stored object. Each carries a URL, the exact words it supports, and hashes the build adds:

- `quote_sha256` is computed when the article is saved, from the quote with its whitespace collapsed.
- On verify, the build fetches each URL with no credentials, keeps `snapshot_sha256` of the bytes it got back ([the hash of the page as fetched](https://miscsubjects.com/schemas/jarticle.json)), reduces the page to text, and sets `quote_found` only if the quote is in it.
- With the default setting, the article stays a draft while any quote was not found.

The verifier sends no authentication, which is a deliberate constraint: an article that claims a task's record has to cite a page a stranger can open. For a task, that is its proof page opened by a proof token. A hash [detects whether messages have been changed since the digests were generated](https://csrc.nist.gov/pubs/fips/180-4/upd1/final); it does not certify that the source is correct or that the task succeeded. It narrows the question a reader has to trust anyone on, which is the most a document can do.

Every source on this page was fetched and its quote found before it was published. The hashes are in [/a/task-payload-provenance-article.json](https://miscsubjects.com/a/task-payload-provenance-article.json).

## Why this matters for training and evaluation

Take the mechanism out of the business setting and put it in an evaluation harness. Each rollout runs under its own task-bound token: the tools, arguments, paths, uses and lifetime it may spend are fixed before it starts. The checker then produces, independently of the policy's own transcript, a record of what the rollout actually did and every point where it tried to exceed its grant, each refusal naming the caveat that stopped it.

That gives a grader three signals that do not depend on the model's account of itself: whether the required calls happened; whether any attempt crossed the grant, counted directly as refusals; and whether work continued after the task closed, as the third T-0229 call did. A policy cannot improve any of those by narrating better, because none of them is read from the narration. The record is also replayable: event sourcing means [the event log can be used to reconstruct past states](https://martinfowler.com/eaaDev/EventSourcing.html), so a grader can rebuild the rollout's world at any step.

This has not been run on a training job. T-0229 shows the mechanism on one hand-made task. The claim is that the mechanism is the right shape for that use, and [[authority-outside-the-model|Authority outside the model]] sets out what making it fully true would require.

## Where this stops today

- The task-to-article projection is a specification, not code.
- A proof page is private until a proof token is minted for it and shared.
- The checker reads tasks from the document the task list was kept in before it moved to a database table. A token bound to a task created since the move is refused as "does not exist" until the checker is pointed at the table.
- Calls made outside the task-bound path, with another credential or in another system, are not on the task's page. Today the build's own agents still hold the build key, so their work is recorded on the Ledger but not filed under tasks by this mechanism.
- Recording is bounded by the Ledger's stated limits: deferred writes can be lost if a Worker dies first; bodies over 8 MiB are recorded as missing.
- The page is generated by this system and controlled by its operator. It is an inspectable execution record, not a proof against operator tampering.

## The rest of the series

- [[directory-ledger-pattern|The Directory and the Ledger]]: where the contracts live and where the bytes go.
- [[unified-dispatch-macaroon-atoms|Unified dispatch]]: the runner every call crosses, and the token construction the `task` caveat belongs to.
- [[atomic-tooling|Atomic tooling]]: the tools whose answers these records hold, and why each stores its whole result before returning a slice.

## Facts

```json
{
  "measured_at": "2026-10-09T05:10:00Z",
  "binding": {
    "caveat": "task = T-XXXX",
    "ledger_session": "task:<TASK>",
    "task_index": "one index per task listing every token minted for it",
    "refused_when": "the task is marked done (the proof door still opens)",
    "removable_by_narrowing": false
  },
  "proof_page": {
    "address": "/w/<TASK>",
    "opens_for": [
      "the owner",
      "any token for that task whose ways include proof"
    ],
    "shows": [
      "the task exactly as written, and its state",
      "every token minted for it, with its limits in plain words",
      "every call in order: time, tool, door, duration, answered / failed / refused and by which caveat",
      "the arguments and the exact request and answer of each call, one tap away, with credential values replaced by their names",
      "every chat turn"
    ],
    "drawn_from": "the call log the checker writes on every use and every refusal, and the Ledger rows under session task:<TASK>",
    "never_a_use": "opening the page does not spend a use"
  },
  "recorded_check": {
    "task": "T-0229",
    "as_written": "WHAT: prove a task-bound scoped token end to end on the live build. DONE WHEN: two runs are on this task's proof page, a refused run is refused, and the token stops when this task is done.",
    "state": "done",
    "summary_on_page": "3 tool runs (2 answered, 0 failed, 1 refused by a limit), 0 chat messages, under 2 tokens",
    "tokens": [
      {
        "label": "Live check: work token for T-0229",
        "caveats": [
          "tools = WIKIPEDIA_SUMMARY,HTTP_ECHO_HEADERS",
          "ways = rest,mcp",
          "expires < 2026-10-03T22:41:13.648Z",
          "uses <= 10",
          "task = T-0229"
        ]
      },
      {
        "label": "Live check: proof link for T-0229",
        "caveats": [
          "ways = proof",
          "expires < 2026-10-03T22:41:15.872Z",
          "task = T-0229"
        ]
      }
    ],
    "calls": [
      {
        "at": "2026-10-03T21:41:18.679Z",
        "tool": "WIKIPEDIA_SUMMARY",
        "door": "rest",
        "args": {
          "title": "Macaroon"
        },
        "result": "answered",
        "status": 200,
        "ms": 2321
      },
      {
        "at": "2026-10-03T21:41:23.646Z",
        "tool": "HTTP_ECHO_HEADERS",
        "door": "rest",
        "args": {
          "title": "Macaroon"
        },
        "result": "answered",
        "status": 200,
        "ms": 1351
      },
      {
        "at": "2026-10-03T22:02:42.146Z",
        "tool": "WIKIPEDIA_SUMMARY",
        "door": "rest",
        "args": {},
        "result": "refused before it ran: nothing was sent anywhere",
        "caveat": "task = T-0229",
        "reason": "its task T-0229 is done"
      }
    ],
    "gap_between_second_call_and_refusal": "21 min 18.5 s",
    "read_from": "the proof page /w/T-0229, opened with the owner key on 9 October 2026"
  },
  "lineage": "task as written → token (task = T-XXXX) → tool calls → call log + Ledger rows (exact bytes under sha256) → proof page → article (facts + body + hashed sources)",
  "worked_example": {
    "status": "illustration of the mechanism with made-up names; not a recorded run",
    "task": "find three plumbers in Marin County with 40 or more reviews and a website, draft one personalised note each, queue the sends, record the provider receipts",
    "token": [
      "task = T-XXXX",
      "tools = LEADS_*,OUTREACH_*,EMAIL_*",
      "ways = rest,mcp",
      "uses <= 20",
      "expires < one week"
    ],
    "steps": [
      "LEADS_SEARCH_GOOGLE_PLACES: query → candidates (one call, one exchange recorded whole)",
      "LEADS_LIST: filter to three by the task's rules",
      "OUTREACH_DRAFT: one draft per lead; the draft as first emitted is the receipt, not the later edit",
      "EMAIL_SEND: one send per draft; the provider's answer (message id, accepted) recorded",
      "email tracking: delivered, opened, clicked, replied, each an event with the bytes as they arrived"
    ]
  },
  "task_to_article": {
    "status": "specified, not built",
    "route": "POST /api/articles/from-task {task, slug?, publish_when?}",
    "reads": [
      "the task as written",
      "the task's token index",
      "each token's call log",
      "the Ledger rows for each call id"
    ],
    "writes": {
      "facts.task": "the task as written",
      "facts.tokens": "each token's limits",
      "facts.events": "one entry per call: tool, door, time, call id, result",
      "facts.payloads": "the structured answers",
      "sources": "one per event, pointing at the record, with the quote hashed",
      "body": "the narrative plus a JSON widget over facts.events"
    },
    "then": "verify every source; publish only if every quote was found",
    "estimated_size": "about one module and one route; the write path is the one the ARTICLE tool already uses"
  },
  "source_verification": {
    "quote_sha256": "computed on save from the quote with whitespace collapsed",
    "verify": "fetch with no credentials; snapshot_sha256 of the bytes; quote_found if the quote is in the page text",
    "publish_when": [
      "all_quotes_found (default)",
      "always",
      "never"
    ]
  },
  "series": [
    "directory-ledger-pattern",
    "unified-dispatch-macaroon-atoms",
    "atomic-tooling",
    "task-payload-provenance-article"
  ]
}
```

## Sources

1. [Scoped access: the proof page](https://miscsubjects.com/a/scoped-access) — “The worker could not leave a call out, because it could not act without the token, and it could not add one, because the build writes the record.” (sha256 3c09e45853488de9)
2. [Scoped access: the task limit](https://miscsubjects.com/a/scoped-access) — “Binds the token to one task: every run is recorded under that task, shows on its proof page, and the token stops working when the task is done.” (sha256 ba3f76baceedea47)
3. [Scoped access: showing a task to someone else](https://miscsubjects.com/a/scoped-access) — “it opens that page read-only and runs nothing.” (sha256 de627508d9f4d74b)
4. [Scoped access: what a use is](https://miscsubjects.com/a/scoped-access) — “A use is one tool run or one chat message; opening a page is not a use” (sha256 c40614e3dfcfa2cb)
5. [Scoped access: turning a token off](https://miscsubjects.com/a/scoped-access) — “Turning off a token turns off every copy narrowed from it; turning off one copy leaves the token it came from working.” (sha256 aaa928c953aa19df)
6. [The jarticle/1 schema](https://miscsubjects.com/schemas/jarticle.json) — “set by verify: the hash of the page as fetched” (sha256 f927480db3c191bc)
7. [Articles as JSON: how every page here is built](https://miscsubjects.com/a/json-articles-how-they-work) — “hashes the page that came back” (sha256 7e2c147a14a640c0)
8. [FIPS 180-4, Secure Hash Standard](https://csrc.nist.gov/pubs/fips/180-4/upd1/final) — “The digests are used to detect whether messages have been changed since the digests were generated.” (sha256 060aed903fca7812)
9. [Event Sourcing: rebuilding state](https://martinfowler.com/eaaDev/EventSourcing.html) — “we can also use the event log to reconstruct past states” (sha256 6a02825a92d76ea0)
10. [Authority outside the model](https://miscsubjects.com/a/authority-outside-the-model) — “In this build a model does not set its own authority” (sha256 09ffea57374c7155)
11. [Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud (NDSS 2014)](https://research.google/pubs/macaroons-cookies-with-contextual-caveats-for-decentralized-authorization-in-the-cloud/) — “macaroons embed caveats that attenuate and contextually confine when, where, by who, and for what purpose a target service should authorize requests” (sha256 c9d336b5640d0404)

---

**above:** [[unified-dispatch-macaroon-atoms]]

**adjacent:** [[directory-ledger-pattern]], [[atomic-tooling]], [[scoped-access]], [[authority-outside-the-model]]
