| name | deep-finish |
| description | Use when the user finishes a deep-work session โ merging, opening a PR, keeping the branch as-is, or discarding. Triggers on `/deep-finish`, "finish session", "wrap up session", "์ธ์
๋ง๋ฌด๋ฆฌ", "์ธ์
์ข
๋ฃ", "PR ๋ง๋ค์ด์ค", or orchestrator auto-call after Phase 5 (Integrate) completes. Emits an M3-envelope-wrapped `session-receipt.json` (and optionally a cross-plugin `handoff.json`). Supports `--skip-integrate` (Phase 5 bypass), `--handoff-to=<plugin>` / `--no-handoff` (post-finish handoff control). |
| user-invocable | true |
Invocation
์ด ์คํฌ์ ๋ ๊ฐ์ง ๊ฒฝ๋ก๋ก ํธ์ถ๋ฉ๋๋ค โ ์ด๋ ์ชฝ์ด๋ ๋ณธ SKILL ๋ณธ๋ฌธ์ ์ ์ฐจ๋ฅผ ๊ทธ๋๋ก ์คํํฉ๋๋ค:
- Claude Code ์ฌ๋์ โ ์ฌ์ฉ์๊ฐ
/deep-finish [args...] ์
๋ ฅ (skill ์ user-invocable: true ๊ฐ ์ฌ๋์ ์ง์
์ ํ์ฉ).
- ํ ์์ด์ ํธ / Codex / Copilot CLI / Gemini CLI / SDK โ
Skill({ skill: "deep-work:deep-finish", args: "..." }) ํํ๋ก ๋ช
์ invoke (cross-platform ํ์ค ๊ฒฝ๋ก).
๋ ๊ฒฝ๋ก ๋ชจ๋ args ๋ ๋์ผํ ํ ํฐ ๋ฌธ์์ด๋ก ์ ๋ฌ๋๋ฉฐ, ๋ณธ๋ฌธ ($ARGUMENTS ์๋ฆฌ) ์ ํ์๊ฐ ๋์ผํ๊ฒ ์ฒ๋ฆฌํฉ๋๋ค.
Inputs (skill args)
| ์ธ์ | ์๋ฏธ |
|---|
| (์์) | AskUserQuestion: outcome=merge|pr|keep|discard ๋ถ๊ธฐ |
--skip-integrate | Phase 5 ๊ฐ ์๋ฌ/์ฌ์ฉ์ ์์ฒญ์ผ๋ก ์ค๋จ๋ ๊ฒฝ๋ก โ Section 1c ๋ก ๊ฐ์ ์งํ |
--handoff-to=<plugin> | ์๋ฃ ํ ๋ช
์๋ plugin (deep-evolve / deep-wiki ๋ฑ) ์ผ๋ก fully-automated handoff |
--no-handoff | outcome=merge/pr ์ด์ด๋ handoff ์ ์ ์คํต |
๋น args / ๋งค์นญ๋์ง ์๋ ํ ํฐ โ ๋ณธ๋ฌธ์ default ๋ถ๊ธฐ๋ก ์ง์
.
Prerequisites
์ด entry skill ์ deep-work-orchestrator (Phase dispatch) ๋ฐ deep-work-workflow (reference skill โ Phase ๊ท์ฝ/Exit Gate/M3 envelope) ์ ํจ๊ป ๋์ํฉ๋๋ค. ํ์ฑ deep-work ์ธ์
์ด ์์ ๋๋ ์ธ์
state file (.claude/deep-work.<SESSION_ID>.md) ์ ๋ณ์ (work_dir, current_phase, active_slice ๋ฑ) ๋ฅผ ์ฝ์ด ๋์ํ๋ฉฐ, ์ธ์
์ธ๋ถ์์๋ standalone ์คํ์ด ๊ฐ๋ฅํ ๊ฒฝ์ฐ ๋ณธ๋ฌธ์ ๋ถ๊ธฐ๋ฅผ ๋ฐ๋ฆ
๋๋ค.
Cross-platform self-containment: Claude Code ์์๋ sibling skill ์ด description ๋งค์นญ์ผ๋ก ์๋ ๋ก๋๋ฉ๋๋ค. Codex / Copilot CLI / Gemini CLI / Agent SDK ์์ Skill() ๋ก ํธ์ถ ์ sibling auto-load ๋ณด์ฅ์ด ์ฝํ ์ ์์ผ๋ฏ๋ก, ๋ณธ๋ฌธ์ self-contained ์ผ๋ก ๋ณด์กด๋์ด ์์ต๋๋ค โ state file ํด์, $ARGUMENTS ํ์ฑ, AskUserQuestion ๋ถ๊ธฐ, ์ถ๋ ฅ ํฌ๋งท์ด ์ธ๋ผ์ธ.
Runtime dependencies (cross-platform invokers must provide):
CLAUDE_PLUGIN_ROOT env var โ absolute path to the deep-work plugin root (used by ยง7-Z wrap-receipt-envelope.js and ยง7-Z-A emit-handoff.js).
PROJECT_ROOT env var โ absolute path to the repository root (used by ยง1, ยง7-Z, ยง7-Z-A). Standalone invokers can derive via git rev-parse --show-toplevel.
- Node 20+ on PATH (the two hook scripts above are zero-dep Node CLIs).
- Sibling skill helper at
skills/deep-integrate/phase5-record-error.sh (used by ยง1c when --skip-integrate is taken with a stalled Phase 5).
Internal (v6.3.0) โ orchestrator๊ฐ ์ด ํ์ผ์ ๋ก์ง์ ์ฐธ์กฐํฉ๋๋ค. ์๋ ํธ์ถ์ด ์ฃผ ๊ฒฝ๋ก์ด๋ฉฐ, ์๋ ํธ์ถ๋ ๊ณต์ ๊ฒฝ๋ก์
๋๋ค(ํนํ test ํต๊ณผ ํ ์ธ์
์๋ฃ ์).
์ฐธ์กฐ์ฒ: skills/deep-work-orchestrator/SKILL.md Step 3-6 (Read skills/deep-finish/SKILL.md). skills/deep-test/SKILL.md๊ฐ test pass ํ ์๋ ํธ์ถ์ ์๋ด.
Deep Work Session Completion (v4.1)
Finish the current Deep Work session with an explicit branch completion workflow.
Language
Detect the user's language from their messages or the Claude Code language setting. Output ALL user-facing messages in the detected language. The display templates below use Korean as the reference format โ translate naturally to the user's language while preserving emoji, formatting, and structure.
Instructions
1. Verify session exists
Resolve the current session's state file:
- If
DEEP_WORK_SESSION_ID env var is set โ .claude/deep-work.${DEEP_WORK_SESSION_ID}.md
- If
.claude/deep-work-current-session pointer file exists โ read session ID โ .claude/deep-work.${SESSION_ID}.md
- Legacy fallback โ
.claude/deep-work.local.md
Set $STATE_FILE to the resolved path.
Read $STATE_FILE. If the file doesn't exist:
โน๏ธ ํ์ฑํ๋ Deep Work ์ธ์
์ด ์์ต๋๋ค.
์ ์ธ์
์ ์์ํ๋ ค๋ฉด: /deep-work <์์
์ค๋ช
>
current_phase ๋ถ๊ธฐ (v6.3.0):
current_phase๊ฐ empty โ ์์ ๋์ผ "์ธ์
์์" ๋ฉ์์ง.
finished_at ํ๋ ์กด์ฌ โ ์ด๋ฏธ ์ข
๋ฃ๋ ์ธ์
. "โน๏ธ ์ด ์ธ์
์ ์ด๋ฏธ ์ข
๋ฃ๋์์ต๋๋ค (finished_at: <๊ฐ>). ์ ์ธ์
์ ์์ํ๋ ค๋ฉด /deep-work <์์
>์ ์คํํ์ธ์." โ exit 0. (v6.3.0 review W-R2 โ --skip-integrate ๋ถ๊ธฐ๊ฐ finalized ์ธ์
์ ์ฌ์คํํ์ง ์๋๋ก ์ต์์ ๊ฐ๋)
current_phase == "idle" + phase5_completed_at ํ๋ ์กด์ฌ โ Phase 5 ์๋ฃ ์ํ๋ก ๊ฐ์ฃผํ๊ณ ์ ์ ์งํ (Section 1a, 2, 3... ๊ณ์).
current_phase == "idle" + phase5_completed_at ๋ถ์ฌ:
phase5_entered_at ์กด์ฌ:
$ARGUMENTS์ --skip-integrate ์์ โ ์ ์ ์งํ. Phase 5๊ฐ ์๋ฌ/์ฌ์ฉ์ ์์ฒญ์ผ๋ก ์ค๋จ๋์ด orchestrator๊ฐ ๊ฐ์ finish๋ฅผ ํธ์ถํ ๊ฒฝ๋ก.
v6.3.0 review RC-2 defensive guard: ์๋ WORK_DIR resolve(Section 1 ๋ง๋ฏธ) ์ดํ Section 2๋ก ์ง์
ํ๊ธฐ ์ ์ Section 1c๋ฅผ ์คํํ์ฌ integrate-loop.json์ terminated_by๋ฅผ "error"๋ก defensively ๊ธฐ๋กํ๋ค. ์ด ๋ก์ง์ Section 1a(Phase 5 ํํธ) ๋ค์, Section 2 ์ด์ ์ ๋ฐฐ์นํ๋ค.
--skip-integrate ์์ โ Phase 5๊ฐ ์ค๋จ๋ ์ํ. ๋ฉ์์ง: "Phase 5 Integrate ๋ฃจํ๊ฐ ์ค๋จ๋์์ต๋๋ค. /deep-integrate๋ก ์ฌ์ง์
ํ๊ฑฐ๋ --skip-integrate์ ํจ๊ป /deep-finish๋ฅผ ๋ค์ ์คํํ์ธ์." โ exit 0.
phase5_entered_at ๋ถ์ฌ โ ๊ธฐ์กด "์ธ์
์์" ๋ฉ์์ง.
- ๊ทธ ์ธ (
brainstorm/research/plan/implement/test) โ ์ ์ ์งํ.
Extract: work_dir, task_description, worktree_enabled, worktree_path, worktree_branch, worktree_base_commit.
Resolve $WORK_DIR (used by Section 1a below):
WORK_DIR="${PROJECT_ROOT}/$(read_frontmatter_field "$STATE_FILE" work_dir)"
1a. Phase 5 Integrate ํํธ (v6.3.0, ์ ํ์ )
$WORK_DIR/integrate-loop.json ์กด์ฌ ์ฌ๋ถ ํ์ธ:
-
์กด์ฌ & terminated_by != null โ ์ ์ ์งํ (Section 2๋ก).
-
์กด์ฌ & terminated_by == null:
-
v6.3.0 review Codex P2: $ARGUMENTS์ --skip-integrate ์์ โ prompt ์์ด Section 1c๋ก ์งํ (orchestrator auto-flow๊ฐ ์ง๋ฌธ์ ๋งํ์ง ์๋๋ก).
-
--skip-integrate ์์ โ Phase 5 ๋ฃจํ๊ฐ ์ค๋จ๋ ์ํ (Ctrl-C ๋๋ ์ฌ์ง์
๋๊ธฐ). AskUserQuestion:
โ ๏ธ Phase 5 Integrate ๋ฃจํ๊ฐ ์ค๋จ๋ ์ํ์
๋๋ค.
(1) /deep-integrate๋ก ์ฌ์ง์
(๊ถ์ฅ)
(2) ๊ฐ์ ๋ก ๊ฑด๋๋ฐ๊ณ finish ์งํ (--skip-integrate ์์ด๋)
- (1) ์ ํ โ "exit ํ /deep-integrate ์คํํ์ธ์" + exit 0.
- (2) ์ ํ โ ๊ธฐ์กด ์ ์ฐจ ๊ณ์.
-
๋ถ์ฌ & $ARGUMENTS์ --skip-integrate ์์ โ AskUserQuestion:
โน๏ธ Phase 5 Integrate๋ฅผ ์์ง ์คํํ์ง ์์์ต๋๋ค.
`/deep-integrate`๋ก AI์ ๋ค์ ๋จ๊ณ ์ถ์ฒ์ ๋ฐ์ ์ ์์ต๋๋ค.
(1) /deep-integrate ๋จผ์ ์คํ (๊ถ์ฅ)
(2) Phase 5 ๊ฑด๋๋ฐ๊ณ ๋ฐ๋ก finish ์งํ
-
(1) ์ ํ โ "exit ํ /deep-integrate ์คํํ์ธ์" ์๋ด + exit 0.
-
(2) ์ ํ โ ๊ธฐ์กด ์ ์ฐจ ๊ณ์.
-
$ARGUMENTS์ --skip-integrate ์์ โ ํํธ ์คํตํ๊ณ ๋ฐ๋ก Section 2.
1c. Phase 5 defensive error marker (v6.3.0 review RC-2 + W3-1 + RC4-2)
$ARGUMENTS์ --skip-integrate๊ฐ ์๊ณ Section 1์ ๋ถ๊ธฐ์์ phase5_entered_at์ด ์์ผ๋ phase5_completed_at์ด ์์ด ์ด Section์ ๋๋ฌํ ๊ฒฝ์ฐ์๋ง ์คํํ๋ค. ์ด ์์ ์๋ Section 1 ๋ง๋ฏธ์์ $WORK_DIR๊ฐ ์ด๋ฏธ resolve๋์์ผ๋ฏ๋ก ์๋ helper ํธ์ถ์ด ์ ํจํ๋ค.
LLM์ ์๋ ๋ช
๋ น์ ๊ทธ๋๋ก Bash tool๋ก ๋จ์ผ ํธ์ถํ๋ค (compound ์ฐ์ฐ์ยทshell metacharacter ์์ด ๋จ์ผ ๋ช
๋ น์ด์ด์ผ Phase 5 guard helper exception ์ ์ฉ, RC4-1/RC5-1):
bash skills/deep-integrate/phase5-record-error.sh <ABSOLUTE_WORK_DIR>
์ค์ (v6.3.0 review W5-1): Claude Code์ Bash tool์ ๋งค ํธ์ถ๋ง๋ค ์ shell์ spawnํ๋ฏ๋ก ์ด์ ๋จ๊ณ์์ exportํ $WORK_DIR ๊ฐ์ ๋ณ์๊ฐ persistํ์ง ์๋๋ค. LLM์ state file์์ work_dir์ ๋จผ์ ์ฝ์ด <ABSOLUTE_WORK_DIR> ์๋ฆฌ์ ์ค์ ์ ๋๊ฒฝ๋ก๋ฅผ ์นํ ํ ํธ์ถํ๋ค. literal "$WORK_DIR"๋ฅผ ๊ทธ๋๋ก ์ ๋ฌํ๋ฉด empty string์ผ๋ก ํ์ฅ๋์ด helper๊ฐ usage ์๋ฌ๋ก failํ๋ค.
๋ํ helper๋ state file์ phase5_work_dir_snapshot์ ์ฝ์ด ์ธ์์ ์ผ์นํ๋์ง ๊ฒ์ฆํ๋ฏ๋ก(RC5-3), ์ฌ๋ฐ๋ฅธ ์ธ์
work_dir์ด์ด์ผ ์คํ๋๋ค.
์ด helper๊ฐ integrate-loop.json์ terminated_by๋ฅผ atomically "error"๋ก ๊ต์ฒดํ๊ฑฐ๋, ํ์ผ ๋ถ์ฌ ์ ์ต์ ๊ตฌ์กฐ๋ก ์์ฑํ๋ค.
session-end.sh Stop hook์ terminated_by=interrupted ๋งํน์ ์ฌ์ ํ belt-and-suspenders๋ก ๋จ์์์ด, finish๊ฐ ์คํ๋์ง ์๊ณ ์ธ์
์ด Ctrl-C๋ก ์ข
๋ฃ๋ ๊ฒฝ์ฐ์๋ evidence๊ฐ ๋จ๋๋ค.
2. Read all receipts and generate session receipt
Scan $WORK_DIR/receipts/ for all SLICE-*.json files. For each:
- Count completed (status: "complete") vs total
- Aggregate TDD compliance (strict/relaxed/coaching/override/spike counts)
- Aggregate model usage (haiku/sonnet/opus counts)
- Sum estimated_cost across slices
Generate $WORK_DIR/session-receipt.json (derived cache โ canonical source
is slice receipts) wrapped in the M3 cross-plugin envelope (deep-work
v6.5.0; cf. claude-deep-suite/docs/envelope-migration.md ยง1).
Two-step protocol (v6.5.0): the legacy session-receipt body ("payload")
is built first into a temp file; quality fields (Section 2-1) and
outcome/outcome_ref (Section 7) are appended to that same payload temp
file; only at the end of Section 7 does the wrap helper produce the final
envelope-wrapped session-receipt.json with a single run_id. This avoids
re-generating ULIDs as outcome data lands.
Step 2.1 โ write the session-receipt payload to a temp file
Use the Write tool to emit the payload (legacy session-receipt body) to a
temp path, e.g. $WORK_DIR/.session-receipt.payload.json:
{
"schema_version": "1.0",
"canonical": false,
"derived_from": "receipts/SLICE-*.json",
"session_id": "dw-[timestamp]",
"task_description": "[from state]",
"started_at": "[from state]",
"finished_at": "[now ISO]",
"worktree_branch": "[from state or empty]",
"worktree_base_commit": "[from state or empty]",
"outcome": null,
"outcome_ref": null,
"slices": {
"total": N,
"completed": N,
"spike": N
},
"tdd_compliance": {
"strict": N, "relaxed": N, "override": N, "spike": N, "coaching": N
},
"model_usage": {
"haiku": N, "sonnet": N, "opus": N, "main": N
},
"total_estimated_cost": null,
"total_files_changed": N,
"total_tests": N,
"total_tests_passed": N,
"quality_gates": {
"receipt_completeness": "PASS/FAIL",
"verification_evidence": "PASS/FAIL"
},
"evaluation": {
"evaluator_model": "sonnet",
"plan_review_retries": 0,
"test_retry_count": 0,
"assumption_adjustments": []
},
"contract_compliance": {
"total_contracts": 0,
"contracts_met": 0
},
"deep_work_version": "6.5.0"
}
v6.12.0: Read(../shared/references/model-routing-guide.md#model-routing-state-decode-v612)๋ก
routing carrier๋ฅผ decodeํ๋ค. decoded meta๊ฐ ์์ผ๋ฉด payload์ optional model_routing_meta
ํ๋์ ํฌํจํ๊ณ , ๋ถ์ฌ/์์ ์ ์๋ตํ๋ค. deep-suite payload-registry minor bump๋ suite ์ธก ํ์ ์์
.
schema_version MUST be the literal string "1.0". Section 2-1 will add
quality_score, quality_breakdown, and quality_diagnostics to this same
payload temp file. Section 7's option-specific blocks update
outcome/outcome_ref on the same temp file. The final envelope wrap is
performed by Section 7-Z below โ exactly once per session.
2-1. Calculate Session Quality Score (v5.8 โ 5-component system)
Calculate a quality score (0-100) using the 5-component weighted system with not_applicable proportional redistribution.
Data collection โ read these values from the state file and session artifacts:
- Test Pass Rate (weight: 25%): Read
test_retry_count from state. If 0 retries (passed first try) โ 100. If 1 retry โ 70. If 2 retries โ 40. If 3+ retries โ 10.
- Rework Cycles (weight: 20%): Same as test_retry_count for this metric. Score: 0 retries โ 100, 1 โ 75, 2 โ 50, 3+ โ 20.
- Plan Fidelity (weight: 25%): Read
fidelity_score from state file (written by deep-test drift-check). If not present, default to 80 (assume reasonable fidelity when drift-check wasn't run).
- Sensor Clean Rate (weight: 15%): Read all slice receipts'
sensor_results. Count slices where all sensors are pass or not_applicable. Score: (clean_slices / total_slices_with_sensor_data) * 100. If all slices have sensor_results absent or all statuses are not_applicable โ mark as not_applicable (exclude from denominator).
- Mutation Score (weight: 15%): Read
mutation_testing.score from state file (written by deep-test Section 4-7). If status: "not_applicable" or field absent โ mark as not_applicable (exclude from denominator).
Core Score formula (with not_applicable proportional redistribution):
applicable_weights = sum of weights for components that are NOT not_applicable
score = ฮฃ (component_score ร component_weight) / applicable_weights ร 100
Round to the nearest integer. Clamp to 0-100.
Examples:
- All 5 applicable: score = (tprร0.25 + rwร0.20 + fpร0.25 + scร0.15 + msร0.15)
- Sensor+Mutation not_applicable: score = (tprร0.25 + rwร0.20 + fpร0.25) / 0.70 ร 100
Diagnostic Metrics (informational only โ NOT included in core score):
- Code Efficiency: Read
$WORK_DIR/file-changes.log to count total lines changed. Count total plan items from plan.md. Ratio = lines_changed / plan_items. Score: <50 lines/item โ 100, 50-100 โ 80, 100-200 โ 60, 200+ โ 40.
- Phase Balance: Calculate (research_duration + plan_duration) / total_session_duration. Score: 20-50% โ 100, 10-20% or 50-70% โ 70, <10% or >70% โ 40.
Display:
๐ Session Quality Score: [score]/100
Test Pass Rate: [N]/100 ([detail]) โ weight: 25%
Rework Cycles: [N]/100 ([detail]) โ weight: 20%
Plan Fidelity: [N]/100 โ weight: 25%
Sensor Clean Rate: [N]/100 ([N]/[total] slices) โ weight: 15% [or: N/A (not_applicable)]
Mutation Score: [N]/100 ([N]%) โ weight: 15% [or: N/A (not_applicable)]
Diagnostics (์ฐธ๊ณ ์ฉ):
Code Efficiency: [N]/100 ([detail])
Phase Balance: [N]/100 ([detail])
Persist to session receipt: Add quality_score, quality_breakdown
(object with all 5 component scores + not_applicable flags), and
quality_diagnostics (the 2 diagnostic metrics) to the
$WORK_DIR/.session-receipt.payload.json temp file generated in Step 2.1.
Optional methodology_shadow (v6.11.0)
state์ risk_profile_json์ด ์กด์ฌํ๋ฉด payload์ ๋ค์ optional ๋ธ๋ก์ ์ถ๊ฐํ๋ค (๋ถ์ฌ ์ ์๋ต โ forward-compatible, ์คํค๋ง registry ์ minor ๋ถํ์):
methodology_shadow:
schema_version: 1
risk:
provisional_class: <risk_profile_json.provisional.class ๋๋ null>
authoritative_class: <risk_profile_json.authoritative.class ๋๋ null>
final_score: <authoritative.score ?? provisional.score ?? null>
hard_triggers: <authoritative.hard_triggers์ id ๋ชฉ๋ก ?? provisional ๊ฒ ?? []>
policy:
recommended_profile: <policy_shadow_json.authoritative.profile ?? .provisional.profile ?? null>
based_on: <ํด๋น stage>
routing_diff_count: <์ stage routing_diff์์ excluded_reason ์์ด actual_tier โ recommended_tier์ธ ํญ๋ชฉ ์>
errors_count: <risk_profile_json.errors ๋ฐฐ์ด ๊ธธ์ด>
JSON ํ์ฑ ์คํจ ์ ๊ฒฝ๊ณ 1์ค + ๋ธ๋ก ์๋ต (fail-open).
The final envelope wrap (Section 7-Z) consumes this payload as-is, so any
field placed here ends up under envelope.payload in the wrapped receipt.
Optional methodology_policy and review_execution (v6.12.0)
State์ methodology_policy_json๊ณผ review_execution_json์ ๊ฐ๊ฐ JSON.parseํ๋ค.
์ ํจํ ํ๋๋ง session-receipt payload์ ์๋ optional ๋ธ๋ก์ผ๋ก ์ถ๊ฐํ๊ณ , ๋ถ์ฌ ์
ํด๋น ๋ธ๋ก์ ์๋ตํ๋ค. ํ์ฑ ์คํจ๋ ๊ฒฝ๊ณ 1์ค ํ ๋ธ๋ก๋ง ์๋ตํ๋ fail-open์ด๋ฉฐ ๊ธฐ์กด
methodology_shadow ๋ธ๋ก์ ๊ทธ๋๋ก ์ ์งํ๋ค.
methodology_policy:
schema_version: 1
mode: adaptive
risk_class: high
profile: strict
floors_applied: { implement: { from: standard, to: deep } }
review_execution:
schema_version: 1
points_summary:
final: { mode: dual, completed: 2, failed: 0, rounds: 1, verdict: PASS }
reviewer_failures:
- { point: final, role: executability, channel: codex-cli, reason: timeout, fallback_used: true }
degraded_events: []
risk_acceptances: []
points_summary๋ ๊ฐ point์ mode, ์๋ฃ/์คํจ reviewer ์, rounds, verdict๋ฅผ ์ง๊ณํ๋ค.
reviewer_failures์๋ ์คํจ/timeout/skipped reviewer์ ์ค์ fallback ์ฌ์ฉ ์ฌ๋ถ๋ฅผ
๋ณด์กดํ๋ค. ๋ ๋ธ๋ก์ forward-compatible optional payload์ด๋ฏ๋ก receipt schema version์
๊ณ์ ๋ฌธ์์ด "1.0"์ ์ฌ์ฉํ๋ค.
Authoritative JSONL write: After calculating the quality score, write the finalized session record to harness-sessions.jsonl. This is the authoritative write โ it includes the quality_score field and status: "finalized".
Read assumption snapshot: Read assumption_snapshot from the state file (written at session init by deep-work.md โ see Task 7). Include it in the JSONL entry.
JSONL path: Use the shared path .deep-work/harness-history/harness-sessions.jsonl (NOT the per-session folder). This matches all consumers (deep-status, deep-assumptions, deep-report).
Upsert logic โ use Bash to perform atomic upsert with lock:
JSONL_FILE=".deep-work/harness-history/harness-sessions.jsonl"
LOCKDIR="${JSONL_FILE}.lock.d"
RETRIES=3
while [ "$RETRIES" -gt 0 ]; do
if mkdir "$LOCKDIR" 2>/dev/null; then
break
fi
RETRIES=$((RETRIES - 1)); sleep 0.1
done
if [ -f "$JSONL_FILE" ] && grep -qF "\"session_id\":\"$SESSION_ID\"" "$JSONL_FILE" 2>/dev/null; then
grep -vF "\"session_id\":\"$SESSION_ID\"" "$JSONL_FILE" > "${JSONL_FILE}.tmp" 2>/dev/null
echo "$ENTRY" >> "${JSONL_FILE}.tmp"
mv "${JSONL_FILE}.tmp" "$JSONL_FILE"
else
echo "$ENTRY" >> "$JSONL_FILE"
fi
rmdir "$LOCKDIR" 2>/dev/null || true
The entry JSON includes all existing fields from session-end.sh PLUS: quality_score, quality_breakdown, status: "finalized", and assumption_snapshot.
3. Display session summary
Deep Work ์ธ์
์์ฝ
Task: [task_description]
Branch: [worktree_branch or current branch]
Slices: [completed]/[total] ์๋ฃ
TDD: [strict_count] strict, [override_count] override, [spike_count] spike
Model: haikuร[n] sonnetร[n] opusร[n]
Quality gates: [PASS/FAIL summary]
Quality Score: [score]/100
If any slice has slice_confidence: "done_with_concerns":
Slice Confidence:
โ
done: [N]๊ฐ
โ ๏ธ done_with_concerns: [N]๊ฐ
Concerns:
SLICE-NNN: [concern 1], [concern 2]
SLICE-MMM: [concern 1]
If all slices are done, skip this section.
4. Partial session check
If slices.completed < slices.total:
โ ๏ธ [completed]/[total] ์ฌ๋ผ์ด์ค๋ง ์๋ฃ๋์์ต๋๋ค.
๋ฏธ์๋ฃ ์ฌ๋ผ์ด์ค๊ฐ ์๋ ์ํ์์ ์งํํฉ๋๋ค.
The session receipt will include "partial": true.
4a. Unified finish gate (v6.12)
completion option์ ์ ์ํ๊ธฐ ์ ์ state์ review_execution_json์ parseํ๊ณ
finishGateAllowed(reviewExecutionJson)์ ํธ์ถํ๋ค. ๋ฐํ๊ฐ๋ง ์ธ๋ถ ๋ณ๊ฒฝ ๊ถํ์ ์ ๋ณธ์ด๋ค.
blocking.external_change_lock === true์ด๋ฉด PR/merge/push ์ ์๊ณผ ์คํ์ ๋ชจ๋ ์ฐจ๋จํ๋ค.
blocking.missing_acks์ ๊ฐ review point๋ฅผ ์ฌ์ฉ์์๊ฒ ํ๋ฉดํํ๊ณ ํ์ํ Critical human ack๋ฅผ
๋ฐ์ ๋ค state๋ฅผ ๊ฐฑ์ ํด ํจ์๋ฅผ ๋ค์ ํธ์ถํ๋ค. allowed๊ฐ true๊ฐ ๋๊ธฐ ์ ์๋ Section 5-7๋ก
์งํํ์ง ์๋๋ค. keep/discard์ฒ๋ผ ์ธ๋ถ ๋ณ๊ฒฝ์ ๋ง๋ค์ง ์๋ ์ข
๋ฃ๋ ์ ๊ธ ์ฌ์ ๋ฅผ ์จ๊ธฐ์ง ์๋๋ค.
5. Check gh CLI availability
which gh 2>/dev/null
If gh is not available, the PR option will be marked as unavailable.
6. Present completion options
Use AskUserQuestion:
If worktree_enabled is true:
์ธ์
์ ์ด๋ป๊ฒ ๋ง๋ฌด๋ฆฌํ ๊น์?
1. Merge โ ๋ฒ ์ด์ค ๋ธ๋์น๋ก ๋ณํฉ
2. PR ์์ฑ โ Pull Request ๋ง๋ค๊ธฐ [gh ๋ฏธ์ค์น์: (unavailable โ gh CLI ํ์)]
3. ๋ธ๋์น ์ ์ง โ ๋์ค์ /deep-finish๋ก ๋ค์ ์ ๋ฆฌ
4. ์ญ์ โ ๋ธ๋์น์ worktree ์ญ์
If worktree_enabled is false:
์ธ์
์ ์ด๋ป๊ฒ ๋ง๋ฌด๋ฆฌํ ๊น์?
1. PR ์์ฑ โ Pull Request ๋ง๋ค๊ธฐ [gh ๋ฏธ์ค์น์: (unavailable)]
2. ํ์ฌ ์ํ ์ ์ง โ ์ธ์
๋ง ์ข
๋ฃ
(Merge์ Discard๋ worktree๊ฐ ์์ผ๋ฉด ์ํํ๋ฏ๋ก ๋นํ์ฑํ)
7. Execute chosen option
Option: Merge
- Check for uncommitted changes in worktree:
git -C [worktree_path] status --porcelain
If dirty:
โ ๏ธ Worktree์ ์ปค๋ฐ๋์ง ์์ ๋ณ๊ฒฝ์ด ์์ต๋๋ค.
๋จผ์ ๋ณ๊ฒฝ์ฌํญ์ ์ปค๋ฐํ๊ฑฐ๋ stash ํ์ธ์.
Ask: A) ๋ณ๊ฒฝ์ฌํญ ์ปค๋ฐ ํ ์งํ B) ์ทจ์
- Get base branch from state:
worktree_base_branch (stored at worktree creation time)
- Switch to base:
cd [project_root] && git checkout [worktree_base_branch]
- Merge:
git merge [worktree_branch]
- Merge conflict handling: If merge fails:
โ ๏ธ ์ถฉ๋์ด ๋ฐ์ํ์ต๋๋ค. ์ถฉ๋ ํ์ผ:
[list conflict files]
์๋์ผ๋ก ์ถฉ๋์ ํด๊ฒฐํ ํ /deep-finish๋ฅผ ๋ค์ ์คํํ์ธ์.
Abort: git merge --abort. Stop here.
- On success:
git worktree remove [worktree_path] + git branch -d [worktree_branch]
- Update session-receipt payload: set
outcome: "merge" in
$WORK_DIR/.session-receipt.payload.json (Edit tool โ preserve existing
fields). The envelope wrap happens in Section 7-Z.
Option: PR
- Check
gh is available. If not:
โ ๏ธ gh CLI๊ฐ ํ์ํฉ๋๋ค: https://cli.github.com/
์ค์น ํ `gh auth login`์ผ๋ก ์ธ์ฆํ์ธ์.
Stop here.
- Check
gh auth status. If not authenticated:
โ ๏ธ gh ์ธ์ฆ์ด ํ์ํฉ๋๋ค: `gh auth login`
Stop here.
- Push branch:
git push -u origin [worktree_branch]
- Create PR with session receipt summary as body:
gh pr create --title "deep-work: [task_description]" --body "$(cat <<'EOF'
## Deep Work Session Receipt
- **Slices**: [completed]/[total]
- **TDD compliance**: [summary]
- **Model usage**: [summary]
- **Quality gates**: [summary]
Full receipt: `[work_dir]/session-receipt.json`
EOF
)"
- Worktree is NOT removed (PR review ์ค ์ถ๊ฐ ์์
๊ฐ๋ฅ)
- Update session-receipt payload: set
outcome: "pr",
outcome_ref: [PR URL] in $WORK_DIR/.session-receipt.payload.json (Edit
tool โ preserve existing fields). The envelope wrap happens in Section 7-Z.
Option: Keep
- Update session-receipt payload: set
outcome: "keep" in
$WORK_DIR/.session-receipt.payload.json. The envelope wrap happens in
Section 7-Z.
- Display:
๋ธ๋์น๊ฐ ์ ์ง๋ฉ๋๋ค: [worktree_branch]
๋์ค์ /deep-finish๋ก ๋ค์ ์ ๋ฆฌํ ์ ์์ต๋๋ค.
Option: Discard
- Confirm with AskUserQuestion:
โ ๏ธ ์ ๋ง ์ญ์ ํ์๊ฒ ์ต๋๊น?
๋ธ๋์น: [worktree_branch]
๋ณ๊ฒฝ์ฌํญ์ด ๋ชจ๋ ์ญ์ ๋ฉ๋๋ค.
1. ๋ค, ์ญ์ ํฉ๋๋ค
2. ์๋์ค, ์ทจ์
- If worktree has uncommitted changes:
โ ๏ธ ์ปค๋ฐ๋์ง ์์ ๋ณ๊ฒฝ์ด ์์ต๋๋ค. ๊ฐ์ ๋ก ์ญ์ ํ์๊ฒ ์ต๋๊น?
1. ๊ฐ์ ์ญ์
2. ์ทจ์
- On confirm:
git worktree remove --force [worktree_path] + git branch -D [worktree_branch]
- Update session-receipt payload: set
outcome: "discard" in
$WORK_DIR/.session-receipt.payload.json. The envelope wrap happens in
Section 7-Z.
7-Z. Envelope wrap (v6.5.0 โ runs once, after outcome is decided)
Now that the payload temp file has all fields (Section 2 base + Section 2-1
quality + Section 7 outcome), wrap it in the M3 envelope and write the final
session-receipt.json. Use the Bash tool with the helper script:
Important โ failure semantics: the snippet uses set -euo pipefail so
that any sub-command failure aborts before rm -f runs. The cleanup is
gated with an if/then/else block (equivalent to && for our purposes
under set -e) so that on helper failure the payload temp file is
preserved for retry. To re-attempt a failed wrap, simply re-execute
Section 7-Z (Section 2/2-1/7 do not re-run; the same payload is used).
set -euo pipefail
SESSION_ID="${DEEP_WORK_SESSION_ID:-}"
if [ -z "$SESSION_ID" ] && [ -f "$PROJECT_ROOT/.claude/deep-work-current-session" ]; then
SESSION_ID="$(tr -d '\n\r' < "$PROJECT_ROOT/.claude/deep-work-current-session" || true)"
fi
EVOLVE_PATH=""
if [ -f "$PROJECT_ROOT/.deep-evolve/current.json" ]; then
EVOLVE_SID=$(node -e '
try {
const raw = require("fs").readFileSync(process.argv[1], "utf8");
const obj = JSON.parse(raw);
const v = (obj && typeof obj === "object" && typeof obj.session_id === "string")
? obj.session_id : "";
console.log(v);
} catch (_) { console.log(""); }
' "$PROJECT_ROOT/.deep-evolve/current.json" || true)
if [ -n "$EVOLVE_SID" ] && [ -f "$PROJECT_ROOT/.deep-evolve/$EVOLVE_SID/evolve-insights.json" ]; then
EVOLVE_PATH="$PROJECT_ROOT/.deep-evolve/$EVOLVE_SID/evolve-insights.json"
fi
fi
HARN_PATH=""
if [ -f "$PROJECT_ROOT/.deep-dashboard/harnessability-report.json" ]; then
HARN_PATH="$PROJECT_ROOT/.deep-dashboard/harnessability-report.json"
fi
STATE_FILE_PATH=""
if [ -n "$SESSION_ID" ] && [ -f "$PROJECT_ROOT/.claude/deep-work.$SESSION_ID.md" ]; then
STATE_FILE_PATH="$PROJECT_ROOT/.claude/deep-work.$SESSION_ID.md"
elif [ -f "$PROJECT_ROOT/.claude/deep-work.local.md" ]; then
STATE_FILE_PATH="$PROJECT_ROOT/.claude/deep-work.local.md"
fi
WRAP_ARGS=(
--artifact-kind session-receipt
--payload-file "$WORK_DIR/.session-receipt.payload.json"
--output "$WORK_DIR/session-receipt.json"
--source-artifacts-glob "$WORK_DIR/receipts/SLICE-*.json"
)
[ -n "$SESSION_ID" ] && WRAP_ARGS+=(--session-id "$SESSION_ID")
[ -n "$EVOLVE_PATH" ] && WRAP_ARGS+=(--source-evolve-insights "$EVOLVE_PATH")
[ -n "$HARN_PATH" ] && WRAP_ARGS+=(--source-harnessability "$HARN_PATH")
[ -n "$STATE_FILE_PATH" ] && WRAP_ARGS+=(--session-state-file "$STATE_FILE_PATH")
if node "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/wrap-receipt-envelope.js" "${WRAP_ARGS[@]}"; then
rm -f "$WORK_DIR/.session-receipt.payload.json"
else
echo "wrap helper failed โ preserving $WORK_DIR/.session-receipt.payload.json for retry; re-run Section 7-Z to retry." >&2
exit 1
fi
The helper:
- Generates
envelope.run_id (ULID), sets producer = "deep-work",
artifact_kind = "session-receipt", schema.name = "session-receipt".
- Sets
envelope.parent_run_id from the consumed evolve-insights envelope's
run_id (handoff ยง3.3 cross-plugin chain) when --source-evolve-insights
is passed and the file is itself an envelope.
- Adds slice receipts'
run_id (when envelope-wrapped) plus
harnessability-report.json's run_id to provenance.source_artifacts[]
(intra-plugin chain + multi-source aggregation).
- Test-verification signal (deterministic): when
--session-state-file is
passed, the helper reads the session state's test_passed frontmatter marker
(set by deep-test ยงAll Pass) and stamps x-test-verified: true|false on every
session-receipt payload. This makes the evidence chain carry the deep-test โ
deep-finish verification result in code rather than relying on prompt
compliance. The helper does not rewrite outcome: by the time ยง7-Z runs a
merge/pr is already physically complete (worktree removed + branch -d, or
gh pr create), so demoting it would misreport a done action to
completion-polling / aggregation consumers. The receipt records the fact
(outcome) and the verification signal (x-test-verified) separately โ
downstream consumers judge trustworthiness from the pair.
7-Z-A. Optional cross-plugin handoff emit (v6.6.0 โ M5.7.A)
Ordering: this block runs after Section 7-Z (which wraps the
session-receipt and assigns its run_id) and before Section 8 (state
finalization). It MUST not run before 7-Z โ the handoff payload chains its
parent_run_id to the session-receipt's envelope run_id. If 7-Z fails
(exit 1), skip this block and tell the user to re-run /deep-finish after
resolving the wrap retry.
When the user opts to hand off to another deep-suite plugin (typically
deep-evolve for performance optimization, deep-review for security audit, or
a custom downstream), emit a cross-plugin handoff artifact (handoff.json).
The dashboard's suite.handoff.roundtrip_success_rate metric reads these.
Trigger conditions (LLM decides which path applies):
-
$ARGUMENTS includes --handoff-to=<plugin> (preferred โ fully automated;
no user prompt). Parse the value into HANDOFF_TO.
-
outcome is "merge" or "pr" AND $ARGUMENTS has no --no-handoff AND
the orchestrator is in interactive mode โ AskUserQuestion:
์ธ์
์ ๋ค๋ฅธ ํ๋ฌ๊ทธ์ธ์ ์ธ๊ณํ ๊น์?
1. ์ธ๊ณ ์ํจ โ ์ด๋๋ก ์ข
๋ฃ
2. deep-evolve โ ์ฑ๋ฅ/ํ์ง ์ต์ ํ ๋ฃจํ
3. deep-review โ ๋
๋ฆฝ ์ฝ๋ ๋ฆฌ๋ทฐ
4. (๊ธฐํ) โ ์ง์ ํ๋ฌ๊ทธ์ธ ์ด๋ฆ ์
๋ ฅ
(2)/(3)/(4) โ set HANDOFF_TO accordingly; (1) โ skip this block.
Payload composition (per claude-deep-suite/schemas/handoff.schema.json):
Write $WORK_DIR/.handoff.payload.json with the LLM-composed payload. Pull
fields from the session-receipt + slice receipts + any review reports under
.deep-review/reports/:
{
"schema_version": "1.0",
"handoff_kind": "phase-5-to-evolve",
"from": {
"producer": "deep-work",
"session_id": "<SESSION_ID>",
"phase": "integrate",
"completed_at": "<RFC 3339 โ session-receipt.finished_at>"
},
"to": {
"producer": "<HANDOFF_TO>",
"intent": "<short label โ LLM-derived from session goals or user-supplied>",
"scope_hint": "<optional: src path or module name>"
},
"summary": "<one paragraph: outcome + slice GREEN count + key metric baseline>",
"next_action_brief": "<seed prompt for receiver: WHY + CURRENT STATE + TARGET (measurable)>",
"key_artifacts": [
{ "path": "<work_dir>/session-receipt.json", "kind": "session-receipt", "run_id": "<from 7-Z>" }
],
"completed_actions": ["<from session-receipt + slice receipts>"],
"context_window_state": {
"compacted_at": "<RFC 3339>",
"compaction_strategy": "key-artifacts-only",
"preserved_artifact_paths": ["<work_dir>/session-receipt.json"]
}
}
handoff_kind defaults to phase-5-to-evolve when HANDOFF_TO == "deep-evolve";
use custom + x-handoff-subkind for non-canonical receivers (e.g.
HANDOFF_TO == "deep-review").
Emit:
set -euo pipefail
[ -z "${HANDOFF_TO:-}" ] && exit 0
RECEIPT_PATH="$WORK_DIR/session-receipt.json"
HANDOFF_PAYLOAD="$WORK_DIR/.handoff.payload.json"
HANDOFF_DIR="$PROJECT_ROOT/.deep-work/handoffs"
HANDOFF_OUT="$HANDOFF_DIR/$(date -u +%Y%m%dT%H%M%SZ)-${SESSION_ID:-session}.json"
mkdir -p "$HANDOFF_DIR"
if node "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/emit-handoff.js" \
--payload-file "$HANDOFF_PAYLOAD" \
--output "$HANDOFF_OUT" \
--source-session-receipt "$RECEIPT_PATH" \
${SESSION_ID:+--session-id "$SESSION_ID"}; then
rm -f "$HANDOFF_PAYLOAD"
echo "โ handoff emitted โ $HANDOFF_OUT (receiver: $HANDOFF_TO)"
else
echo "โ ๏ธ handoff emit failed โ payload preserved at $HANDOFF_PAYLOAD for inspection" >&2
fi
The helper:
- Sets
envelope.parent_run_id = session-receipt.envelope.run_id automatically
(closes the intra-plugin chain the dashboard reads for
suite.handoff.roundtrip_success_rate).
- Sets the identity-triplet (
producer = deep-work, artifact_kind = handoff,
schema.name = handoff, schema.version = "1.0") โ required by the
dashboard's unwrapStrict.
- Validates payload required fields before writing โ exit 1 with the missing
field; the temp payload is preserved for retry.
Receiver workflow (informational; not part of /deep-finish):
/deep-evolve --resume-from-handoff .deep-work/handoffs/<file>.json
This block does NOT replace Section 8 โ state finalization still runs.
8. Finalize state
Update $STATE_FILE:
current_phase: "idle"
finished_at: [now ISO]
8a. Unregister session from registry
If the session has a session_id field in the state file:
unregister_session "$SESSION_ID"
Delete the pointer file if it points to this session:
CURRENT_POINTER=$(read_session_pointer)
if [ "$CURRENT_POINTER" = "$SESSION_ID" ]; then
rm -f "$PROJECT_ROOT/.claude/deep-work-current-session"
fi
Display:
โ
Deep Work ์ธ์
์ด ์๋ฃ๋์์ต๋๋ค.
๊ฒฐ๊ณผ: [merge/PR/keep/discard]
Receipt: [work_dir]/session-receipt.json