Skip to content

Summon a familiar from a likeness: image-driven rite with a holographic familiar card #4370

Description

@lmvdz

OpenCoven Beta Hackathon 2026 contribution / Team: Lars / Submission repository: https://github.com/lmvdz/coven-cave

The idea

Summoning a familiar today means filling in a form: pick a vessel, type a name, type a role, type a description. The result is text you wrote about an agent you have not met yet.

Let the likeness do the work. Paste or drop an image, and a local vision-capable harness reads it and proposes the familiar: name, office, purpose, the line that describes it, and its manner — voice, temperament, how it reasons. Every value arrives as an editable suggestion, never a silent commit. What you get is a holographic trading card that assembles as you answer, and a real familiar whose SOUL.md was shaped by the picture rather than by a template.

What it adds

POST /api/scry — takes an image, picks the first local vision-capable harness from /api/harnesses, spawns it directly, and streams back parsed suggestions. No familiar is required, which matters: /api/chat/send needs a familiarId to resolve harness/model/workspace, but scrying needs only a harness, a model and an image path. coven doctor reports harnesses ready with an empty roster, so first-run works.

A summoning rite — the card floats on the left, one question at a time on the right. Picking advances; there is no Next button. Choices are objects rather than controls: vessel tiles, model stones sized by real context window (contextWindowForModel, cube-rooted — so a bigger stone genuinely holds more, rather than implying a ranking models do not have), and multi-select office sigils. Everything after the likeness is skippable.

A deterministic foil generator (src/lib/foil) — halftone and signed-distance-function plates, isomorphic so the same maths runs in the browser for a live preview and on the server at print resolution. Seeded from identity only, so one seed composes identically at any resolution.

Material-aware foil — the plate is masked by the artwork's own specular regions, so foil lands where the image would actually reflect and matte areas stay dead. The mask strategy is chosen from the image: brightness always, with a local-texture gate only when the backdrop is bright (on a dark backdrop that gate is actively wrong — chrome is smooth, patterned fabric is not). The luminance cut is solved from the histogram for a target coverage rather than fixed, so exposure differences between portraits do not change how much foil a card gets.

A fullscreen card view from a familiar's avatar — drag to spin, hover for foil. It renders an existing identity and can never re-derive one: aura comes from the stored override so a colour cannot drift because someone looked at it, and a CI test walks the overlay's whole import graph and fails if any module so much as mentions /api/scry.

Deliberate design decisions, and why

No power rating. Models are not a ranking. A card implying one leads people to pick the shiny option and get a worse fit for their task. The stat is CTX — the model's real context window — which is a true magnitude.

Pronouns are never inferred from an image. They default to they/them and the field is flagged. Guessing gender from a picture is exactly the confident-wrong-answer this flow would otherwise produce at scale.

The model supplies qualities; it does not author the file. Voice, temperament and reasoning fill slots in the existing scaffolder, which stays pure and templated. A model writing SOUL.md wholesale drifts out of contract shape and cannot be validated. Generated content passes evaluateFamiliarContract with zero violations and zero warnings, proven by the existing test running the real validator.

A familiar's purpose is a job, not a caption. The scry returns purpose separately from description; the description stays with the card art. (This one was found by three familiars independently, once they could read their own contracts — one of them put it as "I have a very well-specified appearance and a genuinely underspecified job.")

Two engine findings worth having regardless of this feature

WebKit drops background-blend-mode on a tiled layer. As soon as a second tile instance is painted, the multiply stops applying. A pointer-driven background-position that pans positive forces that second tile, so the effect breaks in three of four quadrants and works in the fourth. Fixed by oversizing the layer so it never tiles.

origin/main has zero background-blend-mode today, so nothing upstream is affected — but two things make this expensive to rediscover:

  • CSS hot-reload silently no-ops in the Tauri shell (No link element found for chunk …css). Edit CSS, look at the window, and you are seeing what it loaded at startup. An agent verifying a CSS change there will report success without having tested anything.
  • Playwright's WebKit does not reproduce it. A headless old-vs-new × WebKit-vs-Chromium matrix showed no difference in any of eight cells. Headless WebKit is not a safe proxy for WKWebView, and an e2e spec would not have caught this either.

Status

Implemented and working on feat/cave-3rz-conjure-flow in the fork above, rebased onto current main. typecheck, lint, and all four suites green. Verified in the real Tauri desktop shell, including a pre-fix/post-fix A/B at matched pointer positions.

Opening a PR against this issue. Happy to split it — the foil generator and the scry endpoint are both self-contained — or to reshape it entirely if maintainers would rather it landed differently.

Data handling

No provider tokens, credentials, private prompts or sensitive paths included.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions