# Temporal Parameter Staleness

> Trigger Pattern interval|period|duration|delay|cooldown|lock_period|timelock|deadline|maturity|expiry|getTime - Inject Into Breadth agents, depth-state-trace

- Skill: `plamentsv/temporal-parameter-staleness-3` (Agent Skill)
- Install (CLI): `npx skillmds@latest add plamentsv/temporal-parameter-staleness-3`
- Raw SKILL.md: https://api.skillmd.com/api/skills/plamentsv/temporal-parameter-staleness-3/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: plamentsv (https://skillmd.com/u/plamentsv)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/plamentsv/temporal-parameter-staleness-3

---


# TEMPORAL_PARAMETER_STALENESS Skill (DAML)

> **Trigger Pattern**: `interval|period|duration|delay|cooldown|lock_period|timelock|deadline|maturity|expiry|valid_until|getTime`
> **Inject Into**: Breadth agents, depth-state-trace
> **Finding prefix**: `[DML-TPS-N]`
> **Rules referenced**: R2, R8, R10, R13, R14

Cached parameters in multi-step (multi-transaction) operations become stale when an authority changes the source between steps. On DAML, time is read with `getTime : Update Time` inside a choice, returning **ledger time** (a record time the submitter does NOT choose; it is bounded by the transaction's ledger-time window). A deadline/duration is a template **field** (`Time` or a `RelTime`/`Int` of seconds); a value is "cached" when a choice copies a config field into a successor contract at Step 1, and Step N reads the cached copy instead of re-fetching the live config. There are no slots/epochs/blocks — time is the ledger `Time` value. This skill is the staleness/cross-transaction lens; ENSURE_INVARIANTS §4 is the single-choice deadline-enforcement boundary lens — cross-reference, do not duplicate.

---

## Step 1: Enumerate Multi-Step (Multi-Transaction) Operations

Find all operations that span multiple transactions, typically a Propose/Accept or initiate/complete pattern:

| Operation | Step 1 (Initiate) | Wait Condition | Step N (Complete) | Time Source |
|-----------|-------------------|----------------|-------------------|-------------|
| {op_name} | {Propose choice / create} | {deadline / cooldown} | {Accept / Complete choice} | `getTime` (ledger time) |

For each multi-step operation:
- What parameters/fields are read and copied into a successor contract at Step 1?
- What fields are re-fetched from the live config at Step N?
- What fields are USED at Step N but NOT re-fetched (cached, possibly stale)?
- **Is time read via `getTime` (ledger time) or taken from a caller-supplied `Time` argument?** A caller-supplied time lets the caller choose "now" — always a finding.

### DAML Time Semantics

| Source | Type | Who Controls | Notes |
|--------|------|--------------|-------|
| `getTime` | `Time` (in `Update`/`Script`) | Ledger (record time, within the tx ledger-time window) | The ONLY trustworthy "now" inside a choice |
| Caller-supplied `Time`/`RelTime` argument | choice argument | The submitting party | NOT trustworthy as "now" — the caller picks it |
| A `deadline : Time` field on a contract | template field | Whoever authorized the `create` | Only enforced if a choice compares it to `getTime` |

**Critical property**: within a single transaction (one choice and its consequences), all `getTime` calls return the same `Time` — there is no intra-transaction time variation. Multi-step time attacks require separate transactions.

---

## Step 2: Identify Cached Parameters

For each parameter used across steps:

| Parameter (field) | Source Contract | Copied Into (Step 1 successor) | Cached? | Authority-Changeable? (which choice) | Re-Fetched at Step N? |
|--------------------|-----------------|--------------------------------|---------|--------------------------------------|------------------------|

**DAML caching patterns**:
- **Snapshot into successor**: a Propose choice copies `config.feeRate`/`config.deadline` into the Proposal contract; Accept reads the Proposal's copy, not the live config.
- **Cached deadline**: a Proposal stores `deadline = addRelTime now period` computed at Step 1; if `period` later changes in config, the Proposal's deadline is stale.
- **Re-fetch pattern**: Step N does `cfg <- fetchByKey @Config ...` and reads live values — no staleness for those fields.

**Red flags**: a field is copied into the Step-1 successor AND an authority can change the source config AND Step N does NOT re-fetch the source.

---

## Step 3: Model Staleness Impact

For each cached parameter that can become stale:

```
Scenario A: Parameter INCREASES between steps
1. Party initiates at Step 1 — successor contract stores param = X
2. Authority exercises a setter choice: config param = X + delta
3. Party completes at Step N — uses cached X from the successor
4. Impact: {what happens with stale X when live config is X + delta}

Scenario B: Parameter DECREASES between steps
1. Party initiates at Step 1 — successor stores param = X
2. Authority setter: config param = X - delta
3. Party completes at Step N — uses cached X
4. Impact: {what happens with stale X when live config is X - delta}
```

**BOTH directions are mandatory** — increase and decrease often differ.

### DAML-Specific Staleness Vectors

| Vector | Description | Severity Modifier |
|--------|-------------|-------------------|
| **Stale snapshot in successor** | Proposal/initiate contract holds a copied config field; Accept never re-fetches the live config. | HIGH if it governs value movement; MEDIUM otherwise |
| **Cached absolute deadline** | `deadline` computed at Step 1 from a `period` that later changes; the Proposal's deadline no longer matches policy. | Medium if the deadline is safety-critical |
| **Caller-supplied time** | A choice compares against a `Time` passed as an argument instead of `getTime`. | High — the caller chooses "now", defeating the gate |
| **Config archived mid-operation** | The config contract a Step-N choice `fetch`es/`fetchByKey`es is archived between steps. | High — Step N aborts CONTRACT_NOT_FOUND / NO_SUCH_KEY (liveness brick) |
| **Retroactive rate-at-complete** | Step N reads the LIVE config rate (not the Step-1 snapshot), so an authority change retroactively alters all pending operations. | Medium–High depending on who is harmed |

---

## Step 3b: Update-Source Audit

For each parameter updated from another contract (a price/rate feed contract fetched in a choice):
- Is the source the correct representation of what this parameter tracks?
- **Is the source contract's identity validated?** Can a forged config/feed `ContractId` be substituted via a caller-supplied argument (cross-reference CID_CAPABILITY_SAFETY)?
- Should the parameter be fixed for a period (per epoch) rather than re-read every completion?
- Which choice updates it, who controls that choice, and what is the protocol state if it is never exercised (no refresh)?

---

## Step 4: Retroactive Application Analysis

For fee/rate/deadline fields that apply to existing in-flight state:

| Parameter (field) | Set By (choice) | Applies To | Retroactive? | Impact |
|-------------------|-----------------|-----------|--------------|--------|

**DAML retroactive patterns**:
- **Config setter**: an authority archives+recreates the config with a new `feeRate`. All pending completions that re-fetch the live config now use the new rate — retroactively changes returns for parties who initiated under the old rate.
- **Cooldown/period setter**: changing `cooldownPeriod` in config makes parties who already initiated wait longer/shorter — retroactive on in-flight Proposals (if Step N recomputes from live `period`).
- **Rate-at-complete pattern**: if Step N reads the live config field (not the Step-1 snapshot), any authority change between steps applies retroactively.

**Rule 2 direction check**: can the authority's change make a party-facing choice behave unexpectedly (e.g. setting `cooldownPeriod = 0` removes a withdrawal-delay protection; setting `maxWithdrawal = 0` blocks all withdrawals)? Does the change retroactively affect parties in active operations?

---

## Step 5: Assess Severity

For each staleness issue:
- **Who is affected?** (one party, all parties with pending operations, the protocol)
- **Is the impact bounded?** (capped by a field range, an `ensure`, a max delay)
- **Can it be exploited intentionally?** (authority times a setter to a specific ledger to harm a pending operation)
- **Is there a recovery path?** (cancel + re-initiate; an authority override choice)
- **Ledger precision**: how precisely can an attacker time exploitation relative to the deadline?

### Severity Assessment (Rule 10 — Worst-State)

Use worst realistic operational state:
```
Severity assessed at: pending_ops=MAX, rate_delta=MAX_RATE-MIN_RATE, value=$XX
Rationale: protocol designed for up to {N} concurrent pending operations per documentation
```

---

## Key Questions (must answer all)

1. What multi-step (multi-transaction) operations exist? (Propose/Accept, initiate/complete)
2. For each cached field: can an authority change the source between steps?
3. What happens if a delay DECREASES after initiation? (parties complete too early relative to new policy)
4. What happens if a delay INCREASES after initiation? (parties locked longer than expected)
5. Are fees/rates applied retroactively to in-flight operations, or only to new ones?
6. Is there a max field range bounding the staleness impact (an `ensure`)?
7. **DAML-specific**: does any time comparison use `getTime` (ledger time) or a caller-supplied `Time`?
8. **DAML-specific**: can the config contract a Step-N choice fetches be archived between steps (CONTRACT_NOT_FOUND / NO_SUCH_KEY brick)?
9. **DAML-specific**: is there a refresh/update choice for an external feed? What if it is never exercised?
10. **DAML-specific**: does a Step-1 snapshot vs Step-N re-fetch decision change which value (stale-cached vs retroactive-live) governs the outcome?

---

## Common False Positives

- **Immutable config**: if the config has no setter choice (or the setter's controller is revoked/governance), no staleness
- **Bounded ranges**: if an `ensure` limits the change magnitude, impact may be Low
- **Cancellable operations**: if parties can archive a pending Proposal and re-initiate, reduced severity
- **Two-step authority change**: if config changes themselves require a Propose/Accept delay, parties have time to react
- **Same-transaction operations**: choices completing in one transaction cannot have time staleness between steps
- **Re-fetch at completion**: if Step N re-fetches the live config (not a Step-1 snapshot), no staleness for that field

---

## Instantiation Parameters

```
{TEMPLATES}        - Templates to analyze
{MULTI_STEP_OPS}   - Identified multi-step (Propose/Accept) operations
{CACHED_PARAMS}    - Fields snapshotted into Step-1 successors
{AUTHORITY_PARAMS} - Authority-changeable config fields
{DELAY_PARAMS}     - Deadline/cooldown fields (Time / RelTime / seconds)
{FEE_PARAMS}       - Fee/rate fields that may apply retroactively
{TIME_SOURCE}      - getTime (ledger time) vs caller-supplied Time argument
```

---

## Output Schema

| Field | Required | Description |
|-------|----------|-------------|
| multi_step_ops | yes | Multi-transaction operations found |
| cached_params | yes | Fields snapshotted across steps (into which successor) |
| staleness_vectors | yes | How cached fields can become stale |
| retroactive_fees | yes | Fees/rates applied retroactively |
| time_source_audit | yes | getTime vs caller-supplied, and whether appropriate |
| config_archival_risks | yes | Config contracts that could be archived mid-operation |
| finding | yes | CONFIRMED / REFUTED / CONTESTED |
| evidence | yes | Code locations with line numbers |
| step_execution | yes | Status for each step |

---

## Finding Template

```markdown
**ID**: [DML-TPS-N]
**Verdict**: CONFIRMED / PARTIAL / REFUTED / CONTESTED
**Step Execution**: ✓1,2,3,3b,4,5 | ✗(reasons) | ?(uncertain)
**Rules Applied**: [R2:___, R8:___, R10:___, R13:___, R14:___]
**Severity**: Critical/High/Medium/Low/Info
**Location**: {Module}.daml:LineN (template X, choice Y)
**Title**: {stale cached field / caller-supplied time / retroactive rate / config-archival brick}
**Description**: {which field is cached at Step 1, which authority changes it, why Step N is stale}
**Impact**: {quantified at worst-state — who is harmed by the stale/retroactive value}
**PoC steer**: multi-transaction Script — initiate (snapshot field), authority `submit` changes config, complete and assert the stale value governs (or the live value retroactively applies); for caller-supplied time, `submit` a past/future Time and show the gate passes; `passTime`/`setTime` for deadline windows.
```

---

## Step Execution Checklist (MANDATORY)

| Step | Required | Completed? | Notes |
|------|----------|------------|-------|
| 1. Enumerate Multi-Step Operations | YES | | |
| 2. Identify Cached Parameters | YES | | |
| 3. Model Staleness Impact (both directions) | YES | | |
| 3b. Update-Source Audit | YES | | |
| 4. Retroactive Application Analysis | YES | | |
| 5. Assess Severity | YES | | |

### Cross-Reference Markers

**After Step 2**: if cached fields are authority-changeable → MUST complete Step 3 with BOTH increase and decrease scenarios.
**After Step 3**: if any time comparison uses a caller-supplied `Time` instead of `getTime` → FLAG (the caller chooses "now").
**After Step 3b**: if the source config/feed is referenced by a caller-supplied `ContractId` → cross-reference CID_CAPABILITY_SAFETY.
**After Step 4**: cross-reference SEMI_TRUSTED_ROLES for the authority choices that change these fields, and ENSURE_INVARIANTS §4 for single-choice deadline enforcement.

