Files
PaperClipAI/server/src/services
DottaandPaperclip 5b99bdae47 fix(native): verify requested publication at finalization (#19130)
## Thinking Path

> - Paperclip manages work done by AI agents.
> - Native execution reports work completion through a saved result.
> - Some native providers can build that result from their final
message.
> - This path can bypass the finish tool's publication check.
> - A task can then be marked done without the document the user
requested.
> - This change checks publication again at finalization and status
commit.
> - Missing output uses the existing bounded repair path with specific
feedback.

## Linked Issues or Issue Description

**What happened?**

A native provider returned a final message about a local Markdown
briefing. It had not published a task document or a verified attachment.
Its synthetic result claimed success. The server accepted the claim and
marked the task done. A real PostgreSQL regression reproduced this on
public master.

**Expected behavior**

A requested task document must have valid saved publication evidence
before the task becomes done. The check must apply even when the
provider does not call the finish tool. A plain answer still needs no
document. An explicit document-free fallback requires the actual human
rejection for the current unchanged request.

**Steps to reproduce**

1. Request a Markdown briefing document on a task.
2. Complete a native turn with a synthetic
`provider:opencode:agent-message` result and no published output.
3. Admit the result through the normal control-plane port and finalize
it.
4. The old code marks the task done. The repaired code keeps it open and
queues one repair with publication instructions.

**Paperclip version or commit**

Reproduced from public master
`7d1e9b3477f12780e987c32fb8191d476c210fda`.

Related: #18661 added the finish-tool publication guard. This change
applies that guard to finalization. #18180 covers a different fallback
problem: no final text. This change preserves native provider fallback
generation and its saved claims.

## What Changed

- Run the existing deliverable guard before accepting a native done
decision. Keep the original provider result unchanged.
- Route missing publication through the existing single
incomplete-result continuation. Include the publication feedback in the
wake. A repeated incomplete result uses the existing named recovery
action.
- Inspect publication through a typed safe projection of the accepted
ledger. Keep whole-protocol validation at result admission. An
authoritative done decision must pass publication checks even when its
accepted ledger is partial.
- Recheck publication under the status-commit transaction. Hold only
matching proof rows with non-waiting share locks. Changed or locked
proof uses the existing bounded status replanning path.
- Permit an explicit document-free decline fallback only for the saved
human rejection selected by the current continuation. Require the same
original request, current saved description, source revision and source
contract. Model prose or an unrelated old decision is insufficient.
- Add real database controls for publication, plain answers,
authenticated decline, stale or wrong decisions, bounded recovery, and
deletion between assessment and commit.

## Verification

- The first regression failed before the repair: the synthetic result
marked an unpublished task done. The retained real PostgreSQL run took
21.846 seconds, including setup and cleanup.
- Two description-edit regressions also failed before their repair: a
description change after admission or at the commit boundary still let
the old decline waive the newly required document. Both now keep the
task open. The red run took 25.738 seconds.
- The commit-race regression also failed before its repair: removing
publication after assessment still allowed done. That run took 17.448
seconds.
- All thirteen focused real database controls pass after the repair.
They exercise the normal control-plane port, finalizer, semantic
publication tools, saved human rejection and status transaction. Owned
database processes, data and output storage are removed after each run.
- All 469 tests across the seven relevant and affected suites pass on
head `f8870469f9879a53551f0faaff0fe5b1234ea303`, based on master
`c7390bef20`. The six-suite run passed 426 tests in 97.319 seconds
including owned setup and cleanup. The unchanged reassessment-worker
suite passed 43 tests in 48.901 seconds. All required current-head
checks pass in [CI run
41937653209](https://github.com/paperclipai/paperclip/actions/runs/41937653209),
including aggregate verify, typechecking, build, general and serialized
tests, native Runner tests and all eight browser shards. Fresh Greptile
review is 5/5 on this exact head, with no open findings.
- Local scoped server typechecking reported no errors in the four
changed files and 11 errors in reused dependency declarations and
exports. That setup failure remains retained. Fresh current-head CI
passes the full Typecheck + Release Registry gate and Build. Local
typecheck is not claimed as passing.
- CI on the previous head found one shared status-projection regression
across Studio, routine, status-authority and worker controls. Those
phases use opaque accepted ledgers. The correction inspects only
publication references, preserves the accepted marker and bindings, and
forces the publication check for an authoritative done decision. A new
real commit control verifies that an empty accepted ledger cannot close
a requested-document task. All original affected assertions remain
unchanged. The retained failed run is
[41934314437](https://github.com/paperclipai/paperclip/actions/runs/41934314437).
- No provider call, paid campaign, staging deployment or eval regrade is
part of this PR. No model, timeout, assertion, workflow, dependency or
lockfile change.

Focused suites:

```sh
./node_modules/.bin/vitest run --config "$PWD/server/vitest.config.ts" --root server \
  src/services/native-runtime/paperclip-control-plane-port.test.ts \
  src/services/native-runtime/native-completion-feedback.test.ts \
  src/services/native-runtime/native-deliverable-feedback.test.ts \
  src/services/native-runtime/native-skill-test-status.test.ts \
  src/services/native-runtime/native-routine-status.test.ts \
  src/__tests__/native-status-arbiter-corpus.test.ts \
  src/__tests__/native-status-reassessment-work.test.ts
```

## Risks

A native result that previously closed a task without its requested
output now leaves it open. The existing repair path can run once and
then records a named recovery action. Database contention does not grant
completion. The existing first-task decision gate still runs first. The
conditional decline check is deliberately conservative; unmatched
wording or missing authoritative source data does not waive publication.
This change closes a false completion path. It does not establish why a
provider omitted publication, prove a live provider repair, or qualify a
full evaluation plan.

## Model Used

OpenAI Codex, based on GPT-6, assisted with diagnosis, code and
controls. The exact serving model identifier and context-window size are
not exposed in this session. Capabilities used: repository inspection,
code editing, local code execution and GitHub tools.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have described the issue in-PR following the relevant issue
template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal issue
identity
- [x] I have run relevant tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have documented the behavior and limits in this PR; no operator
command or public API changed
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open findings
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-04 21:46:29 -06:00
..
…
…
…
…