Install
$ agentstack add skill-mdproctor-cc-praxis-work-end ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
work-end
Closes the current branch cleanly. Promotes artifacts, merges the journal, closes the issue, rebases the project branch onto the project base branch, marks the branch closed, returns to the workspace base (main).
Code review is mandatory before any push or PR. You MUST invoke the code-review skill on the branch diff before Step 8j (push/PR). There are NO exempt branches — not "mechanical" changes, not "tests already pass", not "small diff". The review catches what you missed. Skipping it is the #1 failure mode of this skill.
Doc sync is mandatory. update-claude-md and implementation-doc-sync are part of the pre-close sweep and default to ON. They catch convention drift and stale documentation that compounds across sessions.
Main-branch mutations go through work-end only. Never run git checkout main && git merge manually — this bypasses pull-before-merge, squash-before-push, and fork-first delivery. If work needs to land on main, use work-end on the branch. There is no safe shortcut. The pre-push hook blocks diverged pushes, but prevention is better than detection.
Red Flags — thoughts that mean STOP
| Thought | Reality | |---------|---------| | "This branch was mechanical" | Mechanical changes have mechanical bugs. Review catches them. | | "Tests passed, it's fine" | Tests verify behaviour, not code quality or spec compliance. | | "The diff is small" | Small diffs have the highest bug-per-line ratio. | | "I'll review after merging" | Post-merge review is post-incident review. | | "Doc sync has nothing to sync" | Run it and let the skill decide. Your guess is often wrong. | | "CLAUDE.md hasn't changed" | Conventions established during implementation need to be captured. |
Path Resolution (run first, always)
Run the bundled context script — installed, version-controlled, no hardcoded paths:
python3 ~/.claude/skills/project-init/ctx.py
Never write a script to /tmp/ for path resolution. /tmp/ is shared across sessions — a stale script from a previous session in a different project will silently return the wrong workspace and project paths, contaminating the entire close operation.
Use the printed values as concrete strings in ALL subsequent commands. Never re-assign to shell variables. Replace every `, , , , , , , ` placeholder with the actual value from the script output.
Pre-conditions
Run python3 ~/.claude/skills/project-init/ctx.py first. Use CURRENT_BRANCH from its output. Check in order:
- If
$WORKSPACE/design/.pause-stackexists and has entries — check whether
the target branch is in the stack:
- Current branch is in the stack (ending a paused branch without resuming it):
allowed. After all close steps complete, remove this branch from the stack (Step 9 will handle it — see "Stack cleanup on end" below).
- Current branch is NOT in the stack but stack is non-empty: inform the user
the stack has N other paused branches. Continue — this is normal when ending the active branch while others are paused.
$WORKSPACE/design/.metamust exist on the current branch → proceed.
- If
$WORKSPACE/design/.metaexists but$CURRENT_WORKSPACE == main(orphaned)
→ hard stop. Offer to switch to the surviving branch and close from there, or discard.
- Workspace must have a clean working tree — run before any other work:
``bash git -C "$WORKSPACE" status --short ` If any output appears, hard stop: > "Workspace has uncommitted changes on $BRANCH_NAME`. Commit or discard them > before running work-end — stash is not used in this workflow." Do not proceed until the working tree is clean. Never stash automatically.
- Project base branch must have a clean working tree — run before any other work:
``bash git -C "$PROJECT" status --short git -C "$PROJECT" log "$PROJECT_BASE_BRANCH"..origin/"$PROJECT_BASE_BRANCH" --oneline ``
- If
git status --shorthas output → hard stop:
> "⚠️ Project $PROJECT_BASE_BRANCH has staged or unstaged changes — a previous operation > was left incomplete. Resolve before closing this branch."
- If remote is ahead of local → warn (non-blocking):
> "⚠️ Remote $PROJECT_BASE_BRANCH is ahead of local — rebase before landing this branch's work."
Do NOT check whether local is ahead of remote. At work-end, local $PROJECT_BASE_BRANCH will naturally be ahead once the branch is rebased onto it (step 8j). The mandatory fork push in step 8j is the mechanism that ensures work is preserved — not a pre-condition check. Checking "local ahead of remote → hard stop" would always fire incorrectly at work-end.
Step 0 + Step 1 — Context (resolved by Path Resolution script)
All values — WORKSPACE, PROJECT, OWNER_REPO, BASE_BRANCH, BRANCH_NAME, PROJECT_SHA, ISSUE_N, ISSUE_REPO, COVERS — come from ctx.py output. Do not re-extract them with shell commands.
COVERS is a comma-separated list of all issue numbers this branch closes (e.g. "5,19,32,24"). When the branch was started for a single issue, COVERS equals ISSUE_N. When absent from .meta (branches created before this feature), COVERS defaults to ISSUE_N.
Branch summary — always print before proceeding
Immediately after running the Path Resolution script, print a summary using concrete values from its output — one command per line, no shell variables:
gh issue view --repo --json title --jq '.title' 2>/dev/null
git -C log --oneline .. 2>/dev/null
git -C diff --shortstat ..HEAD 2>/dev/null
grep "^### " /design/JOURNAL.md 2>/dev/null | wc -l
Output format:
╔══ Branch Summary ═══════════════════════════════════╗
║ Branch:
║ Issue: # — (primary)
║ Covers: #, #, # ← omit if COVERS == ISSUE_N (single issue)
║ Started:
║
║ Commits ():
║
║
║ Changed:
║ Journal: (or: no journal)
╚═════════════════════════════════════════════════════╝
If the issue title cannot be fetched (no network, no tracking), omit that line. If $COVERS contains more than one issue, fetch and display each title. If no commits are found on the branch (work landed directly on base), note that.
This summary is informational only — it does not block the close and requires no user input.
Step 2 — Flyway V re-scan
Re-scan at close time — another branch may have claimed the same V numbers since branch creation.
git -C "$PROJECT" fetch --all 2>/dev/null || echo "⚠️ No network — scan skipped"
If conflict detected: offer [R] renumber affected migration files, [A] abort. Block close until resolved.
Step 3 — Resolve routing and set DESIGN_REPO
Read three-layer routing cascade for each artifact type. Warn on deprecated vocabulary (base, project repo, design-journal). Show resolved table; user confirms before proceeding.
Capability detection — for each resolved destination:
detect_capability() {
local dest="$1"
if [ -d "$dest/.git" ]; then
git -C "$dest" remote get-url origin &>/dev/null 2>&1 && echo "remote-git" || echo "local-git"
else
echo "filesystem"
fi
}
Specs routing — check the CLAUDE.md Routing table for a specs row. If present, honour it (workspace or project). If absent, default to project ($PROJECT/docs/specs/). Unlike earlier skill versions, specs routing IS configurable — projects that keep all methodology artifacts in the workspace should declare specs → workspace in their CLAUDE.md Routing table and specs will be promoted there instead.
$DESIGN_REPO — read from .meta, do NOT re-derive from routing config:
DESIGN_REPO_KEY=$(grep "^design-repo:" "$WORKSPACE/design/.meta" | sed 's/design-repo: //')
case "$DESIGN_REPO_KEY" in
workspace)
DESIGN_REPO="$WORKSPACE" ;;
project)
DESIGN_REPO="$PROJECT" ;;
cross-repo:*)
CROSS_REPO_NAME="${DESIGN_REPO_KEY#cross-repo:}"
CANDIDATE="$(dirname "$PROJECT")/$CROSS_REPO_NAME"
if [ -d "$CANDIDATE/.git" ]; then
DESIGN_REPO="$CANDIDATE"
else
echo "⚠️ Cross-repo path not found at $CANDIDATE — cannot merge journal."
echo "Options: [S]kip journal merge [A]bort close"
# Wait for user response before continuing
fi ;;
*)
echo "⚠️ Unknown design-repo key '$DESIGN_REPO_KEY' — defaulting to project."
DESIGN_REPO="$PROJECT" ;;
esac
$DESIGN_REPO must remain available through Step 8d. Do not recalculate it in subsequent steps.
Step 3b — Pre-close sweep
Before inventorying artifacts, verify the branch leaves nothing behind. Present this checklist:
Pre-close sweep — create before presenting the close plan?
[x] 1 write-content capture any work on this branch worth a diary entry
[x] 2 adr record any significant architectural decisions without a formal ADR
[x] 3 protocol sweep formalise any project rules established or re-enforced this branch
[x] 4 forage sweep check for gotchas, techniques, or undocumented behaviours
[x] 5 update-claude-md sync any new workflow conventions to CLAUDE.md
[x] 6 implementation-doc-sync sync documentation with code changes this branch made
Type numbers to toggle, "all" to toggle all, or "go" to proceed:
Defaults: all six on. The user may deselect any that clearly don't apply (e.g. "go" immediately if the branch was a one-line typo fix). Do not auto-skip — the point is to make the decision explicit.
Run checked items in this order:
- Forage sweep — while context is full; findings may feed the blog entry
- Protocol sweep — while context is full (invoke
protocolskill withSWEEPoperation) - update-claude-md — sync new conventions before doc-sync reads them
- implementation-doc-sync — sync documentation with code changes
- adr — invoke
adrskill for each candidate identified - write-content — last, so it can synthesise the full branch narrative including any forage/protocol submissions
Why this step exists: Step 4 inventories artifacts that were written. Without this sweep, the close plan accurately reports "blog: no new entries" when it should say "blog: no new entries (and none were considered)." The sweep converts the inventory from a snapshot into a verified statement. Only after this step is complete does the close plan accurately reflect what the branch leaves behind.
After all checked items complete, proceed to Step 3c.
Step 3c — Code review (mandatory — HARD GATE)
This step cannot be skipped. Invoke the code-review skill on the branch diff before proceeding. The review covers the full branch — all commits from the base branch to HEAD.
git -C "$PROJECT" diff "$PROJECT_BASE_BRANCH"..HEAD --stat
Invoke code-review with the branch diff. If the review surfaces issues:
- Critical/Important issues → fix before proceeding. Re-run review after fixes.
- Minor issues → fix or note. Do not block on minors alone.
Only proceed to Step 4 after code review passes (no critical/important issues open).
Why here and not at Step 8j: By Step 8j you've already built the close plan, merged the journal, posted specs, and closed issues. Discovering a code problem at that point means unwinding all of that. Reviewing here — before any close machinery runs — means fixes are cheap and the close plan reflects reviewed code.
Step 4 — Inventory artifacts
ls "$WORKSPACE/adr/" 2>/dev/null | grep -v INDEX.md
ls "$WORKSPACE/blog/" 2>/dev/null | grep -v INDEX.md
ls "$WORKSPACE/snapshots/" 2>/dev/null | grep -v INDEX.md
ls "$WORKSPACE/specs/$BRANCH_NAME/" 2>/dev/null
ls "$WORKSPACE/plans/" 2>/dev/null | grep -v "^attic$"
cat "$WORKSPACE/design/JOURNAL.md"
Check whether the blog directory has any entries at all. This only determines whether to run publish-blog — the skill itself handles the "what's new" check by comparing the workspace blog against the destination:
BLOG_HAS_ENTRIES=$(ls "$WORKSPACE/blog/" 2>/dev/null | grep -v INDEX.md | grep -q "\.md$" && echo yes || echo no)
Step 5 — Journal validation
5a — DESIGN.md existence If $DESIGN_REPO/DESIGN.md is missing:
[C]Create from journal entries — journal becomes the initial DESIGN.md content[S]Skip merge entirely
5b — Section heading drift Re-hash H2 headings in $DESIGN_REPO/DESIGN.md. Compare against design-section-hashes in .meta. For each §Section anchor in JOURNAL.md, verify its heading still exists unchanged in DESIGN.md.
grep "^design-section-hashes:" /design/.meta
python3 ~/.claude/skills/project-init/section_hashes.py /DESIGN.md
Use the first command's output as STORED, the second as CURRENT. If drift: [U] update journal anchors, [S] skip drifted sections, [A] abort.
5c — Anchor validation Count ^### .*·.*§ lines vs total ^### lines in JOURNAL.md. If any entries lack anchors: [F] fix via java-update-design, [S] skip merge, [C] continue accepting loss.
5d — Empty journal If no entries at all:
[W]Write retrospective via java-update-design[S]Skip and accept permanent loss
Step 6 — Select specs for GitHub posting
If tracking enabled: list $WORKSPACE/specs/$BRANCH_NAME/, ask which to post as collapsible comments on the GitHub issue. Skip silently if tracking disabled.
Step 7 — Present close plan
Present the plan:
work-end close plan —
Flyway V check ✅ no conflicts
Artifact routing
├── adr/ → project [remote-git]
├── blog/ → workspace [remote-git]
├── specs/ → project [remote-git]
└── snapshots/ → workspace [remote-git]
Plan archiving → plans/attic// [workspace main]
Journal merge → DESIGN.md ( sections)
Spec posting → # ()
Issues → close #
Publish blog → 8g (N unpublished entries → destination)
Project rebase →
Squash /main..HEAD (mandatory before any push)
Fork push → origin/main (mandatory, no skip — fork is always updated first)
Blessed repo → prompt: push / PR / skip (upstream remote, if present)
Approve all, or step by step? (all / step)
The Publish blog line is always shown. publish-blog compares the workspace blog against the destination and publishes only what's missing — it handles the "what's new" check. Do not attempt to pre-count new entries here.
Step 8 — Execute
Failures are reported but do not stop remaining steps, except: journal merge failure prompts the user before continuing to issue close.
8a — Batch workspace-main operations (single main-visit)
Build a comma-separated list of all workspace-routed artifact paths from the Step 4 inventory (blog entries, snapshots, plan files to archive, etc.). Include plan files that need archiving — the script handles mkdir -p and mv to plans/attic/ internally.
Run: python3 ~/.claude/skills/work-end/artifact_promote.py to-workspace-main branch= artifacts= Read PROMOTED= and PUSHED=yes|no from output.
WORKSPACE DESIGN REPO CASE: If $DESIGN_REPO_KEY = workspace, the journal merge must also happen during this main-visit. After the script returns, cherry-pick JOURNAL.md from the epic branch and run the 8d merge steps on workspace main (baseline=$PROJECT_SHA, target=$WORKSPACE/DESIGN.md). Commit the merged DESIGN.md and push. Then 8d is complete for the workspace case — skip the 8d block below.
8b — Project-routed artifact promotion (ADRs, specs)
Build a comma-separated list of all project-routed artifact paths from the Step 4 inventory (ADRs, specs, etc.) — paths relative to the workspace root.
Run: python3 ~/.claude/skills/work-end/artifact_promote.py to-project artifacts= Read PROMOTED= and PUSHED=yes|no from output. If PUSHED=no, report the push failure but continue.
8c — Spec cleanup (only if 8b push exit code wa
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: mdproctor
- Source: mdproctor/cc-praxis
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.