Skip to content

Archive task file on /close instead of deleting it - #19

Merged
gering merged 8 commits into
mainfrom
task/archive-tasks-on-close
Jun 29, 2026
Merged

Archive task file on /close instead of deleting it#19
gering merged 8 commits into
mainfrom
task/archive-tasks-on-close

Conversation

@gering

@gering gering commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • /close now archives the finished task file instead of rm-ing it: it moves tasks/<name>.md into tasks/archive/<name>.md with a closed-stamp and logs a one-line entry, so finished-task context (goal, acceptance criteria, shipping PR) is no longer lost — tasks/ is untracked by design and had no git history to fall back on.
  • All stateful logic is encapsulated in a new tested helper (archive-task.sh); close/SKILL.md only branches on its key=value output, per the prose-drift convention.

Changes

  • New scripts/archive-task.sh — move + closed-stamp (date · PR + short merge SHA · branch, or "closed manually (no merged PR)"), append-only tasks/archive/_index.md log, collision-safe (-2/-3 suffix, never clobbers), reports tracked=yes/no.
  • Adaptive tracking, no .gitignore surgery — the archive inherits whatever tasks/ does: gitignored → local-only; tracked → committable. The script never commits; /close asks first and stages only tasks/archive/ + the removed file, never a blanket git add tasks/.
  • /close step 10 rewritten to use the helper (merged + manual paths), plus the top-of-file path-rule example and the final summary.
  • /list shows an archived count in its summary.
  • work-system 1.5.0 (plugin.json + marketplace.json); README updated; knowledge entry curated.

Readiness

  • ✅ README updated (command table + close section)
  • ✅ Version bumped to 1.5.0 (both files in sync)
  • ✅ Knowledge curated (features/task-archiving-on-close.md)
  • check-structure.py — 0 errors (2 pre-existing warnings in unrelated plugins)
  • ➖ No changelog / language build in this declarative repo

Test plan

  • Merged-PR close: task file lands in tasks/archive/ with PR #N (merged @ <sha>) stamp + _index.md line
  • Manual close (no merged PR): stamp reads "closed manually (no merged PR)"
  • Re-close same name: archived file suffixed -2, prior archive untouched
  • Gitignored tasks/tracked=no (local-only, no commit prompt); tracked tasks/ → commit stages only the archive
  • /list summary shows the archived count

🤖 Generated with Claude Code

gering and others added 8 commits June 24, 2026 22:29
- Add archive-task.sh: move tasks/<name>.md to tasks/archive/<name>.md
  with a closed-stamp (date, PR + short merge SHA, branch), append a
  one-line tasks/archive/_index.md entry, collision-safe (-2/-3 suffix)
- Adaptive tracking with no .gitignore surgery: archive inherits tasks/'s
  ignore status; helper reports tracked=yes/no, /close asks before commit
  and stages only tasks/archive/ + the removed file
- Rewrite /close step 10 + path-rule example + summary to use the helper
- /list: show archived count in the summary
- Bump work-system to 1.5.0; update README and curate knowledge
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bEuNsKPWrfHPQX9dZ4HfY
- Flatten slash-bearing task names (adopted feature/a/b branches) so the
  archive write can't crash mid-/close, after the worktree/branch are gone
- Key _index.md entries and the final summary to the actual archived
  filename so a -2/-3 collision entry maps back to its file
- Make the archive write atomic (tmp + mv) so an interrupted write can't
  orphan a truncated, real-looking archive file
- Rename the helper's tracked= output to committable= (check-ignore measures
  "not ignored", not "tracked") and reword /close accordingly
- Extract precise commit staging into an archive-task.sh `stage` subcommand
  (no blanket `git add tasks/`); scope /close's status display too
- Source the merge SHA from task-status.sh `assess` instead of a second
  `gh pr view` round-trip; fix a merge_sha local-var collision
- Anchor index-title extraction to the first heading line (skip shebangs /
  fenced `#` comments)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bEuNsKPWrfHPQX9dZ4HfY
- Revert the merge-SHA fold into task-status.sh assess: re-fetch it via a
  separate best-effort `gh pr view` in /close so the safety-critical pr_state
  lookup never depends on the optional `mergeCommit` json field (old-gh safe)
- Require a value for archive-task.sh --pr/--sha so a bare trailing flag errors
  instead of silently stamping a merged task as "closed manually"
- Record the _index.md line before dropping the source, so a failed index
  write leaves the original intact and /close re-runnable
- Catch a failed archive write, clean the temp, and abort with the source kept
- Chain /close's `stage && commit` so a stage failure can't commit the wrong tree
- Sync the knowledge doc's merge-SHA-source note to the restored design
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bEuNsKPWrfHPQX9dZ4HfY
When tasks/ is committable, /close left the archive commit on local main,
unpushed — diverging from origin/main and breaking the next /close's
--ff-only sync. Fold the push into the same approval: commit, then push the
archive as a clean fast-forward onto origin/main (step 5 already synced
main, so it sits one commit on top). A failed push (protected/offline/
pre-existing divergence) is non-fatal and never force-pushed — the commit
stays local with a "push when ready" note. Update README + knowledge doc.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bEuNsKPWrfHPQX9dZ4HfY
- Stop stamping "merged @ null": guard --jq with `// empty` and have
  archive-task.sh treat a literal "null" sha as no-sha
- /kickoff: enumerate tasks with the `tasks/*.md` glob so the new
  tasks/archive/ dir is no longer offered as a fake task
- Anchor /close's `gh pr view` SHA fetch to the main repo via a scoped
  subshell cd, so the inside-worktree path (cwd removed in step 7) still resolves
- Encapsulate stage -> commit -> ff-push in a tested `commit-push` helper
  (replaces the SKILL-prose orchestration); it refuses to commit when the
  main repo isn't on <main-branch>, and stages a previously-tracked task
  file's removal even when the archive path is gitignored
- archive: roll the moved file back if the _index.md write fails, so a
  re-run leaves no orphaned, unrecorded archive
- Fix the stale /close subtitle ("delete task file" -> "archive"), the
  committable=no wording, and trim the knowledge index line
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bEuNsKPWrfHPQX9dZ4HfY
Correctness bugs introduced by the round-3 commit-push helper:
- Commit with an explicit pathspec (`git commit -- <archive> <index> <removal>`)
  so unrelated pre-staged work is never swept into the "Archive task" commit
  and pushed to origin/main
- Report result=commit-failed on a non-zero commit (rejecting hook, GPG,
  locked index) instead of masking it as nothing-to-commit
- Flatten the source lookup (foo/bar -> tasks/foo-bar.md), so an adopted
  multi-segment task is actually found and archived, not silently skipped
- Make the source rm non-fatal: a remove failure now warns and still emits
  the archive output (no silent duplicate-with-no-output mid-/close)
- /close: handle archive exit != 3 as a real failure, and add the
  commit-failed branch; fix the wrong-branch "staged" wording (nothing is staged)
- /kickoff: enumerate with `find` (nullglob-immune) instead of `ls tasks/*.md`
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bEuNsKPWrfHPQX9dZ4HfY
Both correctness bugs trace back to round-4's non-fatal rm + batched add:
- Revert the non-fatal source rm to a fatal remove with archive rollback,
  done BEFORE the index write: if the source can't be removed, roll the
  moved archive back, keep the source intact, and exit non-zero (/close's
  exit!=3 branch reports it). This stops commit-push from ever seeing a
  leftover source and committing a pushed duplicate of source + archive.
- commit-push: stage each path independently and commit only the paths that
  actually carry a staged change, so one non-matching pathspec (a stale
  archived_path) can no longer abort the whole `git add` and silently drop
  the real source deletion as a false nothing-to-commit.
- Index write is now best-effort (the archive file is the record); warn
  instead of aborting on a failed log append.
- /kickoff: show "No tasks found" by checking for empty `find` output
  (find exits 0 on no matches, so the `|| echo` fallback never fired).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bEuNsKPWrfHPQX9dZ4HfY
- Scope the archive push: only push when the archive commit is the SOLE
  commit ahead of origin/<main-branch> (ahead == 1). A pre-existing unpushed
  commit on main is no longer published under the archive-only approval —
  commit-push reports committed-local reason=unpushed-history instead
- Add an integrity guard: when the archive file was meant to be committed
  but failed to stage (stale/lost archived_path), return archive-not-staged
  and unstage, rather than committing the source deletion alone and claiming
  the task was archived
- Emit archive_committed=yes|no so /close words a gitignored-archive close
  honestly ("removal committed; archive kept local") instead of claiming the
  archive file was committed
- Document the new result codes in the script header, SKILL, and knowledge doc
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bEuNsKPWrfHPQX9dZ4HfY
@gering
gering merged commit a2b7cf0 into main Jun 29, 2026
1 check passed
@gering
gering deleted the task/archive-tasks-on-close branch June 29, 2026 19:28
gering added a commit that referenced this pull request Jul 16, 2026
Second dogfooding round at the deepest profile (/swarm:review --max --fix:
per-lens split live — gate pruned style, 10 xhigh finders + codex
gpt-5.6-sol@xhigh + grok-4.5 + composer; 24 findings, 10 consensus).
Fixes the agreed findings:

- Never auto-accept an all-untagged consensus cluster: no tagged lens
  backs it, so it is verified like a solo. Verify/auto-accept now derive
  from ONE needsVerify predicate (structural exactly-once partition);
  kind vote in clearer every-form (#1, consensus critical; #21, #22)
- Design verifier sees the finding's recommendation — the proposal the
  applicability rubric actually tests — and carries an escape hatch:
  a genuine defect mis-filed under a design lens is not refuted away
  (#12, #3); "solo" dropped from verifier prompts (#4)
- Untagged findings from multi-lens cluster finders fall back to
  'unspecified' (safe defect bucket), not lenses[0] (#7); merge-agent
  free-text lens validated, majority-member fallback (#9)
- Improvement invitation scoped to design finder units — defect-lens
  finders stay defect-only (#8); merge prompt clusters by issue, not
  only defect (#13); schema descriptions generalized for design
  findings (#14)
- LENS_BRIEF startup assertion (#6); gate prompt interpolates
  LENS_CLUSTERS.design (#19); new test_lens_sync.py guards all lens
  mirrors: SKILL HDR prompt, LENS_BRIEF, pr-post DESIGN_LENSES (#5)
- Workflow assigns stable finding num (defects first, shared sequence);
  presenter/pr-post render it verbatim (#20)
- pr-post.py: design lens is the backup kind signal when the handoff
  drops kind (explicit defect still wins); single-pass partition (#17, #24)
- Balance: REFUTED is its own segment (refuted ⊄ solo since design
  consensus can be refuted) (#2); LOCKED design-table column precedence
  in --loop rounds clarified (#11); "verifies solos" doc sweep across
  manifests/README/knowledge (#4); cluster failure-isolation trade-off
  documented (#10); knowledge index line trimmed (#15)

Declined: #16 (JS test harness for sandbox code; lens-sync test covers
the drift class), #18 (gate-fail under --max runs all lenses — the
documented never-silently-narrower degrade), #23 (micro-opt vs readability).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126cGxsBYxqgEFH4AcpriNk
gering added a commit that referenced this pull request Jul 17, 2026
Swarm loop round 3 — 3 findings, all in the probe. Rather than patch a probe
that had a security jail bolted on (rounds 1-3 all fixed jail-adjacent bugs),
this removes the jail: a readiness check passes no untrusted diff, so it never
needed one — the sibling `codex login status` in ready_check is unjailed too.

- Run `grok models` directly, not through sandboxed(). This roots out the
  round-3 CRITICAL (#18): the jail's `_init_sandbox` builds its deny profile
  with python3, so routing the probe through it made python3 a hard dependency
  of the formerly-local `ready`/`list` paths — and a missing python3 then
  misreported as "grok models failed (rc=65)". No jail, no python3 dep, honest
  reasons.
- Bound with `timeout -k 3` (#17): plain `timeout` only SIGTERMs, so a grok
  that ignores SIGTERM (or forks a stdout-inheriting child) kept the command
  substitution blocking past the deadline — the "must never hang" hole. `-k`
  SIGKILLs after a grace period. rc 124 (SIGTERM at the deadline) reports as
  "timed out"; rc 137 (SIGKILL) reports as "killed" — almost always our own
  `-k`, but an OOM/external kill shares the code, so the message doesn't assert
  a timeout that may not have happened.
- Take only the FIRST id per bullet line (#19): scanning the whole line also
  matched a grok-4.5 mentioned in prose on another model's line
  ("* grok-5 (successor to grok-4.5)"), reporting a retired model as offered.
- Fold _build_jail back into sandboxed(). It existed ONLY to share the jail
  with the probe; with the probe unjailed, the split — and the shared-warning
  (#12) and per-backend-memo (#15) fixes it forced last round — are moot.
  sandboxed() is back to its pre-split shape; _init_sandbox keeps the backend
  key (harmless, strictly more correct).
- Fix a stale swarm-review.js input-contract comment ("subset of the three" →
  codex, grok).
- Docs (header/README/CHANGELOG/knowledge) drop the jail claim. The knowledge
  entry records the real lesson: five rounds of patching a jailed probe ended
  the moment the jail was deleted — a feature added to be safe that generates
  every round's bugs is a shape problem; cut it, don't harden it. The composer
  removal (the PR's subject) drew zero findings across all five rounds.

Verified: live grok ready; SIGTERM-ignoring grok bounded by -k in ~5s, reported
as killed; SIGTERM-obeying grok reported as timed out; model-gone → honest hint,
no warning; probe rc=1 → warn + trust-auth; NO python3 invoked on the ready
path; review path still fully jailed (sandbox-exec + AWS/GH secret stripping);
end-to-end review returns a schema-valid finding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gering added a commit that referenced this pull request Jul 17, 2026
Second dogfooding round at the deepest profile (/swarm:review --max --fix:
per-lens split live — gate pruned style, 10 xhigh finders + codex
gpt-5.6-sol@xhigh + grok-4.5 + composer; 24 findings, 10 consensus).
Fixes the agreed findings:

- Never auto-accept an all-untagged consensus cluster: no tagged lens
  backs it, so it is verified like a solo. Verify/auto-accept now derive
  from ONE needsVerify predicate (structural exactly-once partition);
  kind vote in clearer every-form (#1, consensus critical; #21, #22)
- Design verifier sees the finding's recommendation — the proposal the
  applicability rubric actually tests — and carries an escape hatch:
  a genuine defect mis-filed under a design lens is not refuted away
  (#12, #3); "solo" dropped from verifier prompts (#4)
- Untagged findings from multi-lens cluster finders fall back to
  'unspecified' (safe defect bucket), not lenses[0] (#7); merge-agent
  free-text lens validated, majority-member fallback (#9)
- Improvement invitation scoped to design finder units — defect-lens
  finders stay defect-only (#8); merge prompt clusters by issue, not
  only defect (#13); schema descriptions generalized for design
  findings (#14)
- LENS_BRIEF startup assertion (#6); gate prompt interpolates
  LENS_CLUSTERS.design (#19); new test_lens_sync.py guards all lens
  mirrors: SKILL HDR prompt, LENS_BRIEF, pr-post DESIGN_LENSES (#5)
- Workflow assigns stable finding num (defects first, shared sequence);
  presenter/pr-post render it verbatim (#20)
- pr-post.py: design lens is the backup kind signal when the handoff
  drops kind (explicit defect still wins); single-pass partition (#17, #24)
- Balance: REFUTED is its own segment (refuted ⊄ solo since design
  consensus can be refuted) (#2); LOCKED design-table column precedence
  in --loop rounds clarified (#11); "verifies solos" doc sweep across
  manifests/README/knowledge (#4); cluster failure-isolation trade-off
  documented (#10); knowledge index line trimmed (#15)

Declined: #16 (JS test harness for sandbox code; lens-sync test covers
the drift class), #18 (gate-fail under --max runs all lenses — the
documented never-silently-narrower degrade), #23 (micro-opt vs readability).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126cGxsBYxqgEFH4AcpriNk
gering added a commit that referenced this pull request Jul 17, 2026
Second dogfooding round at the deepest profile (/swarm:review --max --fix:
per-lens split live — gate pruned style, 10 xhigh finders + codex
gpt-5.6-sol@xhigh + grok-4.5 + composer; 24 findings, 10 consensus).
Fixes the agreed findings:

- Never auto-accept an all-untagged consensus cluster: no tagged lens
  backs it, so it is verified like a solo. Verify/auto-accept now derive
  from ONE needsVerify predicate (structural exactly-once partition);
  kind vote in clearer every-form (#1, consensus critical; #21, #22)
- Design verifier sees the finding's recommendation — the proposal the
  applicability rubric actually tests — and carries an escape hatch:
  a genuine defect mis-filed under a design lens is not refuted away
  (#12, #3); "solo" dropped from verifier prompts (#4)
- Untagged findings from multi-lens cluster finders fall back to
  'unspecified' (safe defect bucket), not lenses[0] (#7); merge-agent
  free-text lens validated, majority-member fallback (#9)
- Improvement invitation scoped to design finder units — defect-lens
  finders stay defect-only (#8); merge prompt clusters by issue, not
  only defect (#13); schema descriptions generalized for design
  findings (#14)
- LENS_BRIEF startup assertion (#6); gate prompt interpolates
  LENS_CLUSTERS.design (#19); new test_lens_sync.py guards all lens
  mirrors: SKILL HDR prompt, LENS_BRIEF, pr-post DESIGN_LENSES (#5)
- Workflow assigns stable finding num (defects first, shared sequence);
  presenter/pr-post render it verbatim (#20)
- pr-post.py: design lens is the backup kind signal when the handoff
  drops kind (explicit defect still wins); single-pass partition (#17, #24)
- Balance: REFUTED is its own segment (refuted ⊄ solo since design
  consensus can be refuted) (#2); LOCKED design-table column precedence
  in --loop rounds clarified (#11); "verifies solos" doc sweep across
  manifests/README/knowledge (#4); cluster failure-isolation trade-off
  documented (#10); knowledge index line trimmed (#15)

Declined: #16 (JS test harness for sandbox code; lens-sync test covers
the drift class), #18 (gate-fail under --max runs all lenses — the
documented never-silently-narrower degrade), #23 (micro-opt vs readability).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126cGxsBYxqgEFH4AcpriNk
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