workspace: last-active frontmatter + checkpoint handoff, bump to v0.2.0 - #255
workspace: last-active frontmatter + checkpoint handoff, bump to v0.2.0#255fonta-rh wants to merge 10 commits into
Conversation
Projects with a last-active: YYYY-MM-DD field in their CLAUDE.md frontmatter now sort by that date instead of filesystem mtime. Falls back to mtime for older projects without the field. Also bumps the recent-projects display count from 3 to 5. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The skill now sets last-active: YYYY-MM-DD on every update, driving the SessionStart ordering hook.
New projects get last-active: YYYY-MM-DD (same as created) so the SessionStart hook can order them from day one.
Closing a project stamps last-active to today, keeping the SessionStart hook accurate through the project's full lifecycle.
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: fonta-rh The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
WalkthroughThe workspace plugin now tracks project activity with ChangesWorkspace lifecycle
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 9 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (9 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
plugins/workspace/scripts/recent-projects.py (1)
91-106: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRename the overloaded
"mtime"key to avoid future misuse.The
"mtime"key now stores either the real file mtime or an end-of-day timestamp derived from thelast-activefrontmatter date. A future reader ofcollect_projects()output could reasonably assumeentry["mtime"]is always the actual file modification time, leading to a wrong assumption if that value is ever used for anything besides sorting.Rename the key (for example, to
"sort_ts") to make clear it is a sort key, not a literal mtime.♻️ Proposed rename
entries.append({ "name": d.name, "type": fm.get("type", "—"), "status": fm.get("status", "—"), - "mtime": sort_ts, + "sort_ts": sort_ts, "date_str": date_str, }) - entries.sort(key=lambda e: e["mtime"], reverse=True) + entries.sort(key=lambda e: e["sort_ts"], reverse=True) return entries🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/workspace/scripts/recent-projects.py` around lines 91 - 106, Rename the `"mtime"` field created in `collect_projects()` to `"sort_ts"` (or another clearly sort-oriented name), since it stores both file modification times and last-active-derived timestamps. Update every consumer of the collected entry field to use the renamed key while preserving the existing sorting behavior.docs/superpowers/plans/2026-08-04-last-active-frontmatter.md (1)
270-312: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePlan documents a
sort_sourcefield that the shipped script omits.This plan step defines
collect_projectswith a"sort_source"key on each entry, described in the Interfaces section (line 29) as existing "for testability." The reviewedrecent-projects.pyimplementation does not add this key. Update the plan to reflect the final implementation, or add the field back if test coverage forsort_sourcewas intended.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/superpowers/plans/2026-08-04-last-active-frontmatter.md` around lines 270 - 312, The plan’s documented `sort_source` contract does not match the shipped `collect_projects` implementation. Update the plan’s `collect_projects` example and Interfaces description to remove the `sort_source` field and its testability claim, unless the final implementation is intentionally expected to expose it; keep the documentation consistent with the reviewed script.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/workspace/tests/test_recent_projects.py`:
- Around line 61-183: Add a negative test class alongside TestFallbackToMtime
that writes a project with a malformed last_active value and an older CLAUDE.md
mtime, plus a project without last_active and a newer mtime. Run the existing
run_hook flow and assert both projects remain in the systemMessage, with the
newer mtime-only project ordered before the malformed-date project, confirming
fallback to newest_mtime().
---
Nitpick comments:
In `@docs/superpowers/plans/2026-08-04-last-active-frontmatter.md`:
- Around line 270-312: The plan’s documented `sort_source` contract does not
match the shipped `collect_projects` implementation. Update the plan’s
`collect_projects` example and Interfaces description to remove the
`sort_source` field and its testability claim, unless the final implementation
is intentionally expected to expose it; keep the documentation consistent with
the reviewed script.
In `@plugins/workspace/scripts/recent-projects.py`:
- Around line 91-106: Rename the `"mtime"` field created in `collect_projects()`
to `"sort_ts"` (or another clearly sort-oriented name), since it stores both
file modification times and last-active-derived timestamps. Update every
consumer of the collected entry field to use the renamed key while preserving
the existing sorting behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited)
Review profile: CHILL
Plan: Enterprise
Run ID: 7f068d59-0235-4628-a025-970443e692a8
📒 Files selected for processing (6)
docs/superpowers/plans/2026-08-04-last-active-frontmatter.mdplugins/workspace/scripts/recent-projects.pyplugins/workspace/skills/close-project/SKILL.mdplugins/workspace/skills/new-project/SKILL.mdplugins/workspace/skills/update-project/SKILL.mdplugins/workspace/tests/test_recent_projects.py
Collapses the update-project → /clear → resume-project sequence into checkpoint → /clear by writing a single-use marker that a SessionStart hook consumes on the next clear. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add negative test for malformed last-active fallback to mtime. Co-Authored-By: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Addressed CodeRabbit review-body findings:
|
Development artifact — not part of the shipped plugin. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/workspace/.claude-plugin/plugin.json`:
- Line 5: Update the workspace entry in the marketplace configuration to version
0.2.0 so it matches the version declared by the plugin manifest.
In `@plugins/workspace/scripts/handoff.py`:
- Around line 150-155: Update the project validation around project_dir to
reject absolute project names and any path containing .. components, then
canonicalize the candidate path and confirm it remains within the canonical root
/ "projects" directory before allowing the handoff. Preserve the existing error
response for invalid or nonexistent projects.
- Line 56: Resolve the Ruff findings by adding narrow, reasoned noqa annotations
without changing behavior: at plugins/workspace/scripts/handoff.py:56 suppress
S606 for the fixed Python executable and script; at
plugins/workspace/scripts/handoff.py:226 suppress BLE001 using the existing
SessionStart boundary rationale; and at
plugins/workspace/tests/test_handoff.py:30-34, 44-48, and 250-253 suppress S603
for each controlled test subprocess.
- Around line 89-90: Update the marker deletion flow around unlink_quietly so it
returns whether removal succeeded, and have the caller return None when the
consumed marker cannot be removed. Preserve the marker return only after
successful deletion to maintain the single-use contract.
- Around line 110-112: Update the load_files validation before the return in the
data-loading flow to require both a list value and string type for every
element. Treat any non-list or mixed/non-string list as the invalid marker so
the existing fallback path runs and build_directive() never receives invalid
values; preserve valid lists of strings unchanged.
- Around line 158-165: Constrain checkpoint load paths to the project directory:
in plugins/workspace/scripts/handoff.py lines 158-165, reject absolute paths and
any .. components when constructing load_files, and resolve plus revalidate each
marker path beneath P.dir immediately before the Read tool uses it. Update the
corresponding path-handling guidance in
plugins/workspace/skills/checkpoint/SKILL.md lines 48-60 and
plugins/workspace/skills/resume-project/SKILL.md lines 146-154 to require the
same project-directory validation.
In `@plugins/workspace/skills/checkpoint/SKILL.md`:
- Around line 1-5: Update the checkpoint skill frontmatter to use a
colon-namespaced <domain>:<action> name and add the minimal allowed-tools
declaration for this workflow, including Skill and Bash; preserve the existing
description, argument hint, and invocation setting.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited)
Review profile: CHILL
Plan: Enterprise
Run ID: c9a5faa8-c9db-468d-86f8-95f9b140a89a
📒 Files selected for processing (8)
plugins/workspace/.claude-plugin/plugin.jsonplugins/workspace/CLAUDE.mdplugins/workspace/README.mdplugins/workspace/hooks/hooks.jsonplugins/workspace/scripts/handoff.pyplugins/workspace/skills/checkpoint/SKILL.mdplugins/workspace/skills/resume-project/SKILL.mdplugins/workspace/tests/test_handoff.py
The plugin.json bump was not reflected in the root marketplace manifest. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/workspace/scripts/handoff.py`:
- Around line 81-90: Update the marker handling flow around the
path.is_file/read_text logic to atomically rename handoff.json to a unique
claimed filename in the same directory before reading; only the process that
wins the rename should parse and emit the directive, and the claimed file must
be removed in a finally block. Add a concurrent read regression test in
test_handoff.py asserting exactly one process emits “Checkpoint handoff
pending.”
- Around line 168-171: Harden the marker-write flow around marker_path by
canonicalizing and validating the workspace path, rejecting traversal and
symlinked .claude or handoff.json components before writing. Replace
mkdir/write_text with no-follow directory and file operations that cannot follow
symlinks, and add a regression test covering redirected marker writes.
In `@plugins/workspace/tests/test_recent_projects.py`:
- Line 29: Add narrow `# noqa: S603` annotations to the `subprocess.run` calls
at the two reported locations in `test_recent_projects.py`, documenting that
each is a controlled repository-local test boundary using `sys.executable`
without a shell. Do not disable S603 globally or alter the subprocess behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited)
Review profile: CHILL
Plan: Enterprise
Run ID: d1ac03e7-4bfe-4d04-a0e4-05f53b047456
📒 Files selected for processing (14)
.claude-plugin/marketplace.jsonplugins/workspace/.claude-plugin/plugin.jsonplugins/workspace/CLAUDE.mdplugins/workspace/README.mdplugins/workspace/hooks/hooks.jsonplugins/workspace/scripts/handoff.pyplugins/workspace/scripts/recent-projects.pyplugins/workspace/skills/checkpoint/SKILL.mdplugins/workspace/skills/close-project/SKILL.mdplugins/workspace/skills/new-project/SKILL.mdplugins/workspace/skills/resume-project/SKILL.mdplugins/workspace/skills/update-project/SKILL.mdplugins/workspace/tests/test_handoff.pyplugins/workspace/tests/test_recent_projects.py
🚧 Files skipped from review as they are similar to previous changes (10)
- plugins/workspace/hooks/hooks.json
- plugins/workspace/skills/checkpoint/SKILL.md
- plugins/workspace/.claude-plugin/plugin.json
- plugins/workspace/CLAUDE.md
- plugins/workspace/README.md
- plugins/workspace/skills/update-project/SKILL.md
- .claude-plugin/marketplace.json
- plugins/workspace/scripts/recent-projects.py
- plugins/workspace/skills/resume-project/SKILL.md
- plugins/workspace/skills/close-project/SKILL.md
|
Addressed CodeRabbit review-body findings:
|
Summary
last-active frontmatter:
recent-projects.pynow prefers an explicitlast-active: YYYY-MM-DDfield over unreliable filesystem mtime whenordering recent projects. The
update-project,new-project, andclose-projectskills all write this field. Display count raised from 3to 5.
checkpoint handoff: New
/workspace:checkpointskill collapses theupdate-project→/clear→resume-projectsequence intocheckpoint→/clear. A single-use JSON marker(
<workspace>/.claude/handoff.json) is consumed by a SessionStart hookon the
clearmatcher, which emits a resume directive so the next sessionpicks up automatically. Falls through to the existing recent-projects
banner when no handoff is armed.
version bump: 0.1.2 → 0.2.0
Changes
scripts/recent-projects.pylast-activefrontmatter over mtime, display 5 projectsscripts/handoff.pytests/test_recent_projects.pytests/test_handoff.pyhooks/hooks.jsonskills/checkpoint/SKILL.md/workspace:checkpointskills/{update,new,close}-project/SKILL.mdlast-activefrontmatterskills/resume-project/SKILL.mdCLAUDE.md,README.md.claude-plugin/plugin.jsonTest plan
python3 tests/test_handoff.py— 18 passpython3 tests/test_recent_projects.py— 5 passpython3 tests/test_skills.py— 36 passpython3 tests/test_domain_info.py— 14 passbash tests/test_setup.sh— 84 pass/workspace:checkpoint→/clearresumes automatically/clearshows recent-projects banner unchanged🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
/workspace:checkpointto save the current project, next task, and reference files before/clear.Documentation
Tests