원클릭으로
slick
'The flowpad code-design lens. Use this whenever you are writing, reviewing,
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
'The flowpad code-design lens. Use this whenever you are writing, reviewing,
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Cut a Flowpad release — bump the version (patch by default; minor/major on request), build the wheel (UI baked in), publish to PyPI, tag + push to GitHub, and validate the install. Then, if the electron/ directory changed since the previous release, trigger the desktop build. Use when asked to deploy/release Flowpad, publish to PyPI, or cut a new version (including a new minor like 0.3.0).
toplog — topic-tracing assistant for debugging. `run` activates the right trace topics for a given issue (in code or tests) so RCA has better traceability; `scan` reconciles the topics referenced in code against the topic catalog; `learn` consolidates post-RCA findings (enrich a topic, add a new one with its trace points, or retire a stale one). Use when debugging a hard failure and you want richer logs before or alongside RCA, when adding or auditing toplog topics, or after proving a root cause to capture the traceability that helped. Also triggers on "turn on tracing for X", "what topics cover Y", "add a toplog topic", or "audit toplog topics".
Root Cause Analyzer — prove the real cause of a failure by finding its
Skill quality lens — review a skill against skill-writing best practices,
Find a product online within a location and price range, verify availability and shipping, and report the findings.
Analyze an agentic execution from its session transcript and produce
| id | e7d69e47-4ca0-5d06-aa52-ea46e2974787 |
"Slick" is a placement-and-shape discipline. Most ugly code in flowpad is not wrong logic — it's right logic in the wrong layer, or a special case bolted onto shared infrastructure. This skill is the lens that catches that before you write it.
It complements CLAUDE.md (the project root), which holds the non-negotiable
rules — URL-first navigation, entity-id policy (UUID v4/v5 via mint_uuid),
the single type registry, type icons from TypeInfo, and "never raise a
timeout." Read those as hard constraints. This skill is the design judgment
layered on top: where does this go, what's the seam, how do I prove it.
references/anchors.md is the concrete file/symbol index for every pattern
below — open it when you need the exact path or class to model after.
The seven principles below are the rubric. There are three ways to apply them:
slick (default, while authoring) — apply the lens as you write. No
report; just produce slick code.
slick check — diagnose. Run the rubric over the target and return a
concise list of real violations. Diagnosis only — do not edit.
slick suggest — prescribe. For the same violations, list the concrete
change that reaches slick design. Still do not edit unless asked; this is the
plan.
Scope. Default to the working-tree change (git diff HEAD; include
untracked files). If the user names a file, path, diff range, or PR, use that
instead. State the scope you used in one line.
slick check — output contractGoal: high signal, zero noise. Only flag violations of the seven principles that materially hurt placement, reuse, or testability. When in doubt, leave it out — a check that lists ten nits is worse than one that names the two real problems.
In scope (flag these): logic computed in the frontend that the backend should
own; a frontend write that should go through an action; a parallel action/method
~95% duplicating an existing one; if name == "x" / bare-string branching on
shared infra that should be an enum or a driver trait; a bare string used for a
typed value; per-type behavior hardcoded at a call site instead of TypeInfo; a
new dependency that stdlib or an existing dep covers; a behavior that can't be
exercised in a REPL or a <15-line no-mock test (god-object / tangled surface).
Out of scope (do NOT flag): formatting, naming taste, micro-perf, anything
CLAUDE.md already governs (unless egregiously violated), and speculative
"could maybe" concerns. Don't restate what the code does.
Format — one line per issue, ordered by impact, then a one-line verdict:
slick check — scope: <what you scanned>
1. <file>:<line> — P<n> <principle tag> — <the violation in ≤12 words>
2. <file>:<line> — P<n> <principle tag> — <…>
Verdict: <N significant issues> | clean
If nothing significant, say exactly: Verdict: clean (plus the scope line).
Don't manufacture issues to fill the list.
slick suggest — output contractSame scope and same significance bar as check, but each item carries the fix.
Order by impact (the change that removes the most misplaced logic first). Be
concrete — name the layer, the seam, the symbol — using references/anchors.md.
Format:
slick suggest — scope: <what you scanned>
1. <file>:<line> — P<n> <principle tag>
Now: <what it does today, ≤1 line>
Slick: <the concrete change — move to X / replace literal with EntityType.Y /
collapse into the Z action / declare trait on the driver>
2. …
Keep each suggestion to the smallest change that reaches slick — not a rewrite. If a fix would ripple well beyond the scope, say so and stop at the seam.
Before writing any non-trivial code, ask: "What is the lowest layer that can own this, and can I exercise it there with no browser?"
That single question resolves most slick decisions. The layers, lowest-owning first:
built-in python → one focused package → worker harness / driver
→ database query → backend entity + action → frontend (render only)
Push work down to the layer that can truly own it. The frontend is the last resort, and it only ever renders or calls an action — never decides.
The frontend is a projection of backend state, never a source of truth.
The round trip is one-directional: a backend Entity changes →
notify_updated() → WebSocket data_op → the SDK deepAssigns it into the
cached entity → handleFlowData / subscribers re-render. The frontend's only
two jobs are (a) render state derived from the URL or an entity, (b) call an
action.
Why: anything the frontend computes can't be tested without a browser,
can't be reused by the CLI or an agent, and drifts from the backend's truth.
When the backend owns the computation (e.g. Entity.private_context_entities
is merged server-side and the frontend just renders the result), every surface
agrees for free.
The test: could this behavior run with no browser — driven by curl, the
flow CLI, or a pytest? If not, the logic is in the wrong layer.
Slick: the prompt action branches on process.visible server-side; the
frontend calls the same prompt() for both transports and knows nothing.
Not slick: a promptPty() SDK method + inject/launched flags in a React
component deciding how the backend should route. (This exact code existed and
was deleted — the routing moved into the backend action.)
Corollary — this is the same spirit as the URL-first rule in CLAUDE.md: the
loader is the single writer; click handlers only navigate(...).
Don't solve in the UI what the backend can. Don't solve in the backend what a database query or the worker harness already does. Each step down makes the behavior more reusable and more testable.
Why: the lower the layer, the more callers benefit and the smaller the test. A rule enforced in one SQL filter or one driver method can't be bypassed by a second caller the way a UI-level check can.
Reach for the standard library first. Before adding a dependency, check: can this be ~20 lines of stdlib? Is it already a transitive dep? Does it pull a heavy tree for a small need?
Why: every package is install surface, version risk, and a thing the next
reader must learn. flowpad's dep list is already heavy (anthropic, openai,
groq, usearch, tiktoken, numpy) — each new one raises the bar for the
next.
flow_sdk/transcript_analyzer/ parses every vendor transcript with
stdlib only — no parser framework, no pandas. It's the model citizen.Almost every frontend entity (APIEntity subclass, @registerEntity) mirrors a
backend Entity of the same type. New state lands on the backend entity
first; the frontend mirrors it via data_op. The frontend entity is a cache,
never the origin.
Why: one source of truth, reflected, means no reconciliation logic and no "the UI says X but the DB says Y" bugs.
Corollary (see CLAUDE.md / project memory): every EntityType needs a paired
backend Entity class, or its uname and metadata never materialize.
The only channel is: entity.ts builds an ActionInfo and calls
dataManager.callAction → REST → entity.py's @action.post/get method of the
same action_name.
new ActionInfo('prompt', AgenticProcess.type, id, 'POST') // ts_sdk
→ POST /graph/agentic_process/<id>/prompt
→ @action.post(action_name="prompt") // flow_sdk
Rules that keep it slick:
Same action_name on both sides — grep-able as a pair.
Generic behavior goes on the base Entity with types="all"; the named
type is the first consumer, not the scope.
Don't add a parallel action when an existing one can branch. One action that routes internally beats two near-identical actions and the SDK method each would need.
TypeInfoNever compare or persist a bare string for a typed value — use EntityType
(flow_sdk/schema/types.py), the one canonical registry. Per-type metadata and
behavior (icon, meta_model, sync hooks, materialization) live in TypeInfo
and are resolved at runtime, never hardcoded at a call site.
Why: a string literal is an unchecked baseline — a typo or a renamed type
fails silently. An enum is a compile/lint-checked contract; TypeInfo means
adding a type lights up every surface (including the frontend icon registry,
which reads iconForType from the bootstrap, not a hardcoded map).
Slick: pty_submits_on_paste declared on each WorkerDriver; resolved as
self.driver.pty_submits_on_paste.
Not slick: ref.type == "agent" / self.driver.name == "claude" scattered at
call sites. (Real anti-patterns live in agentic_process.py — don't add more.)
if name ==Worker vendors (claude / codex / copilot) differ only behind the WorkerDriver
Protocol — as declared traits (name, pty_submits_on_paste) and
methods (transcript_descriptor, tail_status, compose_prompt). The
orchestrator (AgenticProcess) never branches on driver.name. Vendors share
generic abstractions: WorkerStatus for state, AgentTranscriptFile +
TranscriptDescriptor for transcripts.
Why: onboarding a new vendor should be one new driver file, not edits
sprinkled through the orchestrator. Mixing vendor logic into shared code is how
one vendor's quirk breaks another (e.g. copilot's assistant.turn_end once
mapped to the wrong status and pinned every copilot session "busy").
Generalize the rule beyond workers: any time you're about to write
if name == "x" on shared infrastructure, the difference belongs as a trait or
method on the thing that varies.
Code is slick when you can exercise the behavior three ways:
Team(name="x").model_dump_json() (no DB needed).curl …/graph/<type>/<id>/<action> or a
flow record/navigate/context command.pytest or vitest with no mocks, driving the
real entity + HTTP/WS path.Why: if a behavior needs scaffolding to test, it's too entangled — the slick version falls out naturally as a short function with a clear name. Real flowpad unit tests are 13–46 lines; the cap is 30s and is never raised (a slow test means slow code — fix the code).
Slick: type_id_str(t, id) — one line, trivially tested.
Not slick: a 100-method AgenticProcess god-object where a behavior can only
be reached by booting a worker. Prefer short functions with clear names;
done right, most functionality is provable in under ten lines.
When you catch yourself about to write code, walk this:
if name.If a change forces a special case onto shared infrastructure, that's the signal the fix isn't deep enough — generalize the underlying mechanism instead.