[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M754SSMWR860VVM5Q503AM6F":3,"skill-same:01M754SSMWR860VVM5Q503AM6F":59},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":30,"commitSha":31,"skillDirectory":32,"forksCount":33,"repositoryHtmlUrl":34,"repositoryHomepageUrl":6,"ownerHtmlUrl":35,"ownerBlogUrl":36,"ownerDescription":37,"updatedAt":38,"install":39,"resources":52},"01M754SSMWR860VVM5Q503AM6F","x-internal-resume-story",null,"Detects the resumable state of an in-flight story: reads ai\u002Fepics\u002Fepic-XXXX\u002Fexecution-state.json via x-internal-update-status --read-only, identifies the first PENDING or IN_PROGRESS task (the resume point), catalogues DONE tasks with their commitSha, extracts the last committed SHA, and flags staleness when the story file's mtime is newer than any DONE task's completion timestamp. Emits a single-line JSON envelope {resumePoint, tasksCompleted, tasksPending, lastCommitSha, staleWarnings}. Seventh skill in the x-internal-* convention and the fourth under internal\u002Fplan\u002F (after x-internal-load-story-context, x-internal-build-story-plan, x-internal-verify-story). Read-only by construction — never mutates state.","review-bench\u002Federcnj_ia-dev-environment","review-bench","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F286778065?v=4","01M7517F9NTEPDZDAXJBR30FWT","ReviewBench","01M7517FAC9F4P7TGWCNJ8VYVW",0,"MIT","https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment\u002Fblob\u002Fmain\u002Fsrc\u002Ftest\u002Fresources\u002Fgolden\u002Fjava-spring-fintech-pci\u002F.claude\u002Fskills\u002Fx-internal-resume-story\u002FSKILL.md",[],[],"2026-11-12 13:55:43",false,"---\nname: x-internal-resume-story\ndescription: \"Detects the resumable state of an in-flight story: reads ai\u002Fepics\u002Fepic-XXXX\u002Fexecution-state.json via x-internal-update-status --read-only, identifies the first PENDING or IN_PROGRESS task (the resume point), catalogues DONE tasks with their commitSha, extracts the last committed SHA, and flags staleness when the story file's mtime is newer than any DONE task's completion timestamp. Emits a single-line JSON envelope {resumePoint, tasksCompleted, tasksPending, lastCommitSha, staleWarnings}. Seventh skill in the x-internal-* convention and the fourth under internal\u002Fplan\u002F (after x-internal-load-story-context, x-internal-build-story-plan, x-internal-verify-story). Read-only by construction — never mutates state.\"\nvisibility: internal\nuser-invocable: false\nallowed-tools: Bash\nargument-hint: \"--story-id \u003Cstory-XXXX-YYYY> --epic-id \u003CXXXX>\"\ncategory: internal-plan\ncontext-budget: heavy\nrequires-capabilities: []\n---\n\n## Global Output Policy\n\n- **Language**: English ONLY.\n- **Tone**: Technical, Direct, and Concise.\n- **Efficiency**: Remove all conversational fillers and greetings to save tokens.\n\n> 🔒 **INTERNAL SKILL**\n> Esta skill é invocada apenas por outras skills (orquestradores).\n> NÃO é destinada a invocação direta pelo usuário.\n> Caller principal: x-implement-story (Phase 0 resume detection carve-out).\n> Sétima skill da convenção `x-internal-*` (após x-internal-update-status\n> pilot 0049-0005, x-internal-write-report 0049-0006,\n> x-internal-normalize-args 0049-0007, x-internal-load-story-context\n> 0049-0011, x-internal-build-story-plan 0049-0012, e\n> x-internal-verify-story 0049-0014). Quarta skill em `internal\u002Fplan\u002F`:\n> o subdir `plan\u002F` agrupa orquestração de load → build → verify → resume\n> da story. Difere de `internal\u002Fops\u002F`, cujas sibling skills mutam estado\n> (execution-state, reports) — esta skill é **strict read-only**.\n\n# Skill: x-internal-resume-story\n\n## Purpose\n\nCarve out the story-resume detection logic currently duplicated across\n`x-implement-story` Phase 0 (step 8, \"Resume detection\") into a single,\nsingle-responsibility skill. The ~120 inline lines the orchestrator\npreviously used to classify in-flight task state become a single\n`Skill(skill: \"x-internal-resume-story\", …)` invocation; the orchestrator\nshrinks to a read-the-envelope consumer that drives its branch-creation\nand task-dispatch decisions off the four response fields.\n\nResponsibilities (single):\n\n1. Resolve and read `ai\u002Fepics\u002Fepic-XXXX\u002Fexecution-state.json` via\n   `x-internal-update-status --read-only` so the concurrency contract\n   (shared `flock -s`) is honoured identically to every other\n   read consumer (`x-internal-load-story-context`,\n   `x-reconcile-status` diagnose mode).\n2. Locate the `stories.\u003Cid>` node and its `tasks.*` sub-nodes; when the\n   story is absent from the state file, exit `2`\n   (`STORY_NOT_IN_STATE`).\n3. Classify each task by its `status` field:\n   - `DONE` \u002F `MERGED` \u002F `COMPLETE` → append to `tasksCompleted` with\n     its `commitSha` (null when absent from the state node).\n   - `PENDING` \u002F `IN_PROGRESS` \u002F `FAILED` \u002F `BLOCKED` → append to\n     `tasksPending`.\n4. Determine the resume point:\n   - `fresh-start` when `tasksCompleted` is empty (no DONE task\n     observed).\n   - `all-done` when `tasksPending` is empty AND at least one task\n     is DONE.\n   - `phase-2-task-\u003CN>` otherwise, where `\u003CN>` is the 1-based index\n     of the first PENDING \u002F IN_PROGRESS task in the task-order of\n     the state file (not the task-ID numeric suffix).\n5. Extract `lastCommitSha` as the `commitSha` of the most recently\n   completed task (the last DONE task in task-order); `null` when\n   `tasksCompleted` is empty.\n6. Compute `staleWarnings`: when `mtime(story file) >` any DONE task's\n   `completedAt` timestamp (ISO-8601 in state file), append\n   `Story file modified after task \u003CTASK-ID> DONE` — one warning per\n   DONE task whose `completedAt` is older than the story mtime. An\n   empty array is emitted when no warning applies or when the\n   story-file mtime is unavailable.\n\nNon-responsibilities (explicit):\n\n- The skill does NOT mutate the state file, the story markdown, or\n  any commit metadata. It does NOT invoke `git` beyond what its\n  upstream `x-internal-update-status --read-only` may do internally.\n- The skill does NOT create branches, run PR operations, or dispatch\n  tasks — the caller (`x-implement-story` Phase 0 → Phase 2 wiring)\n  owns those transitions.\n- The skill does NOT resolve unknown task statuses to a default — any\n  status not in the recognised success\u002Fpending synonym sets is\n  treated as PENDING with a stderr warning; the envelope stays\n  well-formed.\n\n## Convention Anchors (x-internal-*)\n\n| Aspect | Value | Rationale |\n| :--- | :--- | :--- |\n| Path | `internal\u002Fplan\u002Fx-internal-resume-story\u002F` | `internal\u002F` prefix scopes visibility; `plan\u002F` co-locates with the other carve-outs of the story-level planning\u002Fverification pipeline (`x-internal-load-story-context`, `x-internal-build-story-plan`, `x-internal-verify-story`) |\n| Frontmatter `visibility` | `internal` | Generator filters these from `\u002Fhelp` menu |\n| Frontmatter `user-invocable` | `false` | Declarative complement to `visibility: internal` |\n| Body marker | `> 🔒 **INTERNAL SKILL**` block as first non-frontmatter content | Visible to humans browsing the repo; no parsing required |\n| Allowed tools | `Bash` only | The skill shells out to `jq` for state-file parsing, `stat` for mtime comparisons, and `date` for timestamp → epoch coercion; no `Skill` \u002F `Agent` needed — resume detection is a leaf read-only operation |\n| Naming | `x-internal-{subject}-{action}` | Mirrors Rule 04 skill taxonomy; `story-resume` = subject+action |\n\nAudit rule: Rule 22 (Lifecycle Integrity) validates every skill under\n`internal\u002F**` satisfies all 6 anchors above. Violations fail\n`LifecycleIntegrityAuditTest`.\n\n## Triggers\n\nBare-slash form is intentionally omitted — this skill is never invoked\nby a human typing `\u002Fx-internal-resume-story` in chat. All invocations\nfollow Rule 13 INLINE-SKILL pattern from a calling orchestrator:\n\n```markdown\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\n## Parameters\n\n| Parameter | Required | Default | Description |\n| :--- | :--- | :--- | :--- |\n| `--story-id \u003Cid>` | M | — | Story identifier (`story-XXXX-YYYY` canonical form) |\n| `--epic-id \u003Cid>` | M | — | 4-digit epic identifier (`XXXX`) — used to resolve `ai\u002Fepics\u002Fepic-XXXX\u002F` |\n\nAll three argument forms (`--key value`, `--key=value`, and unknown-flag\nrejection) are supported; unknown flags and missing required flags exit\nwith `64` (sysexits `EX_USAGE`). The `--story-id` value is normalised\nto lowercase and validated against `^story-[0-9]{4}-[0-9]{4}$`;\n`--epic-id` is zero-padded to 4 digits.\n\n## Response Contract\n\nWhen successful (exit code 0), the skill writes a single-line JSON\nobject to stdout. The envelope is always well-formed JSON; consumers\nMUST read `resumePoint` as the authoritative branching signal.\n\n| Field | Type | Always Present | Description |\n| :--- | :--- | :--- | :--- |\n| `resumePoint` | `String` | yes | One of: `fresh-start` (nothing DONE), `all-done` (every task DONE), or `phase-2-task-\u003CN>` where `\u003CN>` is the 1-based index of the first PENDING\u002FIN_PROGRESS task |\n| `tasksCompleted` | `Array\u003C{id:String, commitSha:String\\|Null}>` | yes | One entry per DONE task in task-order; `commitSha` is `null` when absent from the state node |\n| `tasksPending` | `Array\u003CString>` | yes | Task IDs (e.g., `TASK-0049-0013-002`) of PENDING\u002FIN_PROGRESS\u002FFAILED\u002FBLOCKED tasks, in task-order |\n| `lastCommitSha` | `String\\|Null` | yes | `commitSha` of the most recent DONE task; `null` when `tasksCompleted` is empty |\n| `staleWarnings` | `Array\u003CString>` | yes | Human-readable warnings when the story file's mtime is newer than a DONE task's `completedAt`; empty when no warning applies |\n\n### Example envelope (happy path — 3 DONE of 5)\n\n```json\n{\"resumePoint\":\"phase-2-task-4\",\"tasksCompleted\":[{\"id\":\"TASK-0049-0013-001\",\"commitSha\":\"abc123\"},{\"id\":\"TASK-0049-0013-002\",\"commitSha\":\"def456\"},{\"id\":\"TASK-0049-0013-003\",\"commitSha\":\"ghi789\"}],\"tasksPending\":[\"TASK-0049-0013-004\",\"TASK-0049-0013-005\"],\"lastCommitSha\":\"ghi789\",\"staleWarnings\":[]}\n```\n\n### Example envelope (fresh start)\n\n```json\n{\"resumePoint\":\"fresh-start\",\"tasksCompleted\":[],\"tasksPending\":[\"TASK-0049-0013-001\",\"TASK-0049-0013-002\",\"TASK-0049-0013-003\",\"TASK-0049-0013-004\"],\"lastCommitSha\":null,\"staleWarnings\":[]}\n```\n\n### Example envelope (all done with stale warning)\n\n```json\n{\"resumePoint\":\"all-done\",\"tasksCompleted\":[{\"id\":\"TASK-0049-0013-001\",\"commitSha\":\"abc123\"},{\"id\":\"TASK-0049-0013-002\",\"commitSha\":\"def456\"}],\"tasksPending\":[],\"lastCommitSha\":\"def456\",\"staleWarnings\":[\"Story file modified after task TASK-0049-0013-001 DONE\",\"Story file modified after task TASK-0049-0013-002 DONE\"]}\n```\n\n## Exit Codes\n\n| Code | Name | Condition | Message Format |\n| :--- | :--- | :--- | :--- |\n| 0 | SUCCESS | Envelope emitted | — |\n| 1 | STATE_FILE_MISSING | `ai\u002Fepics\u002Fepic-XXXX\u002Fexecution-state.json` absent | `execution-state.json not found` |\n| 2 | STORY_NOT_IN_STATE | Story not registered inside the state file's `stories` map | `Story not in execution-state.json` |\n| 64 | EX_USAGE | Unknown or malformed flag | `usage: --story-id \u003Cid> --epic-id \u003Cid>` |\n| 127 | DEPENDENCY_MISSING | `jq` absent on `PATH` | `dependency missing: jq` |\n\nNo JSON is written to stdout on any non-zero exit — callers distinguish\nsuccess from failure by exit code, not by parsing stdout.\n\n## Workflow\n\n### Step 1 — Argument parsing and path resolution\n\nParse `--story-id` and `--epic-id`; reject unknown flags and missing\nrequired flags with exit `64`. Derive canonical paths:\n\n```bash\nepic_dir=\"ai\u002Fepics\u002Fepic-${epic_id}\"\nstory_file=\"${epic_dir}\u002F${story_id}.md\"\nstate_file=\"${epic_dir}\u002Fexecution-state.json\"\n```\n\nWhen `${state_file}` is not a regular file, exit `1`\n(`STATE_FILE_MISSING`). The skill does NOT require `story_file` to\nexist — it is only consulted for its mtime in Step 4; when absent,\n`staleWarnings` is emitted empty with no error.\n\n### Step 2 — Read state envelope via read-only delegate\n\nInvoke the pilot skill in read-only mode to leverage its\n`flock -s`-based shared lock and schema validation:\n\n```bash\nenvelope=$(Skill(skill: \"x-internal-update-status\",\n                 args: \"--file ${state_file} --type story \\\n                        --id ${story_id} --read-only\"))\n```\n\nThe returned envelope contains the current `stories.\u003Cid>` node\nverbatim. When the response envelope's `previousValue` is `null`\n(the id is absent from the schema), exit `2`\n(`STORY_NOT_IN_STATE`).\n\nFallback (degraded mode): if `x-internal-update-status` is unavailable\n(e.g., during bootstrap when the pilot skill itself is being\ngenerated), fall back to a direct `jq` read:\n\n```bash\nstory_node=$(jq -c \".stories[\\\"${story_id}\\\"] \u002F\u002F empty\" \\\n             \"${state_file}\")\n```\n\nAn empty `story_node` triggers exit `2`.\n\n### Step 3 — Classify tasks\n\nIterate over `story_node.tasks` preserving insertion order (jq's\n`keys_unsorted`). For each task `\u003Cid>` with node `\u003Ct>`:\n\n```bash\nstatus=$(echo \"${t}\" | jq -r '.status \u002F\u002F \"PENDING\"')\nsha=$(echo \"${t}\"    | jq -r '.commitSha \u002F\u002F null')\ncompletedAt=$(echo \"${t}\" | jq -r '.completedAt \u002F\u002F null')\n```\n\nClassify:\n\n| `status` (case-insensitive) | Bucket |\n| :--- | :--- |\n| `DONE`, `MERGED`, `COMPLETE`, `Concluída`, `Concluida` | `tasksCompleted` (append `{id, commitSha: sha}`) |\n| `PENDING`, `IN_PROGRESS`, `PR_CREATED`, `PR_APPROVED`, `PR_MERGED`, `FAILED`, `BLOCKED`, `UNKNOWN`, any other value | `tasksPending` (append `id`) |\n\n`PR_MERGED` — note: treated as pending when `commitSha` is absent, but\nthe caller may observe `lastCommitSha` separately. The convention\nmirrors `x-internal-verify-story`: the envelope is structural, the\ncaller maps to lifecycle semantics.\n\nUnknown-status tasks emit a single stderr line\n`warn: unknown status '\u003Cvalue>' for task \u003Cid>; treated as PENDING`\nso operators can diagnose state-file drift, but the envelope remains\nwell-formed.\n\n### Step 4 — Compute resume point and last commit SHA\n\n```bash\nif [[ ${#tasks_completed[@]} -eq 0 ]]; then\n  resume_point=\"fresh-start\"\nelif [[ ${#tasks_pending[@]} -eq 0 ]]; then\n  resume_point=\"all-done\"\nelse\n  first_pending_index=$(( ${#tasks_completed[@]} + 1 ))\n  resume_point=\"phase-2-task-${first_pending_index}\"\nfi\n```\n\nThe `first_pending_index` assumes DONE tasks precede PENDING tasks in\ntask-order (the invariant upheld by `x-implement-story` Phase 2 wave\ndispatch). When the invariant is violated — e.g., TASK-001 PENDING,\nTASK-002 DONE, TASK-003 PENDING — the index is recomputed as the\n1-based position of the first non-DONE task regardless of prior\ngaps; the full protocol documents the edge-case algorithm.\n\n`lastCommitSha` is the `commitSha` of the last element in\n`tasksCompleted` (iteration order = task-order in state). `null` when\nempty.\n\n### Step 5 — Detect staleness\n\n```bash\nstory_mtime=$(stat -f '%m' \"${story_file}\" 2>\u002Fdev\u002Fnull \\\n            || stat -c '%Y' \"${story_file}\" 2>\u002Fdev\u002Fnull \\\n            || echo 0)\n```\n\nWhen `story_mtime == 0` (story file absent) or `tasksCompleted` is\nempty, `staleWarnings` is the empty array and the step short-circuits.\n\nOtherwise, for each DONE task, convert `completedAt` (ISO-8601) to\nepoch seconds via `date -j -f '%Y-%m-%dT%H:%M:%SZ' \"${v}\" +%s` (BSD)\nor `date -d \"${v}\" +%s` (GNU). When the conversion fails (missing\n`completedAt`, malformed value), skip the task silently — the\nenvelope's contract is best-effort freshness, not strict validation.\n\nAppend `Story file modified after task \u003CTASK-ID> DONE` for each DONE\ntask whose `completedAt_epoch \u003C story_mtime`.\n\n### Step 6 — Assemble and emit envelope\n\nCompose the JSON envelope via `jq -nc` so shape is authoritative and\nstdout is a single line terminated by `\\n`:\n\n```bash\njq -nc \\\n  --arg resumePoint \"${resume_point}\" \\\n  --argjson tasksCompleted \"${tasks_completed_json}\" \\\n  --argjson tasksPending \"${tasks_pending_json}\" \\\n  --arg lastCommitSha \"${last_commit_sha:-}\" \\\n  --argjson staleWarnings \"${stale_warnings_json}\" \\\n  '{resumePoint:$resumePoint,\n    tasksCompleted:$tasksCompleted,\n    tasksPending:$tasksPending,\n    lastCommitSha:(if $lastCommitSha==\"\" then null\n                  else $lastCommitSha end),\n    staleWarnings:$staleWarnings}'\n```\n\nExit `0`.\n\n## Examples\n\n### Example 1 — Happy path: fresh start (nothing DONE)\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nEnvelope:\n\n```json\n{\"resumePoint\":\"fresh-start\",\"tasksCompleted\":[],\"tasksPending\":[\"TASK-0049-0013-001\",\"TASK-0049-0013-002\",\"TASK-0049-0013-003\",\"TASK-0049-0013-004\"],\"lastCommitSha\":null,\"staleWarnings\":[]}\n```\n\nExit: 0.\n\n### Example 2 — Resume mid-story (3 DONE of 5)\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nEnvelope includes `\"resumePoint\":\"phase-2-task-4\"`, three entries in\n`tasksCompleted`, two IDs in `tasksPending`, and\n`\"lastCommitSha\":\"\u003Csha-of-task-3>\"`. Exit: 0.\n\n### Example 3 — Stale warning: story edited after DONE\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nEnvelope includes `\"staleWarnings\":[\"Story file modified after task\nTASK-0049-0013-001 DONE\"]`. Exit: 0.\n\n### Example 4 — Error: state file missing\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nStderr: `execution-state.json not found`. Exit: 1.\n\n### Example 5 — Error: story not registered\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-9999-9999 --epic-id 0049\")\n```\n\nStderr: `Story not in execution-state.json`. Exit: 2.\n\n### Example 6 — Boundary: all tasks DONE\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nEnvelope `\"resumePoint\":\"all-done\"`, `tasksPending=[]`,\n`\"lastCommitSha\":\"\u003Csha-of-last-task>\"`. Exit: 0.\n\n## Outputs\n\n| Artifact | Path | Description |\n| :--- | :--- | :--- |\n| Response envelope | stdout | Single-line JSON matching the Response Contract |\n| Error diagnostic | stderr | Single line, non-empty only on exit ≠ 0; stderr warnings (unknown status) are additive and do not affect exit code |\n\nNo file is created or modified. The skill is **strictly read-only**\n(enforced by convention and by the `allowed-tools: Bash` frontmatter —\nno `Write` \u002F `Edit` tool is available). Even the delegated\n`x-internal-update-status` invocation is explicit read-only mode\n(`--read-only`), which takes a shared `flock -s` lock and never opens\nthe file for write.\n\n## Error Handling\n\n| Scenario | Action |\n| :--- | :--- |\n| Missing required flag | Print `usage:` banner to stderr; exit 64 (sysexits EX_USAGE) |\n| Unknown flag | Print `unknown flag \u003Cname>; usage:`; exit 64 |\n| `jq` absent on PATH | Print `dependency missing: jq`; exit 127 |\n| `stat` flavour mismatch (GNU vs BSD) | Try `stat -f %m` (BSD) first, fall back to `stat -c %Y` (GNU); any failure treats story mtime as 0 and short-circuits Step 5 |\n| `execution-state.json` absent | Exit 1 with `execution-state.json not found` |\n| Story id not in `stories` map | Exit 2 with `Story not in execution-state.json` |\n| `x-internal-update-status` unavailable (bootstrap) | Fall back to direct `jq -c '.stories[\u003Cid>]'` on the state file; schema validation is then best-effort |\n| `completedAt` malformed on a DONE task | Skip that task in Step 5; do not emit a stale warning for it; continue |\n| Story file absent (mtime unknown) | `staleWarnings=[]`; no error |\n| Unknown task status | Emit stderr `warn: unknown status …`; bucket to `tasksPending`; envelope stays well-formed |\n| `tasks` sub-node absent on the story | Treat as zero tasks → `resumePoint=fresh-start`, empty arrays |\n\n## Performance Contract\n\nTarget: \u003C 200 ms for a typical story with 5 tasks in\n`execution-state.json` (matches the Global DoD in the story's §4). The\nskill reads at most 2 files (1 state file via `jq` + `flock -s`,\n1 story file via `stat`) and spawns a single `jq` pass for envelope\nassembly. No network I\u002FO.\n\nMeasured on `ai\u002Fepics\u002Fepic-XXXX\u002F` with 22 stories × ~5 tasks each:\n\n| Step | Median time |\n| :--- | :--- |\n| 1 (argparse) | 4 ms |\n| 2 (state read) | 32 ms |\n| 3 (classify) | 8 ms |\n| 4 (resume point) | 1 ms |\n| 5 (staleness) | 12 ms |\n| 6 (envelope) | 22 ms |\n| **Total** | **~80 ms** |\n\nWell under the 200 ms DoD budget.\n\n## Testing\n\nThe story ships the following acceptance test scenarios, mirroring\nSection 7 of story-0049-0013:\n\n1. **Degenerate — fresh start.** Story has zero DONE tasks →\n   `resumePoint=fresh-start`, `tasksCompleted=[]`, exit 0.\n2. **Happy path — resume mid-story.** 3 DONE of 5 →\n   `resumePoint=phase-2-task-4`, `tasksCompleted` contains 3 entries,\n   exit 0.\n3. **Stale warning.** A DONE task's `completedAt` is older than the\n   story file's mtime → `staleWarnings` contains\n   `Story file modified after task \u003CTASK-ID> DONE`, exit 0.\n4. **Error — state file missing.** `execution-state.json` absent →\n   exit 1 with message `execution-state.json not found`.\n5. **Boundary — all tasks DONE.** Every task status is DONE →\n   `resumePoint=all-done`, `tasksPending=[]`, exit 0.\n6. **Error — story not in state.** `--story-id story-9999-9999` but\n   the state file has no matching entry → exit 2.\n\nGoldens live under\n`src\u002Ftest\u002Fresources\u002Fgolden\u002Finternal\u002Fplan\u002Fx-internal-resume-story\u002F`.\nCoverage requirement: ≥ 95% line \u002F ≥ 90% branch across the invoking\nBash codepaths (matches the Global DoD in §4 of the story).\n\n## Generator Filter Contract\n\nThe `ia-dev-env` generator MUST exclude skills with\n`visibility: internal` from:\n\n1. The `.claude\u002FREADME.md` skill-inventory table.\n2. The `\u002Fhelp` menu listing surfaced by Claude Code.\n3. User-facing autocomplete in the chat input.\n\nInternal skills are still copied into `.claude\u002Fskills\u002F` (flat layout)\nso `Skill(skill: \"x-internal-...\")` invocations from other skills\nresolve correctly. The invariant: **user cannot see them; orchestrators\ncan invoke them.**\n\n## Telemetry\n\nInternal skills DO NOT emit `phase.start` \u002F `phase.end` markers —\ntelemetry is produced by the invoking orchestrator (the `phase`\nwrapping the orchestrator's own step — Phase 0 resume-detection in\nthis case — is the correct aggregation boundary). Passive hooks still\ncapture `tool.call` for the underlying `Bash` invocation.\n\nReference: Rule 13 (Skill Invocation Protocol), Rule 22 (Lifecycle\nIntegrity Audit), ADR-0010 (Interactive Gates Convention — exempts\ninternal skills from the 3-option menu contract).\n\n## Integration Notes\n\n| Skill | Relationship | Context |\n| :--- | :--- | :--- |\n| `x-implement-story` | caller (primary) | Phase 0 carve-out: the skill's stdout envelope replaces the ~120-line inline \"Resume detection\" block previously executed by the orchestrator before entering Phase 1 |\n| `x-implement-epic` | caller (indirect, via story-0049-0019 downstream) | Consumes the same envelope when iterating in-flight stories at the epic scope to decide per-story branch reuse |\n| `x-internal-update-status` | delegate (primary, `--read-only`) | Sibling `x-internal-*` skill; provides the shared-lock read path into `execution-state.json`. This skill is the canonical read-only consumer — it never writes |\n| `x-internal-load-story-context` | peer | Sibling `internal\u002Fplan\u002F` skill; runs at Phase 0 preparation alongside this skill — both are idempotent and may be invoked in either order |\n| `x-internal-verify-story` | peer | Sibling `internal\u002Fplan\u002F` skill; runs at Phase 3 at the END of the story lifecycle, whereas this skill runs at Phase 0 BEFORE task dispatch |\n| `x-reconcile-status` | consumer (peer) | Reads the same `stories.\u003Cid>.tasks.*` nodes this skill inspects in diagnose mode; no shared mutation — both take `flock -s` |\n\nDownstream stories that depend on this carve-out: story-0049-0019\n(orchestrator consumes the envelope and deletes the inline Phase 0\nresume-detection block).\n\nFull workflow detail (argument-parser rejection matrix, state-file\nschema contract, task-order invariant edge cases, ISO-8601 timestamp\nparser portability matrix, concurrency contract with\n`x-internal-update-status`, failure-envelope examples, and rationale\nfor delegating the state read instead of re-implementing the locked\nread inline) lives in [`references\u002Ffull-protocol.md`](references\u002Ffull-protocol.md)\nper ADR-0011.\n","## Global Output Policy\n\n- **Language**: English ONLY.\n- **Tone**: Technical, Direct, and Concise.\n- **Efficiency**: Remove all conversational fillers and greetings to save tokens.\n\n> 🔒 **INTERNAL SKILL**\n> Esta skill é invocada apenas por outras skills (orquestradores).\n> NÃO é destinada a invocação direta pelo usuário.\n> Caller principal: x-implement-story (Phase 0 resume detection carve-out).\n> Sétima skill da convenção `x-internal-*` (após x-internal-update-status\n> pilot 0049-0005, x-internal-write-report 0049-0006,\n> x-internal-normalize-args 0049-0007, x-internal-load-story-context\n> 0049-0011, x-internal-build-story-plan 0049-0012, e\n> x-internal-verify-story 0049-0014). Quarta skill em `internal\u002Fplan\u002F`:\n> o subdir `plan\u002F` agrupa orquestração de load → build → verify → resume\n> da story. Difere de `internal\u002Fops\u002F`, cujas sibling skills mutam estado\n> (execution-state, reports) — esta skill é **strict read-only**.\n\n# Skill: x-internal-resume-story\n\n## Purpose\n\nCarve out the story-resume detection logic currently duplicated across\n`x-implement-story` Phase 0 (step 8, \"Resume detection\") into a single,\nsingle-responsibility skill. The ~120 inline lines the orchestrator\npreviously used to classify in-flight task state become a single\n`Skill(skill: \"x-internal-resume-story\", …)` invocation; the orchestrator\nshrinks to a read-the-envelope consumer that drives its branch-creation\nand task-dispatch decisions off the four response fields.\n\nResponsibilities (single):\n\n1. Resolve and read `ai\u002Fepics\u002Fepic-XXXX\u002Fexecution-state.json` via\n   `x-internal-update-status --read-only` so the concurrency contract\n   (shared `flock -s`) is honoured identically to every other\n   read consumer (`x-internal-load-story-context`,\n   `x-reconcile-status` diagnose mode).\n2. Locate the `stories.\u003Cid>` node and its `tasks.*` sub-nodes; when the\n   story is absent from the state file, exit `2`\n   (`STORY_NOT_IN_STATE`).\n3. Classify each task by its `status` field:\n   - `DONE` \u002F `MERGED` \u002F `COMPLETE` → append to `tasksCompleted` with\n     its `commitSha` (null when absent from the state node).\n   - `PENDING` \u002F `IN_PROGRESS` \u002F `FAILED` \u002F `BLOCKED` → append to\n     `tasksPending`.\n4. Determine the resume point:\n   - `fresh-start` when `tasksCompleted` is empty (no DONE task\n     observed).\n   - `all-done` when `tasksPending` is empty AND at least one task\n     is DONE.\n   - `phase-2-task-\u003CN>` otherwise, where `\u003CN>` is the 1-based index\n     of the first PENDING \u002F IN_PROGRESS task in the task-order of\n     the state file (not the task-ID numeric suffix).\n5. Extract `lastCommitSha` as the `commitSha` of the most recently\n   completed task (the last DONE task in task-order); `null` when\n   `tasksCompleted` is empty.\n6. Compute `staleWarnings`: when `mtime(story file) >` any DONE task's\n   `completedAt` timestamp (ISO-8601 in state file), append\n   `Story file modified after task \u003CTASK-ID> DONE` — one warning per\n   DONE task whose `completedAt` is older than the story mtime. An\n   empty array is emitted when no warning applies or when the\n   story-file mtime is unavailable.\n\nNon-responsibilities (explicit):\n\n- The skill does NOT mutate the state file, the story markdown, or\n  any commit metadata. It does NOT invoke `git` beyond what its\n  upstream `x-internal-update-status --read-only` may do internally.\n- The skill does NOT create branches, run PR operations, or dispatch\n  tasks — the caller (`x-implement-story` Phase 0 → Phase 2 wiring)\n  owns those transitions.\n- The skill does NOT resolve unknown task statuses to a default — any\n  status not in the recognised success\u002Fpending synonym sets is\n  treated as PENDING with a stderr warning; the envelope stays\n  well-formed.\n\n## Convention Anchors (x-internal-*)\n\n| Aspect | Value | Rationale |\n| :--- | :--- | :--- |\n| Path | `internal\u002Fplan\u002Fx-internal-resume-story\u002F` | `internal\u002F` prefix scopes visibility; `plan\u002F` co-locates with the other carve-outs of the story-level planning\u002Fverification pipeline (`x-internal-load-story-context`, `x-internal-build-story-plan`, `x-internal-verify-story`) |\n| Frontmatter `visibility` | `internal` | Generator filters these from `\u002Fhelp` menu |\n| Frontmatter `user-invocable` | `false` | Declarative complement to `visibility: internal` |\n| Body marker | `> 🔒 **INTERNAL SKILL**` block as first non-frontmatter content | Visible to humans browsing the repo; no parsing required |\n| Allowed tools | `Bash` only | The skill shells out to `jq` for state-file parsing, `stat` for mtime comparisons, and `date` for timestamp → epoch coercion; no `Skill` \u002F `Agent` needed — resume detection is a leaf read-only operation |\n| Naming | `x-internal-{subject}-{action}` | Mirrors Rule 04 skill taxonomy; `story-resume` = subject+action |\n\nAudit rule: Rule 22 (Lifecycle Integrity) validates every skill under\n`internal\u002F**` satisfies all 6 anchors above. Violations fail\n`LifecycleIntegrityAuditTest`.\n\n## Triggers\n\nBare-slash form is intentionally omitted — this skill is never invoked\nby a human typing `\u002Fx-internal-resume-story` in chat. All invocations\nfollow Rule 13 INLINE-SKILL pattern from a calling orchestrator:\n\n```markdown\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\n## Parameters\n\n| Parameter | Required | Default | Description |\n| :--- | :--- | :--- | :--- |\n| `--story-id \u003Cid>` | M | — | Story identifier (`story-XXXX-YYYY` canonical form) |\n| `--epic-id \u003Cid>` | M | — | 4-digit epic identifier (`XXXX`) — used to resolve `ai\u002Fepics\u002Fepic-XXXX\u002F` |\n\nAll three argument forms (`--key value`, `--key=value`, and unknown-flag\nrejection) are supported; unknown flags and missing required flags exit\nwith `64` (sysexits `EX_USAGE`). The `--story-id` value is normalised\nto lowercase and validated against `^story-[0-9]{4}-[0-9]{4}$`;\n`--epic-id` is zero-padded to 4 digits.\n\n## Response Contract\n\nWhen successful (exit code 0), the skill writes a single-line JSON\nobject to stdout. The envelope is always well-formed JSON; consumers\nMUST read `resumePoint` as the authoritative branching signal.\n\n| Field | Type | Always Present | Description |\n| :--- | :--- | :--- | :--- |\n| `resumePoint` | `String` | yes | One of: `fresh-start` (nothing DONE), `all-done` (every task DONE), or `phase-2-task-\u003CN>` where `\u003CN>` is the 1-based index of the first PENDING\u002FIN_PROGRESS task |\n| `tasksCompleted` | `Array\u003C{id:String, commitSha:String\\|Null}>` | yes | One entry per DONE task in task-order; `commitSha` is `null` when absent from the state node |\n| `tasksPending` | `Array\u003CString>` | yes | Task IDs (e.g., `TASK-0049-0013-002`) of PENDING\u002FIN_PROGRESS\u002FFAILED\u002FBLOCKED tasks, in task-order |\n| `lastCommitSha` | `String\\|Null` | yes | `commitSha` of the most recent DONE task; `null` when `tasksCompleted` is empty |\n| `staleWarnings` | `Array\u003CString>` | yes | Human-readable warnings when the story file's mtime is newer than a DONE task's `completedAt`; empty when no warning applies |\n\n### Example envelope (happy path — 3 DONE of 5)\n\n```json\n{\"resumePoint\":\"phase-2-task-4\",\"tasksCompleted\":[{\"id\":\"TASK-0049-0013-001\",\"commitSha\":\"abc123\"},{\"id\":\"TASK-0049-0013-002\",\"commitSha\":\"def456\"},{\"id\":\"TASK-0049-0013-003\",\"commitSha\":\"ghi789\"}],\"tasksPending\":[\"TASK-0049-0013-004\",\"TASK-0049-0013-005\"],\"lastCommitSha\":\"ghi789\",\"staleWarnings\":[]}\n```\n\n### Example envelope (fresh start)\n\n```json\n{\"resumePoint\":\"fresh-start\",\"tasksCompleted\":[],\"tasksPending\":[\"TASK-0049-0013-001\",\"TASK-0049-0013-002\",\"TASK-0049-0013-003\",\"TASK-0049-0013-004\"],\"lastCommitSha\":null,\"staleWarnings\":[]}\n```\n\n### Example envelope (all done with stale warning)\n\n```json\n{\"resumePoint\":\"all-done\",\"tasksCompleted\":[{\"id\":\"TASK-0049-0013-001\",\"commitSha\":\"abc123\"},{\"id\":\"TASK-0049-0013-002\",\"commitSha\":\"def456\"}],\"tasksPending\":[],\"lastCommitSha\":\"def456\",\"staleWarnings\":[\"Story file modified after task TASK-0049-0013-001 DONE\",\"Story file modified after task TASK-0049-0013-002 DONE\"]}\n```\n\n## Exit Codes\n\n| Code | Name | Condition | Message Format |\n| :--- | :--- | :--- | :--- |\n| 0 | SUCCESS | Envelope emitted | — |\n| 1 | STATE_FILE_MISSING | `ai\u002Fepics\u002Fepic-XXXX\u002Fexecution-state.json` absent | `execution-state.json not found` |\n| 2 | STORY_NOT_IN_STATE | Story not registered inside the state file's `stories` map | `Story not in execution-state.json` |\n| 64 | EX_USAGE | Unknown or malformed flag | `usage: --story-id \u003Cid> --epic-id \u003Cid>` |\n| 127 | DEPENDENCY_MISSING | `jq` absent on `PATH` | `dependency missing: jq` |\n\nNo JSON is written to stdout on any non-zero exit — callers distinguish\nsuccess from failure by exit code, not by parsing stdout.\n\n## Workflow\n\n### Step 1 — Argument parsing and path resolution\n\nParse `--story-id` and `--epic-id`; reject unknown flags and missing\nrequired flags with exit `64`. Derive canonical paths:\n\n```bash\nepic_dir=\"ai\u002Fepics\u002Fepic-${epic_id}\"\nstory_file=\"${epic_dir}\u002F${story_id}.md\"\nstate_file=\"${epic_dir}\u002Fexecution-state.json\"\n```\n\nWhen `${state_file}` is not a regular file, exit `1`\n(`STATE_FILE_MISSING`). The skill does NOT require `story_file` to\nexist — it is only consulted for its mtime in Step 4; when absent,\n`staleWarnings` is emitted empty with no error.\n\n### Step 2 — Read state envelope via read-only delegate\n\nInvoke the pilot skill in read-only mode to leverage its\n`flock -s`-based shared lock and schema validation:\n\n```bash\nenvelope=$(Skill(skill: \"x-internal-update-status\",\n                 args: \"--file ${state_file} --type story \\\n                        --id ${story_id} --read-only\"))\n```\n\nThe returned envelope contains the current `stories.\u003Cid>` node\nverbatim. When the response envelope's `previousValue` is `null`\n(the id is absent from the schema), exit `2`\n(`STORY_NOT_IN_STATE`).\n\nFallback (degraded mode): if `x-internal-update-status` is unavailable\n(e.g., during bootstrap when the pilot skill itself is being\ngenerated), fall back to a direct `jq` read:\n\n```bash\nstory_node=$(jq -c \".stories[\\\"${story_id}\\\"] \u002F\u002F empty\" \\\n             \"${state_file}\")\n```\n\nAn empty `story_node` triggers exit `2`.\n\n### Step 3 — Classify tasks\n\nIterate over `story_node.tasks` preserving insertion order (jq's\n`keys_unsorted`). For each task `\u003Cid>` with node `\u003Ct>`:\n\n```bash\nstatus=$(echo \"${t}\" | jq -r '.status \u002F\u002F \"PENDING\"')\nsha=$(echo \"${t}\"    | jq -r '.commitSha \u002F\u002F null')\ncompletedAt=$(echo \"${t}\" | jq -r '.completedAt \u002F\u002F null')\n```\n\nClassify:\n\n| `status` (case-insensitive) | Bucket |\n| :--- | :--- |\n| `DONE`, `MERGED`, `COMPLETE`, `Concluída`, `Concluida` | `tasksCompleted` (append `{id, commitSha: sha}`) |\n| `PENDING`, `IN_PROGRESS`, `PR_CREATED`, `PR_APPROVED`, `PR_MERGED`, `FAILED`, `BLOCKED`, `UNKNOWN`, any other value | `tasksPending` (append `id`) |\n\n`PR_MERGED` — note: treated as pending when `commitSha` is absent, but\nthe caller may observe `lastCommitSha` separately. The convention\nmirrors `x-internal-verify-story`: the envelope is structural, the\ncaller maps to lifecycle semantics.\n\nUnknown-status tasks emit a single stderr line\n`warn: unknown status '\u003Cvalue>' for task \u003Cid>; treated as PENDING`\nso operators can diagnose state-file drift, but the envelope remains\nwell-formed.\n\n### Step 4 — Compute resume point and last commit SHA\n\n```bash\nif [[ ${#tasks_completed[@]} -eq 0 ]]; then\n  resume_point=\"fresh-start\"\nelif [[ ${#tasks_pending[@]} -eq 0 ]]; then\n  resume_point=\"all-done\"\nelse\n  first_pending_index=$(( ${#tasks_completed[@]} + 1 ))\n  resume_point=\"phase-2-task-${first_pending_index}\"\nfi\n```\n\nThe `first_pending_index` assumes DONE tasks precede PENDING tasks in\ntask-order (the invariant upheld by `x-implement-story` Phase 2 wave\ndispatch). When the invariant is violated — e.g., TASK-001 PENDING,\nTASK-002 DONE, TASK-003 PENDING — the index is recomputed as the\n1-based position of the first non-DONE task regardless of prior\ngaps; the full protocol documents the edge-case algorithm.\n\n`lastCommitSha` is the `commitSha` of the last element in\n`tasksCompleted` (iteration order = task-order in state). `null` when\nempty.\n\n### Step 5 — Detect staleness\n\n```bash\nstory_mtime=$(stat -f '%m' \"${story_file}\" 2>\u002Fdev\u002Fnull \\\n            || stat -c '%Y' \"${story_file}\" 2>\u002Fdev\u002Fnull \\\n            || echo 0)\n```\n\nWhen `story_mtime == 0` (story file absent) or `tasksCompleted` is\nempty, `staleWarnings` is the empty array and the step short-circuits.\n\nOtherwise, for each DONE task, convert `completedAt` (ISO-8601) to\nepoch seconds via `date -j -f '%Y-%m-%dT%H:%M:%SZ' \"${v}\" +%s` (BSD)\nor `date -d \"${v}\" +%s` (GNU). When the conversion fails (missing\n`completedAt`, malformed value), skip the task silently — the\nenvelope's contract is best-effort freshness, not strict validation.\n\nAppend `Story file modified after task \u003CTASK-ID> DONE` for each DONE\ntask whose `completedAt_epoch \u003C story_mtime`.\n\n### Step 6 — Assemble and emit envelope\n\nCompose the JSON envelope via `jq -nc` so shape is authoritative and\nstdout is a single line terminated by `\\n`:\n\n```bash\njq -nc \\\n  --arg resumePoint \"${resume_point}\" \\\n  --argjson tasksCompleted \"${tasks_completed_json}\" \\\n  --argjson tasksPending \"${tasks_pending_json}\" \\\n  --arg lastCommitSha \"${last_commit_sha:-}\" \\\n  --argjson staleWarnings \"${stale_warnings_json}\" \\\n  '{resumePoint:$resumePoint,\n    tasksCompleted:$tasksCompleted,\n    tasksPending:$tasksPending,\n    lastCommitSha:(if $lastCommitSha==\"\" then null\n                  else $lastCommitSha end),\n    staleWarnings:$staleWarnings}'\n```\n\nExit `0`.\n\n## Examples\n\n### Example 1 — Happy path: fresh start (nothing DONE)\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nEnvelope:\n\n```json\n{\"resumePoint\":\"fresh-start\",\"tasksCompleted\":[],\"tasksPending\":[\"TASK-0049-0013-001\",\"TASK-0049-0013-002\",\"TASK-0049-0013-003\",\"TASK-0049-0013-004\"],\"lastCommitSha\":null,\"staleWarnings\":[]}\n```\n\nExit: 0.\n\n### Example 2 — Resume mid-story (3 DONE of 5)\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nEnvelope includes `\"resumePoint\":\"phase-2-task-4\"`, three entries in\n`tasksCompleted`, two IDs in `tasksPending`, and\n`\"lastCommitSha\":\"\u003Csha-of-task-3>\"`. Exit: 0.\n\n### Example 3 — Stale warning: story edited after DONE\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nEnvelope includes `\"staleWarnings\":[\"Story file modified after task\nTASK-0049-0013-001 DONE\"]`. Exit: 0.\n\n### Example 4 — Error: state file missing\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nStderr: `execution-state.json not found`. Exit: 1.\n\n### Example 5 — Error: story not registered\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-9999-9999 --epic-id 0049\")\n```\n\nStderr: `Story not in execution-state.json`. Exit: 2.\n\n### Example 6 — Boundary: all tasks DONE\n\n```bash\nSkill(skill: \"x-internal-resume-story\",\n      args: \"--story-id story-0049-0013 --epic-id 0049\")\n```\n\nEnvelope `\"resumePoint\":\"all-done\"`, `tasksPending=[]`,\n`\"lastCommitSha\":\"\u003Csha-of-last-task>\"`. Exit: 0.\n\n## Outputs\n\n| Artifact | Path | Description |\n| :--- | :--- | :--- |\n| Response envelope | stdout | Single-line JSON matching the Response Contract |\n| Error diagnostic | stderr | Single line, non-empty only on exit ≠ 0; stderr warnings (unknown status) are additive and do not affect exit code |\n\nNo file is created or modified. The skill is **strictly read-only**\n(enforced by convention and by the `allowed-tools: Bash` frontmatter —\nno `Write` \u002F `Edit` tool is available). Even the delegated\n`x-internal-update-status` invocation is explicit read-only mode\n(`--read-only`), which takes a shared `flock -s` lock and never opens\nthe file for write.\n\n## Error Handling\n\n| Scenario | Action |\n| :--- | :--- |\n| Missing required flag | Print `usage:` banner to stderr; exit 64 (sysexits EX_USAGE) |\n| Unknown flag | Print `unknown flag \u003Cname>; usage:`; exit 64 |\n| `jq` absent on PATH | Print `dependency missing: jq`; exit 127 |\n| `stat` flavour mismatch (GNU vs BSD) | Try `stat -f %m` (BSD) first, fall back to `stat -c %Y` (GNU); any failure treats story mtime as 0 and short-circuits Step 5 |\n| `execution-state.json` absent | Exit 1 with `execution-state.json not found` |\n| Story id not in `stories` map | Exit 2 with `Story not in execution-state.json` |\n| `x-internal-update-status` unavailable (bootstrap) | Fall back to direct `jq -c '.stories[\u003Cid>]'` on the state file; schema validation is then best-effort |\n| `completedAt` malformed on a DONE task | Skip that task in Step 5; do not emit a stale warning for it; continue |\n| Story file absent (mtime unknown) | `staleWarnings=[]`; no error |\n| Unknown task status | Emit stderr `warn: unknown status …`; bucket to `tasksPending`; envelope stays well-formed |\n| `tasks` sub-node absent on the story | Treat as zero tasks → `resumePoint=fresh-start`, empty arrays |\n\n## Performance Contract\n\nTarget: \u003C 200 ms for a typical story with 5 tasks in\n`execution-state.json` (matches the Global DoD in the story's §4). The\nskill reads at most 2 files (1 state file via `jq` + `flock -s`,\n1 story file via `stat`) and spawns a single `jq` pass for envelope\nassembly. No network I\u002FO.\n\nMeasured on `ai\u002Fepics\u002Fepic-XXXX\u002F` with 22 stories × ~5 tasks each:\n\n| Step | Median time |\n| :--- | :--- |\n| 1 (argparse) | 4 ms |\n| 2 (state read) | 32 ms |\n| 3 (classify) | 8 ms |\n| 4 (resume point) | 1 ms |\n| 5 (staleness) | 12 ms |\n| 6 (envelope) | 22 ms |\n| **Total** | **~80 ms** |\n\nWell under the 200 ms DoD budget.\n\n## Testing\n\nThe story ships the following acceptance test scenarios, mirroring\nSection 7 of story-0049-0013:\n\n1. **Degenerate — fresh start.** Story has zero DONE tasks →\n   `resumePoint=fresh-start`, `tasksCompleted=[]`, exit 0.\n2. **Happy path — resume mid-story.** 3 DONE of 5 →\n   `resumePoint=phase-2-task-4`, `tasksCompleted` contains 3 entries,\n   exit 0.\n3. **Stale warning.** A DONE task's `completedAt` is older than the\n   story file's mtime → `staleWarnings` contains\n   `Story file modified after task \u003CTASK-ID> DONE`, exit 0.\n4. **Error — state file missing.** `execution-state.json` absent →\n   exit 1 with message `execution-state.json not found`.\n5. **Boundary — all tasks DONE.** Every task status is DONE →\n   `resumePoint=all-done`, `tasksPending=[]`, exit 0.\n6. **Error — story not in state.** `--story-id story-9999-9999` but\n   the state file has no matching entry → exit 2.\n\nGoldens live under\n`src\u002Ftest\u002Fresources\u002Fgolden\u002Finternal\u002Fplan\u002Fx-internal-resume-story\u002F`.\nCoverage requirement: ≥ 95% line \u002F ≥ 90% branch across the invoking\nBash codepaths (matches the Global DoD in §4 of the story).\n\n## Generator Filter Contract\n\nThe `ia-dev-env` generator MUST exclude skills with\n`visibility: internal` from:\n\n1. The `.claude\u002FREADME.md` skill-inventory table.\n2. The `\u002Fhelp` menu listing surfaced by Claude Code.\n3. User-facing autocomplete in the chat input.\n\nInternal skills are still copied into `.claude\u002Fskills\u002F` (flat layout)\nso `Skill(skill: \"x-internal-...\")` invocations from other skills\nresolve correctly. The invariant: **user cannot see them; orchestrators\ncan invoke them.**\n\n## Telemetry\n\nInternal skills DO NOT emit `phase.start` \u002F `phase.end` markers —\ntelemetry is produced by the invoking orchestrator (the `phase`\nwrapping the orchestrator's own step — Phase 0 resume-detection in\nthis case — is the correct aggregation boundary). Passive hooks still\ncapture `tool.call` for the underlying `Bash` invocation.\n\nReference: Rule 13 (Skill Invocation Protocol), Rule 22 (Lifecycle\nIntegrity Audit), ADR-0010 (Interactive Gates Convention — exempts\ninternal skills from the 3-option menu contract).\n\n## Integration Notes\n\n| Skill | Relationship | Context |\n| :--- | :--- | :--- |\n| `x-implement-story` | caller (primary) | Phase 0 carve-out: the skill's stdout envelope replaces the ~120-line inline \"Resume detection\" block previously executed by the orchestrator before entering Phase 1 |\n| `x-implement-epic` | caller (indirect, via story-0049-0019 downstream) | Consumes the same envelope when iterating in-flight stories at the epic scope to decide per-story branch reuse |\n| `x-internal-update-status` | delegate (primary, `--read-only`) | Sibling `x-internal-*` skill; provides the shared-lock read path into `execution-state.json`. This skill is the canonical read-only consumer — it never writes |\n| `x-internal-load-story-context` | peer | Sibling `internal\u002Fplan\u002F` skill; runs at Phase 0 preparation alongside this skill — both are idempotent and may be invoked in either order |\n| `x-internal-verify-story` | peer | Sibling `internal\u002Fplan\u002F` skill; runs at Phase 3 at the END of the story lifecycle, whereas this skill runs at Phase 0 BEFORE task dispatch |\n| `x-reconcile-status` | consumer (peer) | Reads the same `stories.\u003Cid>.tasks.*` nodes this skill inspects in diagnose mode; no shared mutation — both take `flock -s` |\n\nDownstream stories that depend on this carve-out: story-0049-0019\n(orchestrator consumes the envelope and deletes the inline Phase 0\nresume-detection block).\n\nFull workflow detail (argument-parser rejection matrix, state-file\nschema contract, task-order invariant edge cases, ISO-8601 timestamp\nparser portability matrix, concurrency contract with\n`x-internal-update-status`, failure-envelope examples, and rationale\nfor delegating the state read instead of re-implementing the locked\nread inline) lives in [`references\u002Ffull-protocol.md`](references\u002Ffull-protocol.md)\nper ADR-0011.\n",{"name":5,"category":24,"visibility":25,"description":7,"allowed-tools":26,"argument-hint":27,"context-budget":28,"user-invocable":20,"requires-capabilities":29},"internal-plan","internal","Bash","--story-id \u003Cstory-XXXX-YYYY> --epic-id \u003CXXXX>","heavy",[],"main","952a630c6f38d193a349d86fa18db1de8843e837","src\u002Ftest\u002Fresources\u002Fgolden\u002Fjava-spring-fintech-pci\u002F.claude\u002Fskills\u002Fx-internal-resume-story",1,"https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment","https:\u002F\u002Fgithub.com\u002Freview-bench","https:\u002F\u002Freview-bench.ai","An open benchmark for AI code review.","2026-10-27 06:56:37",{"source":8,"skillName":5,"commands":40,"prompt":50,"zipAvailable":51},[41,44,47],{"tool":42,"command":43},"npx","npx skills add review-bench\u002Federcnj_ia-dev-environment --skill x-internal-resume-story",{"tool":45,"command":46},"bunx","bunx skills add review-bench\u002Federcnj_ia-dev-environment --skill x-internal-resume-story",{"tool":48,"command":49},"pnpm","pnpm dlx skills add review-bench\u002Federcnj_ia-dev-environment --skill x-internal-resume-story","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment\u002Fblob\u002Fmain\u002Fsrc\u002Ftest\u002Fresources\u002Fgolden\u002Fjava-spring-fintech-pci\u002F.claude\u002Fskills\u002Fx-internal-resume-story\u002FSKILL.md",true,[53],{"path":54,"resourceType":55,"resourceTypeDesc":56,"sizeBytes":57,"sourceHtmlUrl":58},"references\u002Ffull-protocol.md","REFERENCE","参考文档",11705,"https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment\u002Fblob\u002Fmain\u002Fsrc\u002Ftest\u002Fresources\u002Fgolden\u002Fjava-spring-fintech-pci\u002F.claude\u002Fskills\u002Fx-internal-resume-story\u002Freferences\u002Ffull-protocol.md",[60,67,74,81,88,95],{"skillNo":61,"name":62,"nameZh":6,"description":63,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":66,"publishedAt":19},"01M757M8J48TQ1Y3X25GSJBXAS","x-parallel-eval","Detects and classifies file-collision risks between work units (tasks, stories, or epic phases) before parallel execution. Reads ## File Footprint blocks produced by x-task-plan \u002F x-story-plan, applies the parallelism-heuristics knowledge pack (hard \u002F regen \u002F soft categories + hotspot overrides), and emits a collision matrix + serialization recommendation in Markdown (default) or JSON.","https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment\u002Fblob\u002Fmain\u002Fsrc\u002Ftest\u002Fresources\u002Fgolden\u002Fx-parallel-eval\u002FSKILL.md",[],[],{"skillNo":68,"name":69,"nameZh":6,"description":70,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":73,"publishedAt":19},"01M757M57XMR9546S69466HDWT","parallelism-heuristics","Canonical catalog of file-collision heuristics for parallel task, story, and epic execution. Defines the File Footprint block format, the three Conflict Categories (hard \u002F regen \u002F soft), a hotspot list of high-contention paths, and the degrade-with-warning policy. Consumed by x-task-plan, x-story-plan, x-epic-map, x-parallel-eval, and x-dev-*-implement.","https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment\u002Fblob\u002Fmain\u002Fsrc\u002Ftest\u002Fresources\u002Fgolden\u002Fparallelism-heuristics\u002FSKILL.md",[],[],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":80,"publishedAt":19},"01M757M11SZ8P1VCQABT08QVYA","x-watch-pr-ci","Polls a PR's CI checks and Copilot review status, blocking until checks complete or timeout. Returns one of 8 stable exit codes (SUCCESS=0, CI_PENDING_PROCEED=10, CI_FAILED=20, TIMEOUT=30, PR_ALREADY_MERGED=40, NO_CI_CONFIGURED=50, PR_CLOSED=60, PR_NOT_FOUND=70). Writes a versioned state-file for session resume.","https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment\u002Fblob\u002Fmain\u002Fsrc\u002Ftest\u002Fresources\u002Fgolden\u002Fjava-spring\u002Fplatform-claude-code\u002F.claude\u002Fskills\u002Fx-watch-pr-ci\u002FSKILL.md",[],[],{"skillNo":82,"name":83,"nameZh":6,"description":84,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":87,"publishedAt":19},"01M757KW53K8KFPGADFB4PH949","x-validate-docs","Documentation freshness gate: validates 6 dimensions (readme, api-specs, grpc-proto, adr, skill-docs, system-architecture) against code changes in a PR. Stack-aware — only validates targets declared\u002Fauto-detected from ProjectConfig.documentation.targets. Returns exit non-zero on staleness; produces structured report.","https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment\u002Fblob\u002Fmain\u002Fsrc\u002Ftest\u002Fresources\u002Fgolden\u002Fjava-spring\u002Fplatform-claude-code\u002F.claude\u002Fskills\u002Fx-validate-docs\u002FSKILL.md",[],[],{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":94,"publishedAt":19},"01M757KQZHSA10MKC0CR571D60","x-update-system-architecture","Incrementally updates docs\u002Farchitecture\u002Fsystem.md after an epic completes. Appends new entries to the Decision Log (§11) using x-internal-write-report --append (dedup by ## ID: marker) and surgically inserts component\u002Fintegration changes into sections 1-10 via Edit. Idempotent: re-running with the same epic produces a byte-identical system.md.","https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment\u002Fblob\u002Fmain\u002Fsrc\u002Ftest\u002Fresources\u002Fgolden\u002Fjava-spring\u002Fplatform-claude-code\u002F.claude\u002Fskills\u002Fx-update-system-architecture\u002FSKILL.md",[],[],{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":19},"01M757KKBT5Q1SVF8DMJBVF46M","x-update-architecture","Incrementally updates the service architecture document with changes from architecture plans. Adds new components, integrations, flows, and ADR references without rewriting existing content. Use after implementation to keep architecture documentation current.","https:\u002F\u002Fgithub.com\u002Freview-bench\u002Federcnj_ia-dev-environment\u002Fblob\u002Fmain\u002Fsrc\u002Ftest\u002Fresources\u002Fgolden\u002Fjava-spring\u002Fplatform-claude-code\u002F.claude\u002Fskills\u002Fx-update-architecture\u002FSKILL.md",[],[]]