Skip to content

T-954I-lease-payload-adds-ttl-seconds

Status: closed/superseded · Impact: medium · Complexity: small

Auto-generated from a /sdlc:task-work post-mortem. Review and promote to open/ready before picking up.

The heartbeat-floor enforcement in the lease library currently approximates the prior-heartbeat timestamp as expires_at - new_ttl because the lease payload doesn’t carry the TTL that was originally chosen. That approximation is correct in single-TTL operation but wrong any time the operator (or skill) changes TTL between heartbeats, and it also produces the awkward “first heartbeat after claim always trips the floor” behavior documented in T-QC31-add-sdlc-lease-cli-commands’s CLI README. Adding ttl_seconds to the lease payload schema removes the approximation and lets the library compute the floor exactly.

LocationRole today
plugin/lib/lease/schemas/lease.schema.json (best guess — exact path established by T-S0PK-add-lease-protocol-library-and-schemas)Defines the lease.json payload shape. Carries expires_at, lease_id, lease_token, owner, phase, etc. — but not the TTL that produced expires_at.
plugin/lib/lease/primitives.py (best guess)Heartbeat enforcement reconstructs prior heartbeat as expires_at − new_ttl because ttl_seconds is not on the payload.
plugin/scripts/lease_cli/heartbeat.py (best guess)Surfaces the floor error to operators; README documents the “first heartbeat after claim trips floor” behavior as a known v1 quirk.
  • Add ttl_seconds: int as a required field on the lease payload schema (bump the schema version per plugin/conventions/schema-bump-checklist.md).
  • Update the library’s claim/transition primitives to write ttl_seconds whenever they set expires_at. The two values are always set together so the invariant is “if expires_at changes, ttl_seconds is rewritten.”
  • Update the heartbeat-floor logic in the library to compute the prior heartbeat from the payload’s stored ttl_seconds rather than the new TTL being requested. Document the floor as (now − last_heartbeat) < ttl_seconds / 4.
  • Update the CLI’s heartbeat README section to remove the “first heartbeat after claim trips the floor” caveat once the fix lands.
  1. Read the library and schema files introduced by T-S0PK-add-lease-protocol-library-and-schemas to confirm where ttl_seconds should land on the payload (root vs nested).
  2. Add the field to the schema, bump the schema version, run the schema-bump checklist.
  3. Update primitives to persist ttl_seconds alongside expires_at (claim + transition + heartbeat paths).
  4. Replace the heuristic in the heartbeat floor with the precise computation against the stored ttl_seconds.
  5. Update tests: at least one regression that switches TTL between heartbeats and asserts the floor is enforced against the prior TTL, not the new one.
  6. Update the lease-CLI README to drop the v1-quirk note.
LocationKindChange
plugin/lib/lease/schemas/lease.schema.jsonmodifyAdd ttl_seconds as a required integer field; bump schema version. Best guess; path established by T-S0PK-add-lease-protocol-library-and-schemas.
plugin/lib/lease/primitives.pymodifyPersist ttl_seconds on every claim / transition / heartbeat; use stored value for floor computation.
plugin/lib/lease/tests/test_heartbeat.pymodifyAdd a TTL-change-between-heartbeats regression.
plugin/scripts/lease_cli/README.mdmodifyDrop the “first heartbeat after claim trips the floor” caveat once the fix lands.
plugin/lib/lease/README.mdmodifyDocument the ttl_seconds field and the precise floor formula.
  • AC-1: The lease payload schema requires ttl_seconds as a positive integer; existing fixtures fail validation until updated and the schema version is bumped per the bump checklist.
  • AC-2: A claim followed by a TTL-change heartbeat (e.g. claim with TTL 600, heartbeat with TTL 60) enforces the floor against the original TTL — the heartbeat at now + small is accepted iff (now − prior_heartbeat) >= 600/4, not >= 60/4.
  • AC-3: The lease-CLI README no longer documents “first heartbeat after claim trips the floor” as a known quirk; the heartbeat section reads cleanly without that caveat.
  • AC-4: All existing lease library / CLI tests pass under the new schema (no payloads break silently).
  • Adding a CLI flag to override the stored ttl_seconds on heartbeat. The payload is the source of truth; operators who want to change TTL run a transition (which rewrites both expires_at and ttl_seconds).
  • Migrating any in-flight lease refs that were written before this schema bump. Slice 1 has no live deployment; no data migration needed.

Spawned by /sdlc:task-work post-mortem of T-QC31-add-sdlc-lease-cli-commands on 2026-05-23.

Bullet: Heartbeat floor needs ttl_seconds in payload for full correctness. The CLI doesn’t know a lease’s original TTL — only the new TTL being requested. Implemented ‘prior heartbeat at expires_at

  • new_ttl’ as a heuristic. A clean fix would add ttl_seconds to the lease payload schema. Out of slice 1 scope; documented in the CLI README’s heartbeat section. Keywords searched: ttl_seconds, correctness, implemented, expires_at, documented, heartbeat, requested, heuristic Excluded: 2026-05-23-add-sdlc-lease-cli-commands Top candidates (score / status / headline):
  • 10 / closed/done / 2026-05-21-skill-md-runtime-drift-audit — Audit /sdlc:task-work SKILL.md against per-project runtime conventions
  • 10 / in-progress / 2026-05-23-add-lease-protocol-library-and-schemas — Add lease-protocol library and payload schemas
  • 6 / closed/done / 2026-05-19-implement-entities-migrate — Implement /sdlc:entities-migrate to apply mechanical schema-drift fixes
  • 6 / planning/backlog / 2026-05-21-task-define-prompts-for-executable-shadow — task-define prompts for executable-shadow when AC asserts live SKILL.md behavior
  • 4 / closed/done / 2026-05-19-task-work-uses-per-project-quality-checks — Make /sdlc:task-work quality-check commands per-project configurable Decision: SPAWNED

← Back to Tasks