proposal-act

star 62

Accept, defer, dismiss, or resolve a proposal. For accepted proposals, asks how to proceed: start implementing now, create a session task, or note for manual implementation. Activates on messages like "accept PROP-", "dismiss PROP-", "defer PROP-", "resolve PROP-".

gtapps By gtapps schedule Updated 6/16/2026

name: proposal-act description: 'Accept, defer, dismiss, or resolve a proposal. For accepted proposals, asks how to proceed: start implementing now, create a session task, or note for manual implementation. Activates on messages like "accept PROP-", "dismiss PROP-", "defer PROP-", "resolve PROP-".'

Proposal Act

Take action on a proposal: accept, defer, dismiss, or resolve.

Usage

/claude-code-hermit:proposal-act accept PROP-019
/claude-code-hermit:proposal-act defer PROP-015
/claude-code-hermit:proposal-act dismiss PROP-012
/claude-code-hermit:proposal-act resolve PROP-008

If no action or ID is provided, ask the operator which proposal and action.

Resolving a Proposal ID

Before reading any proposal file, resolve the operator's input to a filename using this algorithm:

  1. Trim whitespace and uppercase the input.
  2. Match against /^PROP-(\d+)(?:-(.+))?$/. If no match: error "Not a PROP id."
  3. Zero-pad the integer to 3 digits (e.g. PROP-6PROP-006).
  4. Build the glob pattern. Always anchor: never use bare PROP-NNN*.md (collides with 4-digit NNN files like PROP-0061.md once proposal counts cross 1000).
    • If no suffix (e.g. PROP-006): glob two anchored patterns and union the results: PROP-006.md (legacy exact match) plus PROP-006-*.md (new-format files with that integer).
    • If suffix present (e.g. PROP-006-103612 or PROP-006-capability-brainstorm-103612): glob PROP-006-*<suffix>*.md. The leading -* brackets the slug for timestamp-only inputs; the trailing wildcard catches the a/b/… collision-suffix variant. The disambiguation prompt resolves any over-matches.
  5. Glob .claude-code-hermit/proposals/<pattern> (or each pattern in turn for the two-pattern no-suffix case, then union).
  6. Count the matches:
    • 0 matches: error "No proposal matches [input]. Use /proposal-list to see available proposals."
    • 1 match: proceed with that file.
    • 2+ matches: show a disambiguation prompt:
      Multiple proposals match PROP-NNN:
        PROP-NNN-capability-brainstorm-103612 — [title of first match]
        PROP-NNN-session-cost-tracking-104207 — [title of second match]
      Reply with the full ID to continue.
      
      Re-resolve with the operator's reply.

Timestamp Convention

All timestamps in frontmatter and Operator Decision text use ISO 8601 with timezone offset (e.g., 2026-04-06T14:30:00+01:00). Use the timezone from config.json if set, otherwise UTC.

Accept Flow

When the operator accepts a proposal:

  1. Resolve the proposal file using the resolution algorithm above, then read it.
  2. Update the YAML frontmatter: set status to accepted, add accepted_date as timestamp. Do NOT set resolved_date — resolution happens when reflect confirms the pattern is gone. If the file uses old bullet-point metadata (- **Status:**), update that instead. 2b. First-response tracking: Check if the proposal's responded field is already true. If false: set responded: true in frontmatter, then append a responded event:
    bun ${CLAUDE_PLUGIN_ROOT}/scripts/append-metrics.ts .claude-code-hermit/state/proposal-metrics.jsonl '{"ts":"<now ISO>","type":"responded","proposal_id":"PROP-NNN","action":"accept"}'
    
    Then call bun ${CLAUDE_PLUGIN_ROOT}/scripts/generate-summary.ts .claude-code-hermit/state/. If responded is already true, skip the append (prevents double-counting).
  3. Append a timestamp to the Operator Decision section:
    Accepted on 2026-04-06T14:30:00+01:00.
    

3a. Session tracking: Read state/runtime.json for session_id and session_state (both are used below). If session_id is non-null, set accepted_in_session to that session ID in the proposal's YAML frontmatter. If no session is active (session_id is null), leave accepted_in_session: null.

3b. Routine proposals. If the proposal metadata contains Type: routine and a ## Config section with a JSON block: - Parse the JSON block. Validate: must have id, schedule, skill, enabled fields. - Check for duplicate id in existing config.json routines array — if found, update the existing entry instead of appending. - If no duplicate found, append the routine entry to config.json routines array. - Respond: "Routine '{id}' added to config. Run /claude-code-hermit:hermit-routines load to register it immediately." - Notify the operator. - Skip step 4 — no further implementation needed.

3c. Success signal (optional). Check whether the proposal body has a ## Success Signal section with a non-empty predicate line (ignore comment lines starting with <!--).

  • If a non-empty predicate line is found, validate it:
    bun ${CLAUDE_PLUGIN_ROOT}/scripts/eval-success-signal.ts --validate "<predicate line>"
    
  • Exit 0 → set success_signal: <predicate line> in the proposal's YAML frontmatter.
  • Exit non-zero → log a one-line warning to SHELL.md Findings: PROP-NNN success_signal ignored: <reason printed by the script>. Leave success_signal: null.
  • No ## Success Signal section, or the section is empty / comment-only → leave success_signal: null.
  • Never block accept regardless of outcome.
  1. Ask: "How should this be implemented?"

    • "Start implementing now" (default, typical answer): run the falsification gate, then handle session lifecycle, then execute in this turn. Falsification gate (runs first, before any session transition). Verify the proposal is actionable as written with a read-only pass. Skip only when the body contains ## Skill Improvement and /skill-creator:skill-creator is in the available-skills list (step (e) routes that to /skill-creator:skill-creator) — if ## Skill Improvement is present but skill-creator is absent, the proposal becomes a code-edit implementation, so the gate runs to produce a PROCEED file list for the dispatch. Also skip if the body contains ## Skill Draft — authoring is delegated to /skill-creator:skill-creator on accept, not a code-edit plan — but first check that the source_artifact path listed in ## Skill Draft exists and is readable (if the file is missing or unreadable, REJECT with code stale-paths — the procedure brief was removed or archived; the operator should re-run reflect to generate a fresh brief).

      Agent selection — check the harness's available-skills list (never claude plugin list or disk checks):

      • feature-dev:feature-dev in available-skills → use feature-dev:code-explorer as the subagent.
      • feature-dev:feature-dev absent → fall back to the native Plan agent. Read only the returned text; ignore any file it writes under ~/.claude/plans/.
      • If the agent errors → log a one-line warning to SHELL.md Findings and continue to the session-lifecycle branch. Never block.

      Invoke with the proposal's ## Context and ## Proposed Solution sections plus this fixed instruction:

      "You are a read-only falsification gate. Verify every cited path and symbol against the current code. Return line 1 as exactly: REJECT: <already-done | partially-done | stale-paths | nonexistent-symbols | too-vague> — <one-line evidence> or PROCEED (+ complete file list to modify). If REJECT, give file:line evidence. Do not produce a build plan for a rejected proposal. Do not write any files."

      Append the returned line-1 verdict to the proposal's ## Operator Decision section as provenance, then branch:

      • PROCEED → continue to the session-lifecycle branch below (step (a)). Use the agent's complete file list over any files mentioned in the proposal body.
      • REJECT (stop before any session transition — session_state and SHELL.md Task: stay untouched):
        • Interactive mode → surface to the operator: "Falsification gate: [verdict] — [evidence]. Proceed anyway? Y to override / N to re-scope the proposal first." Y → continue to the session-lifecycle branch below (step (a)). N → stop; status stays accepted. Operator re-scopes and re-runs /proposal-act accept PROP-NNN.
        • Autonomous mode → do not implement; notify via channel: "PROP-NNN: falsification check — [evidence]. Reply 'override PROP-NNN' to implement anyway." a. Use the session_state already read from state/runtime.json in step 3a to branch. b. Idle: delegate to claude-code-hermit:session-mgr to transition to in_progress and fill SHELL.md Task as "Implement PROP-NNN: ". Proceed to (e). c. <strong>In progress:</strong> confirm before switching: "Currently working on: <current task>. Switch to PROP-NNN? Y/N".</li> </ul> </li> <li>Yes: append <code>[HH:MM] switched to PROP-NNN: <title> (prior task: <prior task>)</code> to SHELL.md <code>## Progress Log</code>; overwrite SHELL.md <code>Task:</code> field with "Implement PROP-NNN: <title>"; <code>runtime.json session_state</code> stays <code>in_progress</code>. Proceed to (e).</li> <li>No: fall back to "Create a session task" below. d. <strong>Waiting:</strong> fall back to "Create a session task" without asking, then notify: "PROP-NNN queued. Session is currently waiting." e. Implement the proposal. If the body contains <code>## Skill Improvement</code> and <code>skill-creator:skill-creator</code> is in the available-skills list, use <code>/skill-creator:skill-creator</code> for the implementation (in-main; continues to e.5). Before invoking skill-creator, parse the <code>source_artifact:</code> line from the <code>## Skill Improvement</code> body; if it is present and the path is readable (search <code>compiled/</code> then <code>compiled/.archive/</code>), read the brief and pass its content as input context to skill-creator improve — this anchors the improvement to the skill's original spec. Missing or unreadable anchor: proceed without it (no REJECT — an improve proposal is still actionable without the brief, unlike <code>## Skill Draft</code> which hard-rejects stale paths). If the body contains <code>## Skill Draft</code>, follow the procedure-capture install flow below (in-main; continues to e.5). Otherwise, dispatch the full implementation tail to the native <code>general-purpose</code> agent (this includes <code>## Skill Improvement</code> proposals when skill-creator is absent):</li> </ul> <p> <strong>Dispatch (falsification gate returned PROCEED, no in-main skill handler):</strong> Invoke <code>general-purpose</code> via the Agent tool with this prompt (fill in the bracketed value). The subagent inherits <code>CLAUDE.md</code>/<code>CLAUDE.local.md</code>, can invoke skills, and can spawn nested subagents — so it runs the whole tail (implement → quality gate → verification) in its own isolated context and returns one report.</p> <blockquote> <p>Implement the accepted proposal at <code><absolute path to PROP-NNN-*.md></code>, then run its quality gate and verification. Work entirely in this context; your final message is the only thing returned to the caller.</p> <ol> <li>Read the proposal file. The <code>## Operator Decision</code> section contains a <code>PROCEED</code> line from the falsification gate with the authoritative file list — use that list as your scope (over any files mentioned in the proposal body).</li> <li>Do the edits and any test/fix loops yourself. You may spawn a nested Explore subagent if the proposal warrants a search.</li> <li><strong>Quality gate.</strong> Read <code>.claude-code-hermit/config.json</code> → <code>quality_gate.tier</code> (treat missing/invalid as <code>budget</code>). <code>budget</code> → skip cleanup. <code>quality</code> → invoke <code>/claude-code-hermit:simplify</code> focused on the files you touched. <code>balanced</code> → delegate to the <code>claude-code-hermit:quality-gate-judge</code> subagent (pass the proposal path + touched files); on <code>RUN:</code> invoke <code>/claude-code-hermit:simplify</code> as for <code>quality</code>, on <code>SKIP:</code> skip. Capture <code>/simplify</code>'s totals line (<code>applied N · deduped M · principle-rejected K · …</code>). Best-effort: if the judge or <code>/simplify</code> errors, note it and continue — never block on this step.</li> <li><strong>Verification.</strong> Read the proposal's <code>## Verification</code> section. If it has real steps (more than the HTML-comment placeholder), perform them. If a step fails, attempt <strong>one</strong> fix and re-verify; if it still fails, set <code>Verification: failed</code> with the output and stop (do not loop further). If the section is empty or placeholder-only, set <code>Verification: none defined</code>.</li> <li>You cannot prompt the operator — if you hit an ambiguous spec or an undecidable/destructive choice at any step, <strong>stop and return an escalation block</strong> rather than guessing.</li> </ol> <p>Return exactly this structure as your final message (nothing else):</p> <pre><code>Status: implemented | escalated | blocked: <reason> Touched files: <relative paths, space-separated | none> Tests run: <commands + pass/fail summary | none> Quality gate: <tier> — simplify <totals line> | skipped: <reason> | n/a Verification: passed | failed: <output> | none defined Deferred for operator: <none | what was ambiguous and the safe no-op you took> </code></pre> </blockquote> <p> <strong>After the subagent returns</strong> (the dispatched path ran its own quality gate + verification, so it skips main's e.5/e.6 and is handled here):</p> <ul> <li><code>Status: implemented</code> <strong>and</strong> <code>Verification:</code> is <code>passed</code> or <code>none defined</code> → run <code>/proposal-act resolve PROP-NNN</code>, then notify the operator (interactive) or channel (autonomous), building the message from the <code>Quality gate</code> field: if it carries a simplify totals line → "PROP-NNN implemented and resolved. /simplify applied N edits (M deduped, K rejected on principle)." (use "… /simplify made no changes." when N == 0, and "… /simplify completed (totals unavailable)." if the line is unparseable); if it is <code>skipped:</code> or <code>n/a</code> → "PROP-NNN implemented and resolved."</li> <li><code>Verification: failed: <output></code> → do <strong>not</strong> resolve. Surface the failure output to the operator (interactive) or channel (autonomous). Proposal status stays <code>accepted</code>.</li> <li><code>Status: escalated</code> or <code>Status: blocked: <reason></code> → do <strong>not</strong> resolve. Surface the <code>Deferred for operator</code> block to the operator (interactive) or channel (autonomous). Proposal status stays <code>accepted</code>.</li> </ul> <p> If the body is vague and the falsification gate did not return <code>PROCEED</code>, ask the operator for clarification before proceeding.</p> <p><strong>Procedure-capture install flow (when body contains <code>## Skill Draft</code>):</strong></p> <ol> <li>Parse <code>name</code>, <code>source_artifact</code>, <code>install_target</code>, and <code>triggers</code> from the <code>## Skill Draft</code> block.</li> <li><strong>Collision guard:</strong> if <code>install_target</code> (<code>.claude/skills/<name>/SKILL.md</code>) already exists, do <strong>not</strong> overwrite. Ask the operator: "Skill <code><name></code> already exists at <code><install_target></code>. Overwrite / Rename / Cancel?" Default = <strong>Cancel</strong>.</li> <li>Invoke <code>/skill-creator:skill-creator</code> using <code>source_artifact</code> (the procedure brief in <code>compiled/</code>) as input. Pass the proposed <code>name</code> and <code>triggers</code> so it can author the correct frontmatter and trigger phrases. <code>/skill-creator:skill-creator</code> outputs a proposed SKILL.md.</li> <li><strong>Second confirmation gate:</strong> present the full authored SKILL.md to the operator and require an explicit yes/no before installing. An installed skill auto-loads into every future session, so the operator approves the artifact, not just the intent. Record the operator's verdict (confirmed / declined) in the PROP's <code>## Operator Decision</code> section.<ul> <li>Confirmed: proceed to install.</li> <li>Declined: stop. Notify the operator that they can re-run <code>/proposal-act accept PROP-NNN</code> after revising the procedure brief.</li> </ul> </li> <li>Create <code>.claude/skills/<name>/</code> and write the authored SKILL.md there. The procedure brief in <code>compiled/</code> stays as the permanent audit trail — do not move or delete it.</li> <li><strong>Do not auto-stage or commit</strong> the new skill file. Notify the operator: "Skill <code><name></code> installed at <code><install_target></code>. Commit it if you want it tracked in version control."</li> </ol> <p><strong>Verification for procedure-capture proposals (e.6 note):</strong> the <code>## Verification</code> section of a procedure-capture PROP should instruct reading the installed file's frontmatter (<code>name</code>/<code>description</code> parse) rather than checking the live available-skills list — the harness only picks up new skills on the next session reload, so the live list is unreliable here. A missing or malformed installed file blocks resolution per the normal e.6 contract. e.5. <strong>Quality gate (tier-branched).</strong> Applies to <strong>in-main</strong> implementations only (the <code>## Skill Improvement</code> → skill-creator and <code>## Skill Draft</code> → procedure-capture branches). Dispatched implementations run their own quality gate inside the subagent (see the step (e) dispatch) and are resolved there. Read <code>.claude-code-hermit/config.json</code> → <code>quality_gate.tier</code>. Resolve per this table:</p> <pre><code>| Config state | Resolved tier | |---|---| | `tier` is `"budget"` / `"balanced"` / `"quality"` | use as-is | | `tier` missing, `quality_gate` missing, or value not in enum | `budget` (log one-line warning to SHELL.md Findings) | Build a touched-files list from the writes made during the in-main implementation (skill-creator / skill-draft). This is the precise scope for `/claude-code-hermit:simplify` and for the judge. If you can't reliably enumerate it (multi-turn work), omit it; downstream falls back to `git diff --name-only HEAD`. Branch on the resolved tier: - **`budget`**: skip `/claude-code-hermit:simplify` entirely. Proceed to (f). Resolution notification stays plain: "PROP-NNN implemented and resolved." - **`quality`**: invoke `/claude-code-hermit:simplify` directly. Pass the touched-files list as focus when enumerable, otherwise invoke with no focus (it falls back to the working-tree diff): ``` /claude-code-hermit:simplify focus on PROP-NNN implementation: path/a, path/b ``` The skill runs three parallel reviewers (reuse, quality, efficiency), applies the edits it picks itself, and ends with a totals line: `applied N · deduped M · principle-rejected K · stale-anchor skips L · parse failures P`. Capture that line and pass through. Resolution notification: "PROP-NNN implemented and resolved. /simplify applied N edits (M deduped, K rejected on principle)." When `N == 0`: "PROP-NNN implemented and resolved. /simplify made no changes." If the totals line is missing or unparseable, fall back to "PROP-NNN implemented and resolved. /simplify completed (totals unavailable)." — never block resolution. - **`balanced`**: delegate to `claude-code-hermit:quality-gate-judge` with: ``` Proposal: <absolute path to PROP-NNN-*.md> Touched-Files: <space-separated relative paths> (omit this line if not reliably enumerable) ``` Parse line-1 verdict: - `RUN: <reason>` → invoke `/claude-code-hermit:simplify` per the `quality` tier above. Notification: "PROP-NNN implemented and resolved. Judge: <reason>. /simplify applied N edits (M deduped, K rejected on principle)." When `N == 0` use "… /simplify made no changes." Same totals-missing fallback as the `quality` tier. - `SKIP: <reason>` → skip `/claude-code-hermit:simplify`. Notification: "PROP-NNN implemented and resolved. Judge skipped /simplify: <reason>." **The quality gate is cleanup, not correctness** — `/simplify` does not check that the proposal works. Correctness is verified by the `## Verification` gate in step (e.6); proposals with no defined verification still resolve, but the skip is recorded. Best-effort throughout: if any step errors out (judge fails, `/simplify` failed or totals unavailable, file read fails), log a one-line warning to SHELL.md Findings and fall back to skip. The gate never blocks resolution. </code></pre> <p>e.6. <strong>Verification gate</strong> (in-main implementations only — dispatched implementations verify inside the subagent). Read the proposal's <code>## Verification</code> section. - If it contains real steps (more than the HTML-comment placeholder), perform them now — after the quality gate has applied any <code>/simplify</code> edits — before resolving. If a defined step fails, <strong>do not resolve</strong>: report the failure to the operator (or channel in autonomous mode) and stop. - If the section is empty, missing, or contains only its placeholder comment, append <code>Verification: none defined for PROP-NNN — skipped.</code> to SHELL.md <code>## Findings</code> and proceed. The omission is recorded, not blocked.</p> <pre><code>Unlike the e.5 quality gate (best-effort, never blocks), e.6 **blocks resolution when a defined verification step fails** — that is the correctness check the quality gate does not provide. </code></pre> <p>f. <strong>(in-main path)</strong> When verifiably done: run <code>/proposal-act resolve PROP-NNN</code>, then notify the operator (or channel in autonomous mode) with the tier-appropriate message from (e.5). (Dispatched implementations resolve + notify in the step (e) post-return handling.)</p> </li> <li><p><strong>"Create a session task"</strong> → Write <code>.claude-code-hermit/sessions/NEXT-TASK.md</code>:</p> <pre><code class="language-markdown"># Next Task (from PROP-NNN) ## Task [One-line task derived from the proposal's Proposed Solution] ## Context [Summary of the pattern/problem from the proposal, including Related Sessions] ## Suggested Plan 1. [Step derived from Proposed Solution] 2. [Step derived from Proposed Solution] 3. Verify the fix resolves the pattern </code></pre> <p>If <code>NEXT-TASK.md</code> already exists: do <strong>not</strong> write. Status still flips to <code>accepted</code> (operator intent is recorded). Notify: "PROP-NNN accepted. NEXT-TASK is already pending another proposal. Run <code>/session-start</code> to consume it first, then re-run <code>/proposal-act accept PROP-NNN</code> and pick 'Start implementing now' or manual." Otherwise write the file. Then append any of the following bullets to the end of the Suggested Plan, in order, numbered sequentially from <code>4.</code> (quality-gate bullet is last so <code>/claude-code-hermit:simplify</code> reviews any skill-creator output):</p> <ul> <li><strong>(if the proposal contains <code>## Skill Improvement</code> AND <code>/skill-creator:skill-creator</code> is available)</strong> <code>Use /skill-creator:skill-creator to build and validate the skill.</code></li> <li><strong>(if the proposal contains <code>## Skill Draft</code>)</strong> <code>Use /skill-creator:skill-creator to author the captured procedure from the source_artifact (see ## Skill Draft), present the final SKILL.md to the operator for confirmation, then install it to the install_target only on confirmation.</code></li> <li><strong>(if <code>quality_gate.tier</code> in <code>.claude-code-hermit/config.json</code> is not <code>"budget"</code> — i.e. <code>"balanced"</code> or <code>"quality"</code>)</strong> <code>Run /claude-code-hermit:simplify on the touched files for a cleanup pass, then commit.</code> Confirm: "Task prepared. The next <code>/session-start</code> will offer this as the default task."</li> </ul> </li> <li><p><strong>"I'll handle it manually"</strong> → Just mark accepted. Respond: "Marked as accepted. No further action taken."</p> </li> </ul> </li> <li><p>Notify the operator: "PROP-NNN accepted: [title]"</p> </li> </ol> <p><strong>Note:</strong> There is no "Update OPERATOR.md" path. OPERATOR.md is operator-owned — the agent reads it but does not modify it. If the operator wants to update OPERATOR.md based on a proposal, they do it themselves.</p> <h2>Defer Flow</h2> <ol> <li>Resolve the proposal file using the resolution algorithm above, then read it.</li> <li>Update the YAML frontmatter: set <code>status</code> to <code>deferred</code>, add <code>deferred_date</code> as timestamp. Do NOT set <code>resolved_date</code> — deferral is not a terminal state. If the file uses old bullet-point metadata (<code>- **Status:**</code>), update that instead. 2b. <strong>First-response tracking:</strong> Same as accept flow — check <code>responded</code> field, set to <code>true</code> if <code>false</code>, append <code>responded</code> event with <code>"action":"defer"</code>, call <code>generate-summary.ts</code>. Skip if already <code>true</code>.</li> <li>Ask: "Any note on why it's deferred or when to revisit?" (optional — operator can skip)</li> <li>If a note is provided, append to the Operator Decision section:<pre><code>Deferred on 2026-04-06T14:30:00+01:00. Reason: [operator's note] </code></pre> </li> <li>Respond: "PROP-NNN deferred."</li> </ol> <p>Deferred proposals still appear in <code>/proposal-list</code> but are sorted below open proposals.</p> <h2>Dismiss Flow</h2> <ol> <li>Resolve the proposal file using the resolution algorithm above, then read it.</li> <li>Update the YAML frontmatter: set <code>status</code> to <code>dismissed</code>, add <code>dismissed_date</code> and <code>resolved_date</code> as timestamps. If the file uses old bullet-point metadata (<code>- **Status:**</code>), update that instead. 2b. <strong>First-response tracking:</strong> Same as accept flow — check <code>responded</code> field, set to <code>true</code> if <code>false</code>, append <code>responded</code> event with <code>"action":"dismiss"</code>, call <code>generate-summary.ts</code>. Skip if already <code>true</code>.</li> <li>Ask: "Reason for dismissal?" (optional — operator can skip)</li> <li>If a reason is provided, append to the Operator Decision section:<pre><code>Dismissed on 2026-04-06T14:30:00+01:00. Reason: [operator's reason] </code></pre> </li> </ol> <p>4b. <strong>Dismissal learning</strong> — only when a reason was provided in step 3. Judge whether the reason states a durable preference, rule, or taste that applies to a <em>family</em> of future proposals (e.g. "don't propose process changes for things I do twice a year", "stop suggesting test-coverage proposals on docs-only changes") versus a one-off or proposal-specific response ("not now", "already did this manually", "the analysis is wrong", "duplicate of last week"). If generalizable, issue the standard "remember it" reflection framed as a <code>feedback</code>-type entry: state the preference as a rule, add a brief <code>Why:</code> and <code>How to apply:</code> so proposal-triage and reflection-judge can match it in their memory cross-check. Apply auto-memory discipline: respect <code>WHAT_NOT_TO_SAVE</code> (no file paths, no debugging recipes, no facts derivable from grep), keep it concise. The native auto-memory flow writes <code>feedback_<slug>.md</code> and updates the <code>MEMORY.md</code> index — do not write those files directly. If the reason is one-off or sub-threshold, skip — save nothing. 5. Respond: "PROP-NNN dismissed." If step 4b saved a preference, add: "Remembered that as a standing preference (future similar proposals may be filtered)."</p> <p>Dismissed proposals are hidden from the default <code>/proposal-list</code> view. Use "show all" with <code>/proposal-list</code> to see them.</p> <h2>Resolve Flow</h2> <p>Used when reflect has surfaced a sparse-cadence proposal as a resolution candidate (pattern absent from recent sessions but cadence too infrequent to auto-resolve). Also available directly: <code>/claude-code-hermit:proposal-act resolve PROP-NNN</code>.</p> <ol> <li>Resolve the proposal file using the resolution algorithm above, then read it.</li> <li>Update the YAML frontmatter: set <code>status</code> to <code>resolved</code>, <code>resolved_date</code> to current timestamp. Do NOT set <code>dismissed_date</code>. If the file uses old bullet-point metadata (<code>- **Status:**</code>), update that instead.</li> <li>Append a <code>resolved</code> event to proposal-metrics.jsonl:<pre><code>bun ${CLAUDE_PLUGIN_ROOT}/scripts/append-metrics.ts .claude-code-hermit/state/proposal-metrics.jsonl '{"ts":"<now ISO>","type":"resolved","proposal_id":"PROP-NNN"}' </code></pre> </li> <li>Append to the Operator Decision section:<pre><code>Resolved on 2026-04-06T14:30:00+01:00. </code></pre> If the resolve was triggered by reflect's auto-resolve flow (pattern absent from recent sessions), the caller may append "Pattern confirmed absent." but this is no longer the default — resolve also covers implementation completion via the Start-now branch.</li> <li>Respond: "PROP-NNN resolved."</li> </ol> <p>No first-response tracking on resolve — the proposal was already accepted and that event was already logged.</p> </article> </div> <!-- Right: Metadata & Command Sidebar --> <div class="w-full lg:w-80 shrink-0 flex flex-col gap-6" data-astro-cid-7zzsworf> <!-- Install Card --> <div class="p-6 rounded-xl bg-surface-container border border-border/80 flex flex-col gap-4 shadow-sm" data-astro-cid-7zzsworf> <span class="text-xs font-bold uppercase tracking-widest text-on-surface-variant/60 font-mono" data-astro-cid-7zzsworf>Install via CLI</span> <div class="flex flex-col gap-2" data-astro-cid-7zzsworf> <div id="detail-install-cmd" class="font-mono text-[11px] p-3 rounded-lg bg-black/40 border border-border select-all break-all text-primary font-bold leading-relaxed" data-astro-cid-7zzsworf> npx skills add https://github.com/gtapps/claude-code-hermit --skill proposal-act </div> <button id="detail-copy-btn" class="w-full py-2.5 rounded-lg bg-primary hover:bg-primary-hover text-on-primary font-sans font-bold text-sm shadow transition-all active:scale-95 flex items-center justify-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px]" data-astro-cid-7zzsworf>content_copy</span> <span data-astro-cid-7zzsworf>Copy Command</span> </button> </div> </div> <!-- Details & Stats Card --> <div class="p-6 rounded-xl bg-surface-container border border-border/80 flex flex-col gap-4 shadow-sm text-on-surface" data-astro-cid-7zzsworf> <span class="text-xs font-bold uppercase tracking-widest text-on-surface-variant/60 font-sans" data-astro-cid-7zzsworf>Repository Details</span> <div class="flex flex-col gap-3.5" data-astro-cid-7zzsworf> <div class="flex justify-between items-center text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>star</span> Stars </span> <span class="font-mono font-bold text-on-surface" data-astro-cid-7zzsworf>62</span> </div> <div class="flex justify-between items-center text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>call_split</span> Forks </span> <span class="font-mono font-bold text-on-surface" data-astro-cid-7zzsworf>8</span> </div> <div class="flex justify-between items-center text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>navigation</span> Branch </span> <span class="font-mono bg-surface border border-border px-2 py-0.5 rounded text-[11px] text-on-surface-variant" data-astro-cid-7zzsworf>main</span> </div> <div class="flex justify-between items-start text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5 mt-0.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>article</span> Path </span> <span class="font-mono bg-surface border border-border px-2 py-0.5 rounded text-[11px] text-on-surface-variant truncate max-w-[150px]" title="SKILL.md" data-astro-cid-7zzsworf>SKILL.md</span> </div> </div> </div> <!-- Occupations Tag Card --> <!-- Related Creators Card --> <div class="p-6 rounded-xl bg-surface-container border border-border/80 flex flex-col gap-3 shadow-sm" data-astro-cid-7zzsworf> <span class="text-xs font-bold uppercase tracking-widest text-on-surface-variant/60 font-sans" data-astro-cid-7zzsworf>More from Creator</span> <div class="flex items-center gap-2" data-astro-cid-7zzsworf> <img class="w-8 h-8 rounded-full border border-border" src="https://avatars.githubusercontent.com/u/16778396?u=91e930f8e452a0a8384c5826158741e12100538d&v=4" alt="gtapps" onerror="this.src='https://avatars.githubusercontent.com/u/9919?v=4'" data-astro-cid-7zzsworf> <div class="flex flex-col min-w-0" data-astro-cid-7zzsworf> <span class="font-bold text-sm truncate text-on-surface" data-astro-cid-7zzsworf>gtapps</span> <a href="/?creator=gtapps" class="text-xs text-primary hover:underline font-semibold transition-all" data-astro-cid-7zzsworf>Explore all skills →</a> </div> </div> </div> </div> </div> </div> </div> <script> const copyBtn = document.getElementById("detail-copy-btn"); const installCmd = document.getElementById("detail-install-cmd"); if (copyBtn && installCmd) { copyBtn.addEventListener("click", () => { const cmd = installCmd.textContent.trim(); navigator.clipboard.writeText(cmd).then(() => { const originalText = copyBtn.innerHTML; copyBtn.innerHTML = ` <span class="material-symbols-outlined text-[16px]">check</span> <span>Copied!</span> `; copyBtn.style.background = "#10b981"; copyBtn.style.borderColor = "#10b981"; setTimeout(() => { copyBtn.innerHTML = originalText; copyBtn.style.background = ""; copyBtn.style.borderColor = ""; }, 1500); }); }); } </script> </div> <!-- Footer --> <footer class="border-t border-border bg-surface-container-low text-on-surface-variant py-8 px-gutter mt-16 rounded-xl"> <div class="max-w-container-max mx-auto flex flex-col md:flex-row justify-between items-center gap-6"> <div class="flex items-center gap-2"> <div class="w-6 h-6 rounded bg-primary bg-opacity-20 flex items-center justify-center"> <span class="material-symbols-outlined text-primary text-sm">code_blocks</span> </div> <span class="font-bold text-on-surface text-sm">SkillMD</span> </div> <div class="flex flex-wrap justify-center gap-6 text-sm"> <a href="/about" class="hover:text-primary transition-colors">About Us</a> <a href="/contact" class="hover:text-primary transition-colors">Contact Us</a> <a href="/privacy" class="hover:text-primary transition-colors">Privacy Policy</a> <a href="/terms" class="hover:text-primary transition-colors">Terms of Service</a> <a href="/support" class="hover:text-primary transition-colors">Support</a> </div> <div class="text-xs text-on-surface-variant/80"> © 2026 SkillMD. All rights reserved. </div> </div> </footer> </main> <!-- Script for Theme Toggle, Mobile Menu, and Sidebar Filter Redirection --> <script> // Theme setup const savedTheme = localStorage.getItem("theme") || "dark"; function applyTheme(theme) { document.documentElement.classList.remove("dark", "green", "dracula", "nord"); if (theme === "dark") { document.documentElement.classList.add("dark"); } else if (theme === "green") { document.documentElement.classList.add("dark", "green"); } else if (theme === "dracula") { document.documentElement.classList.add("dark", "dracula"); } else if (theme === "nord") { document.documentElement.classList.add("dark", "nord"); } document.documentElement.setAttribute("data-theme", theme); const themeMoon = document.getElementById("theme-moon"); const themeSun = document.getElementById("theme-sun"); const themeLeaf = document.getElementById("theme-leaf"); const themeDracula = document.getElementById("theme-dracula"); const themeNord = document.getElementById("theme-nord"); if (themeMoon && themeSun && themeLeaf && themeDracula && themeNord) { themeMoon.style.display = theme === "dark" ? "inline" : "none"; themeSun.style.display = theme === "light" ? "inline" : "none"; themeLeaf.style.display = theme === "green" ? "inline" : "none"; themeDracula.style.display = theme === "dracula" ? "inline" : "none"; themeNord.style.display = theme === "nord" ? "inline" : "none"; } } applyTheme(savedTheme); const themeToggleBtn = document.getElementById("theme-toggle-btn"); if (themeToggleBtn) { themeToggleBtn.addEventListener("click", () => { const currentTheme = document.documentElement.getAttribute("data-theme") || "dark"; let newTheme = "dark"; if (currentTheme === "dark") { newTheme = "light"; } else if (currentTheme === "light") { newTheme = "green"; } else if (currentTheme === "green") { newTheme = "dracula"; } else if (currentTheme === "dracula") { newTheme = "nord"; } else { newTheme = "dark"; } applyTheme(newTheme); localStorage.setItem("theme", newTheme); }); } // Mobile menu toggle and sidebar logic const mobileMenuToggle = document.getElementById("mobile-menu-toggle"); const sidebarMenu = document.getElementById("sidebar-menu"); const sidebarOverlay = document.getElementById("sidebar-overlay"); function isMobile() { return window.innerWidth < 768; // 768px is the 'md' breakpoint in Tailwind } function openSidebar() { if (sidebarMenu) { sidebarMenu.classList.remove("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.remove("hidden"); } } function closeSidebar() { if (sidebarMenu && isMobile()) { sidebarMenu.classList.add("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.add("hidden"); } } if (mobileMenuToggle && sidebarMenu) { mobileMenuToggle.addEventListener("click", (e) => { e.stopPropagation(); if (isMobile()) { const isClosed = sidebarMenu.classList.contains("-translate-x-full"); if (isClosed) { openSidebar(); } else { closeSidebar(); } } }); document.addEventListener("click", (e) => { if (isMobile()) { if (!sidebarMenu.contains(e.target) && !mobileMenuToggle.contains(e.target)) { closeSidebar(); } } }); if (sidebarOverlay) { sidebarOverlay.addEventListener("click", () => { if (isMobile()) { closeSidebar(); } }); } // Collapse sidebar when clicking a filter button, creator button, or nav item inside it sidebarMenu.addEventListener("click", (e) => { if (isMobile()) { const clickTarget = e.target.closest("button, a"); if (clickTarget) { closeSidebar(); } } }); // Sync sidebar state on window resize window.addEventListener("resize", () => { if (!isMobile()) { // Desktop: sidebar should be visible, no overlay if (sidebarMenu) { sidebarMenu.classList.remove("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.add("hidden"); } } else { // Mobile: start collapsed if (sidebarMenu) { sidebarMenu.classList.add("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.add("hidden"); } } }); } // If not on homepage, redirect on sidebar filter click const isHomepage = window.location.pathname === "/"; document.querySelectorAll("#occupation-filters .filter-btn").forEach(btn => { btn.addEventListener("click", (e) => { const occ = e.currentTarget.getAttribute("data-occupation"); if (!isHomepage) { window.location.href = occ ? `/?occupation=${encodeURIComponent(occ)}` : "/"; } }); }); document.querySelectorAll("#creator-filters .creator-btn").forEach(btn => { btn.addEventListener("click", (e) => { const creator = e.currentTarget.getAttribute("data-creator"); if (!isHomepage) { window.location.href = `/?creator=${encodeURIComponent(creator)}`; } }); }); </script> </body> </html>