/session-boundary
Internal skill for commands. Resolve and VALIDATE this branch's session boundary - the ref a session's commit range is measured from - and report how much it can be trusted. Do not trigger on user conversation; only when a command needs the session range.
Claude Code
$ npx -y skills add restarter/lets-workflow --skill session-boundary --agent claude-code
Codex
$ npx skills add restarter/lets-workflow --skill session-boundary -a codex -y
Cursor
$ npx skills add restarter/lets-workflow --skill session-boundary -a cursor -y
Gemini CLI
$ npx skills add restarter/lets-workflow --skill session-boundary -a gemini-cli -y
GitHub Copilot
$ npx skills add restarter/lets-workflow --skill session-boundary -a github-copilot -y
OpenCode
$ npx skills add restarter/lets-workflow --skill session-boundary -a opencode -y
Windsurf
$ npx skills add restarter/lets-workflow --skill session-boundary -a windsurf -y
SKILL.md
session-boundary.SKILL.mdname: session-boundary
description: Internal skill for commands. Resolve and VALIDATE this branch's session boundary - the ref a session's commit range is measured from - and report how much it can be trusted. Do not trigger on user conversation; only when a command needs the session range.
user-invocable: false
Session Boundary
Resolve the ref a session's commit range is measured from, validate it, and say how far it can be trusted. `/lets:end` needs it EARLY (it gates which settlements are offered) and `session-snapshot` needs it LATE (the `### Range` block it writes into a durable file). Two copies of a validation ladder drift, and drift here is what produced a confident "260 commits" for a 6-commit session (lets-370mx), so the ladder lives here and nowhere else.
**Contract:** no args. Output: four echoed lines the caller reads VERBATIM.
SESSION_BOUNDARY=<sha | none>
SESSION_TRUST=<exact | prior-session | estimate | none>
SESSION_COMMITS=<N | unknown>
SESSION_RANGE_DESC=<one-line human description, always carrying the trust level>
Diagnostic NOTEs go to stderr - surface them to the user, they are the loud half of every degradation.
**Fail closed.** If the four lines do not arrive, the caller MUST assume `SESSION_TRUST=none` / `SESSION_COMMITS=unknown` and proceed as though no boundary exists. Never re-derive one inline - a second copy is a second answer, which is the defect this skill exists to prevent.
Trust levels
| level | boundary | what the caller may claim | |---|---|---| | `exact` | this session's `session:` line in `.task-<branch-slug>` | the count is this session's work | | `prior-session` | a `session:` line or legacy slugged ref recorded by an EARLIER session | a real recorded boundary, but not this session's - best-effort | | `estimate` | nothing recorded; derived from this branch's point off `$LETS_MERGE_BRANCH` | what this BRANCH added, not what this session did | | `none` | nothing usable (rejected, or absent while HEAD is `$LETS_MERGE_BRANCH`) | nothing - there is no number |
A caller that gates a destructive or task-completing offer on the range must require `exact` or `prior-session`. `SESSION_RANGE_DESC` always embeds the level, so a number cannot travel into a file or a tracker comment without the qualifier that earned it.
Resolve
The NON-slugged `.lets/sessions/.session-start-ref` is deliberately NOT read: one global file answering a per-branch question let any branch adopt another branch's months-old boundary (`done.md` refuses the same file for the same reason).
LETS_PROJECT_ROOT=$(git rev-parse --show-toplevel)
BRANCH=$(git branch --show-current)
# A branch-scoped question needs a branch. Detached HEAD has none (/lets:review's PR checkout
# detaches deliberately), and `.task-` with an empty slug is a path a hostile PR can ship.
if [ -z "$BRANCH" ]; then
echo "NOTE: detached HEAD - no branch to scope a session boundary to." >&2
echo "SESSION_BOUNDARY=none"; echo "SESSION_TRUST=none"; echo "SESSION_COMMITS=unknown"
echo "SESSION_RANGE_DESC=boundary unknown - detached HEAD"
exit 0
fi
BRANCH_SLUG=$(echo "$BRANCH" | tr '/' '-')
# --- READ (session: line, then the legacy SLUGGED ref - never a global one) ---
TASK_FILE="$LETS_PROJECT_ROOT/.lets/sessions/.task-${BRANCH_SLUG}"
SESSION_LINE=$(sed -n 's/^session: //p' "$TASK_FILE" 2>/dev/null | head -1)
# read, not awk field references: Claude Code replaces a dollar sign + digit with a skill argument.
read -r START_REF STORED_SID _ <<EOF
$SESSION_LINE
EOF
# Shape guard (N1): the boundary is a plugin-written SHA - blank anything non-hex before it expands
# unquoted into the git range, so a hand-edited state file can't inject git option args. It
# ANNOUNCES itself: a malformed value means the file was edited, and that is the one input class
# that must never be reported as "nothing was recorded".
case "$START_REF" in *[!0-9a-f]*)
echo "NOTE: session: boundary in ${TASK_FILE##*/} is malformed (not a hex sha) - ignored." >&2
START_REF=""; STORED_SID="" ;;
esac
# `exact` requires a POSITIVE match on BOTH sides. Two empty strings compare equal, and both
# markdown writers can emit a sid-less line, so a `!=` test would grant `exact` on absence of
# evidence - the lets-370mx failure mode through a different door.
TRUST=prior-session
[ -n "$START_REF" ] && [ -n "$STORED_SID" ] && [ -n "$CLAUDE_CODE_SESSION_ID" ] \
&& [ "$STORED_SID" = "$CLAUDE_CODE_SESSION_ID" ] && TRUST=exact
if [ -z "$START_REF" ]; then
START_REF=$(head -1 "$LETS_PROJECT_ROOT/.lets/sessions/.session-start-ref-${BRANCH_SLUG}" 2>/dev/null | { read -r a _; printf '%s' "$a"; })
case "$START_REF" in *[!0-9a-f]*)
echo "NOTE: legacy ref for ${BRANCH_SLUG} is malformed - ignored." >&2
START_REF="" ;;
esac
fi
# --- VALIDATE (a recorded SHA is a claim, not a fact) ---
MERGE_BASE=$(git merge-base HEAD "origin/{LETS_MERGE_BRANCH}" 2>/dev/null || git merge-base HEAD "{LETS_MERGE_BRANCH}" 2>/dev/null)
if [ -n "$START_REF" ]; then
REJECT=""
git rev-parse --verify --quiet "${START_REF}^{commit}" >/dev/null 2>&1 || REJECT="not a commit in this repo"
[ -z "$REJECT" ] && ! git merge-base --is-ancestor "$START_REF" HEAD 2>/dev/null && REJECT="not an ancestor of HEAD (rebase, reset, or another line of work)"
# Branch-point floor: a session on a feature branch cannot have started before the branch did.
# Does not apply on {LETS_MERGE_BRANCH}, where there is no branch point to floor against.
if [ -z "$REJECT" ] && [ "$BRANCH" != "{LETS_MERGE_BRANCH}" ] && [ -n "$MERGE_BASE" ] && [ "$START_REF" != "$MERGE_BASE" ] \
&& git merge-base --is-ancestor "$START_REF" "$MERGE_BASE" 2>/dev/null; then
REJECT="predates this branch's point off {LETS_MERGE_BRANCH}"
fi
# Age floor: a session boundary is hours-to-days old. 14d is the generous outer edge of one
# session; past it the file is stale, not long-running. Catches the trunk case the floor cannot.
# Captured and validated, never inlined intoRead more
name: session-boundary description: Internal skill for commands. Resolve and VALIDATE this branch's session boundary - the ref a session's commit range is measured from - and report how much it can be trusted. Do not trigger on user conversation; only when a command needs the session range. user-invocable: false
Session Boundary
Resolve the ref a session's commit range is measured from, validate it, and say how far it can be trusted. `/lets:end` needs it EARLY (it gates which settlements are offered) and `session-snapshot` needs it LATE (the `### Range` block it writes into a durable file). Two copies of a validation ladder drift, and drift here is what produced a confident "260 commits" for a 6-commit session (lets-370mx), so the ladder lives here and nowhere else.
**Contract:** no args. Output: four echoed lines the caller reads VERBATIM.
SESSION_BOUNDARY=<sha | none> SESSION_TRUST=<exact | prior-session | estimate | none> SESSION_COMMITS=<N | unknown> SESSION_RANGE_DESC=<one-line human description, always carrying the trust level>
Diagnostic NOTEs go to stderr - surface them to the user, they are the loud half of every degradation.
**Fail closed.** If the four lines do not arrive, the caller MUST assume `SESSION_TRUST=none` / `SESSION_COMMITS=unknown` and proceed as though no boundary exists. Never re-derive one inline - a second copy is a second answer, which is the defect this skill exists to prevent.
Trust levels
| level | boundary | what the caller may claim | |---|---|---| | `exact` | this session's `session:` line in `.task-<branch-slug>` | the count is this session's work | | `prior-session` | a `session:` line or legacy slugged ref recorded by an EARLIER session | a real recorded boundary, but not this session's - best-effort | | `estimate` | nothing recorded; derived from this branch's point off `$LETS_MERGE_BRANCH` | what this BRANCH added, not what this session did | | `none` | nothing usable (rejected, or absent while HEAD is `$LETS_MERGE_BRANCH`) | nothing - there is no number |
A caller that gates a destructive or task-completing offer on the range must require `exact` or `prior-session`. `SESSION_RANGE_DESC` always embeds the level, so a number cannot travel into a file or a tracker comment without the qualifier that earned it.
Resolve
The NON-slugged `.lets/sessions/.session-start-ref` is deliberately NOT read: one global file answering a per-branch question let any branch adopt another branch's months-old boundary (`done.md` refuses the same file for the same reason).
LETS_PROJECT_ROOT=$(git rev-parse --show-toplevel)
BRANCH=$(git branch --show-current)
# A branch-scoped question needs a branch. Detached HEAD has none (/lets:review's PR checkout
# detaches deliberately), and `.task-` with an empty slug is a path a hostile PR can ship.
if [ -z "$BRANCH" ]; then
echo "NOTE: detached HEAD - no branch to scope a session boundary to." >&2
echo "SESSION_BOUNDARY=none"; echo "SESSION_TRUST=none"; echo "SESSION_COMMITS=unknown"
echo "SESSION_RANGE_DESC=boundary unknown - detached HEAD"
exit 0
fi
BRANCH_SLUG=$(echo "$BRANCH" | tr '/' '-')
# --- READ (session: line, then the legacy SLUGGED ref - never a global one) ---
TASK_FILE="$LETS_PROJECT_ROOT/.lets/sessions/.task-${BRANCH_SLUG}"
SESSION_LINE=$(sed -n 's/^session: //p' "$TASK_FILE" 2>/dev/null | head -1)
# read, not awk field references: Claude Code replaces a dollar sign + digit with a skill argument.
read -r START_REF STORED_SID _ <<EOF
$SESSION_LINE
EOF
# Shape guard (N1): the boundary is a plugin-written SHA - blank anything non-hex before it expands
# unquoted into the git range, so a hand-edited state file can't inject git option args. It
# ANNOUNCES itself: a malformed value means the file was edited, and that is the one input class
# that must never be reported as "nothing was recorded".
case "$START_REF" in *[!0-9a-f]*)
echo "NOTE: session: boundary in ${TASK_FILE##*/} is malformed (not a hex sha) - ignored." >&2
START_REF=""; STORED_SID="" ;;
esac
# `exact` requires a POSITIVE match on BOTH sides. Two empty strings compare equal, and both
# markdown writers can emit a sid-less line, so a `!=` test would grant `exact` on absence of
# evidence - the lets-370mx failure mode through a different door.
TRUST=prior-session
[ -n "$START_REF" ] && [ -n "$STORED_SID" ] && [ -n "$CLAUDE_CODE_SESSION_ID" ] \
&& [ "$STORED_SID" = "$CLAUDE_CODE_SESSION_ID" ] && TRUST=exact
if [ -z "$START_REF" ]; then
START_REF=$(head -1 "$LETS_PROJECT_ROOT/.lets/sessions/.session-start-ref-${BRANCH_SLUG}" 2>/dev/null | { read -r a _; printf '%s' "$a"; })
case "$START_REF" in *[!0-9a-f]*)
echo "NOTE: legacy ref for ${BRANCH_SLUG} is malformed - ignored." >&2
START_REF="" ;;
esac
fi
# --- VALIDATE (a recorded SHA is a claim, not a fact) ---
MERGE_BASE=$(git merge-base HEAD "origin/{LETS_MERGE_BRANCH}" 2>/dev/null || git merge-base HEAD "{LETS_MERGE_BRANCH}" 2>/dev/null)
if [ -n "$START_REF" ]; then
REJECT=""
git rev-parse --verify --quiet "${START_REF}^{commit}" >/dev/null 2>&1 || REJECT="not a commit in this repo"
[ -z "$REJECT" ] && ! git merge-base --is-ancestor "$START_REF" HEAD 2>/dev/null && REJECT="not an ancestor of HEAD (rebase, reset, or another line of work)"
# Branch-point floor: a session on a feature branch cannot have started before the branch did.
# Does not apply on {LETS_MERGE_BRANCH}, where there is no branch point to floor against.
if [ -z "$REJECT" ] && [ "$BRANCH" != "{LETS_MERGE_BRANCH}" ] && [ -n "$MERGE_BASE" ] && [ "$START_REF" != "$MERGE_BASE" ] \
&& git merge-base --is-ancestor "$START_REF" "$MERGE_BASE" 2>/dev/null; then
REJECT="predates this branch's point off {LETS_MERGE_BRANCH}"
fi
# Age floor: a session boundary is hours-to-days old. 14d is the generous outer edge of one
# session; past it the file is stale, not long-running. Catches the trunk case the floor cannot.
# Captured and validated, never inlined intoA development workflow plugin for Claude Code Stop babysitting your AI. Start shipping with it.
Other skills on lets-workflow.
goal-test
Local sandbox for experimenting with Claude Code's /goal command (stop-hook condition + LLM-evaluator driving autonomous work…
loop-bb-pr
Compose a /loop that monitors a Bitbucket PR for activity (new comments, review state changes, merge, decline) via the bb-api…
loop-gh-pr
Compose a /loop that watches all open GitHub PRs for activity (new PRs, new commits, new comments) and auto-prepares a READ-ONLY…
loop-test
Local sandbox for experimenting with Claude Code's /loop command (interval + dynamic/self-paced scheduling) applied to LETS…
actor-fetch-personalit…
Internal skill for commands. Fetch and validate personality from URL or file path for Actor agent. Do not trigger on user…
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about session-boundary.
- Which coding agents can use session-boundary?
- session-boundary installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

