Issue tracking
An issue tracking app helps a team organize work as tickets. A ticket describes a problem or task, records its progress, and defines what needs to happen before it is complete.
Someone reports a bug in a calculator app: the subtraction button adds numbers instead of subtracting them. The person assigned to the ticket records how the button should behave, checks that the fix produces that result, and marks the ticket done. In this example, the team sees each saved update right away. There is no separate review step: a participant with permission authorizes the update by saving it.
The issue tracking app uses Governed Artifact Protocol (GAP) to record what was saved and who authorized each update. Each saved version also identifies the content rules it follows. A person or an AI agent reading the ticket can use those records to check that the content they received matches the authorized update.
Creating a ticket and saving changes each perform several GAP lifecycle steps together. The issue tracking app organizes those steps into these actions:
A workflow designer proposes the ticket's fields and validation rules as a profile. The workflow owner ratifies that revision so it can be used for new tickets without repeating the model approval.
The requester creates a ticket. That action saves version 1, records the requester's authorization, and makes the ticket available to the team.
The assignee edits a working copy, and the issue tracking app checks the changes as they go. Editing the working copy performs no GAP step and creates no GAP record. Readers continue to see the saved ticket.
The assignee saves the working copy. The issue tracking app creates a new ticket version, records the assignee's authorization, and makes the update available to the team.
The assignee sets the status to Done and describes how they checked the fix. Saving creates another version, with that description recorded as the reason on its authorization.
A reader requests the current ticket or an earlier version and checks its release proof to confirm that the saved content matches the authorized update.
The issue tracking app decides who may create, update, and complete a ticket. GAP lets a person or an AI agent hold any of those permissions, and its roles treat both the same. In the records below, the requester is an AI agent and the workflow owner is a person. Every successful save creates a version and records its authorization and release. Readers receive those records together as a release proof.
The ticket and its content model
The issue tracking app stores the calculator bug as a GAP artifact with the ID ticket:subtraction. Saving a change creates a new version of the ticket under that same ID, and earlier versions remain available. A profile defines the ticket's content model, including its fields and the rules they must satisfy. The bug ticket profile requires four fields:
titleanddescriptionare text, and neither may be blank.acceptanceCriteriais a list of up to twenty entries, each one text. It may be empty when a ticket is filed.statusisto_do,in_progress, ordone. Saving a status change creates a new ticket version, just as saving a changed title does. The issue tracking app decides which status changes are allowed and what a participant must provide before marking a ticket Done.
The bug-ticket profile, revision 1JSON
{
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7",
"ratification": {
"ratifiedAt": "2026-09-10T00:00:00.000Z",
"actorId": "human:workflow-owner",
"actorKind": "human"
},
"payloadContract": {
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": 1,
"maxLength": 5000
},
"description": {
"type": "string",
"minLength": 1,
"maxLength": 5000
},
"acceptanceCriteria": {
"type": "array",
"maxItems": 20,
"items": {
"type": "string",
"minLength": 1,
"maxLength": 5000
}
},
"status": {
"type": "string",
"enum": [
"to_do",
"in_progress",
"done"
]
}
},
"required": [
"title",
"description",
"acceptanceCriteria",
"status"
],
"additionalProperties": false
}
}
Adding an acceptance criterion creates a new ticket version using the same content model. Requiring a new field changes the model itself, so the workflow owner must ratify a new profile revision. Earlier ticket versions still reference the revision they were created under. Each ticket record's profile block includes the profile ID, revision number, and digest, so a reader can identify the exact rules used to check that version.
Example ticket content
This is version 1 of ticket:subtraction, exactly as the issue tracking app stores it: the requester's first save, with no acceptance criteria yet. The assignee adds one later.
ticket:subtraction, version 1JSON
{
"artifactId": "ticket:subtraction",
"artifactVersion": 1,
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"authoredBy": {
"actorId": "agent:requester",
"actorKind": "agent"
},
"payload": {
"title": "Fix the subtraction button",
"description": "The calculator adds numbers when subtraction is selected.",
"acceptanceCriteria": [],
"status": "to_do"
}
}
Create, update, and complete a ticket
The subtraction bug moves through three saved versions: the initial report, the assignee's acceptance criteria, and the completed ticket. Each save makes the update available to the team.
Set up the content model
Step 1: Propose a profileStep 2: Ratify a revisionA workflow designer proposes the ticket's fields, and the workflow owner approves them. New tickets can use that approved profile revision. In the demo, this profile comes already ratified.
Create the ticket
Step 3: Create a versionStep 4: AuthorizeStep 5: ReleaseThe requester chooses Create ticket, with the status "To do." The issue tracking app saves an artifact version, records the requester's release authorization for that exact content, and records its release, all in one action. The ticket is available right away, with no separate approval.
The requester's authorization of version 1, recorded in the same saveJSON
{
"artifactId": "ticket:subtraction",
"artifactVersion": 1,
"payloadDigest": "sha256:710a52e9ac3aa61ecd84812330e842efdd0640bc1809b04dc2f4ef82e0978995",
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"authorizedAt": "2026-08-26T10:00:00Z",
"authorizedBy": {
"actorId": "agent:requester",
"actorKind": "agent"
},
"authorizationSubjectDigest": "sha256:ef9f695c8d3edc3b8a8f1eabd6d1fd0309b72f40fa0d91adea485b386433f54c"
}
The release of version 1: the payload and its profile revisionJSON
{
"artifactId": "ticket:subtraction",
"artifactVersion": 1,
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"payload": {
"title": "Fix the subtraction button",
"description": "The calculator adds numbers when subtraction is selected.",
"acceptanceCriteria": [],
"status": "to_do"
}
}
Edit the ticket
The assignee opens the current version and edits it. The issue tracking app keeps the edits as a working copy, checks them against the profile, and lets the assignee change them as often as needed. Until the assignee saves, the current-ticket view and history continue to show the released versions. The unfinished edits stay in the application’s local workspace, separate from the released ticket. Restarting the demo preserves that working copy.
Save changes
Step 3: Create a versionStep 4: AuthorizeStep 5: ReleaseThe assignee adds "Subtracting 2 from 5 must display 3" to the acceptance criteria and sets the status to "In progress." The assignee has permission to update the ticket, so saving creates, authorizes, and releases a new version. Only the saved result becomes a ticket version; the intermediate edits to the working copy are not added to the ticket's history. Readers can retrieve the update immediately or request any earlier released version.
ticket:subtraction, version 2: acceptance criteria and In progressJSON
{
"artifactId": "ticket:subtraction",
"artifactVersion": 2,
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"authoredBy": {
"actorId": "agent:assignee",
"actorKind": "agent"
},
"payload": {
"title": "Fix the subtraction button",
"description": "The calculator adds numbers when subtraction is selected.",
"acceptanceCriteria": [
"Subtracting 2 from 5 must display 3."
],
"status": "in_progress"
}
}
The assignee's authorization of version 2JSON
{
"artifactId": "ticket:subtraction",
"artifactVersion": 2,
"payloadDigest": "sha256:d6a83009eb9b1b68523b845f4dd846b36a64dcf9b403d951a9cfdc98eeb55235",
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"authorizedAt": "2026-09-11T10:00:00Z",
"authorizedBy": {
"actorId": "agent:assignee",
"actorKind": "agent"
},
"authorizationSubjectDigest": "sha256:4f3fbf1c61b768e0ce2aa0ec531e9bb17e69ac9812be2d8302652b45b65836c3"
}
The release of version 2JSON
{
"artifactId": "ticket:subtraction",
"artifactVersion": 2,
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"payload": {
"title": "Fix the subtraction button",
"description": "The calculator adds numbers when subtraction is selected.",
"acceptanceCriteria": [
"Subtracting 2 from 5 must display 3."
],
"status": "in_progress"
}
}
Complete the ticket
Step 3: Create a versionStep 4: AuthorizeStep 5: ReleaseAfter checking that the calculator subtracts correctly, the assignee marks the ticket Done. If the assignee has permission to make that change, the issue tracking app creates, authorizes, and releases another version. The issue tracking app decides what a ticket needs before it can be marked done; GAP records the authorized change. Here, the authorization's reason contains the assignee's description of the check. Readers can find it in the release proof. The issue tracking app records what the assignee reported; it does not run the calculator or test the fix itself.
Read the ticket or its history
Step 6: Read and verifyReaders can request the current ticket or any earlier released version. Each response includes a release proof with the saved content, its profile revision, and its authorization. Readers can verify those records and compare versions to see what changed.
The release proof of version 1: the release, its profile revision, and the authorizationJSON
{
"release": {
"artifactId": "ticket:subtraction",
"artifactVersion": 1,
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"payload": {
"title": "Fix the subtraction button",
"description": "The calculator adds numbers when subtraction is selected.",
"acceptanceCriteria": [],
"status": "to_do"
}
},
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7",
"ratification": {
"ratifiedAt": "2026-09-10T00:00:00.000Z",
"actorId": "human:workflow-owner",
"actorKind": "human"
},
"payloadContract": {
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": 1,
"maxLength": 5000
},
"description": {
"type": "string",
"minLength": 1,
"maxLength": 5000
},
"acceptanceCriteria": {
"type": "array",
"maxItems": 20,
"items": {
"type": "string",
"minLength": 1,
"maxLength": 5000
}
},
"status": {
"type": "string",
"enum": [
"to_do",
"in_progress",
"done"
]
}
},
"required": [
"title",
"description",
"acceptanceCriteria",
"status"
],
"additionalProperties": false
}
},
"authorization": {
"artifactId": "ticket:subtraction",
"artifactVersion": 1,
"payloadDigest": "sha256:710a52e9ac3aa61ecd84812330e842efdd0640bc1809b04dc2f4ef82e0978995",
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"authorizedAt": "2026-08-26T10:00:00Z",
"authorizedBy": {
"actorId": "agent:requester",
"actorKind": "agent"
},
"authorizationSubjectDigest": "sha256:ef9f695c8d3edc3b8a8f1eabd6d1fd0309b72f40fa0d91adea485b386433f54c"
}
}
The release proof of version 2JSON
{
"release": {
"artifactId": "ticket:subtraction",
"artifactVersion": 2,
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"payload": {
"title": "Fix the subtraction button",
"description": "The calculator adds numbers when subtraction is selected.",
"acceptanceCriteria": [
"Subtracting 2 from 5 must display 3."
],
"status": "in_progress"
}
},
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7",
"ratification": {
"ratifiedAt": "2026-09-10T00:00:00.000Z",
"actorId": "human:workflow-owner",
"actorKind": "human"
},
"payloadContract": {
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": 1,
"maxLength": 5000
},
"description": {
"type": "string",
"minLength": 1,
"maxLength": 5000
},
"acceptanceCriteria": {
"type": "array",
"maxItems": 20,
"items": {
"type": "string",
"minLength": 1,
"maxLength": 5000
}
},
"status": {
"type": "string",
"enum": [
"to_do",
"in_progress",
"done"
]
}
},
"required": [
"title",
"description",
"acceptanceCriteria",
"status"
],
"additionalProperties": false
}
},
"authorization": {
"artifactId": "ticket:subtraction",
"artifactVersion": 2,
"payloadDigest": "sha256:d6a83009eb9b1b68523b845f4dd846b36a64dcf9b403d951a9cfdc98eeb55235",
"profile": {
"profileId": "bug-ticket",
"revision": 1,
"digest": "sha256:137831275b47219dbd4343e028a6ece15067744813da0e464429b4e5162046d7"
},
"authorizedAt": "2026-09-11T10:00:00Z",
"authorizedBy": {
"actorId": "agent:assignee",
"actorKind": "agent"
},
"authorizationSubjectDigest": "sha256:4f3fbf1c61b768e0ce2aa0ec531e9bb17e69ac9812be2d8302652b45b65836c3"
}
}
Because status is part of the ticket's content, every release carries whatever status the ticket had at the time. A released ticket can be "To do," "In progress," or "Done"; release means the version is available, and only the status says whether the work is finished.
Who does what?
A participant who saves a ticket acts as both Author and Authority. The authorization shows they had permission to make the update available; it isn't a review of the content. Editing a working copy creates no GAP record. When the participant tries to save it as a ticket version, the issue tracking app checks whether they have permission to make that update.
The issue tracking app is the Implementation: it checks content, keeps every version, and records each authorization and release together. A participant reading the ticket acts as Consumer. For the content model, the workflow designer is the Author and the workflow owner is the Authority.
Driving the specification
The issue tracking demo implements the working draft independently of the other demos. Building the ticket workflow helps identify rules that are missing or unclear. Those findings lead to draft changes or are recorded as open questions. The example records are updated when the draft changes.
Run the demo
The demo server runs this application beside the others. Set up the demo once, then follow the issue tracking walkthrough: create a ticket, work on a copy, save it, and complete it. The demo runs on your computer with made-up data. The tool reference lists every tool this application offers, with the lifecycle steps each one declares.
To compare these choices with your own work, use the evaluation guide.