Skip to content

feat(svg): premium SVG icons via a render-see-fix loop - #1

Open
momentmaker wants to merge 11 commits into
mainfrom
feat/svg-skill
Open

momentmaker wants to merge 11 commits into
mainfrom
feat/svg-skill

Conversation

@momentmaker

Copy link
Copy Markdown
Owner

Summary

Generated SVG no longer comes out flat and generic or takes several re-rolls. The new core skill svg generates premium, dimensional icons through a render-see-fix loop: it generates from a house-style token system, rasterizes its own output, inspects the render against a concrete rubric, patches, and converges. The two pains it removes are a missing style system (flat/generic) and a missing feedback loop (several tries), neither of which is a generation-horsepower problem.

It ships as a concrete, factorable icon loop. The generic "visual-convergence primitive" the brainstorm first imagined is deferred until a second consumer (charts/diagrams) constrains the interface, per AGENTS.md "don't build beyond the stage."

How it works

  1. Read the brief into one token source (references/brief-schema.md, assets/tokens.json).
  2. Loop: generate from tokens, rasterize with scripts/render.sh, inspect the rendered PNG against references/inspection-rubric.md, patch, re-render. Branch (a) stops when the render satisfies the brief (can fire on round 1). Branch (b) composes convergence-detect on the patch-set with its 3-round floor. A hard cap of 6 bounds the loop.
  3. Finalize: optimize once after the loop, add accessibility by intent, validate path-only output.

The house-style system (references/house-style.md) is where premium comes from: a 24-keyline grid, procedural palette lanes, layered gradients, and a depth stack, with a flat to dimensional weight dial that scales only the depth.

Key decisions

See docs/decisions/2026-06-15-svg-visual-convergence.md.

  • Concrete loop over a generic primitive. Three reviewers plus AGENTS.md flagged the primitive interface as premature with only one consumer. The loop is kept self-contained so it stays factorable.
  • Two-branch stop. convergence-detect is a text-drift detector and cannot judge a render, so visual satisfaction is new in-skill logic (branch a, round-1 terminable); the patch-set drift check (branch b) reuses convergence-detect and its floor.
  • Layered compositing, not feSpecularLighting alone. The single-filter claim was adversarially refuted in research; depth comes from stacking shadow, gradient, highlight, and bevel.
  • Graceful degrade. No rasterizer means style-system-only output and a reported skip (exit 3 is a signal, not an error); the optimizer falls back to the unoptimized SVG and refuses to ship one whose viewBox or title it stripped.
  • Falsifiable quality bar. A per-weight rubric, a kill-criterion (must beat single-shot ad-hoc or ship style-only), and a calibration gate against a labeled eval set.

Rich layout, permissions {bash: true, network: false, fs-write: scoped}, composes convergence-detect@^0.2.

Testing

  • scripts/render.sh and scripts/optimize.sh tested directly: librsvg renders the dimensional bell to a 128x128 PNG; optimize runs svgo and preserves viewBox + title via a verify-and-fallback, never hard-failing; the no-rasterizer path returns exit 3.
  • All 3 eval SVGs are well-formed; the dimensional bell rasterizes through the skill's own pipeline.
  • Catalog regenerated via sitegen (48 skills), no drift on re-run.
  • Code review (recall mode) on the scripts found and fixed 2 fallback-path bugs (cairosvg flag ambiguity, temp-file leak).

CI runs the full lint-skills (ajv schema-compile, SKILL.md presence, catalog-drift) and jutsu eval gates.

Known Residuals

Non-blocking, carried in the ADR:

  1. Identity fit. This is the registry's first design-asset skill; reconfirm skills/core vs community/personal placement before tagging.
  2. Arrow-inversion. If Quiver/Arrow ships as an agent-callable tool, the loop could become a commodity wrapper around a superior generator.
  3. Calibration thresholds unset. The kill-criterion and agreement rate are filled when the eval first runs; until then the loop runs but its quality claim is provisional.
  4. Repo-wide (out of scope here). CI does not validate any skill.yaml against the schema, and the Go loader skips shared/permissions/deps/trust. A maintainer may want a repo-level ajv-validate-skills step.

Post-Deploy Monitoring & Validation

No production or runtime impact: a markdown skill plus shell scripts plus a catalog regen. Validation is green CI (schema-compile, catalog-drift, jutsu eval), then jutsu install svg and generate a few real icons to confirm the loop and the degrade path behave. Per the AGENTS.md dogfood loop, run adversarial jutsu swarm pr-review before tagging.


Compound Engineering
Claude Code

Captures the dream -> deep-research -> brainstorm -> doc-review trail for the
svg skill, and the reviewed build plan. Decision: ship a concrete, factorable
icon render-loop now; defer the generic primitive interface until a second
consumer (charts/diagrams) constrains it.
U1: rich-layout manifest (svg), permissions {bash,!network,scoped}, composes
convergence-detect, trust=kaijutsu-core. SKILL.md frame + README.
U2: assets/tokens.json (geometry/palette/gradient/depth, DTCG-leaning) +
references/house-style.md (layered-compositing recipe, weight dial, anti-slop).
U6: references/brief-schema.md + SKILL.md brief section (input defined before
the loop that consumes it).
…n rubric

U3: scripts/render.sh (probe resvg/librsvg/chromium/cairosvg, engine-match,
exit 3 = degrade not error) + scripts/optimize.sh (svgo after-loop, verifies
viewBox/title survived, never hard-fails). Both tested: librsvg renders the
dimensional bell; optimize preserves viewBox+title.
U5: references/inspection-rubric.md (per-weight visual checklist = branch-(a)
stop signal, kill-criterion, calibration gate) + evals/ labeled calibration set.
U4: SKILL.md loop body — branch (a) round-1 visual-satisfaction, branch (b)
convergence-detect on the patch-set (3-round floor), iteration cap, JSON sidecar.
U7: SKILL.md finalize — optimize-after-loop, a11y by intent, path-only validation.
U8: regenerate docs/index.html + docs/skills.json via sitegen (svg now in the
catalog, source: core, 48 skills total) and add the ADR capturing the
concrete-loop-over-primitive decision, the two-branch stop, the falsifiable
quality bar, and the carried-forward identity/Arrow-inversion questions.
Verified agent-agnostic (no agent branches, no swarm).
Code review (recall mode) on the shell scripts:
- render.sh: cairosvg fallback now uses unambiguous --output-width/--output-height
  (the -W/-H short flags risked erroring instead of degrading); chromium path
  documented as best-effort sizing.
- optimize.sh: switch to mktemp -d so no orphan temp file leaks per run (the old
  name+.svg trick left the mktemp original behind).
Re-tested: both exit 0, viewBox+title preserved, no temp leak.
ce-doc-review (coherence+feasibility) on the ADR and SKILL.md:
- ADR: correct the Go-loader claim (it parses shared/permissions/deps/trust;
  only Validate() skips deep value checks) and the schema path
  (schemas/skill.schema.json).
- tokens.json: add palette.semantics map so named base_hue values
  (success/danger/brand-primary/...) resolve to a hue instead of being invented
  per icon (would have broken set cohesion, AE7).
- SKILL.md branch (b): convergence-detect compares each round's failed-rubric-box
  list, not raw SVG text (XML diffs don't track visual improvement).
…chmark

- good-gear-flat.svg: replace the blind-authored sunburst (rendered as a star,
  not a gear) with a real gear (toothed ring + center hole), verified by
  rendering and looking.
- flawed-gear-unrecognizable.svg: keep the sunburst as a FAIL case for the
  'subject recognizable' rubric box (clean + on-grid but reads as a sun).
- good-shield-soft.svg: new PASS case for the 'soft' weight, so calibration
  covers all three weights and two distinct FAIL modes.
- calibration.md: updated manifest + render-and-look-before-labeling rule.
- benchmark/: loop-vs-quiver quality tier. Commits prompts (text) + protocol
  only; quiver SVGs stay in a git-ignored quiver-refs/ the maintainer populates
  locally (commercial output, not redistributed).
Benchmarked against 5 Quiver/Arrow references: the skill matches a trained model
on constructible marks (geometric icons; compositional/filter effects like
metaball typography) but produces only a generic blob on freehand-organic
illustration (a bear stalled at 'generic quadruped' after two render-loop rounds).
The render-see-fix loop works as designed; the binding constraint is generation.

- SKILL.md: add a 'Not for' section deferring bespoke organic illustration
  (animal/figure silhouettes, mascots, figural logos) to a trained generator.
- ADR: add a benchmark-backed scope-boundary section. Kill-criterion fired for
  that class as planned; sharpens the skill's identity.
Benchmarking against Quiver references reshaped the skill:
- Scope: drop the upfront blocklist. The skill attempts ANY subject; the
  render-loop + rubric are the gate, with an honest-failure escape (say so +
  point to a trained model rather than ship a blob). Predictions proved
  unreliable (a maneki-neko succeeds; a freehand bear fails), so category
  gating would wrongly refuse subjects we nail. (SKILL.md + ADR.)
- Fill the live area: subjects span ~4->20, optically sized; under-drawn icons
  that floated small were the most common 'looks cheap' failure. New rubric box
  catches it. (house-style.md, inspection-rubric.md.)
- Panel framing: optional rounded-rect background for the sticker/logo look the
  reference sets use. (tokens.json framing, brief-schema.md background.)
- Goo-fuse technique: fuse organic primitives with the gooey filter so the
  silhouette flows as one form instead of reading as stacked shapes. The bridge
  between 'recognizable' and 'designed' for organic-ish subjects. (house-style.md.)
Two constructible subjects done well, demonstrating the in-scope range and the
fill-the-frame + panel + goo-fuse techniques: a dimensional torii gate and a
maneki-neko fused into one flowing silhouette on a soft panel.

This branch has not been deployed

No deployments
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