Skip to content

Provenance & attribution

Every piece of content on the platform has a history: it was written, imported, translated, narrated, summarised, or compiled from other things. Provenance is that history, recorded as fact rather than reconstructed after the event. Each act of creating or transforming content is written down once, immutably, at the moment it happens — naming what went in, what came out, and who was responsible.

The reason this matters is commercial as much as it is technical. Packt's authors keep publishing because their work is attributed and they are paid for it. When a course is compiled from three books, or a "nano-book" is summarised from a conference keynote, someone is owed a share — and working out who, and how much, must be a lookup against recorded fact, not a forensic exercise. The same record answers the question every rights team now has to answer about AI: what did an AI model actually touch, and under what contract?

The mechanism is a single append-only structure called the Activity graph. Anything that brings content into existence or changes it is recorded as an Activity: a first-class, immutable node that consumes zero or more source nodes, produces one or more output nodes, and names the agent responsible. Because nothing is ever edited or deleted, three questions that used to require an investigation become single queries against the graph:

  • What did AI touch?
  • What is this derived from?
  • Who is owed?

This replaces the old derived-from tag

Earlier drafts of the platform tracked lineage with a common/derived-from tag listing source IDs. That mechanism is retired. Lineage lives only in the Activity graph, expressed as edges between content nodes. If you are migrating from the old model, stop writing derived-from tags and record an Activity instead — it captures the source, the agent, the authorising contract, and the royalty rule in one place.

The agent is the responsible party — never the model

The single most important rule of provenance is who gets named as the actor. An Activity is always attributable to an agent: a person, an organisation (such as Packt), or an automated system. An AI model is never an agent. When an AI model does the work, the organisation is the agent — recorded with the role GENERATOR — and the model, its version, and the prompt are logged separately as tooling.

This is a deliberate design choice, not a technicality. An LLM is an instrument, like a camera or a printing press; responsibility rests with the party that operated it. Recording it this way keeps "exactly what AI produced or touched" a one-query question — filter Activities by role=GENERATOR or by tooling.model — rather than an audit.

flowchart LR
    OUT["Output<br/>e.g. a French translation"]
    ACT(["Activity<br/>records the transformation"])
    SRC["Source(s)<br/>the content consumed"]
    AGT["Agent<br/>person · organisation · system"]
    TOOL["Tooling<br/>model · version · prompt<br/>(an LLM lives here)"]

    OUT -->|was generated by| ACT
    ACT -->|used| SRC
    ACT -->|was associated with| AGT
    ACT -.->|records| TOOL

Agent kinds and roles

An activity agent has a kindHUMAN, SYSTEM, or ORGANISATION — and a role describing what it did: AUTHOR, TRANSLATOR, NARRATOR, GENERATOR, or OPERATOR. This is a different thing from a contract agent (an author, publisher, or licensee who is a party to a Contract). See the disambiguation note at the foot of the Glossary.

The log and the graph

Every Activity is recorded in two places that do different jobs, and the distinction is worth understanding because it governs which activities carry lineage.

The Activity Log holds everything. Without exception, every Activity is written to an append-only, immutable log that is searchable and filterable — by type, by agent, by date range, by tenant, and by the node it touched. This is the audit substrate: "what happened, when, by whom, over what?" is always answerable, and nothing is ever hidden from it.

The Content Lake graph holds only the subset of Activities that form lineage — those that either create a new content node or connect existing content nodes with an edge. This is the structure that provenance and royalty traversals actually run over. The graph is a projection of the log, not a second write: every graph Activity is also a log entry, but the reverse is not true.

flowchart LR
    A["Every Activity"] --> LOG[("Activity Log<br/>all activities · searchable")]
    A -->|"creates or connects<br/>a content node"| G[("Content Lake graph<br/>lineage subset")]
    ENR["Enrichment<br/>product info · entities · cover"] --> LOG

The rule is precise: an Activity reaches the graph if and only if it either creates a new content node — a Work, Expression, Manifestation, or ContentUnit — or creates a new edge between content nodes. Activities that only enrich an existing node — generating product information, extracting entities, producing a cover — create no lineage and stay log-only. In practice that log-only set is small: it is exactly the three enrichment types. Every origination, derivation, expression, manifestation, and fragment activity reaches the graph.

The edges

Three edges run through every graph Activity, and a fourth pair runs directly node-to-node as a shortcut.

Edge Runs from → to Meaning
USED Activity → each source The content this Activity consumed.
WAS_GENERATED_BY output node → Activity The Activity that produced this node.
WAS_ASSOCIATED_WITH Activity → agent(s) Who performed it.
DERIVED_FROM output → source Lineage shortcut, for fast royalty traversal.
SUPERSEDES output → prior node Replacement shortcut (revisions and editions).

The first three are the authoritative record. DERIVED_FROM and SUPERSEDES are materialised shortcuts — derived once at write time from the Activity, never independently authored — that collapse the was-generated-by → used path so a royalty walk need not hop through the Activity at each step. DERIVED_FROM is emitted for every derivation; SUPERSEDES only when an Activity replaces a prior node — a Resource for a correction, a Work for a new edition.

One important constraint on used

A USED edge always points at content: a Resource, a Source, or a ContentUnit. It never points at a Work, because a Work is a rights grouping, not content — rights are reached through the consumed Resource's Work, not by consuming the Work directly. This keeps the lineage graph strictly about content flow.

The eighteen activity types

There are eighteen activity types, grouped by what they do to the graph. Every one is logged and searchable; the Plane column shows whether it also reaches the lineage graph (GRAPH) or stays in the log only (LOG).

Activity Group Creates Work Plane
INGESTION Origination Work New Work GRAPH
AUTHORING Origination Work New Work GRAPH
RECORDING Origination Work New Work GRAPH
ADAPTATION Derivation (new Work) Work New Work GRAPH
EDITION Derivation (new Work) Work New Work GRAPH
COMPILATION Derivation (new Work) Work New Work GRAPH
ASSESSMENT_GENERATION Derivation (new Work) Work New Work GRAPH
SUMMARISATION Derivation (per instance) Work or Expression instance † GRAPH
PERSONALISATION Derivation (per instance) Expression or Work instance † GRAPH
TRANSLATION Expression (same Work) Expression Same Work GRAPH
NARRATION Expression (same Work) Expression Same Work GRAPH
TRANSCRIPTION Expression (same Work) Expression Same Work GRAPH
REVISION Expression (correction) Expression Same Work GRAPH
MANIFESTATION_GENERATION Manifestation Manifestation Same Work GRAPH
EXCERPTION Fragment ContentUnit Same Work GRAPH
PI_GENERATION Enrichment n/a LOG
ENTITY_EXTRACTION Enrichment n/a LOG
COVER_GENERATION Enrichment n/a LOG

† Decided per instance

SUMMARISATION and PERSONALISATION do not have a fixed tier. A faithful condensation, or a single-source persona edition that merely selects and re-sequences existing material, stays the same Work as a new Expression. A re-authored digest, or a persona book assembled from several Works with new connecting text, becomes a new Work. The deciding factor is whether substantial new authored content was introduced.

The groups map onto plain ideas:

  • Origination brings brand-new content into being — imported (INGESTION), written (AUTHORING), or captured from a live event (RECORDING). Each starts a new Work.
  • Derivation makes a new Work out of existing content — reworking it across a medium (ADAPTATION), publishing a new edition (EDITION), compiling parts of several Works (COMPILATION), or generating assessments (ASSESSMENT_GENERATION).
  • Expression re-realises the same Work in another language, mode, or corrected state — TRANSLATION, NARRATION, TRANSCRIPTION, REVISION. Same content, new form.
  • Fragment pulls a quotable piece out as a ContentUnit (EXCERPTION).
  • Enrichment attaches metadata — product information (PI_GENERATION), entities and tags (ENTITY_EXTRACTION), or a cover image (COVER_GENERATION). These create no lineage node and stay log-only.

The taxonomy is open by design; new product lines will add types over time.

What is deliberately not an Activity

Some things look like activities but are not, because they do not transform content. Pushing a product to a partner is distribution. Overriding a title for one catalogue is catalogue configuration. Review, QA, and approval rounds are a Production concern, modelled separately — there is intentionally no "reviewed-by" record on an Activity. A sale or an API read is a consumption event, attributed to the Product, not the content graph.

Worked example: a course compiled from three books

The clearest way to see why the graph earns its keep is to follow a derived work through it. Suppose a commissioning editor builds a new data-engineering course by selecting one chapter from each of three existing books. The course is genuinely new — it has its own Work, its own Contract, and will sell as its own Product — but it is built entirely from content that other authors wrote and are owed for.

This is a COMPILATION: a new-Work derivation whose USED edges name each source ContentUnit. Those edges are exactly what makes the royalty split computable — the Royalty service can walk them to find every source Work and apply each one's contract terms.

flowchart TD
    CU_A["Chapter 3<br/>Book A"] --> ACT(["Activity · COMPILATION<br/>agent: Commissioning editor"])
    CU_B["Chapter 7<br/>Book B"] --> ACT
    CU_C["Chapter 2<br/>Book C"] --> ACT
    ACT --> WK["New Work<br/>Data Engineering course"]
    WK -. DERIVED_FROM .-> CU_A
    WK -. DERIVED_FROM .-> CU_B
    WK -. DERIVED_FROM .-> CU_C

The solid arrows are the Activity-mediated record: three USED edges into the Activity, and the new Work generated by it. The dashed DERIVED_FROM edges are the materialised shortcuts the Royalty service follows to answer who is owed? — one path back to each source book, so a share can be attributed to each author under their own contract.

The Activity also records how the royalty should flow. Each derivation carries a royalty_flow rule on its lineage edge — INHERIT (use the source's own terms), NET_SHARE (split net receipts across sources), ADDITIVE, or EXCLUDED. A compilation from several Works is a NET_SHARE. Crucially, this only records intent: the Royalty service does the arithmetic by traversing the graph. The Activity's job is to state the rule on the edge it creates, and to pin the Contract that authorised the whole act in its rights_basis.

The COMPILATION Activity (JSON)

A business reader never has to touch this — but this is the exact record behind the diagram above. Note that no LLM appears as an agent; the editor is the agent, and each used entry is a source ContentUnit, version-pinnable for exact reproducibility.

{
  "activity_id": "act_compile_data_eng",
  "type": "COMPILATION",
  "created_at": "2026-03-10T00:00:00Z",
  "performed_by": [
    {
      "agent_id": "agt_ce_data",
      "kind": "HUMAN",
      "name": "Commissioning editor",
      "role": "OPERATOR"
    }
  ],
  "used": [
    { "node_id": "cu_book_a_ch3", "node_type": "CONTENT_UNIT", "role": "PRIMARY" },
    { "node_id": "cu_book_b_ch7", "node_type": "CONTENT_UNIT" },
    { "node_id": "cu_book_c_ch2", "node_type": "CONTENT_UNIT" }
  ],
  "generated": [
    { "node_id": "wk_data_eng_course", "node_type": "WORK" },
    { "node_id": "res_data_eng_course", "node_type": "RESOURCE" }
  ],
  "creates_tier": "WORK",
  "work_relationship": "NEW_WORK",
  "derivation_type": "COMPILATION",
  "royalty_flow": "NET_SHARE",
  "rights_basis": { "node_id": "ctr_data_eng_2026", "node_type": "CONTRACT" },
  "parameters": { "source_works": 3 }
}

When no contract applies: statutory exceptions

Most uses of content are authorised by a contract, and the Activity pins that contract in its rights_basis. But copyright law also permits a narrow set of uses with no grant at all: quoting a book in order to review it, parody, incidental inclusion. A critical review of a rival publisher's title that quotes three short passages needs no agreement with that publisher — and there is deliberately no pretend-contract to reach for (statutory exceptions are not licences).

These uses are recorded as a statutory-exception rights basis on the Activity itself. Instead of pointing at a contract, the rights_basis records which exception is relied on (UK fair dealing for criticism or review, quotation, US fair use, parody…), the jurisdictions whose law it stands on, whether the source had been made public — a legal precondition of several exceptions — a written justification, and who asserted it. Asserting an exception is a deliberate, privileged act: it requires its own permission on top of ordinary write access, and the asserting agent — a tenant user, an organisation, or a system agent, like any other agent on the record — is named in the assertion.

The platform records the claim — it never judges it

Whether a use really qualifies depends on jurisdiction, purpose, publication status, and how much was taken. Those are legal judgements. What the platform contributes is evidence and accountability: the used edges pin exactly which content, at which version, was quoted; the write-time snapshot freezes the proportions; and the assertion names the agent who made the call. If the claim is ever disputed, "what exactly did we take, and on whose say-so?" is a lookup, not an investigation.

Three rules follow from the basis being a defence rather than a grant:

  • No royalty flows. An exception-based edge always carries royalty_flow: EXCLUDED — no contract governs the edge, so nothing accrues through it.
  • Acknowledgement is required. The shipped exceptions (bar incidental inclusion) legally require sufficient acknowledgement, so the output content must credit the source it quotes.
  • The output still needs its own contract. The exception covers consuming the source; the review article itself is a new Work with an ordinary agreement (typically staff or work-for-hire) behind it.
A fair-dealing rights basis (JSON)

The rights_basis of the ADAPTATION Activity that creates the review — everything else on the Activity (agents, used, generated, lineage edges) is exactly as usual.

{
  "rights_basis": {
    "basis_type": "STATUTORY_EXCEPTION",
    "statutory_exception": {
      "type": "FAIR_DEALING_CRITICISM_REVIEW",
      "jurisdictions": ["GB"],
      "source_made_public": true,
      "justification": "Critical review; three quoted excerpts totalling 412 of 78,500 source words, each accompanied by commentary.",
      "asserted_by": {
        "agent_id": "agt_rights_ops",
        "kind": "ORGANISATION",
        "name": "Packt Rights Operations",
        "role": "OPERATOR"
      }
    }
  }
}

One boundary worth knowing: for content the platform holds a contract on, promotional excerpting is a contract question — an article walking customers through an upcoming title of your own is authorised by that title's agreement, recorded as an ordinary contract basis. Exceptions are asserted only where no grant covers the use.

What each question becomes

Because the record is complete and immutable, the three headline questions reduce to graph queries.

Every Activity where an AI model did the work names the organisation as the agent with role GENERATOR and logs the model in tooling. So "everything AI produced or touched" is a filter — by role=GENERATOR, or by a specific tooling.model — over the Activity Log. Because the log is never pruned, this answer is exact even years later.

Follow DERIVED_FROM edges backwards from the node. Each hop lands on a source Resource, Source, or ContentUnit; keep walking to reach the original Works. A TRANSLATION points back to its source text; a COMPILATION fans out to every source chapter it drew on.

Walk the same DERIVED_FROM edges to every source Work, read each Work's Contract, and apply the royalty_flow rule recorded on each lineage edge. The Royalty service does this traversal; the Activity graph is what makes it a computation rather than an investigation.

Corrections and editions: two kinds of supersession

Not every change is a derivation. A same-content fix — errata, a corrected reprint — is a REVISION that SUPERSEDES the prior Resource version without creating a new Work; the old version is never deleted. A substantial new edition, by contrast, is an EDITION: it stands as its own Work with its own ISBN and usually a re-papered contract, carries DERIVED_FROM to the prior edition's content, and SUPERSEDES the prior edition's Work. The superseded Work is retained, so the history stays intact.

How it works under the hood

The graph is an append-only directed acyclic graph. Nothing is ever mutated — that immutability is precisely what lets it answer questions about the past reliably. When content must be removed for legal reasons, a purge redacts the content of the Activities that generated it (free text and endpoint snapshots) while keeping the skeleton — the ID, type, timestamps, agents, and edges — so the provenance graph never dangles. Activities that merely used the purged content keep a frozen, write-time snapshot of the few endpoint attributes (medium, language, size) they needed, so the audit trail stays interpretable even after the endpoint is gone.