Agent skill

Signals

by shift-editor in shift-editor/shift

Canonical rules for writing and reviewing reactive code in Shift.

Apache-2.0Auto-check passed

Install Signals

skills CLI
$ npx skills add shift-editor/shift --skill signals -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install shift-editor/shift signals --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/shift-editor/shift.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.codex/skills/signals .claude/skills/signals && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
signals
GitHub stars
347
Token cost
~2.5k tokens
SKILL.md length
1,273 words
Files
1
Skills in repo
14
Repo updated
First seen
Licence
Apache-2.0

At a glance

Canonical rules for writing and reviewing reactive code in Shift.

  • Works in 8 steps: Derived lookups are computed, never… → Subscribe as narrowly as the reader needs → .value, track, and peek → …
  • Change code that uses signal
  • SKILL.md covers The rule, Rules, When the UI doesn't update and Testing reactive code, plus 1 more section
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Signals is an agent skill from shift-editor/shift. Canonical rules for writing and reviewing reactive code in Shift. Use whenever you add or change code that uses signal, computed, effect, track, peek, untracked, batch, keyedCache, or useSignalState; when you touch the model (Font, Glyph, FontStore, layers), node definitions, render passes, or text layout; and whenever a UI or canvas "doesn't update", updates late, or redraws too often.

Its SKILL.md is about 2.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

The repository describes itself as: A cross-platform font editor built in Rust and TypeScript. The licence is Apache-2.0.

When your agent uses it

  • Change code that uses signal
  • You touch the model (Font
  • Node definitions
  • Canvas doesnt update

Example prompts

  • “t update”
  • “/signals”

Workflow steps

8 steps, taken from the step headings in SKILL.md.

  1. Derived lookups are computed, never mirrored
  2. Subscribe as narrowly as the reader needs
  3. .value, track, and peek
  4. A miss must still subscribe, and something must load it
  5. Replace, don't mutate; don't set what didn't change
  6. Effects are for side effects
  7. Lifetimes
  8. There must be one signals runtime

What it can do on your machine

Read from SKILL.md and the folder at commit 6ade7f2. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are typescript).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Signals loads about 2.5k tokens when it runs. Until then it costs about 105 tokens; SKILL.md has 1,273 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~105
When it runs · the whole SKILL.md, loaded when a task matches
~2.5k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from shift-editor/shift at commit 6ade7f2, republished under its Apache-2.0 licence (© shift-editor). 1,273 words, ~2,456 tokens.

Download SKILL.mdSave it as .claude/skills/signals/SKILL.md (or your agent's skills folder).
name
signals
description
Canonical rules for writing and reviewing reactive code in Shift. Use whenever you add or change code that uses `signal`, `computed`, `effect`, `track`, `peek`, `untracked`, `batch`, `keyedCache`, or `useSignalState`; when you touch the model (`Font`, `Glyph`, `FontStore`, layers), node definitions, render passes, or text layout; and whenever a UI or canvas "doesn't update", updates late, or redraws too often.

/signals — How reactive code is written in Shift

A reactive bug rarely throws. The canvas just stops updating, updates one step late, or redraws on every unrelated change. Each rule below exists because Shift shipped exactly that bug. How the library itself works (equality, laziness, batching, disposal) lives in packages/editor/src/lib/signals/docs/DOCS.md; this skill is about applying it.

The rule

Every value a reactive reader depends on must reach it through a signal it subscribes to, at the narrowest granularity that still works.

If a reader can get a value without subscribing (a plain field, a peek(), a lookup that misses), it will not rerun when that value changes. If it subscribes to more than it needs, it reruns on changes it doesn't care about.

Rules

1. Derived lookups are computed, never mirrored

A Map or Set that indexes reactive state must be a computed over its source signal, or an immutable collection held in a signal. Never a hand-maintained collection kept "in sync" next to a signal.

ts
// Wrong: every reader must remember track(#layersCell); every writer must refill the map.
readonly #layersBySourceId = new Map<SourceId, GlyphLayer>();

// Right: reading it subscribes by construction.
readonly #layersBySourceIdCell = computed(
  () => new Map(this.#layersCell.value.map((layer) => [layer.sourceId, layer])),
);

Lint enforces this with shift/no-mutable-collection-field: mutating a collection field outside the constructor, in a file that uses signals, is an error unless the collection holds signals or the field carries // non-reactive: <reason>. Only annotate after checking the field is never read from a computed, effect, or render path.

Origin: Glyph kept component glyphs in a plain Map mutated in place; render models never saw a base glyph arrive, so added components drew nothing (#453).

2. Subscribe as narrowly as the reader needs
  • A collection that fills in over time (loaded glyphs, projections) gets one signal per key, created lazily on first lookup — see FontStore.#projectionCells. A single signal holding the whole map makes every reader rerun on every load and forces a copy per write.
  • A computed that reads a coarse signal "just in case" reruns on every change to it. Read the specific cell.
  • Walking every entry of a font (65k in large CJK fonts) inside an effect that fires on each load is a performance bug even when it is correct. Walk the changed or loaded subset.

Origin: FontStore.glyphForId once tracked the whole loaded-glyph map, so the scene and text layout reran on every glyph load (#454).

3. .value, track, and peek
WhereRead with
Inside computed / effect bodies, value usedcell.value
Inside a reactive body, value unused (invalidation only)track(cell)
Callbacks invoked from a computed (e.g. functions passed into a render model)track(cell) then cell.peek()
Imperative code (event handlers, commands, tests)cell.peek()

Lint (shift/no-reactive-value-outside-boundary) rejects .value outside reactive boundaries, which is why callbacks use track + peek. A bare peek() in reactive code means "deliberately do not rerun on this"; leave a comment saying why, because it reads identically to the bug.

4. A miss must still subscribe, and something must load it

When a reactive lookup can miss (data not loaded yet), two things must be true:

  1. The miss subscribes to something that fires when the data arrives — a per-key cell that starts null, not a plain Map.get that returns undefined and subscribes to nothing.
  2. Something actually triggers the load. Returning an empty result and moving on leaves it empty forever.

Origin: typing a character whose glyph wasn't loaded laid it out at advance 0 and never re-laid out (#454); a component whose base glyph was never loaded stayed outline-less because the model skipped it instead of loading it (#453).

5. Replace, don't mutate; don't set what didn't change
  • Signals compare with Object.is. Mutating a value in place and re-setting the same reference notifies nobody. Build a new reference.
  • Setting a new-but-equal value notifies everyone. When a write runs on a broad trigger (every directory change, every workspace echo), compare first and skip the set — e.g. Glyph.replaceComponentGlyphs skips unchanged sets so directory updates don't invalidate every render model.
  • A computed that rebuilds an equal object on each run (a filtered list, a { nodes } wrapper) still reads as changed under the default Object.is. When it sits under a broad source, give it a structural equals so readers don't rerun — e.g. Scene compares nodes by identity so selection writes to the shared store don't reach scene readers.
  • Never publish an object mutated in place by re-setting it. Keep it in a plain field, bump a revision signal after each change, and hand readers a value with a new identity (a fresh view, a wrapper, or the revision itself) — see the coordinate buffers and FontStore.committedRevisionCell. A computed that returns the same object after a change looks unchanged and its readers will not rerun.
Show full SKILL.md (523 more words)Show less
6. Effects are for side effects
  • Effects render, sync, and start work; derivations belong in computed.
  • Don't write a signal an effect also reads unless the loop is intended and terminates.
  • Async work started from an effect is fire-and-forget: wrap it in an async method with try/catch that logs, and call it with void (see Font.#loadComponentGlyphs).
  • Render effects use schedule to coalesce to one frame; React uses useSignalState(cell, { schedule: "frame" }) for high-frequency cells.
7. Lifetimes

Whoever creates a computed or effect disposes it. Disposing a computed silently orphans everything that reached its source through it; if a subscriber can outlive the intermediate, give it a direct edge to the source (details in signals/docs/DOCS.md). Use keyedCache for per-key derived objects so identity is stable and old entries dispose.

8. There must be one signals runtime

Two copies of the signals module means two dependency trackers: computeds built with one never see signals from the other, and everything looks subscribed but never updates. In the desktop dev server this happened when workspace packages resolved through pnpm symlinks (#447; fixed by resolve.preserveSymlinks: false in apps/desktop/vite.renderer.config.ts). Any bundler or alias change for @shift/* packages must keep one real path per module.

When the UI doesn't update

Work down this list before changing code:

  1. One runtime? In dev, check the browser's loaded sources for signals/signal.ts twice (e.g. once under node_modules/@shift/editor). Symptom: many unrelated things stale at once.
  2. Is the value behind a signal? Trace from the reader to the storage. A plain field, a peek(), or a mutated-in-place object anywhere on that path breaks it.
  3. Does the reader run inside a reactive boundary? Code called outside computed/effect tracks nothing.
  4. On a miss, what does the reader subscribe to? If nothing fires when the data arrives, it never recovers (rule 4).
  5. Is the data actually loaded? Check the store, not the UI.
  6. Why did it (not) run? Put traceReactiveRun() inside the suspect computed/effect to log which tracked ancestors changed; signalDebug.dump(node) prints its dependency graph.

Testing reactive code

Reproduce with the object the UI actually holds. Get the live render model or layout before the change, subscribe an effect to it the way the renderer does, perform the change through the real editor (TestEditor, real engine), then assert on what the reader ends up seeing. Fetching a fresh object after the change hides exactly the bugs this skill is about.

ts
const model = editor.sceneGlyphRenderModel!;
const seen: number[] = [];
const subscription = effect(() => seen.push(model.contoursCell.value.length));

await editor.addComponent(baseGlyphId);
subscription.dispose();

expect(model.contours).toHaveLength(1);

Reproduce the realistic precondition: data saved and reopened so it is not already loaded (see ComponentEditing.test.ts). For state that arrives asynchronously after an edit settles, use expect.poll. Assert on final observable values, never on how many times something ran — see the writing-tests skill.

Before you finish

  • Every collection field in a signal-using class is derived, holds signals, or is annotated // non-reactive: with a verified reason.
  • Every peek() inside reactive code is intentional and commented.
  • Lookups that can miss subscribe to a per-key cell and trigger the load.
  • No reader subscribes to a whole collection when it needs one entry.
  • Writes on broad triggers skip unchanged values.
  • Nothing walks every glyph in the font per event.
  • A test holds the live object across the change.

© shift-editor, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .codex/skills/signals of shift-editor/shift.

Open the folder on GitHubat commit 6ade7f2

Compare with similar skills

Signals next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Signals compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Signals this skillshift-editor/shift347—~2.5kAutomated safety check: PassApache-2.0
SignalsPostHog/posthog40k—~4.3kAutomated safety check: PassCustom licence
Trader Signalruvnet/ruflo74k1 repos~605Automated safety check: NotesMIT
Agent Signal Pipelinelobehub/lobehub83k—~1.7kAutomated safety check: PassCustom licence
Signal Writegithub/awesome-copilot40k—~1.3kAutomated safety check: PassMIT
Signal Channel for NanoClawnanocoai/nanoclaw31k—~3.6kAutomated safety check: NotesMIT

Similar skills

  • Signals

    PostHog/posthog

    Official

    How to query the documentembeddings table for raw signal data using HogQL.

    40k GitHub stars~4.3k tokensUpdated today
    AI & LLM EngineeringAuto-check passed
  • Trader Signal

    ruvnet/ruflo

    Generate trading signals using npx neural-trader anomaly detection engine with Z-score scoring and neural prediction

    74k GitHub starsUsed in 1 repo~605 tokens
    Data & AnalyticsAuto-check: notes
  • Agent Signal Pipeline

    lobehub/lobehub

    Guides building event-driven background work for LobeHub agents, with sources, signals, actions, policies, workflow handoff and deduplication.

    83k GitHub stars~1.7k tokensUpdated today
    Backend & APIsAuto-check passed
  • Signal Write

    github/awesome-copilot

    Official

    Emit structured agent signals — hands-up, blocked, done, checkpoint, partnership.

    40k GitHub stars~1.3k tokensUpdated today
    Auto-check passed
  • Links NanoClaw to Signal as a secondary device on your existing phone, with a native adapter that talks to a signal-cli daemon and needs no bot API.

    31k GitHub stars~3.6k tokensUpdated yesterday
    Productivity & AutomationAuto-check: notes
  • Traderspy Trading Signals

    sickn33/agentic-awesome-skills

    Fetch and explain TraderSpy's AI crypto futures signals: entry, take-profit ladder, stop, triggers, status against the live price, and how recent signals resolved.

    47k GitHub starsUsed in 1 repo~2.2k tokens
    Business, Finance & HRAuto-check passed

More from shift-editor/shift

All 14 skills in this repo
  • Shift Commit Rules

    shift-editor/shift

    Rules for writing git commits in the Shift font editor repo: Conventional Commits subjects, user-facing changelog wording, concise subjects and logical commit boundaries.

    347 GitHub stars~1.4k tokensUpdated today
    Auto-check: notes
  • Dead Code Removal with Knip

    shift-editor/shift

    Finds unused files, exports and class members with Knip, then verifies each candidate through reference tracing before removing anything, never using knip --fix.

    347 GitHub stars~1.8k tokensUpdated today
    Auto-check passed
  • Shift Subsystem Docs

    shift-editor/shift

    Updates or creates DOCS.md files for Shift subsystems, recording the architecture invariants and constraints that cannot be learned from reading the source.

    347 GitHub stars~1.9k tokensUpdated today
    Auto-check passed
  • Adversarial Docs Audit

    shift-editor/shift

    Fact-checks DOCS.md files against the source code, testing each concrete claim and sorting it as true, false, stale or unverifiable.

    347 GitHub stars~818 tokensUpdated today
    Auto-check passed
  • Shift Issue Writer

    shift-editor/shift

    Sets the rules for finding, writing and updating Shift GitHub issues: search for duplicates first, use outcome-focused titles and testable acceptance criteria.

    347 GitHub stars~1.4k tokensUpdated today
    Auto-check passed
  • Shift JSDoc Contracts

    shift-editor/shift

    Guides writing JSDoc for Shift exported APIs as a stable caller contract, covering ownership, lifetime, side effects and nullability that TypeScript types cannot express.

    347 GitHub stars~3.7k tokensUpdated today
    Auto-check passed

Questions about Signals

What does Signals do?

Canonical rules for writing and reviewing reactive code in Shift. Signals is an agent skill from shift-editor/shift. Canonical rules for writing and reviewing reactive code in Shift.

When should I use Signals?

Signals fits situations like: change code that uses signal; you touch the model (Font; Node definitions; canvas doesnt update.

How do I install Signals in Claude Code?

Run `npx skills add shift-editor/shift --skill signals -a claude-code`. Or copy the skill folder (.codex/skills/signals in shift-editor/shift) into .claude/skills/signals in your project. Claude Code loads it when a task matches its description.

How do I install Signals in Codex?

Run `npx skills add shift-editor/shift --skill signals -a codex`. Or copy the skill folder (.codex/skills/signals in shift-editor/shift) into .agents/skills/signals in your project. Codex loads it when a task matches its description.

Can I use Signals in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add shift-editor/shift --skill signals -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/signals, .gemini/skills/signals, .github/skills/signals and .opencode/skills/signals in your project.

What does Signals need to run?

SKILL.md names no scripts, command-line tools or credentials: Signals is instructions for the agent only.

Does Signals access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Signals safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Signals use?

Signals is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Signals use?

About 2.5k tokens (SKILL.md is roughly 9.8k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Signals?

Skills that share tags, products or a category with Signals: Signals (PostHog/posthog, 40k stars), Trader Signal (ruvnet/ruflo, 74k stars), Agent Signal Pipeline (lobehub/lobehub, 83k stars) and Signal Write (github/awesome-copilot, 40k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Signals?

shift-editor (a GitHub organization) maintains it in shift-editor/shift, which has 347 GitHub stars. The repository holds 14 skills in this directory. The repository was last updated on October 7, 2026.

Source: shift-editor/shift on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.