Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/open-api-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ openapi: 3.0.3
info:
title: The Agent's user-facing API
description: The user-facing parts of The Agent's API service (excluding system-level endpoints, chat completion, maintenance endpoints, etc.)
version: 5.32.3
version: 5.33.0
license:
name: MIT
url: https://opensource.org/licenses/MIT
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-10
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
## Context

User profiles currently begin with a zero balance and persist EULA acceptance on the user row. EULA acceptance is irreversible, may also activate a waitlisted profile, and is saved independently of accounting operations. Credit transfers lock both user rows and commit their balance changes before creating a separately committed usage record.

`THE_AGENT` is a persisted system user. Existing transfer history represents transfers as credit-transfer usage records with the sender as payer and the receiver as counterpart. Public peer transfers require a platform handle and reject sponsored senders and receivers, while welcome issuance must work by user ID for sponsored and non-sponsored profiles.

The user creation date is stored as a calendar date. Eligibility therefore uses whole calendar-day age rather than elapsed hours.

## Goals / Non-Goals

**Goals:**
- Preserve an ordinary transfer from `THE_AGENT` as the visible accounting provenance.
- Make EULA acceptance, activation, balance changes, and transfer-history persistence atomic and idempotent.
- Reuse the ordinary credit balance and credit-transfer history semantics without applying public peer-transfer eligibility rules.
- Serialize concurrent grants to the same recipient through a recipient-row lock.

**Non-Goals:**
- Introduce promotional credit buckets, expiration, restricted spending, or special refund treatment.
- Change API-key, sponsor-key, sponsor-credit, or receiver-credit precedence.
- Backfill profiles that accepted the EULA before deployment.
- Deduplicate welcome transfers when independently activated platform profiles are later connected.
- Replace the existing `max_users` counting and admission model.
- Generalize atomic transaction ownership across unrelated accounting operations.

## Decisions

### Configure amount and per-profile eligibility window together

Add typed configuration values in `util/config.py` for:

- `WELCOME_CREDIT_GRANT_AMOUNT`, default `500.0`;
- `WELCOME_CREDIT_GRANT_ELIGIBILITY_DAYS`, default `7`.

A profile is eligible when its persisted EULA state changes from false to true and its age in whole calendar days is less than or equal to the configured window. The boundary is inclusive. Current balance, purchases, and sponsorships are deliberately excluded from eligibility because received credits are fungible and those signals do not reliably identify a new profile.

An absolute rollout cutoff was considered. The per-profile age window was chosen because it directly expresses "new profile" and naturally excludes older unaccepted records. Profiles that accepted before deployment remain ineligible because there is no false-to-true transition.

### Add a generic credit-grant operation to the transfer service

Add `grant_credits(recipient, amount, commit, note=None)` to the existing credit transfer service rather than routing issuance through its public peer-transfer method. The required `commit` flag makes transaction ownership explicit at the production callsite. The recipient may be a persisted `User` or UUID. The operation locks `THE_AGENT` and the recipient in UUID order, adds the caller-provided amount to `THE_AGENT`, immediately moves that amount through the ordinary sender-minus/recipient-plus balance operation, and records the caller-provided note. With `commit=True`, it commits and sends a best-effort post-commit notification. With `commit=False`, it leaves both the commit and subsequent `notify_grant` call to the transaction-owning caller.

The operation has no EULA, age-window, welcome-configuration, sponsorship, handle-resolution, or peer-transfer eligibility knowledge. This makes it reusable by future administrative APIs or tools while preserving user-visible provenance through the existing credit-transfer history model.

### Own one transaction across acceptance and issuance

The user-settings layer owns welcome eligibility and stages the EULA update before invoking the generic grant operation. The combined flow shall:

1. lock `THE_AGENT` and the recipient in UUID order and read the recipient's current persisted EULA and creation state;
2. if the request would transition an unaccepted waitlisted profile, validate activation capacity before applying any settings changes;
3. apply the complete settings payload and, when activation is permitted, clear the waitlist and invitation flags;
4. determine welcome eligibility from the explicit persisted-false to updated-true EULA transition and the locked creation date;
5. persist the updated recipient without committing;
6. when eligible, call `grant_credits` with the updated recipient, configured welcome amount, note `"Welcome"`, and `commit=False`; the operation reuses the same transaction and lock order, temporarily credits `THE_AGENT`, executes the ordinary balance transfer, and stages transfer history;
7. commit the settings transaction unconditionally after the optional grant;
8. when eligible, send the best-effort grant notification after the caller-owned commit succeeds;
9. roll back the transaction if any validation, settings, balance, history, or commit operation fails.

`THE_AGENT` remains the accounting source in transfer history. Its temporary top-up and transfer debit happen in the same transaction, so its final balance is unchanged and the top-up has no separate history record. Both rows use ordered pair locking. Repository and grant operations support explicit commit deferral while retaining their existing committed behavior where requested.

### Use the irreversible EULA transition as the issuance marker

The persisted false-to-true EULA transition is the one-time issuance marker. Repeated or concurrent requests re-read the row after acquiring the lock; only the request that observes false may issue the transfer. No balance or purchase heuristic is used.

A separate grant marker was considered but rejected as unnecessary while EULA acceptance cannot be revoked or reset. If policy versioning later permits resetting acceptance, a durable welcome-grant identifier must be introduced before that reset ships.

Before the first acceptance, the settings endpoint rejects payloads that omit policy acceptance so account setup cannot precede the required EULA action. `false` remains invalid for every profile. After acceptance, an omitted or `null` payload value retains normal PATCH semantics and leaves the persisted `true` state unchanged.

### Record an ordinary transfer

For an eligible acceptance, create the same transfer-history shape used by peer transfers:

- sender, payer, and owner: `THE_AGENT`;
- receiver/counterpart: the activated profile;
- purpose: credit transfer;
- amount/total credit cost: configured welcome amount;
- note: `"Welcome"`;
- credit use: true;
- external tool costs and maintenance fee: zero.

The record is visible from both the system agent and receiver transfer-history queries. The recipient's resulting balance is the existing undifferentiated credit balance.

### Preserve additive profile merging

Welcome eligibility and idempotency are per platform profile. If two profiles independently accept the EULA while eligible, each receives a transfer. Existing profile connection behavior sums their balances without recognizing or removing either transfer.

## Risks / Trade-offs

- **[Policy acceptance is reused as the idempotency marker]** → Keep acceptance irreversible; introduce a dedicated issuance marker before any future policy-version reset.
- **[Calendar-date storage makes the window coarser than elapsed time]** → Define and test eligibility in inclusive whole calendar days, matching the persisted data.
- **[Hidden transaction ownership could prematurely commit staged caller changes]** → Require an explicit `commit` argument on every grant and use `commit=False` when settings owns the transaction.
- **[Changing repository commit control can accidentally alter existing transactions]** → Make caller-owned commits opt-in and retain current defaults; cover both deferred and existing paths.
- **[A database failure could otherwise leave acceptance without history or balance]** → Keep all database mutations under one transaction and test rollback at the transfer-record boundary.
- **[Notification can fail after commit]** → Treat notification as best-effort; committed credits and history remain authoritative.
- **[Concurrent activation capacity checks remain count-based]** → Ordered pair locking prevents duplicate welcome issuance and grant deadlocks but does not redefine the existing admission model.

## Migration Plan

1. Deploy the new configuration defaults and transactional grant path together.
2. Do not modify existing balances and do not create transfer records for profiles whose EULA is already accepted.
3. Profiles whose EULA is still unaccepted are eligible only when their calendar-day age is within the configured window at acceptance.
4. Rollback disables future welcome transfers. Already committed transfers and balances remain ordinary user funds and are not reclaimed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Why

New platform profiles need enough ordinary credit to try the service without configuring an API key or purchasing credits first. Tying a one-time welcome transfer to timely EULA acceptance ensures credits are issued only to newly activated profiles rather than every record that appears in the database.

## What Changes

- Add configurable welcome-credit settings for the transfer amount, defaulting to 500 credits, and the acceptance eligibility window, defaulting to 7 days after profile creation.
- On the first persisted EULA acceptance within the eligibility window, invoke a generic transactional credit-grant operation with the configured welcome amount and note `"Welcome"`.
- Record the grant as a normal credit transfer from `THE_AGENT` to the recipient; after receipt, the credits have no origin-based classification, restrictions, expiration, or spending differences.
- Keep EULA acceptance, waitlist activation changes, recipient balance mutation, and transfer-record creation in one locked database transaction.
- Make the welcome transfer available to sponsored and non-sponsored recipients without applying peer-transfer restrictions; existing sponsorship billing precedence remains unchanged.
- Grant at most once per platform profile. Connected profiles retain additive balances and may therefore contribute multiple grants after merging.
- Do not grant credits to profiles that already accepted the EULA or that first accept it after the configured post-creation eligibility window.

## Capabilities

### New Capabilities
- `welcome-credit-grants`: Configurable, one-time welcome credit transfers issued when a newly created platform profile accepts the EULA within its eligibility window.

### Modified Capabilities

None.

## Impact

- Configuration gains welcome grant amount and post-creation eligibility-window values.
- User-settings EULA acceptance decides welcome eligibility and invokes the generic credit-grant operation with welcome-specific arguments.
- Accounting and user persistence gain a transaction spanning profile activation, recipient credit issuance, and transfer-history creation.
- Credit history exposes the welcome allocation as an ordinary transfer from `THE_AGENT`.
- Existing credit spending, purchases, refunds, transfers, sponsorship billing precedence, and profile-merge balance behavior remain unchanged.
- Tests must cover eligibility boundaries, sponsored recipients, one-time/idempotent issuance, transfer history, rollback atomicity, and the generic grant API.
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
## Purpose

Define how newly created platform profiles receive a one-time, fully fungible welcome credit transfer after timely EULA acceptance.

## ADDED Requirements

### Requirement: Welcome grant configuration
The system SHALL configure the welcome transfer amount and the maximum profile age in whole calendar days at EULA acceptance. The default amount SHALL be 500 credits and the default maximum age SHALL be 7 days.

#### Scenario: Default configuration
- **WHEN** no welcome grant configuration overrides are provided
- **THEN** the system uses a 500-credit transfer amount and a 7-day maximum profile age

#### Scenario: Overridden configuration
- **WHEN** valid welcome grant configuration overrides are provided
- **THEN** the system uses the configured transfer amount and maximum profile age

### Requirement: Policy acceptance precedes other settings
The system SHALL reject settings updates from an unaccepted profile unless the payload confirms policy acceptance with `true`. A payload value of `false` SHALL always be rejected. After policies have been accepted, an omitted or `null` acceptance field SHALL preserve the accepted state while allowing ordinary settings changes.

#### Scenario: Unaccepted profile omits policy acceptance
- **WHEN** a profile that has not accepted policies submits other settings without `are_policies_accepted=true`
- **THEN** the settings update is rejected without persisting any changes

#### Scenario: Accepted profile omits policy acceptance
- **WHEN** a profile that already accepted policies submits an ordinary settings update without the acceptance field
- **THEN** the other settings are saved and policy acceptance remains unchanged

### Requirement: Grant on timely first EULA acceptance
The system SHALL issue one welcome transfer when a profile changes from not having accepted the EULA to having accepted it and the profile age is not greater than the configured maximum age. Eligibility SHALL depend on the profile lifecycle and SHALL NOT depend on its current credit balance, purchase history, or sponsorship status.

#### Scenario: Eligible first acceptance
- **WHEN** a profile first accepts the EULA no later than 7 whole calendar days after its creation under the default configuration
- **THEN** the profile receives 500 credits

#### Scenario: Acceptance at the eligibility boundary
- **WHEN** a profile first accepts the EULA exactly the configured number of whole calendar days after its creation
- **THEN** the profile receives the welcome transfer

#### Scenario: Acceptance after the eligibility window
- **WHEN** a profile first accepts the EULA more than the configured number of whole calendar days after its creation
- **THEN** the EULA acceptance and any permitted activation still succeed without a welcome transfer

#### Scenario: Existing balance does not affect eligibility
- **WHEN** an otherwise eligible profile first accepts the EULA while it already has credits or purchase history
- **THEN** the profile still receives the full configured welcome transfer

#### Scenario: Sponsored recipient
- **WHEN** an otherwise eligible sponsored profile first accepts the EULA
- **THEN** the profile receives the welcome transfer despite ordinary peer-transfer restrictions on sponsored recipients

### Requirement: One transfer per platform profile
The system SHALL issue at most one welcome transfer for each platform profile. A request that observes an already accepted EULA SHALL NOT issue another welcome transfer.

#### Scenario: Repeated acceptance request
- **WHEN** a profile that already accepted the EULA submits settings with EULA acceptance again
- **THEN** the profile receives no additional welcome credits

#### Scenario: Concurrent acceptance requests
- **WHEN** concurrent requests attempt the same profile's first EULA acceptance
- **THEN** exactly one request issues the welcome transfer and the profile receives the configured amount only once

#### Scenario: Profiles later connected
- **WHEN** two independently eligible platform profiles each receive a welcome transfer and are later connected
- **THEN** both balances contribute additively to the merged profile

### Requirement: Ordinary transferable credits
Credits received through the welcome transfer SHALL be indistinguishable from other credits after receipt. They SHALL have no promotional bucket, expiration, spending priority, refund protection, or restrictions based on their welcome origin. Existing user-level restrictions SHALL continue to apply regardless of credit origin.

#### Scenario: Use welcome credits
- **WHEN** a profile receives welcome credits
- **THEN** subsequent spending, transfers, sponsorship eligibility, and profile merging use the same balance behavior as credits from any other source

#### Scenario: Sponsored billing precedence
- **WHEN** a profile with welcome credits is sponsored
- **THEN** the existing sponsorship billing precedence remains in effect and the receiver's balance remains untouched while the sponsorship is active

### Requirement: Transfer provenance
The welcome allocation SHALL appear in credit history as a credit transfer of the configured amount from `THE_AGENT` to the recipient with the note `"Welcome"`. In the same transaction, issuance SHALL first add the amount to `THE_AGENT` without a separate history record and then execute the ordinary transfer, leaving `THE_AGENT` with the same final balance.

#### Scenario: Successful welcome transfer history
- **WHEN** an eligible EULA acceptance commits
- **THEN** credit history contains one transfer from `THE_AGENT` to the profile for the configured amount with the note `"Welcome"`

#### Scenario: System agent balance
- **WHEN** a welcome transfer succeeds
- **THEN** `THE_AGENT` has no net balance change

### Requirement: Atomic acceptance and issuance
The system SHALL commit the EULA transition, waitlist activation changes, temporary `THE_AGENT` balance increase, ordinary transfer balance movement, and transfer-history record in one locked database transaction. Failure of any database operation SHALL leave all of those values unchanged.

#### Scenario: Transfer recording fails
- **WHEN** transfer-history persistence fails during an otherwise eligible EULA acceptance
- **THEN** the EULA state, activation flags, recipient balance, and transfer history are all rolled back, and `THE_AGENT` remains unchanged

#### Scenario: Ineligible acceptance
- **WHEN** a profile accepts the EULA outside the eligibility window
- **THEN** EULA and activation changes commit without changing either balance or creating a welcome transfer record
Loading
Loading