/aport-complete — Verify Before Done
Your APort passport defines what you must deliver before a task is complete. This skill enforces that contract. Do not mark any task done without running this first.
When to use this skill
- Before telling a user "I'm done" or "it's complete"
- Before closing a task, ticket, or GitHub issue
- Before opening a PR and considering work finished
- Before handing off to another agent
- Any time you would otherwise call a task complete
Prerequisites
You must have an APort passport with a deliverable contract.
If you don't have one, create a passport first:
- Web: https://aport.id
- CLI:
npx aport-id - Agent skill: Read https://aport.id/skill and follow the instructions
You need APORT_AGENT_ID in your environment. This is returned when you create
your passport and saved to aport-passport.json.
Step 1 — Compile your completion context
Before calling the API, gather:
| Field | Required | How to get it |
|---|---|---|
task_id | Yes | The task/ticket/issue identifier |
output_type | Yes | code, document, analysis, plan, data, or other |
summary | If passport requires it | Write a clear summary of what you did |
tests_passing | If passport requires it | Run your test suite, report result |
author_agent_id | If reviewer required | Your own APORT_AGENT_ID |
reviewer_agent_id | If reviewer required | The agent_id of the reviewing agent |
criteria_attestations | Always | One entry per criterion in your passport |
output_content | If scan_output enabled | Your full output (for pattern scanning) |
Step 2 — Build your attestations
Your passport defines acceptance criteria as a list of descriptions.
Each criterion maps to a criterion_id — the description slugified to lowercase
with spaces replaced by hyphens.
Example:
Criterion description: "A concrete output artifact must be produced"
criterion_id: "a-concrete-output-artifact-must-be-produced"
For each criterion in your passport, you must submit:
{
"criterion_id": "a-concrete-output-artifact-must-be-produced",
"met": true,
"evidence": "Concrete evidence — a file path, PR URL, CI run id, command output"
}
Evidence must be non-empty. "I believe this is met" is not evidence.
Good evidence: PR #47 at github.com/org/repo/pull/47
Good evidence: grep -r "TODO" src/ returned 0 results
Good evidence: CI run #1234 passed — https://ci.example.com/runs/1234
Step 3 — Call the verify endpoint
POST https://aport.io/api/verify/policy/deliverable.task.complete.v1
Content-Type: application/json
{
"context": {
"agent_id": "YOUR_APORT_AGENT_ID",
"task_id": "TASK_IDENTIFIER",
"output_type": "code",
"author_agent_id": "YOUR_APORT_AGENT_ID",
"summary": "What you did — must meet minimum word count in your passport",
"tests_passing": true,
"criteria_attestations": [
{
"criterion_id": "criterion-slug-here",
"met": true,
"evidence": "Concrete evidence string"
}
]
}
}
Note: agent_id goes inside context, alongside the policy-specific fields.
Step 4 — Handle the response
ALLOW response
{
"decision": {
"decision_id": "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx",
"allow": true,
"reasons": [
{ "code": "capability_verified", "message": "...", "severity": "info" }
],
"created_at": "2026-03-13T09:00:00Z",
"signature": "...",
"policy_id": "deliverable.task.complete.v1",
"agent_id": "ap_..."
},
"request_id": "..."
}
The task is done. You may mark it complete, close the ticket, open the PR.
Reference the decision_id in your completion message for the audit trail.
DENY response
{
"decision": {
"decision_id": "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx",
"allow": false,
"reasons": [
{ "code": "oap.summary_insufficient", "message": "Summary is required...", "severity": "error" }
]
},
"request_id": "..."
}
Do not mark the task done. Check decision.reasons[].code for the deny code. Fix the issue and retry.
| deny_code | What it means | What to do |
|---|---|---|
oap.summary_insufficient | Summary too short or missing | Rewrite summary with more detail |
oap.tests_not_passing | Tests failing or not submitted | Fix tests, resubmit with tests_passing: true |
oap.criteria_not_met | A criterion has met: false | Resolve the criterion, re-attest |
oap.evidence_missing | An attestation has no evidence | Add a concrete evidence string |
oap.criteria_incomplete | Missing attestation for a criterion | Add attestations for all passport criteria |
oap.self_review_not_allowed | Reviewer and author are the same | Get a different agent to review |
oap.blocked_pattern_detected | Output contains blocked content | Remove blocked patterns, resubmit |
oap.passport_suspended | Passport is suspended | Contact APort — your passport needs attention |
oap.unknown_capability | Passport missing deliverable.task.complete | Create a new passport with a deliverable contract |
Step 5 — Announce completion
Once you receive allow: true, tell the user:
- What you completed
- The decision_id (if they want the audit trail)
- Any next steps
Do not say "done" before receiving allow: true. The contract defines done, not you.
Retry behaviour
The deny_code is designed for autonomous retry. If you receive:
oap.summary_insufficient→ rewrite your summary and retry immediatelyoap.evidence_missing→ add evidence strings and retry immediatelyoap.tests_not_passing→ attempt to fix the failing tests, then retryoap.blocked_pattern_detected→ remove the blocked content, then retryoap.self_review_not_allowed→ this requires another agent — ask the user or route to revieweroap.criteria_not_met→ attempt to resolve the criterion, then retry
Maximum retries: 3. After 3 denials on the same task, surface the issue to the user. Do not loop indefinitely.
Links
- Create a passport: https://aport.id (web) or
npx aport-id(CLI) or https://aport.id/skill (agent) - Your passport: https://aport.id/passport/YOUR_SLUG
- Verify a decision: GET https://aport.io/api/verify/decisions/get/DECISION_ID
- API docs: https://aport.io/api/documentation