Skip to content

fix(claws): enforce 24h window and dedupe state in sageox-summary#504

Merged
galexy merged 1 commit intomainfrom
worktree-summary-skill-time-range
Apr 13, 2026
Merged

fix(claws): enforce 24h window and dedupe state in sageox-summary#504
galexy merged 1 commit intomainfrom
worktree-summary-skill-time-range

Conversation

@galexy
Copy link
Copy Markdown
Contributor

@galexy galexy commented Apr 13, 2026

Summary

  • Enforce the "last 24h" window in sageox-summary using the UTC date prefix in distilled filenames (YYYY-MM-DD-<uuid>.md). Over-includes today + yesterday to cleanly cover the day boundary, then filters against a new state file so repeated runs within the window never re-summarize the same content.
  • Add ~/.openclaw/memory/sageox-summary-state.json: a per-team included_files ledger with atomic write + window-based pruning. First run = empty state = everything in the window is new. Malformed state = stderr warning (never mixed into stdout), treated as empty, overwritten on the next successful run.
  • Move mechanics out of SKILL.md into two deterministic helpers under scripts/ following the Agent Skills spec scripts/ convention: scripts/select-new-files.sh (enumeration + set-difference) and scripts/update-state.sh (merge + prune + atomic rename). SKILL.md carries orchestration and rationale; the scripts carry the shell plumbing.

Why

The skill's prompt told Claude "read the last 24 hours", but nothing enforced the window — claude -p was pointed at the entire memory/daily/ dir and trusted to filter by filename, which in practice meant every historical distill file was in scope. This PR makes the filter real, idempotent across re-runs, and atomic on state-file write so a kill mid-run can't silently poison the next invocation's candidate set.

The script extraction is an agent-UX win beyond just token count: the executing OpenClaw agent no longer needs to parse my bash, pick a set-difference method, handle BSD-vs-GNU date portability, or get atomic-write semantics right. It just invokes two scripts with documented contracts.

Depends on

Test plan

  • First run against a team with no state file — summarizes today+yesterday UTC-date files and creates sageox-summary-state.json
  • Re-run within the same 24h — prints No new distilled content since last summary. and does not invoke Claude
  • Add a new distill file and re-run — summarizes only the new file, leaves the prior ones alone
  • Multi-team manifest — each team's included_files updates independently; teams with no new files pass through unchanged
  • Malformed state file — stderr warning, treated as empty, overwritten on next successful run
  • Kill the skill mid-Step-6 (SIGKILL during state write) — state file is either the pre-state or the post-state, never half-written
  • End-to-end smoke via OpenClaw against a real manifest (the path I could not run from Claude Code)

What I already verified locally

  • clawhub-skill-lint: PASS, 5 files, 29 KB / 50 MB, 0 critical, 0 warning
  • Smoke tests for both scripts covering: first run, idempotent re-run, new-file incremental, malformed state, multi-team isolation, pruning entries older than yesterday_utc, BSD-vs-GNU awk portability (a bug my first pass shipped that the smoke tests caught)
  • make lint / make test not run — change is pure markdown + bash, no Go

Agent-UX review summary

Ran the changes through an agent-ux coworker review before committing. All criticals (C1 atomic state-file write, C2 concrete enumeration recipe, C3 stderr warning on malformed state) and warnings (W1 relative paths in the file list, W2 drop dead read-order hint, W3 explicit timeout 600, W4 trust-model cross-ref) addressed in the same commit. The subsequent scripts/ extraction then removed most of the inline shell entirely, so the W1/C2-level shell plumbing now lives in deterministic helpers instead of SKILL.md prose.

🤖 Generated with Claude Code

Co-Authored-By: SageOx

Summary by CodeRabbit

  • New Features

    • Persistent per-team state to avoid re-summarizing previously processed files
    • Automatic pruning of stale entries outside the processing window
    • Daily-file selection now spans the UTC day boundary (today/yesterday) and short-circuits when no teams have new files
    • Prompt input switched from directory lists to an explicit file list; state updates are applied atomically
  • Documentation

    • Updated workflow, input contract, and reporting guidance (including stderr warnings for malformed state)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fa0687ad-f767-422e-ae06-1efc7004a046

📥 Commits

Reviewing files that changed from the base of the PR and between d5721f1 and 55b731b.

📒 Files selected for processing (5)
  • claws/openclaw/sageox-summary/README.md
  • claws/openclaw/sageox-summary/SKILL.md
  • claws/openclaw/sageox-summary/assets/SUMMARIZE.md
  • claws/openclaw/sageox-summary/scripts/select-new-files.sh
  • claws/openclaw/sageox-summary/scripts/update-state.sh
✅ Files skipped from review due to trivial changes (1)
  • claws/openclaw/sageox-summary/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • claws/openclaw/sageox-summary/assets/SUMMARIZE.md
  • claws/openclaw/sageox-summary/scripts/update-state.sh

📝 Walkthrough

Walkthrough

Adds stateful per-team tracking and a new file-selection stage that picks UTC date-prefixed daily files (today/yesterday) and skips processing when none are new. Prompts now use an explicit file list; two new scripts select new files and atomically merge/prune state.

Changes

Cohort / File(s) Summary
Documentation & prompts
claws/openclaw/sageox-summary/README.md, claws/openclaw/sageox-summary/SKILL.md, claws/openclaw/sageox-summary/assets/SUMMARIZE.md
Rewrote workflow and skill docs to introduce sageox-summary-state.json, replace the {{DIR_LIST}} placeholder with {{FILE_LIST}}, insert a "select new daily files" stage, add short-circuit behavior when no new files exist, and update step numbering and output messages.
Selection & state management scripts
claws/openclaw/sageox-summary/scripts/select-new-files.sh, claws/openclaw/sageox-summary/scripts/update-state.sh
Added select-new-files.sh to emit basenames of date-prefixed daily markdowns (UTC today/yesterday) excluding already-included entries from state; added update-state.sh to atomically merge new basenames into per-team included_files, prune older entries, and persist via atomic rename. Both validate args, require jq, handle missing/malformed state by warning to stderr, and clean up temp files.

Sequence Diagram(s)

sequenceDiagram
    participant Skill as "sageox-summary Skill"
    participant Select as "select-new-files.sh"
    participant State as "sageox-summary-state.json"
    participant Claude as "Claude API"
    participant Update as "update-state.sh"

    Skill->>State: load (or init) state
    Skill->>Select: request new files per team (today/yesterday)
    Select->>State: read included_files for team
    Select-->>Skill: return new basenames (0..N)
    alt no new files for any team
        Skill-->>Skill: short-circuit and emit single-line status
    else new files exist
        Skill->>Claude: invoke with --add-dir + {{FILE_LIST}} for teams with files
        Claude-->>Skill: return summaries
        Skill->>Update: send newly-summarized basenames per team via stdin
        Update->>State: atomically merge, prune by cutoff, write updated JSON
        Update-->>Skill: success (no stdout)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hopped through files both new and old,

I picked the dates the logs had told.
I saved our state, pruned what’s past,
Now summaries are fresh and fast. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: enforcing a 24-hour window and deduplicating state in sageox-summary.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-summary-skill-time-range

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: SageOx <ox@sageox.ai>
SageOx-Session: https://sageox.ai/repo/repo_019c5812-01e9-7b7d-b5b1-321c471c9777/sessions/2026-04-13T13-46-galexy-Oxa4Ab/view
@galexy galexy force-pushed the worktree-summary-skill-time-range branch from d5721f1 to 55b731b Compare April 13, 2026 21:33
@galexy galexy merged commit c4f277e into main Apr 13, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant