Srib model
Skill sayonsom/srib-godot-skills-demo/.claude/skills/srib-model
Standalone proof that six Claude Code skills develop a Godot 4.6 feature end-to-end: from an Excel backlog to a runnable SmartThings home view (shaders, animation, UI, integration). Includes one-step Linux/Windows setup.
npx -y skills add sayonsom/srib-godot-skills-demo --skill srib-modelAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Implement model-layer features in the SRIB Godot 4.6 Android app — mathematical models, animation curves, tweens, easing functions, physics behaviors, interpolation, state machines, simulation logic, data processing, and GDScript computation that is independent of rendering. Use when a feature is about how something moves, evolves, computes, or processes data over time. Triggered by: "srib-model", "implement the animation logic", "add the state machine", "fix the easing", "compute the device state", "physics behavior", or any SRIB ticket classified as "model" by srib-identify. Do NOT use for shader/visual work (srib-view) or connecting data to scene nodes (srib-integration). Always read the ticket from issues/SRIB-NNN.md before starting.
SKILL.md
6.6 KB, as published. Nobody here has run it
srib-model
Project: SRIB — Godot 4.6 Android application with a 3D SmartThings home view. Role: Math and behaviour layer. Everything about how values change, objects move, and data gets processed — independent of how anything is drawn.
Invocation
srib-model SRIB-NNN
Read issues/SRIB-NNN.md for the full spec. If no ticket is given, ask the user to describe the behaviour and confirm the scope before implementing.
Scope boundary
The model layer produces values. The view layer consumes them. Never import a ShaderMaterial or call set_shader_parameter from a model script — that belongs in srib-integration. Model scripts emit signals or expose properties that integration scripts read.
Model script (RoomModel.gd)
→ emits: visual_params_changed(params: Dictionary)
← never references: MeshInstance3D, ShaderMaterial, CanvasItem
Workflow
Step 1 — Read the ticket and locate relevant files
cat issues/SRIB-NNN.md
find . -name "*.gd" -path "*/scripts/*" | head -30
grep -r "AnimationPlayer\|Tween\|StateMachine" --include="*.gd" -l
Identify: which GDScript file owns this behaviour, and what signals it emits or receives.
Step 2 — Choose the right animation / computation approach
Use this decision table before writing any code:
| Situation | Recommended approach |
|---|---|
| One-shot transition (e.g., door open animation) | Tween — create, tween_property, play |
| Looping animation (e.g., breathing light) | AnimationPlayer with looped animation, or Tween with set_loops(0) |
| Interruptible transition (e.g., user taps mid-animation) | Tween — kill previous, create new |
| Complex multi-state object (e.g., AC unit: off/cooling/heating/fan-only) | StateMachine pattern (see below) |
| Physics-influenced motion | Godot physics body + _physics_process |
| Frame-rate-independent value smoothing | lerp(current, target, weight * delta) or spring function |
| Single computed property with no time dimension | Pure function, called on data change |
Step 3 — Implement
Tween pattern (preferred for UI/device state transitions):
var _tween: Tween
func transition_to_on() -> void:
if _tween:
_tween.kill()
_tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC)
_tween.tween_property(self, "emissive_value", 1.0, 0.4)
_tween.finished.connect(_on_transition_done)
Easing cookbook:
| Feel | Ease type | Trans type |
|---|---|---|
| Snappy, physical | EASE_OUT | TRANS_SPRING |
| Smooth UI slide | EASE_IN_OUT | TRANS_CUBIC |
| Elastic bounce | EASE_OUT | TRANS_ELASTIC |
| Sudden stop | EASE_IN | TRANS_EXPO |
| Linear progress | — | TRANS_LINEAR |
State machine pattern (for multi-state devices):
enum State { OFF, IDLE, ACTIVE, ALERT }
var _state: State = State.OFF
func set_state(new_state: State) -> void:
if _state == new_state:
return
_exit_state(_state)
_state = new_state
_enter_state(_state)
state_changed.emit(_state)
func _enter_state(s: State) -> void:
match s:
State.ACTIVE:
_start_pulse_animation()
State.ALERT:
_start_alert_animation()
_:
_stop_all_animations()
func _exit_state(s: State) -> void:
pass # cleanup if needed
Spring / damped lerp (for smooth camera or position following):
# Call in _process(delta). velocity is a member var.
func spring_lerp(current: float, target: float, velocity: float, stiffness: float, damping: float, delta: float) -> Array:
var spring_force = (target - current) * stiffness
var damping_force = -velocity * damping
velocity += (spring_force + damping_force) * delta
current += velocity * delta
return [current, velocity]
Bezier curve evaluation (for custom easing on shader params):
static func cubic_bezier(p0: float, p1: float, p2: float, p3: float, t: float) -> float:
var u := 1.0 - t
return u*u*u*p0 + 3.0*u*u*t*p1 + 3.0*u*t*t*p2 + t*t*t*p3
Step 4 — Signal contract
Every model script that feeds the view layer must document its signal contract in a comment block at the top:
## Emitted when visual parameters change.
## params keys: "emissive_strength" (float 0..1), "base_color" (Color), "pulse_phase" (float 0..1)
signal visual_params_changed(params: Dictionary)
This contract is what srib-integration uses to wire the view. Be explicit and stable.
Step 5 — Test
- Write a small test scene or use an existing debug scene
- Print signal emissions to Output — verify values are in expected ranges
- Test edge cases: rapid state toggling, delta = 0, extreme values
- Confirm no
_processor_physics_processcallbacks run when the node is not needed (useset_process(false)when idle)
GDScript conventions for model code
- Model nodes extend
Node(not Node3D or Control) — they hold no visual representation - Use
@exportfor tunable constants (ease values, speeds) so they're editable in the Inspector without code changes - Prefer
@onreadyoverget_node()in_ready() - Use type hints everywhere:
var velocity: float = 0.0,func set_state(s: State) -> void: deltamust be passed to all time-dependent functions — never useTime.get_ticks_msec()for animation logic
Acceptance checklist
- Logic matches ticket description — values transition correctly
- Signals emit with documented keys and types
- No direct references to visual nodes (MeshInstance3D, ShaderMaterial, etc.)
- Edge cases handled: rapid transitions, zero delta, extreme input values
- Tested in editor (Play Scene, verify Output panel shows expected signal emissions)
-
@exportvars used for tunable constants (ease, speed, threshold) - Type hints on all method signatures and member vars
- No regressions in existing model scripts