Add actor-aware admission for external Forgejo issues and comments #1583

Closed
coilyco-ops wants to merge 1 commit from issue-1571 into main
Member

External Forgejo issues and comments are now gated by actor-aware admission so unapproved reporter content stays consult-only until a Ward-authored approval snapshot exists. Machine-state markers only count from Ward automation identities, and the director/preflight paths now carry provenance through the queue, reservation, QA, and merge gates.

  • Adds the approval snapshot record and approval command.
  • Redacts unapproved external content from preflight and engineer prompts.
  • Updates docs and tests for the author-aware gate.

Tests: go test ./cmd/ward -count=1

closes #1571

External Forgejo issues and comments are now gated by actor-aware admission so unapproved reporter content stays consult-only until a Ward-authored approval snapshot exists. Machine-state markers only count from Ward automation identities, and the director/preflight paths now carry provenance through the queue, reservation, QA, and merge gates. - Adds the approval snapshot record and approval command. - Redacts unapproved external content from preflight and engineer prompts. - Updates docs and tests for the author-aware gate. Tests: `go test ./cmd/ward -count=1` closes #1571
Add actor-aware admission for external issues
All checks were successful
test / test (pull_request) Successful in 49s
107267e7bf
closes #1571
Author
Member

Blocking admission review at head 107267e7bf. Do not merge this head. First, issueRequiresApproval returns false when the issue author is empty, so missing provenance bypasses approval and admits raw content. The Shortcut adapter explicitly sets Author empty, making this reachable. Missing or ambiguous provenance must fail closed. Second, latestIssueSnapshotApproval treats content hash and issue update time as optional. An automation-authored marker with omitted fields can become a valid approval. Require every immutable field and exact match. Third, the approval hash covers only the selected comment IDs. A later untrusted comment does not invalidate the approval, and resolveAgentWork retains the full current comment list once any approval summary exists, so that later comment reaches preflight and the engineer prompt. Reject approval when any non-Ward comment is newer or not present in the approved snapshot, and pass only the approved comment set into execution. Fourth, the approval command accepts an arbitrary approved-by string without binding it to an authenticated trusted collaborator, which weakens the audit record. Add adversarial tests for missing author, incomplete approval fields, a later untrusted comment, and an exact-current-SHA forged approval record. The host-context test change also belongs to #1565 and should be linked or split from this security PR.

Blocking admission review at head 107267e7bfa364a6acdb42633dc3135e364dd502. Do not merge this head. First, issueRequiresApproval returns false when the issue author is empty, so missing provenance bypasses approval and admits raw content. The Shortcut adapter explicitly sets Author empty, making this reachable. Missing or ambiguous provenance must fail closed. Second, latestIssueSnapshotApproval treats content hash and issue update time as optional. An automation-authored marker with omitted fields can become a valid approval. Require every immutable field and exact match. Third, the approval hash covers only the selected comment IDs. A later untrusted comment does not invalidate the approval, and resolveAgentWork retains the full current comment list once any approval summary exists, so that later comment reaches preflight and the engineer prompt. Reject approval when any non-Ward comment is newer or not present in the approved snapshot, and pass only the approved comment set into execution. Fourth, the approval command accepts an arbitrary approved-by string without binding it to an authenticated trusted collaborator, which weakens the audit record. Add adversarial tests for missing author, incomplete approval fields, a later untrusted comment, and an exact-current-SHA forged approval record. The host-context test change also belongs to #1565 and should be linked or split from this security PR.
Author
Member

Additional blocking path in humanFeedbackRules.wardAuthoredComment: the current code returns true for any comment from an ignored author even when the body is ordinary feedback, and it returns true for any configured automation-marker prefix without checking the comment author. That is still an author-blind human-feedback exemption. Machine-state classification must require both an approved automation actor and a recognized machine marker. A custom marker from an external author must remain ordinary untrusted feedback, and an ordinary unmarked comment must not become machine state merely because an ignored or bot login wrote it. Add both adversarial fixtures and keep this in the #1584 correction.

Additional blocking path in humanFeedbackRules.wardAuthoredComment: the current code returns true for any comment from an ignored author even when the body is ordinary feedback, and it returns true for any configured automation-marker prefix without checking the comment author. That is still an author-blind human-feedback exemption. Machine-state classification must require both an approved automation actor and a recognized machine marker. A custom marker from an external author must remain ordinary untrusted feedback, and an ordinary unmarked comment must not become machine state merely because an ignored or bot login wrote it. Add both adversarial fixtures and keep this in the #1584 correction.
Author
Member

Approval authority is also not enforced at the command boundary. runDirectorApprove checks the repository owner and then posts with whichever tracker client currentAgentMode provides, but it never proves that the caller is a director or a trusted human operator. An engineer container carrying the normal push credential can invoke the subcommand and supply any approved-by string, allowing automation to self-grant the snapshot that is supposed to require collaborator approval. The fix must enforce the role and broker authority at execution time, not only place the command under a director-shaped CLI path, and the recorded approver must come from authenticated authority rather than free text. Add a negative engineer-mode invocation test and a positive authorized-director test.

Approval authority is also not enforced at the command boundary. runDirectorApprove checks the repository owner and then posts with whichever tracker client currentAgentMode provides, but it never proves that the caller is a director or a trusted human operator. An engineer container carrying the normal push credential can invoke the subcommand and supply any approved-by string, allowing automation to self-grant the snapshot that is supposed to require collaborator approval. The fix must enforce the role and broker authority at execution time, not only place the command under a director-shaped CLI path, and the recorded approver must come from authenticated authority rather than free text. Add a negative engineer-mode invocation test and a positive authorized-director test.
Author
Member

The prompt gate is also skipped for pull-request refs. resolveAgentWork applies issueRequiresApproval only in the non-merge-request branch. resolveAgentPullRequestWork can therefore place an external PR title, body, PR comments, linked issue body, and linked issue comments into preflight and execution without any approved snapshot. Carry PR author provenance and require approval for every untrusted source object before a PR-ref engineer or QA run sees its content. Add an external-PR fixture with a malicious body and later comment, plus a linked external-issue fixture, and prove both stay out of host one-shots and agent prompts until the exact snapshots are approved.

The prompt gate is also skipped for pull-request refs. resolveAgentWork applies issueRequiresApproval only in the non-merge-request branch. resolveAgentPullRequestWork can therefore place an external PR title, body, PR comments, linked issue body, and linked issue comments into preflight and execution without any approved snapshot. Carry PR author provenance and require approval for every untrusted source object before a PR-ref engineer or QA run sees its content. Add an external-PR fixture with a malicious body and later comment, plus a linked external-issue fixture, and prove both stay out of host one-shots and agent prompts until the exact snapshots are approved.
Author
Member

The approval record is not yet an immutable content record. issueSnapshotApprovalComment stores the title, issue author, selected comment IDs, timestamps, and one hash, but it does not store the approved issue body or the selected comment text and authors. After an edit, the old approved content cannot be reconstructed or audited from that record. The #1571 contract explicitly requires the approved title, body, selected comments, content hash, issue update timestamp, approving actor, and approval time. Persist the exact approved snapshot in the Ward-authored record or another immutable Ward-owned artifact, then have execution consume that snapshot rather than re-reading mutable current text.

The approval record is not yet an immutable content record. issueSnapshotApprovalComment stores the title, issue author, selected comment IDs, timestamps, and one hash, but it does not store the approved issue body or the selected comment text and authors. After an edit, the old approved content cannot be reconstructed or audited from that record. The #1571 contract explicitly requires the approved title, body, selected comments, content hash, issue update timestamp, approving actor, and approval time. Persist the exact approved snapshot in the Ward-authored record or another immutable Ward-owned artifact, then have execution consume that snapshot rather than re-reading mutable current text.
Author
Member

Final disposition: this PR is closed without merge. Its branch is retained. The reviewed implementation is superseded by the complete P0 replacement contract at #1586. PR #1585 also failed review and was closed. Containment remains active.

Final disposition: this PR is closed without merge. Its branch is retained. The reviewed implementation is superseded by the complete P0 replacement contract at https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/1586. PR #1585 also failed review and was closed. Containment remains active.
coilyco-ops closed this pull request 2026-07-28 04:18:41 +00:00
All checks were successful
test / test (pull_request) Successful in 49s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/ward!1583
No description provided.