Keep provider activation signals and external refresh intent in the same owning transaction. Preserve all other qualified owner and incoming master blobs.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Preserve all 15 qualified confirmation files and exactly the 23 incoming master UI, documentation, and test paths.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Basic task journeys must prove that people can answer an agent and
receive saved work.
> - The question journey asks for a choice, then a typed reference, then
a saved note.
> - A native choice card can offer a real written-answer control as well
as predefined options.
> - The old observer rejected that form before a person could type the
reference.
> - This pull request accepts that explicit native control and keeps
exact answer and work evidence.
> - The separate text-only and named-tool qualifications keep their
stricter contracts.
## Linked Issues or Issue Description
Refs #18346 and #18456. The integration inherits the reviewed fixture
prerequisite #18387 and Agent Chat recovery #18465 through their actual
merged parent. The producer fix in #18456 is separate from this basic
journey contract correction. Public duplicate PR and issue searches
found no other correction for this form equivalence.
**What happened?**
The basic question journey requires the reference card to have
`answerMode: text`. It rejects a native single-select card even when its
canonical form explicitly enables a written answer. This makes the
observer test a widget shape in addition to the requested behavior.
**Expected behavior**
The person must make a real choice and type the complete reference. A
native card with an explicit written-answer control can satisfy the
second step. Selecting a predefined option cannot satisfy it. The agent
must resume the bound work and save both actual answers in its own task
document. The document must survive reload.
**Steps to reproduce**
Use the focused recording and real QuestionForm browser test in this PR.
Keep the reference as a single-select native question with one
predefined option and `customAnswer.enabled: true`. The previous grader
rejects `question-resume.forms`. The previous browser helper rejects
`single_select` before it uses the written-answer control.
**Paperclip version / commit**
Original source base `f2db909393de124caf6a5380a52d4e0e1d621661`. Current
integration `a417fd827347ed6f7998774a9de6aee70b76d867` merges exactly
`12b0cc630471ee151ec5bc8e41a34f09236d6723` into reviewed question source
`faec4ce6d775a34d303d200987e4e84d6dc26caf`. The scheduled failure on
`82d15946c5844d3ae41c6ade62fa224053f20a9a` remains a failure. Its saved
card had no canonical written-answer capability and would still fail
this change. The retained normalized events do not prove whether the
original provider flag was omitted or false.
**Deployment mode**
Attached native task journeys, including QA Cloud. This PR changes the
observer and grader. It does not change the app, provider prompt, model,
budget, timeouts, cleanup, or runtime deployment.
## What Changed
- Accept one provider-bound single-select reference card with explicit
canonical custom answers and valid, distinct options. Keep pure-text
reference support.
- Make the browser select the actual written-answer control and fill the
real rich input. Opt in only for the basic question journey.
- Keep the exact no-option typed response, immutable card, board actor,
native request and continuation, saved agent document, reload, model,
environment, checkpoints, and existing one-to-three-run checks.
- Reject missing or disabled custom capability, inferred legacy
capability, malformed or extra questions, foreign native identities,
changed cards, substituted options, and wrong or missing reference text.
- Add a real production QuestionForm and TaskChatRichInput Chromium
probe. It first selects a predefined option, then proves the helper
clears it and submits only the exact custom text.
- Document the contract correction and preserve the historical failed
attempt and its separate cleanup outcome. Keep necessary-question and
named-tool documentation qualification unchanged.
## Verification
- Before the observer change, the focused grader regression failed only
the new valid-written-reference case; the other 108 cases passed.
- Before the browser helper change, real Chromium passed the text
control and four rejection controls but failed the valid native
written-answer control at the old pure-text assertion.
- Initial focused Runner E2E unit selection at `af07f4de`: **256 passed
across 9 files**. This includes question/resumption, clarification
evidence/flow/collection/deadline, necessary question, documentation,
contracts, and report catalog tests.
- Initial real Chromium selection at `af07f4de`: **12 passed**. Six new
production QuestionForm/control checks and all six existing
document-readiness checks pass. No form or rich-input component is
mocked.
- Historical test-only head `faec4ce6d775a34d303d200987e4e84d6dc26caf`
adds a direct canonical-absent legacy `freeText` grader negative and
renders the actual legacy presentation in the browser. All four
production helper blobs remain identical to the reviewed head. A
temporary legacy inference mutant makes the new negative fail. After
restoring production source, **179 focused unit tests and all 6 real
QuestionForm checks pass**. The six document-readiness specs and their
helper are unchanged.
- Original [CI run
41205748589](https://github.com/paperclipai/paperclip/actions/runs/41205748589)
failed the staged-listen Runner handoff test; that failure is preserved
and was not retried. Its Runner source closure was identical to an
earlier passing canonical lane. The symptom also recurred on other heads
and is tracked independently; no infrastructure attribution or live pass
is claimed.
- Current integration `a417fd827347ed6f7998774a9de6aee70b76d867`
preserves **all 12 question files byte-for-byte** from faec. Every other
tracked path is identical to exact merged parent
`12b0cc630471ee151ec5bc8e41a34f09236d6723`, which contains actual
prerequisite `baf651c8d0122efc93f3ef03e891919eeb666215`. There is no
manual conflict resolution or additional production edit.
- On that integrated head: **257 focused Vitest tests across 9 files,
206 required Node source/reporter tests, and 12 real Chromium tests
pass**. Map/module-boundary checks and catalog discovery pass. The
actual `--preview` command passes with a synthetic question target and
an intentionally unavailable adapter; it performs no credential read,
adapter loading, provisioning or model call. It retains maxRuns3, a
600000ms campaign bound and zero retries.
- Exact-head [CI run
41210806950](https://github.com/paperclipai/paperclip/actions/runs/41210806950)
passes, including official typecheck, build, all eight browser shards,
both Runner Vitest lanes and aggregate verify. All 59 current PR
contexts are terminal with no failures. Fresh [Greptile
summary](https://github.com/paperclipai/paperclip/pull/18459#issuecomment-6543878408)
explicitly reviews `a417fd827347ed6f7998774a9de6aee70b76d867` at 5/5
with no findings; its exact-head check succeeds and all threads are
resolved. No fresh deployed journey pass is claimed yet.
- Catalog discovery lists the existing question journey. Map checks and
module-boundary checks pass. Diff checks pass.
- `tsc -p tests/runner-e2e/tsconfig.json` was attempted. The fresh
integrated checkout still lacks compiled Runner, plugin SDK, and adapter
declarations. The attempted local check reports 344 diagnostic lines
from dependency/type closure and zero diagnostics in all 12
question-owned paths. Official exact-head CI establishes the full
typecheck/build gates. Full repository build, typecheck, and test suites
were not repeated locally for this observer change.
- The first broad local report selection lacked the CLI loader and then
hit sandbox IPC restrictions. Locked dependency installation and an
authorized local IPC run resolved those setup errors; the full selected
256 tests pass. The initial localhost probe also needed the normal
local-listen permission.
- No fresh QA pass is claimed. The coordinator must qualify the final
reviewed runtime and harness together. Prior failed results remain
preserved.
## Risks
The basic journey accepts one additional form with explicit native
provenance. It still rejects the original closed card without a
written-answer capability. The separate producer default fix must reach
the selected remote provider pack before it can affect a deployed run.
Local browser and grader tests do not prove that artifact served or that
a model completed the live journey. Existing cleanup and physical-stop
obligations remain separate.
## Model Used
OpenAI Codex, GPT-6 family, with reasoning, source inspection, browser
testing, and code execution. The host does not expose the exact
underlying model version or context-window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with available 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
related work above
- [x] I have described the issue in-PR following the bug template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
ticket or instance-derived details
- [x] I have run the selected tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented risks
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task conversations must show the saved instruction and recent
replies quickly.
> - Compact task reads already limit the saved message window.
> - The conversation still waits for completed run transcripts even when
a saved reply contains the result.
> - Removing that wait alone can insert activity after the reader starts
reading.
> - This pull request defers that output until the reader opens history
and keeps required output in the initial reveal.
> - The benefit is a useful conversation sooner, with a stable first
layout and a working composer.
## Linked Issues or Issue Description
Refs #17593, #18092, #18312, #18358, #18422.
**What happened?**
Opening a task with a long saved investigation can wait for native
events or legacy logs before showing its existing replies. A slow
transcript request can hold the conversation behind the loading screen
until the 15-second fallback.
**Expected behavior**
Show the bounded saved conversation without reading completed output
that already has a usable saved reply. Keep that output available
through the existing history controls.
**Steps to reproduce**
1. Create a task with many completed runs and long saved agent replies.
2. Delay its run events and logs.
3. Open the compact task directly. Before this change, the transcript
read blocks the saved replies and composer.
## What Changed
- Defer completed successful run transcripts on compact task opens when
a usable saved reply is present. Load that output through the existing
history controls.
- Keep required output on the initial path: live and failed runs,
yielded runs, pending interactions, placeholder/progress-only replies,
explicit anchors, chats, and full history. Retain output already
selected during this visit when its run completes.
- Keep saved plans visible when their owning run output is deferred or
still loading.
- Keep the same composer and draft through history expansion. Offer
**More run output** when a single saved run has deferred output.
- Position same-task comment links after their target arrives. Wait only
for a missing target, with a 15-second limit. Unrelated stalled metadata
cannot block an available target after the reveal deadline.
- Preserve the reading anchor when a queued scroll event arrives during
a properties-pane width transition.
- Add a 200-reply/200-run PostgreSQL browser fixture with a saved plan,
covering both task views on desktop and mobile. Correct stale browser
setup for current routes and the shared mobile scroller.
- Remove a random company-prefix collision in the isolated privacy test
fixture that failed during CI.
## Verification
- Final revision `fba3f2fcbb`: 386 focused thread/scroller unit cases
and all 32 production-browser journeys passed. The browser cases cover
both task views on desktop/mobile, saved plans, delayed transcripts,
history, comment links, Back, drafts, retry, avatar continuity, media
and pane resizing. The 25 privacy fixture cases also passed against
PostgreSQL.
- Workspace typecheck and full build passed with Node 24. A broader
local UI run passed 10,220 cases across 849 files before the final
navigation/plan review fixes; the changed components were then rechecked
in the 386-case focused run.
- Five alternating pairs compared master `12b0cc6304` with this
revision, using 200 long saved replies and 200 completed native runs.
Both production UI builds used the same loopback API/database, Chromium
151.0.7922.34, 1440 × 900, fresh browser contexts, service workers
blocked, no CPU throttle, and a controlled 3-second events/log delay.
Builds and other suites were stopped during measurement. No page errors
occurred.
| Useful conversation + editable composer | Baseline median (range) |
Fixed median (range) |
| --- | --- | --- |
| Cold direct open | 3.570s (3.538–3.616s) | 1.056s (1.022–1.141s) |
| Task-list navigation | 3.373s (3.342–3.394s) | 0.899s (0.885–0.915s) |
| Back | 22ms (22–25ms) | 25ms (25–28ms) |
- Median paired savings: 2.519s cold, 2.470s from the list.
Completed-output requests: two per baseline sample, zero per fixed
sample. Recent-comments API control medians were 13.6ms for both, with
ranges 12.1–18.7ms and 11.6–50.8ms. No Back improvement is claimed; an
earlier revision's run also included one 1-second Back outlier, retained
in the local evidence.
- The full local default test command did **not** pass: it stopped on
the existing chat test line-selection error. After restoring embedded
PostgreSQL library symlinks omitted by an install with scripts disabled,
Node 24 passed 672 cases in the first two chat shards, then failed to
select the third. This is separate from the passing focused suites and
remote CI.
- Earlier CI failures were addressed: the privacy prefix collision is
fixed; the delegated task-completion reply case passed locally after
merging master's reply-recovery fix (#18465). Browser fixture
corrections retain the original behavior assertions. Red tests
reproduced the loading, navigation, resize and saved-plan regressions
before their fixes.
- Map, module-boundary, token-gate and added-line token checks passed.
The full-tree token scan still finds pre-existing active-username
fixtures; this PR adds zero forbidden token lines.
- Greptile: **5/5**, final revision, all four review threads resolved.
The final-revision PR CI workflow passed, including build, typecheck,
all server/chat/workspace test shards, all eight E2E shards, and every
Runner lane. GitHub reports 55 successful checks and three skipped
checks. The separate **Paperclip Review** check remains queued with
**Waiting for the assigned agent**; it has not produced an assessment
and has not been bypassed. The branch has no merge conflicts.
## Risks
- The default compact task omits completed transcript details when the
saved result is sufficient. The reader must open history to inspect
them.
- Required structural data remains inside the initial reveal boundary.
Slow server responses for that data remain visible latency.
- Link positioning falls back after 15 seconds when its target never
arrives. The conversation remains readable.
- The measurements use a controlled local delay. They do not establish a
deployed staging speedup.
- No API, schema, authorization, or migration changes.
## Model Used
OpenAI Codex, GPT-6, with tool use and code execution.
## 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 either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass (focused suites pass; the
full default wrapper has the existing selection error documented above)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (code CI passed; the separate
assigned-agent Paperclip Review remains queued)
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the five independently reviewed retention repair paths change. Preserve every other owner and master blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the five independently reviewed retention repair paths change. Preserve every other owner and master blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the five independently reviewed retention repair paths change. Preserve every other owner and master blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the five independently reviewed retention repair paths change. Preserve every other owner and master blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Share the canonical human/open predicate with the feed, retain source visibility and actor authority, and preserve manual controls for existing rows. Prove ordinary retry exhaustion remains visible after a denied pre-archive.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Preserve the original retention owner and producers alongside current native provider-pack admission, route execution-lock protection, canonical work-signal imports, and master CI/UI changes.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the qualified queued-run fixture changes. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Use tracked execution settlement and explicit adapter-entry barriers instead of independent short polls. Add a committed-read control proving the pre-registration drain gap while preserving durable state and concurrency assertions.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Contributors need fast, reliable CI feedback.
> - Diff-aware CI selects test files and partitions them into runner
jobs.
> - Stale cost estimates put several slow suites on the same runner.
Broad imports also couple small tests to large modules.
> - This PR refreshes test costs, runs independent graph discovery in
parallel, and narrows three dependency boundaries.
> - Selection safeguards and CODEOWNER-only AWS routing stay in force.
## Linked Issues or Issue Description
Refs #18398. This follows the audit of [PR #18427's verification
run](https://github.com/paperclipai/paperclip/actions/runs/41169978784).
The audit found 403 selected general-server files without timing
weights. Three suites measured at 232s, 122s and 116s shared one shard
because each received a roughly 2s fallback weight. Local graph
discovery across four projects took 43.7s serially and 21.7s with two
workers. A reverse-order repeat took 37.7s serially and 22.6s with two
workers. Both comparisons produced identical selection results. This is
a local measurement, not a claim about production CI speed.
## What Changed
- Refresh general-server costs for 1,285 of 1,309 current files and
serialized costs for all 163 files from two successful protected-master
Cloud readiness runs. Retain prior costs for unmeasured files, including
dedicated chat and native-runner suites.
- Record passing server file costs, including collection, setup and
environment preparation. A weekly hosted workflow validates trusted
source runs and opens a timing-only PR. It tests shard coverage, pauses
while a refresh PR is open, and never merges automatically.
- Discover at most two project graphs concurrently in isolated,
heap-limited Node processes. Cache repeated inventory reads within one
selection. Worker errors and timeouts retain the full task plan.
- Fetch only the merge commit and parents for the selector. A real
shallow-clone regression proves the complete diff still includes more
than 300 files, renames and deletions.
- Share the identical log viewer and run details between both
AgentDetail pages. The viewer test imports the shared implementation and
runs each of its 13 distinct cases once.
- Extract the pure API error class while preserving the API client's
public exports. Import delivery work signals through their existing
narrow DB export.
## Verification
- PASS: `pnpm -r typecheck` and `pnpm build`.
- PASS: 284 CI selection, timing, shallow-diff, sharding and routing
tests.
- PASS: 74 further cache, routing and graph-proof tests.
- PASS: 94 targeted UI assertions across the viewer, run invocation, API
errors/client and progress helpers. All 76 assertions in the 12 server
suites made independently selectable also pass.
- PASS: `pnpm check:token-gates`, module-boundary checks, YAML parsing
and `git diff --check`.
- PASS: [Full PR CI and required `ci / verify`
aggregate](https://github.com/paperclipai/paperclip/actions/runs/41206683345),
including all 12 general-server shards, all nine serialized shards,
workspace groups, chat suites, Runner lanes and build checks.
- PASS: [GitHub hosted real-workspace selector and cache/routing
qualification](https://github.com/paperclipai/paperclip/actions/runs/41206683205).
- PASS: Both Greptile and Paperclip reviews are 5/5 with no actionable
findings or inline comments on `7dc65c51b8`. The PR has no merge
conflicts.
- PASS: The production refresh parser accepted all 107 structured
file-cost records from a real successful server CI job, including
setup/loading cost.
- Local `pnpm test:run` completed all six chat shards successfully. The
duplicate monolithic local run was stopped after the complete remote
suite passed; it was not completed locally.
The focused log-viewer command improved from 18.84s (26 duplicate
variant cases) to 8.49s (all 13 distinct behavior cases). The dependency
audit confirms 12 server files and the API-error test no longer need
conservative always-run retention. Repartitioning the audited PR with
the new costs changes the slowest shard from 810s to 591s of summed
historical test execution (1,124 of 1,142 files measured). This is a
workload simulation, not measured new CI wall time.
## Risks
- Two graph processes use more peak memory. Each has a 2 GiB heap limit
and a three-minute timeout. Any failure preserves the original task
plan. Real workspace qualification runs in GitHub CI.
- Historical logs contain test/hook costs only. New reporter records add
loading/setup costs after merge. The manifests identify measurement
coverage and source runs. These weights change placement, never
permission to skip tests.
- Runner load adds timing noise. The refresh uses medians and requires
at least 90% measurement coverage in each lane.
- GitHub may require approval to run checks on a PR created with
`GITHUB_TOKEN`. Refreshes remain reviewable and do not merge themselves.
- The shared viewer is a mechanical extraction. Both pages retain their
existing exports, and the behavior assertions remain covered. AWS runner
authorization is unchanged.
## Model Used
OpenAI Codex, GPT-6, with repository inspection, reasoning, code
execution and GitHub tools. The exact deployment ID and context-window
size were not exposed in this session.
## 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 either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub references)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (focused checks and six
chat shards; complete suite passed in CI as detailed above)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Chat feature evaluations send their original results to TestOps.
> - A final reader checks the stored case, run, source, status, and
attachments.
> - Two real chat cases passed, but the final reader returned a generic
mismatch.
> - Later server responses passed the same reader, so they do not
explain the earlier failure.
> - This change records the exact failed predicate without recording
field values.
> - The next failure can be diagnosed in its actual caller context.
## Linked Issues or Issue Description
Refs #18462, #18394, and #18322. The current base includes the
independent browser-fixture fix in #18409.
**What happened?**
The post-upload reader returned `chat_testops_upload_mismatch`. Its
closed artifact had no phase or field detail. The original evaluation
and TestOps case passed. Later responses from the same five endpoints
passed the unchanged reader. Those later interactive responses do not
prove what the protected API caller received at failure time.
An earlier CI run also exposed a flat-text browser-fixture mismatch.
Current master independently fixes that assertion in #18409. This branch
adopts that complete upstream test; the final PR diff contains only the
two diagnostic files.
**Expected behavior**
Keep every report-reader acceptance check. Report a fixed phase, failed
predicate names, match booleans, and field type names. Do not record
response values, credentials, unknown URLs, or traces.
**Steps to reproduce**
1. Supply a missing case ID, a conflicting job ID, or a wrong export
field to the reader.
2. Project the failure with `failureProjection`.
3. The parent returns only the generic error code. This change also
identifies the failed check and distinguishes a missing field from a
value of the wrong type.
**Paperclip version or commit**
The latest retained failure used
47f81e850b. The branch now includes
current master `3ee4c5b6138da82d0b1f51aa010540561bf43c22`. The upstream
API is unchanged.
**Deployment mode**
GitHub Actions and AWS, with TestOps reporting.
## What Changed
- Add private diagnostics to the existing same-code failure throws.
- Emit only fixed names, booleans, and closed type names through the
existing failure projection.
- Copy projected metadata so a caller cannot change the stored
diagnostic.
- Add failure-path, missing-versus-conflicting-field, and
secret-redaction controls.
## Verification
- Five new controls fail against the original parent module. A combined
early-mismatch/null-entry control also fails against the first
diagnostic implementation. Null-safe diagnostic traversal preserves that
known failure.
- `node --test tests/runner-e2e/chat-feature-testops-api.test.mjs`: 53
passed.
- The existing CLI, reporter facade, and exporter tests: 22 passed.
- Both changed MJS files pass `node --check`. `git diff --check` passes.
- The earlier browser-fixture failure remains recorded. The owning test
is now byte-identical to the independently shipped base fix in #18409.
No browser-test or production-file delta remains in this PR.
- Removing the private diagnostics and restoring the ten original throws
reconstructs the entire parent API file byte for byte. Acceptance
expressions, API order, deadlines, and successful output are unchanged.
- All five retained current server responses still produce VERIFIED.
Successful output matches the parent output byte for byte. These
responses used interactive authentication at a later time; the original
protected-token failure remains unexplained.
- Local checks used Node 26.4. Normal CI
[41213493964](https://github.com/paperclipai/paperclip/actions/runs/41213493964),
attempt 1, passed on 78a40d6b67. Both
required checks passed, including browser e2e. The adopted upstream
browser shard passed. Greptile is 5/5 on this head, and all review
threads are resolved.
- Full local typecheck, build, app, database, native SDK, provider, and
model runs were not run. The diagnostic slice uses pure tests, mocks,
and file-system controls.
- No public command or user-facing behavior changes. No documentation
update is needed.
## Risks
- A diagnostic must not expose a sensitive value. A private WeakMap,
fixed field names, boolean checks, and closed type names limit the
output. Tests include synthetic secret strings and mutation attempts.
- Diagnostic checks mirror validation to keep the original guards byte
for byte. Future validation edits must update the paired diagnostic and
its targeted controls. This is a known maintenance tradeoff.
- This patch does not explain or erase either historical reader failure.
It changes no Product E2E grade, model, retry, source guard, API route,
workflow, or deadline.
- Live failure diagnostics remain unqualified until a later authorized
run uses this code.
## Model Used
OpenAI Codex, `gpt-6.1-sol`, with reasoning, source inspection, and code
execution. The host does not expose the context-window limit.
## 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 either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Remote native sessions can use an installed pack or a retained
earlier pack.
> - The current app revision does not identify the pack that a session
admitted.
> - Core already verifies the selected pack and binds its launch
artifacts.
> - Normal run records lack a closed receipt for that admitted
selection.
> - This pull request records that receipt after session admission and
attachment.
> - Operators can compare the actual Core admission identity with their
expected pack.
## Linked Issues or Issue Description
Refs #17749 for protected provider-pack selection and #18343 for
retained controller recovery. The related OpenCode producer repair is
#18456. Those changes do not publish the closed run receipt added here.
A search found no existing `nativeProviderPackAdmission` PR.
**What happened?**
Core can admit a compatible installed provider pack with a source
revision that differs from the current app. A retained session can also
keep its original pack. The normal run record does not show the admitted
pack digest, proxy digest, and source revision. An expected launch path
or current app revision cannot fill that evidence gap.
**Expected behavior**
The run has a closed, server-owned receipt after actual session
admission and attachment. The first receipt remains immutable history. A
separate current-proof state verifies only the actual current admission
epoch. Retained recovery needs positive original provenance, and missing
legacy evidence stays unknown.
**Steps to reproduce**
1. Select and verify an installed pack whose digest differs from the
current app pack.
2. Open the native session successfully.
3. Inspect the normal run profile for a bound admission receipt.
**Paperclip version or commit**
The original selected base was
`8bcc5a01edd5d87540401521e6f30d3df43e1848`. The branch now includes
exact actual prerequisite parent
`3ee4c5b6138da82d0b1f51aa010540561bf43c22`; the PR diff against it is
the five receipt, executor, test and documentation files.
**Deployment mode**
Core remote native execution with provider-pack selection and retained
session attachment.
## What Changed
- Retain the verified selected pack identity only after a backend
returns the matching admitted session.
- Publish `runnerProfileJson.nativeProviderPackAdmission` from the
session callback after attachment succeeds.
- Mark the receipt with `stage: session_admitted` and `authority:
core_verified_selection`.
- Store bound company, agent, run, session, runner, logical
`runnerEnvironmentLeaseId`, pack digest, OpenCode proxy digest, and
source revision fields. Retained attachments identify
`inheritedFromRunId`. The logical Runner scope is distinct from the
physical environment lease.
- Lock the current coordinator claim before the profile write. Check
owner, attempt, generation, controller boot, lease expiry, phase, and
absence of an accepted result. Merge JSON fields without replacing other
profile data.
- Restore retained metadata only from the exact prior closed Core
receipt, positively verified prior state and matching retained binding.
Capture same-run prior provenance from the locked claim row before
invalidation; cross-run recovery reads only the exact source. Legacy
absence remains unknown while recovery continues.
- Preserve the first receipt as history. Atomically mark a new opaque
admission epoch pending in the existing mandatory claim transaction.
Publish current verification only for the matching claim and epoch after
actual attachment; changed or unsupported admission becomes unknown
without blocking authorized execution.
- Preserve synchronous session publication and quarantine ordering
before the best-effort receipt write. Fresh replacement selects its
newly verified pack; a captured retained-process flag cannot relabel the
replacement with an older pack. Preserve the existing private pack
authority even when a local dirty revision is ineligible for the closed
receipt.
- Add focused admission, recovery, identity, PostgreSQL ownership, lock,
and JSON-preservation tests. Document the receipt contract.
## Verification
- RED: the actual managed-backend path verified and admitted an
installed pack but had no closed metadata. Failed open published no
evidence. Additional controls fail when retained recovery invents
missing history or dirty-revision eligibility gates the existing private
authority.
- RED calibration: removing mandatory pending invalidation fails the
actual pre-admission control; reusing the retained flag for fresh
replacement falsely restores the old pack. Removing the epoch fence
fails the real PostgreSQL stale-callback case; reintroducing a
receipt-specific execution error fails both authorized
Runner/logical-lease replacement controls.
- GREEN: 49 focused Core tests, all 33 admission identity/transaction
tests (20 pure and 13 PostgreSQL), and all 23 existing private-authority
tests passed on exact integrated head
`165a9096cf3e0d34d2db1e6b52960ad4ac0ad132`. No all-skipped run is
counted.
- Final test-only head `11ac34baf00552a5d322b5f5f1a8e491859cacec` adds
the positive executor-callback SQL-writer wiring control, exact
session/identity/claim/epoch assertions, profile preservation, and
null-session zero-write check. Removing only the production writer call
makes it fail on a missing receipt. All70 focused identity/Core tests
pass with this closure; production code, the13 PostgreSQL tests and
the23 private-authority tests are byte-identical to the qualified165
source (106 distinct passing controls across the combined proof).
- The PostgreSQL tests used an owned loopback-only cached Postgres 17
container with the supported `PAPERCLIP_TEST_POSTGRES_ADMIN_URL` helper
and aliases to current DB/shared source. The container was removed
afterward. They cover immutable history, JSON preservation, stale claims
and epochs, expired leases, terminal runs, changed/unsupported
replacements, and a failed write retaining pending proof.
- Historical pushed `f7f12fe` CI executed all 24 earlier admission
tests, including nine PostgreSQL cases, in server5. Its warm-quarantine
failure exposed the new hook-ordering issue fixed here. The same run
also encountered a six-character UUID fixture-prefix collision and the
independently repaired cached issue-link browser selector; those
historical failures are not claimed as green.
- Node 24.22.2 and Vitest 5.0.3. The credential-free normal QA runtime
import preflight passes with networking disabled. Map contracts, token
gates and `git diff --check` pass.
- Local TypeScript validation still sees stale borrowed Runner/shared
declaration mismatches outside the changed ranges. No diagnostics remain
in the new helper or changed test/hook ranges; clean current-head CI is
required.
- Repository-wide typecheck, full tests and build were not completed
locally. A full local executor attempt reached this host's unsupported
native PostgreSQL listener and was interrupted; its result is not
counted. Canonical run `41214512239`, attempt 2, is successful on exact
final head `11ac34baf00552a5d322b5f5f1a8e491859cacec`. All 59 checks are
terminal: 55 success, 3 skipped and 1 neutral; none failed or pending.
The actual Linux suites execute all 33 admission tests (including 13
PostgreSQL cases) and all 1,022 executor tests successfully. The
[original five failures and bounded unchanged-source
controls](https://github.com/paperclipai/paperclip/pull/18469#issuecomment-6545593767)
remain preserved. One failed-only request was made; GitHub expanded the
reusable workflow call and dependents. No second request was made. Fresh
exact-head Greptile review is 5/5, all review threads are resolved, and
the PR is mergeable.
- These local results claim no live provider admission, rollout or
recovery.
## Risks
- The authority is Core's verified selection plus successful session
admission. It is not an independent provider statement or continuous
runtime measurement.
- Receipt fields cannot grant execution or replace the existing
provider, process, lease, workspace, or recovery checks.
- Legacy missing receipts and unavailable database reads remain unknown.
The current release cannot label an older admitted pack.
- The closed receipt requires a clean, 40-character hexadecimal source
revision. Verified local `-dirty` packs still execute and retain their
existing private recovery authority; their closed admission proof stays
`unknown`, so it cannot certify an immutable source revision.
- The first receipt is historical. Consumers must require current state
`verified`; pending or unknown cannot certify the current pack. Changed
Runner scope or pack evidence does not add execution policy.
- A receipt save failure leaves the atomically invalidated state
pending. The existing artifact authority, cancellation, workspace and
process checks remain responsible for execution safety.
- The receipt adds no manifest, environment, credential, or filesystem
path to the run record.
## Model Used
- OpenAI GPT-6 through Codex, with reasoning, code editing, and tool
use. The session does not expose an exact model build ID or
context-window capacity.
## 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 either (a) linked existing issues with `Fixes: #` /
`Closes: #` / `Refs #` OR (b) described the issue in-PR following the
relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub references)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runs (the Paperclip Runner) report their outcome with
`paperclip_finish` or `paperclip_block`, and the status arbiter then
sets the task status
> - Every runner tool, `paperclip_finish` included, requires that the
task's `executionRunId` is the current run
> - When the run sends its own status change through `PATCH
/api/issues/{id}` (for example with `call_api`), the issue update clears
`executionRunId` and `checkoutRunId`, so the run removes its own
execution lock
> - After that, every tool call fails, the run cannot report, same-run
recovery spends its retries, and the task goes to `blocked`
> - This pull request rejects that status change with a 409 before it
commits, and the message tells the run to report with `paperclip_finish`
or `paperclip_block`
> - The benefit is that the run corrects itself in the same turn, and
the task does not get stuck
## Linked Issues or Issue Description
No public issue exists. This is the bug description.
**What happened?**
An agent on the Paperclip Runner followed task instructions that said
"set the task to `in_review` and keep it assigned to yourself" while it
waited on a scheduled monitor. It sent `call_api` with `PATCH
/api/issues/{id}` and `{ "status": "in_review" }`. The server applied
the change and cleared the execution lock. Then:
1. `get_task`, `set_task_monitor`, and `paperclip_finish` all failed the
run binding check. The provider saw only "Paperclip semantic operation
failed", or the monitor-wait message from `paperclip_finish`.
2. The `call_api` receipt for the PATCH showed `status: null, outcome:
unknown`, because the authority check after the response failed when the
lock was gone.
3. The session ended with no semantic result. Exact-checkpoint resume
ran out of retries (`native_session_retry_exhausted`), and recovery
would not replay the run because the mutation outcome was unknown. The
task went to `blocked`.
This occurred on each check where the agent tried the status change.
Runs that did not try it completed normally.
**Expected behavior**
A native run that holds the task's execution lock cannot remove its own
lock. It gets a clear, actionable error and reports its outcome through
`paperclip_finish` or `paperclip_block`.
**Steps to reproduce**
1. Assign a task to an agent that uses the Paperclip Runner (native
runtime).
2. While the run executes, have it send `call_api` with `PATCH
/api/issues/{id}` and `{ "status": "in_review" }` for its own task.
3. Look at the next tool calls of the run. They fail, `paperclip_finish`
cannot complete, and recovery blocks the task.
**Paperclip version or commit**
`master` at 493e229d93.
## What Changed
- `server/src/routes/issues.ts`: add
`assertNativeRunKeepsOwnExecutionLock` to `PATCH /issues/:id`. The guard
returns 409 `native_run_status_reported_by_finish` when all of these are
true:
- the actor is the assigned agent, and its run is the task's execution
or checkout run
- the run is `native` and `running`
- the request changes the status and keeps the assignment
- the task is not a conversation
- The 409 message names `paperclip_finish` (`needs_review`, `done`, or
`yielded` with a `monitor` continuation) and `paperclip_block`.
`call_api` returns HTTP denials as data, so the provider can read the
message.
- Conversation tasks are out of scope. A conversation turn settles from
its final reply (`nativeConversationReplyResult`), not from a runner
tool. So a reply that moves its conversation to `in_review` still
settles, and that contract stays.
- When the request also carries blockers, the guard first runs
`assertOnboardingFirstTaskDependenciesAllowed`. A blocker that the
dependency rules reject keeps its specific 400. Every other case gets
the 409, including `{ "status": "todo", "blockedByIssueIds": [] }`.
- `server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts`:
add 14 cases. They cover rejection for `in_review`, `done`, and
`blocked` (also with an explicit self-assignment, an empty blocker list,
and a real blocker), and the onboarding 400 that wins over the 409.
Pass-through cases assert a 200, the patched status, and one update for:
other field edits, legacy runs, native runs without the lock, stopped
runs, handoffs to a human (after a confirmed stop), conversation
replies, and board users.
## Verification
- `npx vitest run
server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts`
passes (193 tests).
- With the guard disabled, the 4 rejection cases fail. With the `native`
runtime condition removed, the legacy pass-through case fails. The tests
pin both sides.
- `chat-project-tools` and `onboarding-first-task-admission` pass (74
tests). These are the integration suites that caught the first version
of the guard in CI.
- `npx vitest run` on `issue-queued-comments-routes`,
`task-workspace-binding`, and `issue-recovery-actions` together with the
ownership suite passes (455 tests).
- `npx tsc --noEmit -p server/tsconfig.json` is clean after `pnpm
--filter @paperclipai/paperclip-runner build`.
## Risks
- Behavior changes only for native runs that change the status of the
task whose execution lock they hold. That change always stranded the run
before this PR, so no working flow depends on it.
- Legacy adapters, board users, agent handoffs or reassignments, and
conversation tasks do not change.
- Other validation errors that run later (in the service update) are
replaced by the 409 for these requests. Each request is still rejected,
and the 409 points the run to `paperclip_finish` or `paperclip_block`.
The onboarding parent-blocker error keeps its 400, because the guard
runs that check first.
- If a request names the current assignee with a reference that is not
its id, the guard does not apply, and the request follows the earlier
rules.
- No migration.
Related gaps this PR does not change:
- The authority check after the response turns a committed mutation into
`outcome: unknown`.
- The runner hides tool binding errors as "Paperclip semantic operation
failed".
- The `paperclip_finish` monitor-wait error does not say that the run
lost its lock.
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, in Claude Code with
tool use (shell, file edits, test runs). It did the root-cause analysis
from run events and the activity log, wrote the code and tests, and ran
local verification.
## 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 either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the two qualified card interval paths change. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the two qualified card interval paths change. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the two qualified card interval paths change. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the two qualified card interval paths change. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the two qualified card interval paths change. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Keep membership provenance from describing an interval-owned evaluation deadline; retain existing cadence and source handling.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Normal parent merge; only the five qualified card/search paths change. Preserve every other owned and upstream blob.
Co-Authored-By: Paperclip <noreply@paperclip.ing>