marketplace/bundles/plan-marshall/skills/manage-terminal-title/SKILL.md
Pure platform-agnostic terminal-title composition consumed by platform-runtime via PYTHONPATH — plan-scoped pm:{phase} bodies and orchestrator Orchestrator-{SlugName} bodies
npx skillsauth add cuioss/plan-marshall manage-terminal-titleInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Pure, platform-agnostic terminal-title composition. This is a library skill
with no user-facing workflow and no CLI entry point — its single module is
imported via PYTHONPATH by platform-runtime, mirroring how script-shared
modules are consumed.
manage_terminal_title.py owns the title-composition contract: the body-format
function, the TITLE_TOKEN_GLYPHS lock-state glyph map, the icon palette +
process-state→icon resolver, and the pure
compose(state_dict, process_state, icon_override=None) function. It is a
leaf library — it imports NEITHER manage-status NOR platform-runtime.
platform-runtime imports it one-directionally to render the title string after
it has read status.json.
Execution mode: script-deterministic library — no LLM, no CLI dispatch.
The composer is a pure function imported and called by platform-runtime.
Prohibited actions:
compose and
its helpers operate solely on the passed state_dict. The caller
(platform-runtime) owns all reads of status.json and all emission.manage-status or platform-runtime — the module is a leaf in
the dependency graph; the only permitted direction is platform-runtime →
manage-terminal-title.execute-script.py.Constraints:
platform-runtime) MUST consume them via import rather than
re-declaring them.persona-plan-marshall-agent.compose(state_dict, process_state, icon_override=None) -> str | None
composes '{icon} {glyph} {body}'. It is pure — no I/O, and no clock read. The
three inputs are independent.
process_state is a value from the target-neutral PROCESS_STATES vocabulary
(active / waiting / busy / done), or None for push-mode / statusLine.
The composer knows no hook-event vocabulary: the caller maps its own
target-specific events to a process state first. On Claude Code that mapping is
claude_runtime._claude_event_to_process_state(hook_event_name, tool_name).
_compose_body(state_dict) renders the body from current_phase and the
optional short_description — or, when the state carries kind: orchestrator,
the orchestrator body from the state's slug:
| Condition | Body |
|-----------|------|
| Active phase, short_description present | pm:{phase}:{short} |
| Active phase, no short_description | pm:{phase} |
| Terminal phase (complete / archived), short present | pm:Completed:{short} |
| Terminal phase, no short | pm:Completed |
| current_phase empty / missing | None (true no-op) |
| kind: orchestrator, slug present | Orchestrator-{SlugName} |
| kind: orchestrator, slug empty / missing | None (true no-op) |
A terminal phase renders the Completed body — NOT None — so a finished plan
still shows in the title (with the ✅ override below).
Orchestrator body: when the passed state dict carries kind: orchestrator,
the body is Orchestrator-{SlugName} (slug from the state's slug field)
instead of the plan-scoped pm:{phase}[:{short}] form — e.g.
Orchestrator-token-optimization. Icon and glyph slots keep their existing
semantics (process icon, build-busy 🔨 icon-slot override); only the body
composition branches on the kind, and the composer stays a pure leaf function.
The orchestrator state is read by platform-runtime's session push-title-token --store orchestrator --slug {slug} seam, which resolves the
epic's status.json via get_store_dir('orchestrator', slug).
TITLE_TOKEN_GLYPHS)The lock-state glyph, prepended when a live token is present:
| State | Glyph |
|-------|-------|
| lock-waiting | ⏳ |
| lock-owned | 🔒 |
manage-status persists a structured {owner, state, set_at} record in the
title_token field. The composer reads it through
title_token_state(state_dict, now=None), which extracts the state marker and
— when an explicit now is supplied — applies the aged-token staleness rule
(TITLE_TOKEN_STALE_AFTER_SECONDS). The clock is always an argument, never
a read, so compose calls it with now=None and stays pure; the caller that
already performed the I/O to load the record is the layer that supplies now.
This map is the single owner of the state→glyph rendering. The glyph is omitted
('{icon} {body}') in three cases:
title_token is set in the plan state.title_token field is not a usable record — a legacy bare state string,
or a record carrying no state — which reads as absent rather than raising.current_phase is a terminal phase (complete / archived) — a finished
plan holds no live lock state, so the glyph is suppressed regardless of
any persisted title_token value. The suppression is token-agnostic: both
TITLE_TOKEN_GLYPHS states (⏳/🔒) are uniformly suppressed for a terminal
plan.The build-busy state is deliberately absent from TITLE_TOKEN_GLYPHS:
it is rendered as a 🔨 icon-slot override (see Icon below), NOT as a prepended
glyph. Its absence from the map makes glyph suppression automatic —
TITLE_TOKEN_GLYPHS.get('build-busy') is None, so the glyph-prepend block emits
no glyph segment for it, and an active build-busy plan renders 🔨 pm:{phase}
(icon-slot override, no glyph).
resolve_icon + terminal override)resolve_icon(process_state) maps a target-neutral process state — not a
hook event — to the process icon:
| process_state | Icon |
|-----------------|------|
| active (and the None / unknown default) | ➤ active |
| waiting | ? waiting |
| busy | ⚙ busy |
| done | ✓ done |
The event→state mapping is the CALLER's half and is deliberately not owned here:
on Claude Code it lives in
claude_runtime._claude_event_to_process_state(hook_event_name, tool_name),
which resolves Stop → done, Notification and PreToolUse:AskUserQuestion
→ waiting, PreToolUse:Bash → busy, and everything else → active. Keeping
that table on the caller is what lets this module stay target-neutral.
The ⚙ busy icon (_ICON_BUSY, U+2699) is therefore surfaced while a
long-running Bash tool call executes: PreToolUse:Bash maps to busy on enter
and PostToolUse:Bash maps to active on exit, bracketing the busy window. ⚙
is deliberately distinct from ➤ active, ? waiting, and the two
TITLE_TOKEN_GLYPHS lock-state values (⏳ / 🔒).
Terminal-state override: when state_dict['current_phase'] is complete or
archived, compose forces the icon to ✅ (_ICON_TERMINAL, U+2705 — the thick
check-mark, distinct from the thin ✓ _ICON_DONE) regardless of process_state
or icon_override. The process icons ➤ (active) and ? (waiting) MUST NOT appear
for a finished plan.
build-busy icon-slot override: when the live title_token record's
state is build-busy on an active (non-terminal) phase, compose forces the icon to
🔨 (_ICON_BUILD, U+1F528) — a token-keyed icon-slot override (NOT a glyph)
that supersedes both icon_override and the resolved process icon for the
duration of the orchestration call, rendering 🔨 pm:{phase}. The 🔨 build symbol
is deliberately distinct from the ⚙ busy icon: ⚙ is the momentary per-tool busy
state, whereas 🔨 is the persistent orchestration-busy state held for the whole
blocking window. The full icon precedence is terminal ✅ > build-busy 🔨 >
icon_override > process icon — the terminal ✅ override still wins, so 🔨 never
appears for a finished plan. build-busy is set and cleared by the
machine-owned render-hook bracket — PreToolUse:Bash sets it, the paired
PostToolUse:Bash clears it — so no LLM turn owns either half. See
standards/terminal-title-architecture.md
§ Channel Delivery Contract ruling (c) for the record shape, owner vocabulary,
arbitration rule, and staleness threshold.
For non-terminal phases without a build-busy token, icon_override (push-mode)
supersedes the state-resolved icon when provided.
The module is imported via PYTHONPATH the same way script-shared modules are
(the executor's PYTHONPATH generation scans immediate subdirectories of each
scripts/ directory). platform-runtime imports it as:
from manage_terminal_title import compose, resolve_icon, TITLE_TOKEN_GLYPHS
This skill is registered in plugin.json per the library-skill convention (same
as script-shared): user-invocable: false, context-loaded / library, no 3-part
script notation.
plan-marshall:platform-runtime — the one-directional consumer: resolves
session→plan, reads status.json, calls compose, and emits per platform. It
owns the single canonical repaint seam (session push-title-token,
--icon optional) and the relocated session→plan binding (session bind
last-driven-wins / session resolve-plan / session doctor, in
session_binding.py). See
standards/terminal-title-architecture.md
for the full state / compose / emit split, the drive seam, and the binding
policy.plan-marshall:manage-status — fires the repaint + bind drive seam
(_surface_drive) after every persisted current_phase write, so a phase
change repaints the title live instead of freezing.plan-marshall:manage-locks — merge_lock.py drives the SAME repaint seam for
the ⏳/🔒 lock-state surface and a plain icon-less repaint on the release/clear
path.plan-marshall:script-shared — the analogous PYTHONPATH-imported library skill.development
Domain-owned OpenRewrite log-line finding parser for the java-cui domain — parses the
development
Domain-owned OpenRewrite marker detection for the java-cui domain — scans Java/Kotlin sources for cui-rewrite TODO markers, categorizes them by recipe, and fails the gate on any detected marker
development
Operator control surface for the marshalld build server — enrol/drop a project in the machine-global registry (the opt-in enable signal and anti-laundering wall), manage the daemon lifecycle (start, stop, drain, status, install, upgrade) version-pinned to the verified bundle copy, and inspect the daemon's per-project interaction-audit log (read-only)
tools
The tiny build-consumption client for the marshalld build server — submit a build job, bounded long-poll for its result, ping the daemon identity, and preflight registry-plus-liveness in one call; consumption only, never provisioning or enrolment