Install
openclaw skills install @uchibeke/aport-completeVerify a completed task against your APort passport's deliverable contract before marking it done. Enforces acceptance criteria, summary requirements, test status, and reviewer checks deterministically. Use this before calling any task complete.
openclaw skills install @uchibeke/aport-completeYour 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.
You must have an APort passport with a deliverable contract.
If you don't have one, create a passport first:
npx aport-idYou need APORT_AGENT_ID in your environment. This is returned when you create
your passport and saved to aport-passport.json.
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) |
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
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.
{
"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.
{
"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 |
Once you receive allow: true, tell the user:
Do not say "done" before receiving allow: true. The contract defines done, not you.
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 retryMaximum retries: 3. After 3 denials on the same task, surface the issue to the user. Do not loop indefinitely.
npx aport-id (CLI) or https://aport.id/skill (agent)