Files
gitea-agent/harness/agents/impl/skills/gitea-issue-rework/references/readback.md
T

9.5 KiB

Canonical PR rework completion readback

Scope and source

Canonical entry point: <HARNESS_ROOT>/harness/agents/impl/skills/gitea-issue-rework/scripts/rework_comment.py. The adjacent rework_readback.py derives readback inputs; rework_readback_cli.py provides the role-local GET-only transport and private artifact writer. Update these canonical files and this reference in place; do not copy them into another profile or use a review-role credential for impl delivery.

snapshot and verify-live never create, edit, retry, or submit a Gitea comment. The create action verifies an already observed single comment addition; reuse verifies an unchanged collection with exactly one matching existing comment. An independently authorized publication workflow remains responsible for its single external write, uncertain-write reconciliation, Issue lease, and fresh pre-write delegation admission.

Prerequisites and independent expectations

  • The canonical access contract is harness/operations/gitea-access.md.
  • Run in the impl role's own HERMES_HOME. Only its owned, non-symlink, mode-0600 .env file supplies GITEA_TOKEN. No ambient token or other-profile fallback.
  • Independently supply the expected stable impl account ID. The first API read must authenticate that ID and the gitea-impl-agent login.
  • Generate expected.json with the existing canonical renderer. Its exact target, final head, reviewed head, review ID, body, marker and digest bind the workflow.
  • Supply a reviewed provider contract and its independently expected SHA-256. The exact provider version is read from the server and must match the contract. A digest calculated from an untrusted delegate file is not independent evidence.
  • Own a private mode-0700 runtime directory. Inputs must be owned regular files, not group/world writable or symlinked. Output files use mode 0600; a failed verification does not replace an existing receipt.

Provider contract

Schema: gitea-agent.rework-provider/v1. The contract explicitly declares the aggregate semantics for PR and underlying PR-Issue comments fields. Each components array contains unique values from top_level, reviews, inline and must include top_level. Choose the reviewed deployment semantics, not the combination that happens to fit a current response. There is no guessed version or automatic capability downgrade.

Shape example only, not a claim about this deployment:

{
  "schema": "gitea-agent.rework-provider/v1",
  "version": "<independently-observed-version>",
  "aggregate": {
    "pr": {"field": "comments", "components": ["top_level"]},
    "pr_issue": {"field": "comments", "components": ["top_level"]}
  },
  "inline": {
    "mode": "per_review",
    "field": "review_comments",
    "components": ["inline"]
  }
}

For inline.components, the explicit projection modes are ["inline"], ["reviews"], and ["reviews", "inline"]. The review-only mode projects only the number of reviews into review_comments; it does not add per-review inline comments. The inline-only mode projects collected per-review inline comments; the combined mode projects both. These are caller-supplied policies: select one only from independently verified deployed-provider semantics, never automatically detect a mode or choose one to fit observed counters. Both per_review and per_review_or_pr_count retain the same explicit fallback policy; per-review counters, complete collection pagination, duplicate IDs and record cardinality remain checked.

per_review requires every exact-review comment endpoint. Only publication snapshot/verify-live with an explicit per_review_or_pr_count capability permits endpoint-unavailable 404/405 fallback. Initial input-gate instead requires real inline records and rejects this fallback; its PR-wide counter projections are recorded as non-blocking counter_diagnostics, not used to establish collection completeness. See input-gate.md for the phase boundary. Publication counter crosschecks, Source Issue protection and review decision rules are unchanged. Permission errors, redirects, malformed JSON and conflicting metadata never trigger fallback. Fallback uses a fresh PR count, the declared review/inline components and complete review comments_count records; disagreement is BLOCK. Counts-only fallback is recorded as such and is not proof of inline comment bodies or a complete inline-record inventory.

Native wire-shape reference: the pinned Gitea v1.24.6 serializers in services/convert/pull.go and modules/structs/{issue,pull}.go expose PR url and html_url as HTML URLs, and PR-Issue pull_request.html_url without a native pull_request.url. Require the actual native relation URL; do not invent a missing field. This source pin is fixture/protocol evidence, not a claim about the deployment version. The caller's independent provider contract still binds that version. In Gitea v1.27.3, services/convert/pull.go delegates the PR review-comment count to models/issues/pull.go's GetReviewCommentsCount, which filters CommentTypeReview; this is source evidence for that version's review-only projection, not a blanket claim for all versions or a live-gate success. In per_review mode only, an omitted review_comments is accepted as zero only when complete per-review records and their counters prove a zero projection. Explicit null, nonzero projection or counts-only fallback with a missing scalar is rejected. Raw pagination Link evidence is preserved and first, prev, next, last are checked against the declared total, including the empty sentinel page after an exact multiple of the page size.

Commands and canonical artifact paths

The caller's admitted Issue runtime is <HARNESS_ROOT>/repositories/<owner>/<repository>/worktrees/issue-<issue>/runtime/<run_id>. Place the canonical rework-baseline.json and rework-receipt.json there, outside the product checkout. Preserve the baseline SHA-256 returned by its producer independently; never replace the baseline after the external write.

Through terminal, before publication:

python3 -B <HARNESS_ROOT>/harness/agents/impl/skills/gitea-issue-rework/scripts/rework_comment.py snapshot \
  --expected <RUNTIME>/expected.json \
  --provider-contract <TRUSTED_PROVIDER_CONTRACT> \
  --provider-contract-sha256 <INDEPENDENT_PROVIDER_SHA256> \
  --actor-id <INDEPENDENT_IMPL_ACCOUNT_ID> \
  --output <RUNTIME>/rework-baseline.json

After the authorized write or existing-comment reuse, through terminal:

python3 -B <HARNESS_ROOT>/harness/agents/impl/skills/gitea-issue-rework/scripts/rework_comment.py verify-live \
  --expected <RUNTIME>/expected.json \
  --provider-contract <TRUSTED_PROVIDER_CONTRACT> \
  --provider-contract-sha256 <INDEPENDENT_PROVIDER_SHA256> \
  --actor-id <INDEPENDENT_IMPL_ACCOUNT_ID> \
  --baseline <RUNTIME>/rework-baseline.json \
  --baseline-sha256 <INDEPENDENT_BASELINE_SHA256> \
  --action create \
  --output <RUNTIME>/rework-receipt.json

Use --action reuse only for an unchanged scoped collection. Do not recapture a baseline or retry a write merely because readback fails. Exit 0 and a receipt are required; stdout reports only the artifact path, fingerprint and read-only flag. The existing offline verify command remains compatible, but manually supplied JSON/true flags or archived artifacts alone are not fresh provider readback.

Verified invariants

  • Source Issue, PR, underlying PR-Issue, repository, exact source review and heads are independently bound to authenticated endpoint responses.
  • Pagination is bounded; duplicate IDs, contradictory totals, truncated pages, malformed metadata and off-target continuation links are rejected.
  • Genuine empty arrays without a count header are supported; null is not silently accepted as an empty collection.
  • Existing top-level comments, Source Issue fields/comments, native reviews and admitted inline evidence remain unchanged. Create admits exactly one matching impl-authored comment. Reuse admits no addition.
  • Only provider-declared count projections and the bounded PR/underlying-PR-Issue state-delta/exact-timestamp admission for that verified new comment are admitted; Source Issue metadata, including updated_at, is fully immutable. This admission is not proof of causal event attestation; the receipt records that limitation.
  • The adapter computes normalized inputs and passes them to the same verifier used by rework_comment.py verify, rather than accepting caller success flags.

Limits and verification

The CLI enforces verified HTTPS, no redirects, GET-only requests, a one-megabyte response cap, ten-second socket timeouts and a 120-second operation budget. Body budget checks occur between socket reads, so an already-blocking read may add its remaining socket-timeout allowance. There are no automatic retries.

The authenticated transport and independently supplied actor/provider/baseline expectations are trust inputs, not a sandbox or proof that arbitrary caller code is honest. The adapter cannot prove missing inline bodies from scalar counts, and state deltas plus exact timestamps are not independent causal-event attestation.

Run focused tests with terminal:

python3 -B -m unittest harness.tests.impl.test_rework_readback harness.tests.impl.test_rework_readback_transport harness.tests.impl.test_rework_comment -v
python3 -B harness/tests/run.py

Local synthetic transport and HTTP fixtures validate the executable paths; they do not constitute a live Gitea readback or an external-write completion record.