--- name: dev-issue-unattended description: > Run one or more GitHub issues through their full lifecycle end-to-end with NO pauses for user input — investigate, decide the approach, gather test context, and run the CI/review loop entirely autonomously, documenting every judgment call for after-the-fact review instead of asking. Accepts a single issue, a batch of issue numbers/URLs, or an unfiled bug/request described in plain text. Use ONLY when the user has explicitly said they will be unreachable (away from the computer, asleep, offline) and wants the issue(s) shipped as mergeable PR(s) without them. Do not use this for normal work — use dev-issue instead, which asks for input at the same points this skill auto-resolves. argument-hint: "[issue-number|issue-url][, issue-number|issue-url ...] | problem description" --- # Full Issue Lifecycle, Unattended (HMIS) This is `dev-issue` with every input-gate replaced by an autonomous, evidence-documented decision. **`dev-issue` itself is unchanged and remains the default** — use it whenever the user is actually present. Reach for this skill only when the user has told you up front they won't be reachable. Invoking this skill is explicit authorization for every commit/push/PR/issue step below, including filing the GitHub issue itself if none exists yet — do not re-ask before any of them. ## 0. Parse input Accept a comma/space/newline-separated list mixing bare issue numbers (`23100`) and full GitHub issue URLs. Accept only bare numeric tokens or a URL matching exactly `^https://github.com/hmislk/hmis/issues/(\d+)/?$` — any other host or repo path (e.g. a URL pointing at a fork or a different project) is an invalid token, not a source of an issue number for this repo. Report invalid tokens rather than guessing at what they meant. - Dedupe the resulting set of valid numbers. - Validate each with `gh issue view --repo hmislk/hmis --json title`. Only drop-and-continue when GitHub itself confirms the issue doesn't exist (a clean "not found" from `gh`) or the token was invalid per the URL rule above — note "could not resolve issue #N" (or the raw invalid token) for the final summary (step 15) and move on. Any other failure from this command — auth, permission, rate-limit, network/transport errors — is not evidence the issue doesn't exist; it's the "true whole-batch abort" case from the Hard limits section below (`gh` itself is unreachable/broken), since it will affect every remaining issue the same way. Stop and report it rather than silently dropping issues one by one. - Process the remaining issues **one at a time, lowest issue number first**. Steps 1–14 below are the per-issue body of this loop: each issue gets its own branch (via `start-issue`), its own commits, its own PR, and its own review loop, exactly as if `dev-issue-unattended` had been run solo on just that issue. - If an issue hits any stop — an existing hard limit, the "insufficient issue description" flag (see Hard limits below), or the "could not resolve" case above — record the outcome for that issue and move on to the next one. The batch is done once every issue in the list has been attempted. - A single free-text problem description with no issue number (the existing "if given only a problem description" mode in step 1) is unchanged and is not part of batch mode — it still handles exactly one ad hoc request per run. ## Hard limits — never bypassed, no matter how confident These are not judgment calls. If one of these is required to proceed, STOP, post the blocker as a comment on the issue (creating one first if needed), and end the run — do not guess. **"End the run," here and everywhere else in this skill, is always scoped to the current issue**: in batch mode (step 0), it means stop work on this issue, record its outcome, and continue the batch with the next one — never abort the whole batch. Reserve a true whole-batch abort for a failure that isn't scoped to one issue at all (e.g. `gh` itself is unreachable) — that's outside anything documented here, and warrants stopping and asking rather than guessing. - Never merge a PR, and never push directly to `development`, `master`, or any production branch — everything goes through a PR. - Never touch anything outside this repo (plus its `../hmis.wiki` sibling, needed for step 10's documentation publishing), the local Payara/MySQL dev environment, and the GitHub API for this repo — no remote/production hosts. - Never write a security-privilege or access-control change autonomously. - Never write, apply, or execute a database schema/migration change autonomously — if the fix needs one, stop and describe what's needed instead of designing it unsupervised. Step 5a's DDL regeneration is the one narrow exception, and only as far as `generate-ddl` itself goes: generating the `tmp/createDDL.jdbc` artifact for a column that's a direct, evidence-backed part of the approved fix. Applying that DDL to any database — even local — stays a human's call via the admin UI's "Add Missing..." page, same as it always is; this skill never runs it. - Never write to a **remote** database — production, staging, or anything reached over an SSH tunnel. Read-only there, always, no exceptions. A **local** database (localhost, no tunnel) is a disposable test environment: no development environment is ever set up on a hosting server, so "the database is local" is a reliable proxy for "this is safe to modify freely". Confirm it genuinely is local before the first write — the JNDI name in `persistence.xml` plus a `localhost` host with no active tunnel on the DB port. If a datasource points anywhere else, treat it as remote and stop. Create, mutate, and abandon local test data as needed — do **not** revert it afterwards or treat local rows as precious. Preferring to generate data *through the app* still applies as guidance (`playwright-e2e` [§15](../../../developer_docs/testing/playwright-e2e-workflow.md#15-always-generate-test-data--never-fall-back-to-code-only-verification)), because a fixture that bypasses the app's own validation can pass a test while proving nothing — but locally that is a judgement call, not a hard limit, and direct SQL is fine when it is simply the faster route. See step 4. - Never put a specific hospital's data in any GitHub issue, PR, or comment: patient/doctor/staff names, production record identifiers (bill/BHT/PHN numbers, entity IDs), affected-record counts, production schema names, cutover dates, per-staff statistics, data-fix logs, or credentials. Describe the defect, not the deployment — see [What May Go Into a GitHub Issue, PR, or Comment](../../../developer_docs/git/github-public-content-policy.md). Naming which hospital *reported* the bug is allowed; publishing its data is not. Non-negotiable here especially, since there's no human proofreading before it posts. - Never resolve genuinely ambiguous behavior — where the codebase, git history, and related issues give no clear signal either way — by picking an option silently. That is exactly the "stop and flag" case in steps 3 and 14. ### Insufficient issue description — hand back to the reporter, don't just stop Two flavors of stop below get a different resolution than the rest of this section — this one and "Cannot reproduce with adequate information" further down. This one: when a stop at step 2, 2a, or 3 traces back to **the issue's own description being inadequate** — not a code-architecture question, not a schema/security limit — hand it back to whoever filed it instead of posting a bare blocker comment. This is a judgment call at runtime, same as any other step-3 decision: document the reasoning. When genuinely unclear whether a stop is a description problem or an architecture problem, default to the plain hard limit above (post a blocker comment and stop) — don't reassign work to a reporter who can't actually resolve a code-level question. When it does fire: 1. Look up the issue's creator first, before composing anything: `gh issue view --repo hmislk/hmis --json author --jq '.author.login'`. If this fails or returns an empty login, stop here — do not post the comment, edit the assignee, or touch the project board with a broken or missing `@mention`. Record this issue's outcome as "stopped — could not resolve issue creator" for the batch summary (step 15) and continue to the next issue. 2. Post a comment on the issue that opens with `@` and asks **only** for what actually stalled this run — drawn from, not a fixed template dumped every time: - the exact page/screen: URL or menu breadcrumb (`Menu > Submenu > Page`) - a clear description of current vs. expected behavior - for report/analytics requests: desired filters, columns, grouping - repro steps that pin down the scenario without a real record identifier — ask for a redacted/synthetic example (e.g. "a BHT like the one in this scenario, with any real patient/bill numbers replaced") rather than a real BHT or bill number, same "no patient-linked identifiers in a GitHub comment" rule as the hard limits above - screenshot(s) of the current behavior or desired layout Ground the ask in what was actually tried, e.g. "Searched for a page matching this description under Inward and Reports; couldn't identify which screen this refers to. Could you share the URL or navigation path?" — not a bare template. 3. `gh issue edit --repo hmislk/hmis --add-assignee ` — **added alongside** `buddhika75`, never replacing them. 4. Set the project-board (#11) Status field back to **Backlog** (same GraphQL mutation pattern `start-issue` step 5 uses to set it forward to "In Progress" — same field, different target option). 5. Check each of steps 2–4 actually succeeded (non-error `gh`/API output) before calling this "needs info" — if one failed partway (e.g. the comment posted but the assignee edit errored), don't silently record it as fully done; note exactly which parts succeeded in the batch summary (step 15) so it's clear what still needs finishing by hand. 6. Record this issue's outcome as "needs info" for the batch summary (step 15), then continue to the next issue in the batch (step 0). ### Cannot reproduce with adequate information — close and invite discussion The second special-cased flavor: step 2a's bug genuinely does not reproduce under a reasonable, documented attempt, **and** the issue already gave enough to work with — this is distinct from the missing-specifics case above, which stays on the "ask for more info, keep open" path. Here, more async back-and-forth isn't likely to help; close the issue instead of leaving it open indefinitely with just a blocker comment, and let the reporter bring it back with a live discussion if it's still happening. 1. Look up the issue's creator first, same as the Insufficient issue description flow above — including its failure guard: if the lookup fails or returns an empty login, stop here without posting or closing. Record "stopped — could not resolve issue creator" for the batch summary (step 15) and continue to the next issue. 2. Post a comment that opens with `@`: what was tried, what didn't reproduce, and an explicit invite to discuss rather than reopen async — e.g. "Attempted to reproduce via ; did not observe the described behavior. Closing for now — if this is still occurring, let's discuss and file a fresh issue with updated repro details." 3. `gh issue close --repo hmislk/hmis --reason "not planned"`. 4. Check that both the comment and the close actually succeeded before calling this outcome final — if the close command errors after the comment posted, note that in the batch summary rather than assuming the issue is closed. 5. Record this issue's outcome as "closed — could not reproduce" for the batch summary (step 15), then continue to the next issue. ## 1. Setup **If given an issue number:** run the `start-issue` skill for it, as `dev-issue` step 1 does. **If given only a problem description (no issue number):** do step 2's investigation first, using `gh issue list --search` to check for an existing duplicate. If none exists, file the issue yourself with `gh issue create` — structure it like a normal bug/feature issue (Problem / Root cause found / Proposed fix / Acceptance criteria), then run `start-issue` on the number you just created. Filing the issue is not optional busywork — it's what makes the rest of this run auditable later. **Redact before filing** — the supplied problem description may itself contain an institution/patient name; strip it per the hard limit below before it becomes the public issue body. ## 2. Investigate Same as `dev-issue` step 2: read the issue, explore the code (`Explore` agent for anything spanning more than a few files), identify the entities/pages involved and the existing patterns to follow. For bug issues, try to pin down root cause by reading code — git archaeology (`git log -p -S`, `git blame`, related closed issues/PRs) is often decisive here and costs nothing to try before falling back to live reproduction. If this investigation — including the git archaeology above — cannot identify which entities/services/pages are even involved, that's the **Insufficient issue description** case from the Hard limits section, not a reason to guess. Route there instead of continuing to step 2a/3. ## 2a. Reproduce the bug (bug issues only, root cause still unconfirmed) Skip for feature/enhancement issues and for bugs where step 2 already found a confirmed root cause from code + history alone. - Prefer non-mutating reproduction first (read-only navigation, API `GET`s) against existing data. - If reproduction needs a record that doesn't exist in the local DB, **do not** ask which one to use — auto-discover the closest real match with a read-only query, and only fall back to generating one through the app (see step 4) if nothing suitable exists. - If it still doesn't reproduce under a reasonable, documented attempt: stop rather than guessing at a fix for a bug you couldn't observe. If the reason it didn't reproduce is missing specifics from the issue itself (no concrete example record, no repro steps, an ambiguous "sometimes it fails" with no stated conditions), that's the **Insufficient issue description** case — route there. If instead the issue gave enough to try and it genuinely doesn't reproduce, that's the **Cannot reproduce with adequate information** case — close it and invite a discussion instead of leaving a blocker comment open indefinitely. This is a hard limit either way, not a style preference — an unverified fix for an unreproduced bug is worse than no fix. ## 3. Decide the approach (no Plan Mode pause) Where `dev-issue` enters Plan Mode and waits for approval, instead: 1. Gather the same evidence a plan would need — related issues/PRs, git history of the affected code, in-code comments explaining prior intent (e.g. this is how issue #22931's fix found and extended the intent behind the original #19963 design instead of guessing at a new one). 2. Pick the option best supported by that evidence. When two options are both plausible and the evidence doesn't clearly favor one, that is "genuinely ambiguous" — stop, but which stop depends on *why* it's ambiguous: if the codebase itself gives conflicting signals (two existing patterns both plausible), use the plain hard limit above (post a blocker comment and stop). If the ambiguity is instead about *what the reporter wants* — e.g. a report request with no filters/columns/grouping specified, a feature request with no acceptance criteria — that's the **Insufficient issue description** case; route there instead. 3. Write the reasoning down **now**, in a form that survives to the PR description (step 13) and, for any non-obvious interpretation, an issue comment — not just in conversation. The user is reviewing this after the fact instead of before, so the trail has to carry the weight a Plan Mode approval normally would. ## 4. Gather test context (no AskUserQuestion pause) Where `dev-issue` step 4 asks for department/records/environment, instead query the local DB yourself for something real and relevant: ```sql -- e.g. find an existing record this feature already touches SELECT ... FROM WHERE ORDER BY LIMIT 5; ``` - Prefer an existing record over creating one — it's already representative. - If nothing suitable exists, **generate it through the app** (per `playwright-e2e` [§15](../../../developer_docs/testing/playwright-e2e-workflow.md#15-always-generate-test-data--never-fall-back-to-code-only-verification): create a purchase before a return, a shift-start before a shift-end, etc.) instead of asking which record to use. Going through the app is preferred because it exercises the same validation and business logic the fix has to survive. - If the app can't get you there (the path is blocked by unrelated broken data, or needs a second user session you don't have credentials for), **write the local database directly** — `INSERT`/`UPDATE` is fine on a local DB. Say so in the PR, and be aware of what a hand-built fixture skips: if it bypasses the very validation the fix depends on, the test proves less, so prefer the app route when the difference matters. - **Never** do any of this against a remote/tunnelled database (see Hard limits). Locally, don't revert or clean up test data afterwards — a local DB is disposable and the next run can reset it. - Environment is local Payara unless the issue explicitly requires otherwise — never assume a remote/production environment unattended (hard limit). ## 5. Develop Same as `dev-issue` step 5 — delegate by file type (`java-backend-developer`, `jsf-frontend-dev`), review each agent's actual diff before moving on. ## 5a. Regenerate the DDL if the schema changed Same as `dev-issue` step 5a — but see the hard limit above: this covers *generating* DDL for a column that's a direct, already-decided part of the fix, not designing new schema unsupervised and not applying/executing the generated script against any database. ## 6. Build and local redeploy Same as `dev-issue` step 6 (adapt the exact commands to whichever machine this session is running on — see the `playwright-e2e` skill and this project's local-environment memory for the current host's paths/ports). Check the server log for deploy errors before moving on. ## 7. Test with Playwright + verify in DB Same as `dev-issue` step 7: exercise the feature with the department/records from step 4, screenshot each meaningful stage into `tmp/`, verify in the DB. **Reach every page through the menus, never by URL** — a URL-loaded page renders against uninitialised session state and produces false findings (`playwright-e2e` §2). Record the menu path in the PR. With no user to correct you, an unreachable-by-menu page is itself the finding — do not work around it with a URL. If step 4 generated new records through the app, this is also where you confirm the fix's actual effect on them (e.g. confirm a bypassed guard left a pending record untouched rather than silently resolving it). ## 8. Iterate Same as `dev-issue` step 8 — fix, rebuild, retest until it passes end-to-end. If 3+ fix attempts don't converge, that's the systematic-debugging architecture-question trigger, not a reason to keep guessing: stop, post findings, end the run. ## 8a. Claude self-review before first push The only review this skill otherwise gets is step 14's CodeRabbit/Codex loop — and both bots can be unavailable at once (rate-limited, usage-exhausted) with no fallback, leaving a PR that ships with zero substantive review. This step adds an earlier, Claude-driven pass so a caught bug becomes part of the initial push instead of a second commit reacting to a bot (or a human) after the fact. It runs once step 8's Iterate loop passes end-to-end, before step 9/11. 1. **Pick an effort level.** Default `medium`. Bump to `high` if the diff touches billing, pharmacy, API (`ws/`), or security/privilege code — the same shared/core risk areas `merge-gate` already flags ("touches shared/core code (API, billing, pharmacy) where a regression could silently break unrelated functions"), extended here to security/privilege code given this skill's existing hard limit against writing such changes at all. 2. **Run `/code-review`** at that effort level against the working diff — the uncommitted changes on the branch, before the first commit/push, not against an already-open PR. 3. **Triage each finding into one of three buckets:** - **In-scope, confirmed bug** → fix it, rebuild/redeploy, re-run the relevant Playwright/DB verification from step 7, and fold the fix into the same commit as the original change. - **Valid but outside the issue's named files/screens** → leave this PR's diff alone; file a separate GitHub issue describing the pattern (same shape as issue #23385, filed from this exact gap), and reference it in this PR's description under a short "Follow-up" note. This is filing an issue, which step 0's blanket authorization already covers — no separate confirmation needed. - **Low-confidence / stylistic / reuse-nitpick** → no fix, no new issue; note it under the PR's "Decisions made without approval" section (step 13) so a human can look later. 4. **Document the pass** in that same PR section — which findings came up, which bucket each landed in, and why — the same judgment-call-logging convention already used for steps 3 and 13. Step 14's CodeRabbit/Codex loop is unchanged by this step — this is an earlier, additional layer, not a replacement. ## 9. Record learnings Same as `dev-issue` step 9. ## 10. Publish evidence and update the wiki Same as `dev-issue` step 10 — including its **required** wiki-page update (find the page, embed the screenshots, replace outdated images, correct any text the change makes wrong, or create/skip the page with the reason stated), and linking the updated page(s) in the issue comment. Publishing an image without wiring it into a page leaves it orphaned; that is not a completed step 10. Two additions for unattended runs: - **Extra weight on redaction**, since no human reviews the screenshots before they're published: when in doubt about a screenshot, crop tighter or drop it rather than publish it uncertain. This applies to the wiki page too — a page edit is as public as an issue comment. - **Wiki prose: only publish what you can verify.** A wrong page edit is public the moment it's pushed, and noting it in the PR afterwards doesn't unpublish it. So the test is not "am I confident?" but **"can I point at the code, or at evidence from this run, that shows the current text is wrong?"** - **Publish** — inserting verified screenshots, and correcting text that demonstrably contradicts the code or the behaviour you just verified. Cite the evidence in the PR (e.g. *"page documented room discharge via `roomDischargeDateTime`; `hasActiveRoom()` deliberately stopped using that field in #21935"*). - **Leave it and flag it** — anything you'd be *inferring*: prose that merely reads as unclear or outdated, claims about behaviour outside what this run touched, or restructuring a page's scope. Note it in the PR as a documentation issue for a human, rather than rewriting it unattended. Record every prose change either way in the "Decisions made without approval" section of the PR (step 13). ## 11. Pre-push check Same as `dev-issue` step 11 — restore `persistence.xml` placeholders before staging. ## 12. Commit and push Same as `dev-issue` step 12 (commit format per [Commit Conventions](../../../developer_docs/git/commit-conventions.md), restore local JNDI unstaged after push) — and fold step 3's documented reasoning into the commit body so it's not only in the PR description. A commit body is published the moment it's pushed: redact it per the hard limit above before writing it, same as an issue or PR body. ## 13. Create the PR Same as `dev-issue` step 13 — including its required **Documentation** section linking the wiki page(s) updated in step 10 (or stating that none was needed, and why) — plus a **"Decisions made without approval"** section up front listing every step-3 judgment call in one place, so the user can scan exactly what to double-check first. Any wiki prose you rewrote belongs in that list. Redact this body per the hard limit above too — same as every other publication point. ## 14. Review loop (until mergeable) — waiting without the user present Repeat, up to **3 cycles**: 1. Use `Monitor` (poll `gh pr checks --json name,bucket`, emit each newly-resolved check) to wait for CI instead of a synchronous watch. Use `ScheduleWakeup` as a fallback heartbeat (~20 min) in case the monitor is missed, per this project's autonomous-session pattern. 2. If checks fail: investigate, fix, push, go to 1. 3. Once checks pass: run the `review-pr` skill. Auto-apply fixes matching its documented false-positive/valid-fix patterns. 4. A genuinely ambiguous review comment is a hard-limit stop, same as step 3 — post your assessment as a reply and end the run rather than guess. 5. Checks passing and no unresolved comments are necessary but not sufficient. Confirm mergeability itself: `gh pr view --json mergeable,mergeStateStatus,isDraft,reviewDecision` — done only once `mergeable: MERGEABLE`, `mergeStateStatus: CLEAN`, `isDraft: false`, and `reviewDecision` isn't blocking (e.g. not `CHANGES_REQUESTED`). A required-approval `reviewDecision` with no reviewer assigned isn't something this skill can resolve — that's normal (a human still has to approve/merge, see step 15), not a stop condition. 3 cycles without convergence → stop, summarize the sticking point, end the run (same as `dev-issue`). ## 14a. File what you found along the way The run is not finished while a defect you noticed but did not fix lives only in chat or `tmp/`. From step 2 onward, keep a **Found along the way** list (in the batch's `tmp/` master plan, or `tmp//found.md`). Anything outside the issue's scope goes on that list, not into the PR. Before Notify: 1. **Confirm each item** against the code, or reproduce it. Drop anything unconfirmed, and say in Notify that you dropped it. A growl you didn't see is not proof of a silent failure. 2. **Search first**: `gh issue list --state all --search ""`. If an open issue matches, comment on it. If a closed one fixed the same bug on another page, cite it in the new issue. 3. **File one issue per defect**, following step 0's public-content rules: symptom, cause with `file:line`, steps, expected, fix direction, and honest impact (say so if it is unreachable or low). 4. **List the new issue links** in Notify. ## 15. Notify Produce one skimmable summary covering **every issue in the batch** (a single issue is just a batch of one), one line each: - `#N — shipped as PR #M` (link to the PR) - `#N — needs info from reporter` (link to the comment posted in the Insufficient issue description flow) - `#N — closed, could not reproduce` (link to the closing comment) - `#N — stopped: ` (link to the blocker comment) - `#N — could not resolve issue number/URL` - `Found along the way → #K` (one line per issue filed in step 14a) For issues that shipped, include what was found, every decision made and why (from step 3/13), what was verified and how, and links to the issue/PR/wiki — same depth as a solo run. For issues that didn't ship, the link to the comment is enough; don't re-summarize what's already written there. **Never merge.**