Commit Graph
9 Commits
Author SHA1 Message Date
DottaandPaperclip 8dfed893d8 fix(server): wake decision target checks from committed changes (#17486)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Decisions can depend on strict references to other tasks.
> - A cancelled, deleted, or completed target can make a decision
obsolete.
> - The server currently scans open decisions on every maintenance tick
to find those changes.
> - The owning issue writes already have transaction boundaries that can
signal the change.
> - This pull request reconciles affected decisions after commit and
reconstructs open targets at startup.
> - Valid open decisions and completed history stop causing periodic
SQL.

## Linked Issues or Issue Description

**What existing behavior does this improve?**
Decision target invalidation. This is slice 12 of the recurring-server
sleep stack, based on #17482. TTL expiry is #17479 and continuation
delivery is #17478. Retention remains separate work.

**Subsystem affected**
Server decision policy, issue mutation and deletion, and startup wiring.

**Current behavior**
The aggregate sweep repeatedly reads open decisions and their strict
targets. It shares a maintenance tick with unrelated jobs even when
every target remains valid.

**Proposed behavior**
Committed decision creation and terminal issue updates request a scoped
check. Deletion requests a bounded full scan because its target links
can cascade away. Startup reconstructs open target state. Once
reconciliation settles, no target scan remains scheduled.

**Reason and benefit**
Remove normal empty polling without losing target invalidation,
transactional delivery, or restart recovery. Reuse existing decision
rows and target links instead of adding a table.

**Breaking changes**
No API or schema change. The main tick stops calling the combined
decision sweep after its four duties have separate owners. The public
combined method retains its default behavior.

**Additional context**
The initial decision implementation stored secondary links as well as
primary links. This migration does not assume a new legacy backfill.
Independent SQL writers still require an explicit signal integration.

## What Changed

- Replace the periodic target scan with committed-change activation and
bounded startup reconstruction. Keep the existing decision policy and
expiry/activity owner.
- Require production notifications to identify an issue or decision.
Before deletion, lock the issue and capture its linked open decisions in
the same company; emit their IDs before links cascade. This avoids
resetting full scans and fences concurrent proposal creation.
- Reuse that owner helper for issue removal and routine failure cleanup.
No linked decisions means no target signal. Remove company deletion's
ineffective global signal; its pre-existing restrictive decision foreign
keys remain unchanged.
- Retain PostgreSQL timestamp text in paging cursors so rows within one
millisecond cannot repeat indefinitely.
- Remove the unused combined internal decision sweep after its final
duty is split; exercise each owner directly in the existing behavioral
tests.
- Keep query policy and notification discovery in `decision-targets.ts`.
The coordinator registration contains lifecycle wiring only. Add no
queue table, per-route recovery wrapper or external-wake exemption.

## Verification

Current candidate normally integrates landed effect recovery
`e9e84bb8a1`. Its runtime and acceptance tests are byte-identical to
tested target head `85ce6545bf`; the only new source files are four
independently merged Runner E2E accounting files, preserved exactly. The
248-case/type/build evidence comes from test revision
`54936a919f36c2ea421e7ce8e15912b1105b8dcc`, followed by
documentation-only changes. Fresh current-head CI and reviews are
required. This remains the target-invalidation duty only.

- **248 cases pass** in all seven complete suites: 25 target, 80
routine, 54 decision, 26 effect-recovery, 20 expiry, 42 startup and one
scoped-deadline case. Database suites use separate migrated PostgreSQL
fixtures. Server production typecheck and build pass.
- **Four regressions failed before repair:** paging across microsecond
timestamps, deletion behind a full valid startup page, unnecessary
global work from unrelated deletion, and unnecessary work after deleting
an empty company. All four pass.
- A real concurrent proposal is observed waiting on the issue-row lock
between pre-delete discovery and deletion commit. Removing that lock
made the test fail because the proposal committed instead of waiting.
Restoring the implementation passes. The mutation was restored before
commit.
- Existing real producer tests cover commit/rollback, deletion with
scoped hints in one outer transaction, secondary targets, re-opened
tasks, valid/lenient history quietness, startup paging, failure retry,
drain/resume and shutdown. The routine failure case now requires no
signal when its cleanup has no linked decision.
- The earlier dropped-hint and drain-fixture failures remain in [the
behavior audit](doc/idle-decision-targets.md). Extra upstream
embedded-only tests attempted on the parent could not initialize
locally; CI must cover that environment. No new schema migration or
table is added.

The final review cleanup removes the combined internal `sweepExpired`
method, cursor, flags and unused target-policy export. A
repository/package/SDK audit found no remaining production caller or
public exposure. Behavioral tests now call the dedicated workers;
isolation tests prove TTL does not recover stale effects and effect
recovery does not expire open decisions. HTTP and plugin SDK contracts
are unchanged. The full 248-case selection passes after this cleanup and
parent reconciliation; types/build pass with the updated Runner vendor.


Current-head CI run 40602159553 (attempt 1) failed in the workspace CLI
group: company import/export startup exceeded its 30-second health
deadline, and the dense ZIP case exceeded its unchanged five-second
timeout. The preceding UI group passed all 4,169 cases. Both CLI files
and the transfer implementation are unchanged from the landed parent.
The complete ZIP-transfer file passes all 17 cases locally on this exact
head; the CLI startup E2E is skipped because the host's embedded
PostgreSQL bootstrap fails. This does not prove Linux startup locally.
The failure logs remain recorded; no timeout or assertion is changed.
One bounded retry of the failed jobs is requested after the workflow
completed. Its result and the current-head gates remain required before
merge.

Commands: server typecheck/build and Vitest for `decision-target-work`,
`routines-service`, `decisions-service`,
`decision-effect-recovery-work`, `decision-expiry-work`,
`server-startup-feedback-export` and `scoped-deadline-work`. Local
targeted acceptance does not establish deployed suspension or fleet
savings. Fresh exact-head CI and Greptile/Ponytail remain merge gates.

## Risks

- Deletion now holds the issue row lock while discovering dependent
decisions and completing the existing removal transaction. This is
needed to serialize new FK references. Provider calls are not placed
inside that transaction.
- Company deletion with decision-origin references still has the
pre-existing restrictive FK limitation. Removing the futile notification
does not implement a new company-data cleanup policy.
- Supported writers share the application DB owner and maintain existing
target links. Independent SQL writers, damaged links or multiple active
processes need their own wake contract; startup still reconstructs all
open targets.
- Cloud sleep blockers and durable external alarms are unchanged. Worker
quiet is not proof of deployed host/database suspension.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and code execution. The exact
serving model identifier and context window were not exposed.

## 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
- [ ] 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>
2026-10-27 18:53:50 -05:00
DottaandPaperclip e9e84bb8a1 fix(server): recover decision effects from saved claim deadlines (#17482)
## Thinking Path

> - Paperclip manages agent work and the decisions that authorize it.
> - A committed decision can leave effects unfinished after an
interrupted execution.
> - Recovery must preserve that debt without scanning settled history
forever.
> - Claims already store the state and time needed to schedule recovery.
> - A failed claim must get another attempt even while newer claims
arrive.
> - Malformed claims need a durable failure outcome instead of an
endless retry.
> - This PR uses committed signals, saved deadlines, and bounded
recovery rotations.

## Linked Issues or Issue Description

**What happened?**

Periodic decision maintenance queries for unfinished effects even when
none remain. A cursor that only wraps after a short page can postpone an
old failure indefinitely under continuous new work. Missing saved
deciders or chosen options can also leave non-executable claims scanning
forever.

**Expected behavior**

Open decisions and completed history produce no recurring
effect-recovery SQL. Committed claims wake recovery, and restart
reconstructs their deadlines. Genuine failures retain retries. Known
malformed outcomes settle with an audit and preserve completed effects.
Recovery must not invent authority, repeat committed effects, or lose
continuation delivery.

**Steps to reproduce**

Create an old failing claim with a one-row recovery page, then keep
adding newer due claims. Before the rotation fix, the old failure is
never revisited. Separately, recover a claim with a missing decider or
chosen option: the old implementation skips or throws on every attempt
without settling it. Creating an ordinary open decision also caused an
unnecessary effect scan.

Stacked on #17479. Other decision expiry, continuation, target and
retention duties have separate PRs in the stack.

## What Changed

- Coalesce sequential committed notifications for up to one second
without delaying already saved deadlines. This bounds instance-wide
recovery reads and adds no idle polling.

- Register effect recovery with the shared scheduler and
transaction-aware notifications. Derive deadlines from the existing
claim timestamp and recovery grace.
- Remove effect recovery from recurring aggregate maintenance when the
worker owns it. Preserve the explicit combined recovery entry point.
- Page within a frozen final timestamp/ID and retain PostgreSQL
precision at both boundaries. Failed claims and healthy later claims
both make progress.
- Load the current claim and derive recovery authority inside the shared
execution flight. Preserve foreground joining, current authorization and
per-effect locks.
- Recheck known malformed claims under a row lock. Save a failed
outcome, or partial when an executed effect receipt exists, with
`effectRecoveryError` and an atomic `decision.recovery_failed` audit.
Publish activity after commit and use normal continuation delivery.
- Avoid effect-recovery notifications for open-decision creation. Claim
and settlement writes retain their existing notification boundary.
- Add generated migration `0390_salty_killraven.sql`, the concurrent,
idempotent partial index for running-claim lookup. No queue table is
added.
- Cover quietness, transaction and lifecycle boundaries, retry fairness,
malformed outcomes, and concurrent repair. Document the contract in
`doc/idle-decision-effect-recovery.md`.

## Verification

Current candidate normally integrates landed expiry `3421366903`,
including expiry-only creation signals and all 21 independent master
changes. The test revision is
`dba8a79d1e1a5b97b8b5f6dac0f7c7837536531a`; the publication adds only
this evidence documentation.

- **143 current cases pass** across all 26 recovery, 54 decision, 20
expiry, 42 startup and one scoped-deadline cases. Server types/build
pass with the rebuilt Runner vendor (1,524 inputs and 1,563 outputs).
These results follow the landed dependency reconciliation; prior 69- and
141-case results remain historical evidence for their stated revisions.
- Generated migration `0390_salty_killraven.sql` adds the existing
running-claim partial index through the concurrent executor. All 388
parent journal entries are unchanged; five generated snapshots remain.
Schema drift, numbering/safety and actual migration apply/replay pass.
- Existing regressions exercise failed-claim rotation under sustained
arrivals, malformed decider/option outcomes, atomic audit failure,
row-lock repair races, current authority, foreground effect joining,
coalesced tenant notifications, transaction commit/rollback,
drain/resume, paging with PostgreSQL timestamp precision, and
shutdown/restart. Completed effects are not repeated.
- The first integrated run passed 140 cases and failed an overlap test
that assumed the effect deadline was the scheduler's earliest deadline.
Continuation coalescing now creates an earlier registered wake. The test
services that earlier wake before asserting the original effect grace
deadline; it still requires one shared effect flight, two lock attempts
including the test gate, one committed comment and one continuation. No
production timing was relaxed.
- Earlier red/green fairness, malformed-claim, notification
amplification and fixture failures remain recorded in [the owning
audit](doc/idle-decision-effect-recovery.md). Extra upstream
embedded-only tests attempted during parent integration could not
initialize locally; CI must exercise that environment.

- Previous candidate `f54a707d74` received both 5/5 reviews but its CI
server shard 6 was interrupted (run 40597921712). The step was
cancelled, with no failing assertion or suite summary, and the verify
gate also failed. No retry or passing claim was made for that run. The
necessary parent repair is included here; fresh CI and both reviews are
required.

Commands: server typecheck/build and Vitest for
`decision-effect-recovery-work`, `decisions-service`,
`decision-expiry-work`, `server-startup-feedback-export`, and
`scoped-deadline-work`; DB migration checks, schema snapshot drift and
real migration apply/replay. No full local repository-suite or deployed
sleep/cost claim is made. Fresh current-head CI and Greptile/Ponytail
are required before merge.

## Risks

Missing deciders and missing chosen options now produce durable failed
or partial outcomes instead of remaining running forever. Their audit
and receipts remain available; no effect executes under invented
authority. Only these known malformed states are settled this way.
Database, audit, and other execution failures retain retries.
Continuations retain their existing at-least-once delivery contract.

The supported writer model remains process-local notifications plus
restart reconstruction. Independent SQL writers and multiple replicas
gain no new wake protocol. Cloud admission and durable external alarms
are unchanged. These tests establish worker behavior, not deployed host
suspension or fleet savings.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and code execution. The exact
serving model identifier and context window were not exposed.

## 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
- [ ] 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>
2026-10-27 18:24:55 -05:00
DottaandPaperclip 3421366903 fix(server): schedule decision TTL expiry from saved deadlines (#17479)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Open decisions have a saved expiry time.
> - The main maintenance tick scans those times even when none is due.
> - Committed creation and resolution can maintain the actual next
deadline.
> - The existing scheduler can run expiry only when that deadline
arrives.
> - This pull request gives decision TTL expiry its own worker.
> - Terminal history becomes quiet while expiry and continuation
delivery remain recoverable.

## Linked Issues or Issue Description

Targets master after #17478 merged. This is slice 10 of the recurring
server work migration. It owns decision TTL expiry. Target invalidation,
interrupted effects, and retention remain separate migrations. The full
ledger is in `doc/plans/2026-10-24-recurring-server-sleep-stack.md`.

**What existing behavior does this improve?**

Time-based decision expiry in the server.

**Current behavior**

The combined decision maintenance sweep queries expired open decisions
on each tick. It mixes that query with target invalidation, effect
recovery, and continuation delivery.

**Proposed behavior**

Creation, bundle creation, and existing decision updates notify the
shared scheduler. A dedicated worker runs at the first saved open
expiry. Resolving an open decision removes its deadline. The combined
tick opts out of TTL selection while its other duties remain intact.

**Reason and benefit**

A future decision no longer requires repeated TTL queries before its
deadline. Terminal history and an empty decision table leave no
recurring TTL work. No queue table is added.

## What Changed

- Add an expiry owner for bounded due selection, next-deadline lookup,
and expiry settlement. Reuse it for expiry-on-use and the existing
target expiration transition.
- Use the central expiry-only creation notification before insert,
including bundles and caller-owned transactions. Update and terminal
transitions continue to notify expiry and continuation.
- Register the enabled-heartbeat TTL worker before decision
housekeeping. Preserve disabled-heartbeat behavior and the public
combined sweep's default batch limit.
- Honor `PAPERCLIP_DECISIONS_SWEEP_BATCH_SIZE` (default 100). Keep a
fair expiry/ID cursor across failures. Stop between candidates on
shutdown and reconstruct saved work after restart.
- Recheck open status and due TTL during settlement. Merge metadata and
persist activity atomically with expiry. Publish activity and deliver
continuation after commit. Handle exact timestamp equality.
- Continue successful overdue batches after one second through the
existing deadline adapter; preserve thirty-second failure retry and all
default callers.
- Add generated migration `0389_brave_hulk.sql`, an idempotent
concurrent partial index on open expiry and ID. Preserve five generated
snapshots.
- Add 20 real-PostgreSQL lifecycle cases and startup ownership
assertions. Document contracts and limitations in
`doc/idle-decision-expiry.md`.

## Verification

Current candidate: `aa03d2c4c25af4e6c2acf49d3160bbaf6ada030b`. This PR
targets master after #17478 merged. Creation now signals only its real
expiry obligation; terminal updates preserve continuation delivery.

- **117 current cases pass** across all 20 expiry, 54
decision/continuation, 42 startup and one scoped-deadline cases. Server
typecheck/build pass. Separate migrated PostgreSQL fixtures exercise
real transaction and notification behavior.
- Single and bundle creation each failed a regression before the
expiry-only signal repair: open rows unnecessarily notified continuation
recovery. Both now arm their saved expiry without a continuation signal.
Cancelling each row afterward still signals and delivers continuation
normally. The distinction is in the existing central notification owner;
there are no per-route flags or counters.
- **Three new real PostgreSQL regressions failed before repair:** a
configured batch of two was ignored, a successful 101-row backlog waited
for the thirty-second error retry, and an activity insert failure left
the row expired without its audit. All three pass. The audit failure
rolls back expiry; retry records one activity, preserves unrelated
metadata and wakes once.
- The shared deadline adapter has optional `continuationMs`. Only this
owner opts into one-second successful overdue continuation. Other
callers keep the previous default, and errors still retry after thirty
seconds. The archive consumer passes with the option absent.
- Migration `0389_brave_hulk.sql` is regenerated from the parent's
metadata. Parent SQL/journal entries are preserved, five generated
snapshots remain, schema drift and migration numbering/safety pass.
Fresh migration apply and replay pass.
- Lifecycle coverage includes first producer activation, future-deadline
quiet, terminal history, exact equality, commit/rollback, restart,
drain/resume, failed expiry and wake delivery, fair paging through
failed rows at PostgreSQL timestamp precision, shutdown and recovery.
- Failure accounting: the first full repair selection passed 113 cases
and failed a startup assertion that observed a pending follow-up
notification before reconciliation. The assertion now waits for the
exact saved future deadline, then still proves no early expiration.
Earlier fault-fixture and timestamp-cursor failures remain recorded in
[the behavior audit](doc/idle-decision-expiry.md). Extra upstream
embedded-only tests attempted on the parent could not initialize
locally; CI must cover that environment.

Commands: server typecheck/build and Vitest for `decision-expiry-work`,
`decisions-service`, `decision-retention-service`,
`server-startup-feedback-export` and `scoped-deadline-work`; DB
migration checks, snapshot drift and actual migration apply/replay. No
full local repository-suite or deployed sleep/cost claim is made. Fresh
current-head CI and Greptile/Ponytail remain required before merge.

## Risks

- Expiry uses `expiresAt <= now`; the old combined loop skipped exact
equality. The deadline boundary is now covered explicitly.
- TTL expiry records the TTL reason. Target-driven expiry preserves its
own reason. When both causes apply, the first terminal transition wins.
- Supported producers share the application DB signal owner. Independent
SQL writers or multiple active processes need a separate notification
contract. New deadline-edit APIs must use the existing helper.
- The index requires top-level concurrent migration execution. It is
registered with the migration owner and replayed in verification.
- Future in-process deadlines are not external Cloud alarms. Existing
sleep guards remain conservative. This PR does not claim host/database
suspension or measured savings.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and code execution. The exact
serving model identifier and context window were not exposed.

## 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
- [ ] 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>
2026-10-27 18:10:36 -05:00
DottaandPaperclip a63d19816f fix(server): wake decision continuations from committed changes (#17478)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A completed decision can owe a wake to the agent that proposed it.
> - The decision row already stores the pending continuation.
> - The main maintenance tick scans this debt even when no continuation
is ready.
> - Committed changes can activate recovery and leave settled history
quiet.
> - This pull request gives continuation delivery its own worker and
shares foreground delivery ownership.
> - Pending work stays recoverable without an unconditional continuation
scan.

## Linked Issues or Issue Description

Targets master after #17475 merged. This is slice 09 of the recurring
server work migration. Each PR owns one reason for background work. The
remaining inventory is in
`doc/plans/2026-10-24-recurring-server-sleep-stack.md`.

**What existing behavior does this improve?**

Decision continuation delivery and recovery in the server.

**Current behavior**

Foreground paths send a wake and clear a pending flag. The combined
decision sweep scans pending continuations on every maintenance tick. It
can overlap foreground delivery.

**Proposed behavior**

Terminal decision writes notify a dedicated worker through the existing
transaction boundary. Foreground and recovery calls share one in-flight
delivery per decision and DB owner. Recovery re-reads readiness, pages
past failures, and retries only unresolved debt or errors.

**Reason and benefit**

Open decisions, running effects, and delivered history no longer cause
an independent continuation scan. No queue table is added. Decision
expiry, target invalidation, interrupted effects, and retention remain
separate duties.

## What Changed

- Extract continuation readiness, delivery, and paged recovery into the
decision owner. Preserve the wake payload and at-least-once crash
behavior.
- Signal the ten decision update sites before SQL in their existing
transaction scope. Keep effects and wake calls outside those
transactions.
- Explicitly create the continuation obligation on all three
archive-proposal failure settlements, including recovered running rows
without inherited pending metadata.
- Serialize concurrent decision effect execution through final
settlement. Coalesce foreground and worker wakes. Re-read the durable
flag before sending. Fence effect completion to running state so
competing completion cannot restore acknowledged debt.
- Expose an opaque DB work-scope key. Child pools and nested
transactions share that identity; independent roots remain separate.
Existing signal consumers keep their behavior.
- Bound each recovery pass to 100 candidates, retain a fair cursor
across failures, and stop between deliveries on shutdown. Forward the
existing AbortSignal through the shared deadline adapter.
- Register enabled-heartbeat continuation recovery before decision
housekeeping. Remove only its independent scan from the main tick.
Preserve the public combined sweep and disabled-heartbeat behavior.
- Add generated migration `0388_ambitious_micromax.sql`, an idempotent
concurrent partial index for pending continuations, and keep five
generated snapshots.
- Add real producer, transaction, failure, concurrency, paging, quiet,
and shutdown tests. Document ownership and remaining limits in
`doc/idle-decision-continuations.md`.

## Verification

Current candidate: `18f682863cadb91281a86b4da2553a4579ee054a`, normally
integrated with landed archive squash `5785090ff1`. All 31 independent
incoming master files are preserved exactly, including
controller-authority recovery. All 97 decision/startup/deadline cases
plus server types/build pass after the required Runner vendor rebuild.
The preceding full five-suite selection passed 293 cases; the
continuation owner behavior is unchanged by master reconciliation.

- **97 cases pass at this master integration** across decision, startup
and scoped-deadline suites, plus server types/build. The preceding
parent candidate passed the full 293-case
decision/gateway/archive/startup/deadline selection; those results
remain evidence for that revision. All selected cases executed with
separate PostgreSQL fixtures where needed.
- Tenant-driven global scan amplification reproduced six sweeps for six
sequential real cancellations. Existing thirty-second notification
coalescing now permits one recovery sweep per window while retaining
pending work. The first green fixture wrongly expected the intent hold
to clear before coalesced reconciliation; the hold remains and quiet is
checked after settlement. Foreground delivery stays immediate.
- The archive contention test uses a signaled dedicated
single-connection read and two FIFO SQL round trips instead of a fixed
delay. Removing effect coalescing makes it fail with two contender
reads; restoring production code passes. No production test hook was
added.
- **Three new real PostgreSQL regressions failed before repair**:
recovered archive proposals with invalid signatures, mismatched
manifests or archive execution failures and no inherited pending flag.
Each now persists failure, wakes once, acknowledges continuation
delivery and remains quiet on replay. Running-state fencing is
preserved.
- The unmerged continuation index is regenerated as
`0388_ambitious_micromax.sql` after parent migrations 386/387. All
parent journal entries are unchanged, five generated snapshots remain,
numbering/safety and snapshot/schema-drift checks pass. Fresh database
migration apply and replay pass; the concurrent index is valid.
- Existing lifecycle coverage includes foreground/worker coalescing,
child pools, transaction commit/rollback, failed acknowledgement,
restart, drain/resume, shutdown and fair 100-row recovery. Earlier
bounded-pass and concurrent archive-result regressions and their repairs
remain recorded in [the owning behavior
audit](doc/idle-decision-continuations.md).
- Failure accounting: the first combined command used an incorrect
retention fixture variable and skipped 15 cases. The corrected separate
external-PostgreSQL retention run passed all 15. The historical nine
embedded-only work-signal cases were not executed by this selection.
Extra upstream embedded-only suites attempted on the parent could not
initialize locally; they remain CI verification, as recorded in the
parent PR. Earlier reused-fixture, assertion and compile failures remain
in the behavior audit and stack ledger; they are not erased by later
passing checks.

Commands: `pnpm --filter @paperclipai/server typecheck`, `pnpm --filter
@paperclipai/server build`, server Vitest for `decisions-service`,
`decision-retention-service`, `server-startup-feedback-export` and
`scoped-deadline-work`; `pnpm --filter @paperclipai/db
check:migrations`, migration apply/replay, and the generated
snapshot-drift test. Local targeted evidence does not establish a full
local repository run, deployed sleep or fleet savings. Fresh exact-head
CI and both automated reviews remain merge gates.

## Risks

- Notification-driven recovery remains global but is coalesced to thirty
seconds. This bounds request-driven scans; it does not introduce
company-scoped selection. Foreground delivery remains immediate.
- Delivery remains at least once. A crash or acknowledgement failure
after accepting a wake can send it again. Local coalescing is not a
cross-process claim.
- Supported writers share the application DB signal owner. Independent
SQL writers or multiple active server processes need a separate wake
contract. Wake-producing service methods do not gain a new guarantee
when called inside an uncommitted caller transaction.
- The concurrent index needs top-level migration execution; it is
registered with the existing migration owner and can be replayed safely.
- No Cloud sleep blocker or external alarm coverage changes. Worker
quiet does not prove deployed app or database suspension.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and code execution. The exact
serving model identifier and context window were not exposed.

## 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>
2026-10-27 17:44:51 -05:00
Dotta bc9ba7cd26 feat(runner): project native runs into task threads (#12321)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The experimental Paperclip Runner can execute a guarded Codex run
and persist provider-neutral events.
> - The task page still reads direct-adapter transcripts and cannot
present those native events.
> - Structured runner questions must also use the existing task
interaction experience.
> - Runtime selection must use the persisted run mode, not an adapter
name or a current feature flag.
> - This pull request projects native events and questions into the
existing task thread.
> - Direct adapters keep their existing transcript, composer,
interaction, and finalization paths.
> - The benefit is a complete native Codex task thread without a
behavior change for existing adapters.

## Linked Issues or Issue Description

Refs #12202. This pull request replaces that stale implementation on
current `master`.

**What happened?**

The server persists native runner events and structured input requests.
The task page only consumes direct-adapter transcripts. A native run
therefore cannot present a complete transcript, usage, or question flow
through the normal task experience.

**Expected behavior**

Native runs project persisted provider-neutral events into the existing
task thread. Native structured questions use the existing interaction
card. Direct adapters retain their current behavior.

**Steps to reproduce**

1. Enable the experimental runner.
2. Start a native Codex run that emits progress, usage, a structured
question, and a final reply.
3. Open the task page.
4. Observe that the direct-adapter transcript path cannot project the
native event records.

**Paperclip version or commit**

`master` at `67f9867bc`.

## What Changed

- Add the canonical structured-question validator and shared contract
exports.
- Materialize native input requests as existing task interactions.
- Validate native answers and deliver them through the durable
question-response receipt.
- Resume the original PRP request with an idempotent `request.resolve`
command.
- Project native messages, tool activity, cumulative usage, and final
replies into the existing transcript model.
- Propagate persisted `runtimeMode` to the task page and select native
handling only for `runtimeMode: "native"`.
- Expire pending interactions through the shared issue service on every
terminal transition, including decisions, stalled reviews, tree control,
and pipeline retry cleanup.
- Queue native run cancellation while a transaction is open and execute
it only after the owning transaction commits.
- Keep nonterminal and non-runner issue paths on their existing service
call shapes and behavior.

## Verification

- `pnpm --filter @paperclipai/server typecheck` — passed, including the
Rust runner release build and protocol/catalog drift gates.
- Focused native-thread and lifecycle suites — 18 files and 481 tests
passed during review.
- `issue-execution-policy-routes.test.ts` — 19/19 passed after the final
transactional-queue expectation update.
- `issue-agent-mutation-ownership-routes.test.ts` — 87/87 passed in the
final isolated compatibility rerun.
- GitHub Actions — policy, build, canary, typecheck/release registry, 5
serialized server shards, 8 general-test shards, 3 browser shards, and
both aggregate gates passed on `7793f3193`.
- Security — Snyk, Socket Project Report, Socket PR Alerts, and
Superagent passed.
- Greptile — 5/5 on `7793f3193`; all actionable review threads resolved.
- `git diff --check` — passed.
- Diff against `master`: 44 files.

## Compatibility Boundary

- Native transcript polling only runs when the persisted run reports
`runtimeMode: "native"`.
- Missing or legacy runtime modes continue through
`useLiveRunTranscripts`.
- Legacy questions keep the existing optional free-text choice.
- Native closed select sets can suppress that legacy fallback.
- Terminal cleanup uses the same issue service for native and legacy
interactions; only a bound native question schedules a native run
cancellation.
- Native cancellation happens after transaction commit, so failed or
rolled-back writes do not cancel a still-valid run.
- The durable delivery service checks the original native request before
it considers a continuation run.
- This pull request adds no migration, dependency, workflow, manifest,
or lockfile change.

## Risks

The main risk is routing a direct-adapter task through native handling
or changing terminal issue behavior. The implementation selects the
native path only from persisted runtime facts, retains the existing
nonterminal call shape, and schedules native cancellation only for a
validated bound native question after commit. Focused and
repository-wide tests cover both paths. Native requests remain bound to
the company, issue, run, and agent; answers are validated, durable, and
idempotent across reconnects.

## Model Used

OpenAI Codex, GPT-5 family. The client does not expose the exact
deployment ID or context window. Agentic reasoning, tool use, and code
execution were enabled.

## 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 and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run the affected local tests and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated the compatibility notes for this change
- [x] I have considered and documented risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I addressed all Greptile and reviewer comments before requesting
merge
2026-08-29 19:26:20 -05:00
DottaandPaperclip ef33c1d9ed fix(decisions): retire completed-target decisions and link targets from the card (#10892)
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The decisions desk shows pending decisions that need an operator
response
> - A strict decision cannot apply its effects after its target task
changes
> - A decision still remained pending when every target task finished
after proposal
> - The card also linked only the origin task, even when the decision
acted on another task
> - This pull request expires those moot decisions and links their
target tasks
> - The benefit is an accurate queue and a clear path to the work that
each decision affects

## Linked Issues or Issue Description

Related PR: #10801 removes the issue-page decision strip, which makes
clear queue provenance more important.

**What happened?**

A strict decision stayed pending until its time-to-live limit after
every target task reached `done`. The decision card linked only the
origin task. The origin task is where the agent proposed the decision,
and it can differ from the task that the decision affects. An operator
could therefore open a finished task with no visible decision and no
explanation of the real target.

**Expected behavior**

Paperclip must expire a strict decision when all of its targets finish
after the decision is proposed. The card must show and link every target
task that differs from the origin task.

**Steps to reproduce**

1. Create a strict decision that targets an active task from a different
origin task.
2. Move the target task to `done` without resolving the decision.
3. Run the decision expiry sweep.
4. Observe that the old code keeps the decision open until its
time-to-live limit.
5. Observe that the old card links only the origin task.

**Paperclip version or commit**

The bug reproduces on upstream `master` before this pull request.

**Deployment mode**

Local dev and self-hosted server modes are affected because the behavior
is in the shared decision service and board UI.

## What Changed

- Expire an open strict decision with reason `target_completed` when
every strict target reached `done` after proposal.
- Keep decisions that intentionally target an already-finished task.
- Keep lenient-only decisions open.
- Keep continuation delivery consistent with other expiry reasons.
- Add target-task links to the decision card provenance line.
- Use one shared target-ID helper across signing, execution, expiry,
card provenance, and resolver preloading.
- Add service and UI regression tests for primary, secondary, and
target-completed cases.

## Verification

- `pnpm exec vitest run ui/src/components/DecisionCard.test.tsx
server/src/__tests__/decisions-service.test.ts` — 51 tests passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm check:token-gates` — all gates clean.
- `git diff --check origin/master...HEAD` — passed.

## Risks

- Low migration risk. This change does not alter the database schema.
- The expiry sweep performs the existing strict-target query and adds a
snapshot comparison before expiry.
- A decision remains open if any strict target is active or if a target
was already `done` at proposal time.

> The roadmap lists work queues as planned. This pull request fixes the
existing decisions desk. It does not add a new queue subsystem.

## Model Used

- Implementation: Anthropic Claude through Claude Code. The runtime did
not expose the exact model snapshot or context-window size. The model
used reasoning, repository tools, code execution, and test execution.
- PR preparation: OpenAI Codex with GPT-5. The runtime did not expose a
dated model snapshot or context-window size. The model used reasoning,
repository tools, code execution, and test 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
- [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>
2026-08-05 09:55:01 -05:00
0a09e4d975 feat(decisions): add desk workflow and retention (#10672)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Decisions desk shows work that needs a human decision
> - The queue foundation can group and rank decision work
> - Operators also need a focused daily view and a safe way to handle
old work
> - This pull request adds the desk controls, the aging shelf, and
reversible retention
> - It also binds bulk archive decisions to the exact reviewed item set
> - The benefit is a smaller daily queue without lost or orphaned work

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting: server, UI, database, and shared contracts.

**Problem or motivation**

Decision work can grow into one large company list. Operators need quick
queue and date controls. Old items also need a safe retention path that
does not delete work.

**Proposed solution**

Add queue and date controls to the Decisions desk. Compute the aging
shelf on the server. Archive idle items after 90 days unless an operator
keeps them. Keep archived items searchable and revivable. Notify origin
agents in one batch per sweep. Bind bulk archive proposals to a signed,
exact item manifest.

**Alternatives considered**

Client-only aging can drift across browsers and source kinds. Deleting
old rows removes audit and recovery paths. An unsigned dynamic bulk
query can archive items that the reviewer did not inspect.

**Roadmap alignment**

This work supports the Work Queues and decision-memory directions in
`ROADMAP.md`. It extends the Decisions and attention-feed foundation
from #10651. Related earlier work includes #9380, #10010, and #10474.

## What Changed

- Added the queue rail, date chips, decide split, triage strip, queue
page, and aging shelf UI.
- Added server-owned shelf state with per-queue retention overrides.
- Added reversible retention state, archive history, and an idempotent
notification outbox.
- Added the 90-day archive sweeper, Keep exemption, archived feed query,
and revive actions.
- Added one origin-agent notification per agent and sweep.
- Added signed bulk archive proposals with exact-set and version checks.
- Persisted queue-exclusion reasons atomically and kept cross-domain
source resolution per-item until it has an exact-set transaction
contract.
- Added API contracts, OpenAPI entries, migration coverage, focused
tests, and Storybook screens.

## Verification

- `pnpm -r typecheck`
- `pnpm test:run` (server: 329 files and 3,453 tests passed; UI: 408
files and 3,362 tests passed)
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest
run cli/src/__tests__/secrets.test.ts`
- `pnpm build`
- `pnpm check:token-gates`
- Focused retention, attention, decisions, migration replay, startup,
and UI API tests.
- Complete queue snapshot regression with 51 items across the normal
50-item page boundary.

The unmodified CLI test reports one warning assertion in this runtime
because the harness injects static AWS credential variables. The
isolated test passes when those two variables are removed.

## Risks

- The migration adds retention and notification outbox tables. It uses
idempotent table, index, and foreign-key creation.
- Retention runs on the heartbeat scheduler interval. Compare-and-set
version checks prevent stale archive writes.
- Bulk archive acceptance fails closed when authority, activity,
version, or the reviewed set changes.
- Archive is reversible and does not delete source records.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

OpenAI Codex with `gpt-5.6-sol`. The model used tool calls, code
execution, database migration generation, and test execution. The
context-window size is not exposed in this runtime.

## 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: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-02 10:47:03 -05:00
DottaandPaperclip 95d33e1788 fix(inbox): archive tasks completed by human users (#10668)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Mine inbox gives each human user a personal work queue.
> - A human user can complete a task from the board.
> - The status update did not update the user's inbox archive state.
> - The completed task therefore stayed in the user's Mine inbox.
> - This pull request archives the task for the human user who completes
it.
> - Agent completion does not change another user's inbox archive state.
> - The benefit is that manual completion removes the task from Mine
without an extra action or comment.

## Linked Issues or Issue Description

**What happened?**

When a human user changed a task status to `done`, the task stayed in
that user's Mine inbox.

**Expected behavior**

The status change should archive the task from that user's Mine inbox.
The operation should not add a task comment.

**Steps to reproduce**

1. Open a task that appears in Mine.
2. Change the task status to `done`.
3. Return to Mine.
4. Observe that the completed task is still present.

**Paperclip version or commit**

`master` before this change.

**Deployment mode**

All deployment modes with a board user and the Mine inbox.

**Access context**

Board (human operator).

## What Changed

- Archive the completed task for the board user who changes its status
to `done`.
- Persist the status update, inbox archive, and archive audit
atomically.
- Publish live and plugin activity only after the owning transaction
commits, including recovery, decision, and approval completion paths.
- Keep agent-driven completion from changing a human user's inbox
archive state.
- Add database-backed regression tests for the archive, audit, Mine
filter, rollback, event publication, recovery, and agent paths.

## Verification

- `pnpm exec vitest run
server/src/__tests__/inbox-archive-routes.test.ts` passed all 7 tests.
- `pnpm exec vitest run
server/src/__tests__/issue-recovery-actions.test.ts` passed all 44
tests.
- Five directly affected server test files passed all 74 tests; decision
and comment-route suites passed all 105 tests.
- `pnpm --filter @paperclipai/server typecheck` passed after the final
fix.
- `pnpm -r typecheck` and `pnpm build` passed.
- The initial repo-wide `pnpm test:run` passed 3,223 tests; one
unrelated plugin orchestration wake-reason test failed and reproduced in
isolation.
- All latest-head GitHub Actions gates are green, including all server
and E2E shards.
- Greptile reviewed the latest commit at 5/5 with no unresolved review
threads.
- Public GitHub search found no duplicate issue or pull request.

## Risks

- Low risk. The behavior only runs on a board user's transition into
`done`.
- Reopening and completing the task again updates the existing per-user
archive row.
- Agent status updates do not archive a human user's inbox.
- Transactional callers must supply a post-commit activity queue;
covered completion paths do so and tests exercise rollback and
publication order.
- This bug fix does not duplicate planned core work in `ROADMAP.md`.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

OpenAI Codex from the GPT-5 model family assisted with this change. The
runtime does not expose the exact serving model ID or context window.
Reasoning, repository tools, code execution, GitHub CLI, and Paperclip
API access were enabled.

## 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>
2026-08-02 10:43:36 -05:00
9c1f8e7887 feat(decisions): add first-class propose mode (#10010)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents can currently perform many mutations directly, while humans
often need a durable review point before cross-issue or destructive
actions occur
> - Existing approvals and issue-thread interactions do not provide a
standalone, reusable object for presenting options, collecting typed
inputs, detecting stale targets, and auditing effect execution
> - The control plane therefore needs a first-class propose mode that
separates an agent's recommendation from the governed mutation it may
cause
> - This pull request adds Decisions v1 across the database, shared
contracts, server execution and telemetry, agent skill guidance, and
operator UI
> - The benefit is that agents can propose multi-option actions safely
while operators get explicit provenance, fail-closed execution,
per-effect results, and a focused attention workflow

## Linked Issues or Issue Description

### Subsystem affected

Cross-cutting: `packages/db`, `packages/shared`, `server`, and `ui`.

### Problem or motivation

Agents need a governed way to propose consequential work without
immediately mutating issues, especially when one choice can affect
several issue trees. Existing approvals and issue-thread interactions do
not provide a standalone object with typed options, target snapshots,
effect-level authorization, expiration, execution outcomes, and reusable
attention-feed presentation.

### Proposed solution

Add first-class Decisions that store options and typed inputs, surface
open proposals in the operator attention feed, validate target freshness
and the origin-agent/operator authorization intersection at decision
time, execute a bounded set of auditable effects, and retain terminal
outcomes. Decisions v1 supports comments, status and assignee changes,
follow-up issue creation, blocker resolution, and issue-tree
cancellation, plus bundle grouping, expiration/dismissal, rule-key
telemetry, and agent-facing API guidance.

### Alternatives considered

- Extend approvals with arbitrary effects: rejected because approvals
represent governed yes/no actions and would become an unsafe generic
mutation envelope.
- Model every proposal as an issue-thread interaction: rejected because
decisions can span several targets and need independent lifecycle,
telemetry, idempotency, and effect results.
- Let agents perform the mutation and ask for retrospective review:
rejected because it removes the pre-execution governance boundary this
feature is meant to provide.

### Roadmap alignment

Aligns with `ROADMAP.md` sections **Agent Reviews and Approvals**,
**Enforced Outcomes**, **MCP Tool Gateway & Apps (governed tool
access)**, and **Activity History** by making explicit decisions,
authorization gates, auditable execution, and terminal outcomes
first-class control-plane objects.

### Additional context

This does not replace existing approvals or issue-thread interactions,
and it does not add an unrestricted generic mutation effect.

## What Changed

- Added company-scoped decision, option, target, and effect-execution
schema plus migration and shared TypeScript/Zod contracts.
- Added decision routes and services for propose, list/get, decide,
dismiss, cancel, target freshness checks, authorization intersection,
idempotency, activity logging, and execution auditing.
- Added rule-key decision telemetry and attention-feed metadata so open
decisions are visible and measurable.
- Added agent skill documentation for proposing and resolving decisions
through the Paperclip API.
- Added the Decisions UI: API client, query keys, inline attention
resolver, bundle grouping, target-issue strip, terminal history,
destructive confirmation, and per-effect result rendering.
- Added server service coverage, DecisionCard state tests, and Storybook
stories for the supported visual states.

## Verification

- `pnpm -r typecheck` — passed.
- `pnpm test:run` — 2,876 passed, 1 skipped, with one unrelated
cross-suite cleanup-order failure in
`heartbeat-responsible-user-invariant.test.ts`; the failing file passes
in isolation (`6/6`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-responsible-user-invariant.test.ts` — passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/DecisionCard.test.tsx` — passed (`9/9`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/authz-existence-oracle-guard.test.ts
src/__tests__/openapi-routes.test.ts` — passed (`5/5`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/decisions-service.test.ts` — passed (`16/16`).
- `pnpm --filter paperclipai exec vitest run
src/__tests__/company-import-export-e2e.test.ts` — passed (`1/1`).
- `pnpm --filter @paperclipai/server typecheck` and `pnpm --filter
paperclipai typecheck` — passed.
- `pnpm build` — passed.
- Rebased-head focused suite — passed (`6` files, `88` tests): shared
decision contracts, Decisions service, OpenAPI routes, startup feedback
export, DecisionCard states, and attention helpers. The follow-up
stale-secondary-target regression passes in the DecisionCard suite
(`10/10`).
- Rebased-head scoped typechecks — passed for `@paperclipai/shared`,
`@paperclipai/db`, `@paperclipai/server`, and `@paperclipai/ui`.
- Rebased-head migration numbering and safety checks — passed after
renumbering the additive migration to `0193` and making it replay-safe
for environments that applied the earlier feature-branch number.
- `pnpm check:token-gates` — passed with all gates clean.
- GitHub PR workflow and Greptile review for
`1f9f7645882d05dfdd9c99377c03a1f53f20e8be` — running after the
stale-secondary-target fix and PR metadata refresh on July 27, 2026.
- `pnpm --filter @paperclipai/ui build-storybook` exposes an existing
Storybook version mismatch (`storybook` 10.4.6 vs
`@storybook/addon-docs` 10.5.0); Decisions stories were validated with
the docs addon temporarily disabled and the tracked config remains
unchanged.

## Risks

- **Migration:** Adds replay-safe migration `0193`; migration numbering
and safety checks pass. The new tables and indexes are additive.
- **Authorization:** Effect execution intersects the proposing agent's
permissions with the responsible user context and fails closed; mistakes
could reject a valid proposal rather than silently over-authorize it.
- **Concurrency:** Target snapshots and idempotency keys protect against
stale or duplicate execution, but reviewers should focus on mixed-effect
partial outcomes and retry behavior.
- **UI:** Decisions are integrated into the existing attention feed
rather than a separate navigation surface, reducing routing risk but
increasing the importance of attention-item metadata compatibility.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- OpenAI Codex CLI using `gpt-5.6-sol` for final PR preparation, review
fixes, and verification; repository tools and code execution were
enabled, and context-window size is not exposed in this runtime.
- Anthropic Claude Opus 4.8 with 1M context assisted with the Decisions
UI implementation, as recorded in the relevant commits.

## 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>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 19:17:02 -07:00