Skip to content

feat: show/copy + recommendations menu + stripped-env robustness — PR2#17

Merged
MCamner merged 7 commits into
mainfrom
feat/recommendations-consumer-pr2
Jun 24, 2026
Merged

feat: show/copy + recommendations menu + stripped-env robustness — PR2#17
MCamner merged 7 commits into
mainfrom
feat/recommendations-consumer-pr2

Conversation

@MCamner

@MCamner MCamner commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Stacked on PR1 (#16). Adds the contract's two actions, a dedicated TUI menu, the main-menu entry, and the launcher-robustness fixes the live TTY smoke surfaced.

Base = feat/recommendations-consumer-pr1. Review/merge after PR1, or rebase onto main once PR1 lands.

Steg A — show/copy

  • recommendations-template.sh <id> (show) and recommendations-copy.sh <id> (copy → clipboard).
  • actions.sh (file-wide action gating — allowed_actions is a single contract field, not per-pattern), clipboard.sh.
  • Templates shown/copied verbatim; never executed.

Steg B — menu

  • terminal/menus/recommendations-menu.sh — dedicated, separate from mq-obsidian-menu.sh. Flows: list / detail / template / copy / doctor.
  • select.sh — visible-set-only selection; hidden/mutating patterns are not reachable from the menu (refused by assert_selected_pattern_is_visible).
  • Main-menu entry: slot 10 + recommendations aliases (no number shifts).

Stripped-env robustness (from live TTY findings)

The launcher was starting in a sanitized environment missing PATH and locale:

  • jq and pbcopy now resolve to absolute paths (REC_JQ / REC_PBCOPY) — immune to PATH being emptied mid-session.
  • Launcher bootstrap repairs PATH and sets a UTF-8 locale when none is active (fixes ragged panel borders from byte-mangled box glyphs).
  • Invalid-JSON error now surfaces jq's real reason + binary + size.

Verified from a fully stripped env -i (no PATH/LANG): jq resolves, the file reads, the panel renders with aligned verticals, copy lands on the clipboard.

🤖 Generated with Claude Code

MCamner and others added 7 commits June 24, 2026 02:02
Steg A of the recommended.json consumer's show/copy layer, on top of the
read-only PR1 base. Adds the two contract actions the producer permits:

  - recommendations-template.sh <id>  — print a pattern's command template (show)
  - recommendations-copy.sh <id>      — copy that template to the clipboard (copy)

Grounded in the producer's actual contract: allowed_actions is a FILE-WIDE
field (['show','copy']), so action gating is one contract check
(assert_action_allowed), not a per-pattern lookup — there is no per-pattern
action list and no per-pattern reason field. The per-pattern guard stays
visibility (risk_class), already enforced in PR1. Templates are shown/copied
verbatim (placeholders unexpanded) and never executed: no schema.sh wrapper
layer (parse.sh already holds the keys), no eval/exec/open/route — the only
write is pbcopy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Steg B of the recommended.json consumer: a dedicated TUI menu, standalone and
separate from mq-obsidian-menu.sh (vault navigation) by design. Wires the
existing read-only flows — list, detail, template (show), copy — behind one
surface, plus doctor.

select.sh enforces the boundary: every selection resolves against the
default-visible set (the same 12 patterns recommendations-list.sh shows),
whether picked by number or typed as a literal id. Hidden/mutating patterns are
refused in the menu flow (assert_selected_pattern_is_visible) — reachable only
via the standalone CLI by exact id, never executed. The menu uses the shared
mq-ui surface when present and degrades to plain printf otherwise. No execute/
route/launch surface: the only write is pbcopy (via clipboard.sh).

Not wired into mq-main-menu.sh yet — that entry point is its own commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the dedicated recommendations menu reachable from the main TUI. Minimal
wiring only:

  - source recommendations-menu.sh in the launcher (mirrors the obsidian block)
  - main menu panel: slot 10 in the empty cell beside '9. MQ Obsidian'
  - numeric dispatch 10) and text aliases (recommendations|recommend|rec)
    -> recommendations_menu_main

No number shifts, no other refactors. The menu stays separate from
mq-obsidian-menu.sh; this only adds the entry point.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mqlaunch.sh runs as a non-login shell and inherits the caller's PATH
without path_helper, so a stripped launch env (GUI, non-login shell,
nested launcher) left /usr/bin and /opt/homebrew/bin off PATH — and the
recommendations menu's jq check failed with "jq not found on PATH".
Append the standard system + Homebrew dirs once at launcher start;
only what is missing is added, so an inherited PATH is never clobbered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The launcher PATH bootstrap doesn't cover every launch context, and
assert_recommended_json runs inside $(...) so any PATH repair there dies
with the subshell while the downstream jq renders run in the main shell —
so the menu still failed with "jq not found" in a real TTY.

Add rec_ensure_jq/rec_require_jq in resolve.sh: locate jq by absolute path
(/opt/homebrew/bin, /usr/local/bin, /usr/bin, /bin) when it's not on PATH,
prepend its dir, and have each entry point (the five command wrappers and
the menu) call rec_require_jq in its own main shell before any jq use. The
error now prints the active PATH for diagnosis. Belt-and-suspenders over
the launcher fix; the feature no longer depends on launch-env PATH.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… jq)

The launcher is being started in a sanitized environment that strips both
PATH and the locale. Two visible failures traced to this:

1. jq not found / 'recommended.json is not valid JSON' — the live process
   emptied PATH mid-session (command -v jq and wc both came up empty), wiping
   the earlier PATH-prepend before the jq call ran. Fix: resolve jq to an
   absolute path once (REC_JQ) and route every jq call site through "$REC_JQ"
   instead of a bare jq, so it needs no PATH at all.

2. Ragged panel borders — with no UTF-8 locale the C locale byte-mangles the
   multi-byte box glyphs (─│┌┐) and byte-counts padding, skewing top vs bottom
   fill. Fix: set a UTF-8 locale in the launcher bootstrap when none is active
   (never overriding an existing UTF-8 locale).

Verified from a fully stripped env (env -i, no PATH/LANG): jq resolves, the
consumer reads the file, and the panel renders with aligned verticals.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same class of failure as jq: pbcopy exists (/usr/bin/pbcopy) but the copy ran
in a launcher context whose PATH was stripped, so 'command -v pbcopy' and the
bare pbcopy call failed. Resolve pbcopy to an absolute path once (REC_PBCOPY)
and invoke it directly, immune to the launcher emptying PATH. Verified: with
PATH empty, copy still lands the template on the clipboard via /usr/bin/pbcopy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@MCamner MCamner deleted the branch main June 24, 2026 01:40
@MCamner MCamner closed this Jun 24, 2026
@MCamner MCamner reopened this Jun 24, 2026
@MCamner MCamner changed the base branch from feat/recommendations-consumer-pr1 to main June 24, 2026 01:41
@MCamner MCamner merged commit 1aac00d into main Jun 24, 2026
2 checks passed
@MCamner MCamner deleted the feat/recommendations-consumer-pr2 branch June 24, 2026 01:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant