Profile document
A profile is a JSON document with a key, a name, a description, and a
config object holding everything that shapes the craft. This page documents
every field of config, using the built-in Studio profile
(src-tauri/profiles/music.json, key music) for examples. See
Profiles for the reasoning behind the shape.
Where the vocabulary lives
Section titled “Where the vocabulary lives”Since v0.57 the vocabulary a work is judged and shipped by — axes,
tiers, version_roles, release_kinds, statuses — belongs to the
kind of work, not to the profile. A studio makes songs and the videos cut
to them, and a video judged on hook and lyrics is nonsense: each kind
names its own.
{ "format": 2, "work_kinds": [ { "key": "song", "label": "Song", "axes": [...], "tiers": [...], "version_roles": [...], "release_kinds": [...], "statuses": [...] }, { "key": "video", "label": "Video", "axes": [...], "tiers": [...], "version_roles": [...], "release_kinds": [...], "statuses": [...] } ], "collection_kinds": [...], "work_meta_fields": [...], "marks": [...], "stages": [...], "prompts": [...], "rhythm": {...}}The lists a kind leaves out are empty for works of that kind: a kind with no
axes is scored empty, a kind with no statuses cannot hold a work and the
editor says so. What stays on the profile is what is genuinely about the
profile: collection kinds, meta fields, marks, stages, prompts, the rhythm, the
catalogue columns.
A flat document still reads. A profile written the old way — the five
lists beside work_kinds — is taken as every kind gets these: each kind
that declares nothing of its own receives the flat lists, all of them, and
the document comes out in format 2. A kind that names even one list is taken
to have named its vocabulary on purpose and receives nothing from the flat
ones. The shipped Novel, Blog and Podcast profiles are written flat for that
reason; Studio writes song and instrumental flat and gives video and
short their own. A stored profile still in the old shape is rewritten once,
at the next start.
A kind that arrives later arrives without its judgement. When a kilna
update ships a new kind into a profile your workspace already has — the way
v0.57 shipped video and short into Studio — the kind arrives with its
statuses, roles and kinds of release, and without its axes and tiers. Your
axes are your own words; a stranger’s would not appear silently beside them.
Works of the new kind are scored empty until you write its axes in the
profile editor. A fresh workspace gets the whole kind.
The sections below describe each list; every one of them sits under a kind.
Top level
Section titled “Top level”| Field | Type | Meaning |
|---|---|---|
key |
string | Stable identifier for the profile itself. |
name |
string | Display name — “Music”, “Novel”. |
description |
string | One sentence shown when choosing a profile. |
config |
object | Everything below. |
work_kinds, release_kinds, collection_kinds
Section titled “work_kinds, release_kinds, collection_kinds”Each is an array of kind entries — the vocabulary a work, a release, or a collection can take:
{ "key": "song", "label": "Song" }A work kind carries the five lists described on this page — its axes,
tiers, version_roles, release_kinds and statuses — beside its key
and label, and, for a kind whose works are made in scenes, the two lists of
its storyboard; release_kinds therefore sit inside the work kind whose works go
out that way, and a video’s YouTube and a song’s audio release are
different doors: a door belongs to the work that goes through it, so a song
lists only audio, and the clip cut to it is a video with doors of its
own. collection_kinds stay on the profile.
| Field | Type | Meaning |
|---|---|---|
key |
string | Stable value stored on the row. Never shown directly. |
label |
string or object | What the screen displays. Renamable at any time. See Labels in more than one language. |
Labels in more than one language
Section titled “Labels in more than one language”A label is either a plain string or a map from locale to string:
{ "key": "scored", "label": "Scored" }{ "key": "scored", "label": { "en": "Scored", "ru": "Оценено" } }Both shapes are read wherever a label is read — label, description and
hint, at every depth of the document, including an axis’s rubric marks and
a release kind’s fields. The window shows the entry for the language it is
in; it falls back to en, and then to whatever the map does hold, so a profile
carrying only one language still shows a word rather than a blank.
The profiles that ship with kilna carry English and Russian, because a window set to Russian reading Scored · Song was the interface translated around a hole in its own vocabulary. A label you write stays exactly as you write it: a plain string is never rewritten into a map on your behalf, and renaming a word in Settings replaces it with the one word you typed.
template and method are deliberately not translated. They are
instructions to a model rather than words on a screen, and translating one
changes what the assistant does rather than what the window says.
Studio’s work_kinds are song, instrumental, video and short; its
collection_kinds are album, single and cycle — a collection groups
works one level deep, without nesting.
A release kind carries two extra fields:
{ "key": "audio", "label": "Audio release", "requires": ["lyrics", "style"], "icon": "disc" }| Field | Type | Meaning |
|---|---|---|
requires |
string[] | Version roles a release of this kind cannot ship without. Drives the ready marks and the not-ready warning. |
icon |
string | Glyph the calendar draws this kind with, from the list below. |
axis_weights |
object, optional | Axis weights that apply when a work is judged for this kind of release, keyed by axis key: { "hook": 4.0, "visual": 3.0 }. An axis not named keeps the weight the axis itself declares. Absent means the axes’ own weights — one tier for every kind. |
A premiere lives or dies on its dynamics — the room is watching it live;
the same video as an ordinary upload is carried by its fit to the track.
axis_weights lets one score answer both questions: the tier a work earns
as a premiere can differ from the tier it earns as an upload, from the
same axis values. Every key must name an axis in axes, and every weight
must be zero or above. The verdict per kind is computed by the same rule as
the plain total (see
Scoring) and shown on the score panel, under the
total, once at least one kind names weights of its own — see
What each release makes of it.
A profile where no kind reweighs anything shows nothing there: every row would
carry the number the total already gives.
Every key in requires must name a role in version_roles. An empty or
absent list states no requirements: readiness is then judged on the score
alone, and every role mark reads as not applicable rather than missing —
which is how a profile written before this field existed loads. A workspace
whose stored copy states nothing gains the shipped requirements at the next
start; a list you narrowed yourself is left alone.
Kind glyphs
Section titled “Kind glyphs”kilna does not know what kinds of release your craft ships, so the profile also
says what each one looks like. icon names a glyph from this list:
audio-lines · book · book-open · disc · film · globe · image ·
mail · mic · music · newspaper · radio · rss · send · share-2 ·
smartphone · users · video
A kind with no icon, or one naming a glyph outside the list, is drawn with a
neutral calendar mark. Nothing breaks and nothing warns: the label is still
there in the chip’s tooltip and in the filter above the grid. A workspace
written before the field gains the shipped glyphs at the next start, for the
kinds it still shares by key; a kind you added yourself keeps whatever you gave
it.
What a release goes out as
Section titled “What a release goes out as”A release kind may name the fields a release of it ships with — the title it goes out under, the text beneath it, the words it is found by:
{ "key": "youtube", "label": "YouTube", "requires": ["plot"], "icon": "film", "fields": [ { "key": "title", "label": "Title", "type": "line", "template": "{title}", "limit": 205 }, { "key": "description", "label": "Description", "type": "text", "template": "{role:plot}" }, { "key": "tags", "label": "Tags", "type": "tags", "hint": "Comma separated." }, { "key": "pinned", "label": "Pinned comment", "type": "text" } ]}| Key | What it is |
|---|---|
key |
What the value is stored under, in the release’s meta. Renaming the label never loses what was written; renaming the key does. |
label |
The word above the box. |
type |
line (a title), text (paragraphs) or tags (a list, kept as text with commas between). Absent is line. |
template |
What the field is filled with when generated, in the placeholder language below. Absent means the field is only ever typed by hand. |
hint |
A line under the box saying what goes in it. |
limit |
How many characters the destination accepts. Counted beside the box, never enforced — kilna is not the authority on what a platform takes this month. |
A kind with no fields says nothing about itself, and its releases show no
boxes. That is the state of every profile written before the field existed; a
workspace made before it gains the shipped lists for the release kinds it
still shares by key, and a kind you have already given fields of your own is
left alone.
A field’s template is checked against one work kind — the kind that owns
the release kind — which makes the check sharper than an action’s:
{role:plot} in a song’s audio release is refused at save even though the
video kind has a plot. {scene} is refused outright: it is one row of a
storyboard, filled from the row an action was started on, and a release is
about the whole work. Use {scenes} for the board.
version_roles
Section titled “version_roles”The independent bodies a work carries, same { key, label } shape plus how
each one reads (body, see below). Music defines lyrics and style; Novel
defines text, outline and notes. A work can hold one current version per
role, plus every prior revision of each.
statuses
Section titled “statuses”The states a work moves through, in order. Music: draft, scored,
scheduled, released, shelved.
{ "key": "published", "label": "Published", "derive": "released"}| Field | Type | Meaning |
|---|---|---|
key |
string | Stored on the work. |
label |
string | Display name; free to rename. |
derive |
string, optional | What this status means to the automation. Defaults to manual. |
colour |
string, optional | The badge’s emphasis on the card and down the catalogue — plain, accent, good, warn, bad or info, the same roles a mark takes. Absent draws the badge in outline; the word is always there. Studio ships Scored accent, Scheduled warn, Released good, Shelved plain, and a status without a colour gains the shipped one at the next start. |
derive is how the automation knows which of your words means “it went
out”, without the app dictating the words. One status per meaning:
derive |
Set when |
|---|---|
released |
A release of this work has gone out. |
scheduled |
A release holds a slot in the calendar. |
scored |
The work has been judged at least once. |
draft |
Nothing has happened to it yet. |
manual |
Never set automatically — a decision only a person makes, like shelved. |
See Statuses for how this plays out.
What a work is judged on when scored:
{ "key": "hook", "label": "Hook", "weight": 2.0, "scale": 10.0, "description": "Does the chorus stay with you after one listen?"}| Field | Type | Meaning |
|---|---|---|
key |
string | Stored in every score snapshot’s axes object. Never renamed once scores exist against it — see Scoring. |
label |
string | Display name. Free to rename; old scores stay readable under it. |
weight |
number | Relative importance when axes combine into a total. Zero or above. |
scale |
number | Highest value the axis accepts; values are normalized against it before weighting. Above zero. |
description |
string, optional | Guidance shown next to the axis when scoring. |
kind |
"scale" | "flag" | "choice", optional |
What kind of answer the axis takes. Defaults to scale. |
options |
array, optional | The answers a choice axis offers. Required for a choice, refused on any other kind. |
rubric |
array, optional | What the landmark marks on this axis mean. Absent means the axis says nothing about its marks. |
Rubrics
Section titled “Rubrics”An axis’s description asks the question; a rubric answers what the marks mean:
{ "key": "hook", "label": "Hook", "weight": 2.0, "scale": 10.0, "description": "Does the chorus stay with you after one listen?", "rubric": [ { "at": 2, "label": "you could not hum it back straight after" }, { "at": 5, "label": "the chorus lands, but you have heard it land before" }, { "at": 8, "label": "you catch yourself singing it hours later" } ]}| Field | Type | Meaning |
|---|---|---|
at |
number | A mark on this axis’s own scale, from 0 to scale. Not on the 0–100 total. |
label |
string | What that mark means, in your craft’s words. |
Name only the landmarks — three or so on a scale of ten. A mark with nothing of its own reads the nearest named mark below it, so scoring a 6 against the rubric above shows the sentence written for 5. Below the lowest landmark nothing is shown.
The point is that “is this a seven” stops being a feeling and becomes a
question with an answer: the craft says what a seven is once, and every scoring
after that is measured against the same sentence. Two landmarks naming the same
mark, or a mark outside 0–scale, is refused when the profile is saved.
Axis kinds
Section titled “Axis kinds”A scale takes a number up to scale — the axis every profile had until
v0.50. A flag takes yes or no: “has a chorus”, “explicit”. Yes is worth the
whole scale and no is worth nothing, so a flag with weight 1 and scale 10
counts exactly like a scale axis scored 10 or 0. A choice takes one option
from a short list, each worth a value on the scale:
{ "key": "length", "label": "Length", "weight": 1.0, "scale": 10.0, "kind": "choice", "options": [ { "key": "short", "label": "Too short", "value": 4.0 }, { "key": "right", "label": "About right", "value": 10.0 }, { "key": "long", "label": "Runs long", "value": 6.0 } ]}| Field | Type | Meaning |
|---|---|---|
key |
string | Stored in the score snapshot. Never renamed once scores hold it. |
label |
string | What the option is called when scoring. |
value |
number | What the answer is worth, from 0 to the axis’s scale. |
All three kinds land in the same 0–100 total, and a snapshot never records which kind an axis was: a number reads on any kind, a boolean on a flag, an option key on a choice. So an axis can become a flag or a choice after scores exist against it, and the old numbers still count. An option key the profile no longer offers is skipped like a missing axis rather than counted as zero.
The scoring interface for flags and choices arrives with the versions that build on the model package; the profile document accepts them now.
Score bands, evaluated highest-min-first:
{ "key": "clip", "label": "Strong", "min": 78.0 }| Field | Type | Meaning |
|---|---|---|
key |
string | Stored on a score snapshot as its computed tier. Never rename one: the snapshots under it would be orphaned, which is why Studio’s song tiers were reworded in v0.74.2 and kept their keys. |
label |
string | Display name. |
min |
number | Minimum total (0–100) required to reach this tier. |
A song’s tiers in Studio run hold (0), audio (55), picture (68),
clip (78) — a total of 80 lands in clip, the highest threshold it clears.
A video’s run shelve, rework, post, lead: a kind’s tiers are its own.
work_meta_fields
Section titled “work_meta_fields”Craft-specific fields stored in a work’s meta JSON rather than as database
columns — columns per craft would produce a table that’s mostly NULL for
any given work:
{ "key": "bpm", "label": "BPM", "type": "number" }| Field | Type | Meaning |
|---|---|---|
key |
string | Key inside work.meta. |
label |
string | Display name for the field’s input. |
type |
"text" | "multiline" | "number" | "date" | "boolean" |
Validated in application code — SQLite doesn’t type-check inside the JSON. |
multiline is for a field that runs to paragraphs — a premise, a note on where
a piece came from. It gets a text area spanning the panel rather than a
single-line box, and it is left out of the card header: the header is the line
you glance at, and a paragraph printed there pushes the work off screen.
note_kinds
Section titled “note_kinds”The kinds a note can take, each a key and a label. A scene points at
notes of these kinds, which is what lets a board answer “every scene with
her in it” (see Scenes).
An optional key added in 0.65 — a document without it is the same document, and a note still takes any kind you write. A profile naming no kinds of note lets a scene point at any note at all; once it names some, a scene may only point at those. A workspace made before them gains the craft’s kinds on the next launch, and one you renamed or added stays yours, the way every vocabulary does.
Two flags, both optional, say what is different about a kind (v0.85, ADR 0045):
| Field | Type | Meaning |
|---|---|---|
material |
boolean, optional | Notes of the kind are what works are made from, and are spent by them — an idea, a phrase. Such a note says where it stands: fresh, used, parked or dropped. To a work ties it to the work and marks it used in one gesture, and Make it a work leaves it in the bank, used, instead of moving it. |
line |
boolean, optional | A note of the kind is one line. The Notes screen keeps the kind in a bank of its own, a row each, and leaves it out of All. |
Every shipped profile names idea (material) and phrase (material, one
line). A kind with sections — a card of the canon — can be neither; saving
says so.
Cards of the canon
Section titled “Cards of the canon”A kind that names sections is a kind of card: its notes leave the
Notes screen for the Canon, and what a card
knows is written as facts filed under those sections. A kind without
sections is a plain note. Studio ships a channel, characters, locations,
objects, groups, events, themes and lore as cards, and note as a plain
note; Novel ships characters, locations, objects, groups, events, themes
and lore; Podcast ships guests and segments; Blog ships only plain kinds.
{ "key": "character", "label": "Character", "icon": "user", "describe_from": ["looks"], "sections": [ { "key": "identity", "label": "Identity", "lenses": ["cover", "public"] }, { "key": "looks", "label": "Looks", "hint": "What a picture needs.", "lenses": ["cover"] }, { "key": "bio", "label": "Biography" }, { "key": "circle", "label": "Around", "shape": "relations" }, { "key": "where", "label": "Where it appears", "shape": "appearances" } ]}| Field | Type | Meaning |
|---|---|---|
icon |
string, optional | The glyph the kind is drawn with in the card list: radio, user, map-pin, box, users, calendar, sparkle, book, list, note, lightbulb, quote. Anything else draws a plain note. |
sections |
list, optional | What a card of this kind knows, in the order the card reads. Empty or absent: a plain note. |
root |
boolean, optional | One card of this kind per workspace — the channel, the root of the world. Studio’s channel is the root. A second card of a root kind is refused, and a profile with two root kinds is refused when it is saved. |
describe_from |
list of strings, optional | The sections the card’s description for a picture generator is written from — a person’s looks, not their biography. When a settled public fact in one of them changes, the description says it is stale. Absent: the description is written by hand and never goes stale. Each key must be a section of the kind. |
A section:
| Field | Type | Meaning |
|---|---|---|
key |
string | Identifies the section. A fact is filed under it, so renaming a key leaves its facts under a section the card no longer names; they still read, at the end of the card, under Other. |
label |
string | The section’s name on the card. |
hint |
string, optional | A line under the name: what goes here, and what reads it. |
shape |
string, optional | What an entry of the section is (below). Statements when absent. |
lenses |
list, optional | The outward tasks that read this section: cover (a picture — a cover, a frame) and public (what is said in public — a release’s text, a reply). The work itself reads every section, so work need not be named. A section naming none is read by the work alone. |
kinds |
list of strings, optional | For a section of relations: the kinds of card it gathers — Studio’s character keeps the events she was part of apart from the people around her. Empty gathers every relation no other section of the card claims. |
The shapes:
| Shape | An entry is |
|---|---|
facts |
A statement. The default. |
slots |
A named value: a caption’s slot and its words, a template’s parts. |
details |
A signature detail: its name, a template in English for a generator ([[card:id]] brings in a card’s description), where it acts — cover, short, video — and whether it is on by default. |
palette |
A colour with its name. |
marks |
A variant of a mark: its code, what it means, a description for a generator, and its files. |
styles |
A house style: a brick of the style dictionary. |
relations |
Holds no facts: the card’s relations to other cards, drawn on the card. |
appearances |
Holds no facts: where the card appears, counted from the works it is the hero of, the scenes that point at it, the texts that name it, the covers that picture it and the works its facts cite. |
A fact is read by a lens when one rule says so: the work reads every
fact that is not retired; a cover and a public text read only a fact that is
settled (canon), public, on a card that is public itself, in a section
naming that lens. The Canon screen dims what a lens does not see, the MCP
tool answers through it, and every prompt reads through it.
Saving a profile checks the canon it names: kinds and sections are unique
by key, a section’s shape and lenses are known words, describe_from names
sections of the kind, a section of relations names kinds of card the
profile has, and only one kind is the root. A workspace made before 0.84
gains the craft’s sections, root and glyphs on the next launch; a kind whose
sections you already changed keeps yours.
relation_kinds
Section titled “relation_kinds”The kinds a relation between two cards can take, each a key and a
label — Studio’s family, partner, friend, neighbour, colleague,
pet, member, place, owner, event and other. A relation is drawn
once per pair, with a kind from this list, a word for each side
(“neighbour” one way, “neighbour and first listener” the other) and a layer
of its own. A kind the profile does not have is refused, and a profile with
two relation kinds under one key is refused when it is saved. A profile
naming none has not decided, and any word goes — the leniency a kind of
note has.
duration is a number of seconds, and the Scenes tab divides it between
the scenes of a board (see Scenes).
It shipped as text holding 3:45 until 0.65; the migration retyped it and
converted what was already written. A workspace that had retyped the field
itself keeps its own version, the way every renamed field does — the board
still reads 3:45 there, but the Overview box will not hold you to a number.
Music defines the reference numbers (bpm, key, duration, language) plus
what a song is about: tagline, direction, mood, tempo, vocal,
perspective, instruments and a multiline premise. Podcast additionally
uses date (recorded_on) and boolean (explicit), so every type is in
active use across the built-ins.
A field added to a built-in profile after your workspace was created arrives on
the next launch, appended after your own. Fields are matched by key: one you
renamed or retyped keeps your version, and one you deleted comes back.
version_roles
Section titled “version_roles”The independent bodies a work carries:
{ "key": "review", "label": "Review", "comments_on": "lyrics", "body": "markdown" }| Field | Type | Meaning |
|---|---|---|
key |
string | Stored on each version. |
label |
string | What the lane is called. |
comments_on |
string, optional | The role this one discusses. |
body |
plain or markdown, optional |
How a body in this role is read. Defaults to plain. |
counts_as_version |
boolean, optional | Whether a body in this role is a time the work was written. Defaults to “yes, unless it comments on something”. |
body says how the text is shown, never how it is stored: a plain role is
a monospace column exactly as typed — lyrics, a style prompt — and a markdown
role draws its headings, quotes and tables — a review, a chapter. The craft
says which, because the application cannot tell a lyric sheet from an essay by
looking at it. A value outside the two reads as plain, and the profile editor
says so. A workspace written before the field gains the shipped reading for
the roles it still shares by key; a choice you made yourself stays.
Most roles stand alone: lyrics and style advance separately, and the Versions
tab shows one lane at a time. A role that names comments_on is different —
it is written about another role, so it opens beside what it discusses
rather than in its own lane, matched revision for revision. A review of
revision 2 says nothing about revision 5, so it is not shown there.
Music ships review (a read against the axes), critique (line-by-line)
and neighbours (where the lyric says a spent image in other words, and which
works it says the same thing as), all commenting on lyrics. A profile that names no commentary role keeps the
Versions tab exactly as it was.
counts_as_version answers a different question: how many times the work
has been written, which is the number the catalogue’s Versions column shows.
Commentary is excluded by definition — a critique is not a draft of the song —
but so is anything else the craft says is written about the work rather than
being it. Music sets counts_as_version: false on style: a style prompt
stands on its own and comments on nothing, so the count read a song written
twice as having six versions. The role keeps its own lane, its own revisions
and its own history; it simply is not counted as the song.
Flags a work can be given by hand, beside the status the app derives:
{ "key": "working", "label": "Working on it", "colour": "warn", "icon": "wrench" }| Field | Type | Meaning |
|---|---|---|
key |
string | Stored on the work; renaming the label never touches a work. |
label |
string | What the chip says. |
colour |
"plain" | "accent" | "good" | "warn" | "bad" | "info" |
A palette role rather than a colour, so it reads in both themes. Defaults to plain. |
icon |
string | A glyph beside the word, from this list: tag, wrench, clock, circle-help, flame, star, bookmark, eye, check, heart, lightbulb, thumbs-up, gauge, flag, pin. A name outside it draws tag; the word is always there. Studio ships wrench, circle-help and thumbs-up for its three marks, and a mark without a glyph gains the shipped one at the next start. |
A mark is not a status: the status says where a work stands in the process and is worked out from what happened, while a mark says something the data cannot know — that you are fighting with this one, or that it is the good one. It derives nothing and blocks nothing.
It is not a tag either. Tags are free text — the author’s own words for what a work is, completed from what the workspace already holds — and they stay with the work. A mark comes from this short list and comes off again. Keeping them apart means clearing the flags does not clear the vocabulary.
Marks are optional: a profile written before they existed loads with none, and the built-in ones arrive in an existing workspace on the next launch.
stages
Section titled “stages”The stops on the way from an idea to a finished work — what the dial beside the star snaps to:
{ "key": "polish", "label": "Polishing", "percent": 80, "colour": "accent" }| Field | Type | Meaning |
|---|---|---|
key |
string | Names the stop. The work stores the percentage, not this, so renaming a stop never touches a work. |
label |
string | What the tooltip says, and what stage: accepts in the catalogue’s box. |
percent |
integer 0–100 | Where the stop sits, and how much of the dial it fills. |
colour |
"plain" | "accent" | "good" | "warn" | "bad" | "info" |
A palette role rather than a colour. Defaults to plain. |
A stage is not a status. A status says where the work stands in the process
and is derived from facts — it was scored, a release was booked, it shipped. A
stage says how finished the work itself is, which no fact can answer: a song
can have a complete lyric and still be three verses of placeholder, and only
its author knows that. The two are independent, and a work is routinely
Scored and Polishing at once.
It is not a mark either: a mark is raised or not, and the question here is one of degree.
The work stores a percentage, not a key. A dial is a fraction by nature, and a second table mapping key to fraction would be a second truth about the same thing. A percentage between two stops belongs to the lower one — 79 is still Polishing, because rounding up would tell you a song is finished when you said it was nearly.
Unset is a third state. A work nobody has judged draws an empty ring, and it
is not the same as a work judged to be a bare idea at 0 — that one draws a dot.
Backspace on the dial, or clicking the stop it already stands on, takes a work
back to unset.
Stages are optional: a profile that names none uses the line’s seven — Idea, Rough draft, Half there, Nearly there, Polishing, Finished, Final, at 0, 17, 33, 50, 67, 83 and 100. A dial with nothing to snap to is not a dial, so this is one of the few places the app answers for a craft that said nothing.
Final is the last stop rather than Finished because those are two different claims: a work can be finished for a month before it goes out, and until v0.74.2 nothing on a row told them apart. It is set by hand like every other stop — the stage stays a judgement, and whether a release actually shipped is what the status already derives.
Upgrading a profile renumbers its stops. A stop that keeps a key the shipped
profile still has takes that profile’s percent, while its label stays
whatever you renamed it to. This is the one field of a vocabulary entry that
cannot be left alone: a stage is not a word but a word at a position, and a
stored profile that gained Final at 100 while keeping Finished at 100 would
hold two stops on one number. A stop you added yourself is not in the shipped
list and is never touched.
rhythm
Section titled “rhythm”The pace releases go out at:
{ "every_days": 3, "default_time": "12:00" }| Field | Type | Meaning |
|---|---|---|
every_days |
number | Days the auto-layout keeps between releases. 1 is daily. |
default_time |
string, optional | Time of day (HH:MM) a release usually ships, shown beside the date when editing a release. |
Calendar slots stay whole days, and the usual time lives here as a single fact about the craft. The original reason was that a date was contested per day and a time would have split the contest; the contest went in v0.44 and the shape stayed, because the calendar is read a month at a time and a column of clock times is not what makes a month legible. Since v0.50 a release can also carry its own time of day and time zone, for the platforms that ask for one; this default is what a release starts from.
catalogue_columns
Section titled “catalogue_columns”Which columns the catalogue shows, by column id, in order:
"catalogue_columns": ["title", "marks", "tier", "total", "scored", "updated"]A novel and a record are read down different columns, which is why the list belongs to the profile rather than to the machine. kilna writes it when you pick columns in the catalogue; you can also edit it here. An id this build does not know is dropped on read rather than refused, and the title is always shown. Absent means the catalogue’s own default — and a workspace from before the field opens on what the machine remembered, writing that list here once, so the move costs nobody their layout.
catalogue_columns_by_kind
Section titled “catalogue_columns_by_kind”The columns the catalogue shows while it is narrowed to one kind of work, by kind key:
"catalogue_columns_by_kind": { "video": ["title", "marks", "versions", "updated"]}A video is read down other columns than a song. kilna writes an entry when you
choose columns with the catalogue narrowed to that kind; a kind without an
entry reads down catalogue_columns. Optional — a document without the key is
the same document, and a build that does not know an id drops it on read.
rhythm may be absent, which is how a profile written before the field
existed loads: the auto-layout then refuses with an explanation instead of
inventing a pace. A workspace whose stored copy has no rhythm gains the
shipped one at the next start; a pace you set yourself is left alone.
shot_types, scene_blocks and cover_blocks
Section titled “shot_types, scene_blocks and cover_blocks”Three optional lists on a work kind. The first two are for a kind whose
works are made in scenes — Studio’s video and short. A kind that names
neither has no storyboard, and its cards draw no Scenes
tab. A document without them is the same document: format 2 is not changed.
{ "key": "video", "label": "Video", "shot_types": [ { "key": "wide", "label": "Wide" }, { "key": "close", "label": "Close-up" }, { "key": "detail", "label": "Detail" } ], "scene_blocks": [ { "key": "still", "label": "Still frame", "hint": "The frame as a picture: subject, light, lens, mood." }, { "key": "motion", "label": "Animation", "hint": "What moves, and how the camera moves, from that frame." }, { "key": "negative", "label": "Negative", "hint": "What must not appear." } ], "cover_blocks": [ { "key": "picture", "label": "Picture", "hint": "What the thumbnail shows: subject, framing, light, mood." }, { "key": "negative", "label": "Negative", "hint": "What must not appear on it." }, { "key": "typography", "label": "Typography", "hint": "The words on the cover, and how they sit: size, weight, place." } ]}| Field | Type | Meaning |
|---|---|---|
shot_types[].key |
string | Stored on the scene as its kind of shot. A scene can only take a key this list names — the board is narrowed by it, and a kind of shot typed freely would never be found. |
shot_types[].label |
string | What the strip and the picker show. Renamable. |
scene_blocks[].key |
string | The key a scene stores that block’s text under, and the key a template (v0.62) will read. A block under a key this list does not name is refused on write. |
scene_blocks[].label |
string | The caption over the box and on its copy button. |
scene_blocks[].hint |
string, optional | A line under the box saying what goes in it. |
cover_blocks[].key |
string | The key a work stores that block’s text under, in work.cover. A block under a key this list does not name is refused on write, the same as a scene block. |
cover_blocks[].label |
string | The caption over the box and on its copy button. |
cover_blocks[].hint |
string, optional | A line under the box saying what goes in it. |
Studio also gives both kinds a context version role
for what every scene shares — the hero, the palette, the lens. A workspace
that already has the video kinds gains the two lists at the next start,
where its stored copy names none; a list you narrowed is left alone.
cover_blocks is the same shape as scene_blocks and for the same reason:
the craft names the parts of a cover’s prompt, the code does not know them.
It is not about the storyboard — a song’s cover is its album’s and a song has
no scenes, so a kind can carry either list without the other. Studio gives
video and short three blocks each: picture, negative and
typography — what the thumbnail shows, what must not appear on it, and the
words that sit on it. A kind that names none has no cover prompt, and the
screen draws nothing where the prompt would be. Added in v0.73 — a document
without it is the same document, and a workspace that already has the video
kinds gains it at the next start, where its stored copy names none; a list
you narrowed yourself is left alone, the way scene_blocks is.
style_types
Section titled “style_types”An optional list on the profile, not on a work kind: the types a style can be. A style is a part a picture prompt is built from, and the same character stands in the videos and in the shorts — so the dictionary belongs to the workspace, not to one kind of work. A profile that names none has no style dictionary, and the rail draws no Styles entry. Added in v0.75 — a document without it is the same document.
{ "style_types": [ { "key": "image-style", "label": { "en": "Image style", "ru": "Стиль изображения" }, "hint": { "en": "Describe the render technique and the look: medium, film stock and grain, palette, light, processing. Not what is in the picture — only how it looks.", "ru": "Опиши технику рендера и вид: медиум, плёнка и зерно, палитра, свет, обработка. Не что на картинке — только как это выглядит." }, "icon": "palette" }, { "key": "character", "label": { "en": "Character", "ru": "Персонаж" }, "hint": { "en": "Describe the person as such: age, build, face, hair, distinguishing marks. No clothing, no surroundings.", "ru": "Опиши человека как такового: возраст, телосложение, лицо, волосы, приметы. Без одежды и окружения." }, "icon": "user" } ]}| Field | Type | Meaning |
|---|---|---|
key |
string | Stored on the style as its type. A style can only take a key this list names — the dictionary is grouped and narrowed by it. A style written under a key later dropped from the document still reads, and shows the key. |
label |
string or map | What the chips, the groups and the picker show. Renamable, and bilingual like every other word of the craft. |
hint |
string or map, optional | What to describe for a style of this type. Reaches the assistant when it describes one, and never reaches a generator. |
icon |
string, optional | A glyph from the closed set: palette, user, shirt, tree, type, camera, move, layers, grid. A name outside it draws the generic shape. |
The hint is what makes one dictionary richer than several flat ones.
Given the same photograph, image-style asks for the render technique and
character asks for the person, because the type says which question is
being answered. A type with no hint tells the assistant only its label.
The order of the list is the order the dictionary reads in — the groups on the screen, and the chips above them — rather than the alphabet.
A workspace that already exists gains the shipped types at the next start; one you renamed or added stays yours, matched by key, and a hint or a glyph is filled in only where your stored copy names none.
Studio ships nine: image-style, character, look, environment,
typography, angle, pose, layering and composition.
prompts
Section titled “prompts”Assistant actions scoped to this profile — see Writing a plugin for the separate plugin protocol; prompts are a simpler, built-in mechanism for the assistant panel specifically.
{ "key": "critique", "label": "Critique the lyrics", "description": "Weak lines, tired images, anything that does not sing.", "icon": "spell-check", "template": "Here are the lyrics of a song called \"{title}\".\n\n{role:lyrics}\n\nBe specific and be hard on it: which lines are weak, which images are worn out, what would you cut? Do not rewrite it — say what is wrong."}| Field | Type | Meaning |
|---|---|---|
key |
string | Identifies the prompt. Also what kilna recognises a running action by, so an action started from a card cannot be started twice at once. |
label |
string | Button text, in the AI panel and on a work’s Overview tab. |
description |
string, optional | What the action does, in a sentence. Shown when the button is hovered, so the button itself can stay short. |
icon |
string, optional | The glyph on the button, from the list below. A name kilna does not know draws the generic spark. |
template |
string | The message sent to Claude, with placeholders filled per work. Keep it short: the method carries the how. |
method |
string, optional | How the action is done — the role the assistant takes, what it checks and in what order, the shape of the answer, what it must never say. Markdown; appended to the model’s system prompt on every turn of the chat the action opened. See ADR 0021. |
produces |
string, optional | What the action asks for beyond prose: "score"; "version:<role>" — the whole answer offered as a version in that role; "scenes" — a storyboard to replace the board, or "scenes:add" and "scenes:revise"; "comment" — a comment read off a screenshot; "reply" — the whole answer offered as the reply to a comment; "canon" — cards, facts and relations for the canon; "card-prompt" — the whole answer offered as a card’s description for a picture generator, only in an action with "scope": "canon". Anything else loads as prose and is refused when the profile is saved. |
kinds |
list of strings, optional | The work kinds the action is offered on. Absent or empty is every kind. An action that reads {role:lyrics} is for the kinds that have lyrics — Studio’s song actions say ["song"] — because a button for it on a video would send a prompt with a hole in it. |
scope |
string, optional | "scene" for an action started from a row of the storyboard: it reads the row as {scene}, is offered on each scene rather than above the board, and must produce scenes:revise. "style" for one about a brick of the style dictionary: it is offered on the dictionary and on neither bar of a card, and aimed at a work it is refused by name. "comment" for one about a comment: it must produce comment (read a pasted screenshot) or reply (draft the answer), and is offered on the comments only. "canon" for one about a card of the canon: it is given the card whole ahead of its template — the facts as the work reads them and the card’s free note, or for card-prompt the settled public facts it is described from and its reference pictures — reads no placeholders, is offered on the card and nowhere else, and must produce canon or card-prompt. "selection" for one about lines selected in a work’s text: it reads them as {selection}, is offered on a selection and nowhere else, and started without one it is refused. Absent is the work. |
Keep the label to a word or two. The button carries a glyph and that label;
what the action does belongs in description, which is the tooltip. A row of
five actions spelled out in full — Critique the lyrics, Suggest a revision,
Draft a style prompt — is five sentences where the eye wants five marks, and
buttons like that are neither read nor remembered.
The names icon accepts: sparkles, wand, pen, spell-check, scroll,
tags, gauge, music, film, clapperboard, image, list, lightbulb,
palette, eye, reply, book, quote, orbit.
The same prompt is offered in three places: in the panel it fills the composer
for you to read and send, typing / reaches the same list from the keyboard,
and on Overview — and on Scenes, for a kind with a storyboard — a click
starts it at once in a chat of its own, with an eye beside the button that
shows exactly what the click sends. See
The assistant.
produces
Section titled “produces”An action with "produces": "score" asks the assistant to end its reply with a
json block holding a value for every axis of the work’s kind. kilna appends
that instruction itself — the axes with their labels, descriptions, rubric
marks and weights, and the tiers with what the total means — so the template
only has to say what to judge and how honestly. A template that names axes of
its own contradicts the instruction; the shipped score templates no longer
do, and a stored copy still reading exactly as it shipped before v0.61 follows.
An action with "produces": "version:<role>" offers its whole answer as a
version in that role — Studio’s critique produces version:critique. The
instruction kilna appends tells the model to write only the text, with no
preamble, since the answer is kept word for word.
The answer comes back with the numbers laid out and a button that applies them as an ordinary score. kilna never lets the assistant write to the workspace: a proposal is read before it becomes a fact, always. A value past an axis’s scale is clamped rather than refused, an axis the profile does not have is shown and not applied, and an answer that ignored the instruction and replied in prose is simply an answer.
An action with "produces": "scenes" asks for a storyboard: kilna appends the
shape of the json block with the kind’s kinds of shot and prompt blocks spelled
out, and the answer comes back as the same proposal an agent’s propose_scenes
makes — the board as a table, every block under it, and one button. scenes
is the whole board, replacing what is there by number; scenes:add puts the
scenes after the last; scenes:revise changes only the scenes it numbers, and
only in the fields it gives — the shape a scene action needs. A block in the
wrong words — a kind of shot the profile does not have, a revision that
numbers another scene — is not silently nothing: the chat says why there is
no button. See Scenes.
An action with "scope": "comment" and "produces": "comment" reads a pasted
screenshot. kilna attaches the picture, says which channel it was pasted under
and what day it is, and asks for a json block with the author, the text word for
word, the day it was written and the title it was written under. The channel is
never taken from the answer: the picture cannot say which channel it is. One with
"produces": "reply" drafts the answer to a comment: kilna gives the comment,
the work it is under with the opening of its text, and the replies already
posted on the same channel as the voice to write in; the whole answer is the
reply. Both come back as proposals kept on the comments screen, with every field
open to correction first. Every shipped profile carries read-comment and
reply-to-comment; see Comments.
An action with "produces": "canon" asks for a package for the canon: kilna
appends every kind of card with its sections by key, the kinds of relation,
the layers and the states, and the shape of the json block — new cards,
facts on cards old or new, changes to facts already there, relations. The
answer comes back as a proposal with every item under a box to keep it or
leave it out, and with the facts each one would contradict shown beside it
before anything is written. A fact the assistant proposes is a draft until
you settle it. Started on a work, the version it read is every fact’s
source; started on a card, the card is where its facts go. Studio ships
gather-canon (on a song: gather its facts), to-canon (on a selection:
the selected lines as facts) and, on a card, gather-card (facts out of
the card’s own free note) and describe-card ("produces": "card-prompt":
a description in English for a picture generator, written from the settled
public facts of the kind’s describe_from sections — the answer is kept as
the card’s description). See The canon.
Every shipped profile carries a score action. Anything else declaring
produces gets the same treatment; an unrecognised value is ignored when the
profile loads, so a profile written for a future kilna still opens — and named
when the profile is saved, so a typo does not become an action that never
proposes.
Template placeholders
Section titled “Template placeholders”{title},{kind},{status}— the work’s own fields.{body}— the current version’s body, whatever role that happens to be; or, for an action started from the versions tab, the revision open there.{role:lyrics},{role:style}, … — the latest revision of a specific version role, regardless of which one is current. This is what lets a prompt like Novel’s “Check against the outline” pull in both{role:text}and{role:outline}at once. An action started on a revision reads that revision for its own role. A role the work has no version in yet is a refusal — “Harbour lights” has no Plot yet: write it first — not an empty gap.{scenes}— the storyboard as a table: number, section, seconds, kind of shot, description. The blocks stay out. An empty board says so.{scene}— the scene a scene action was started on, whole: its fields and every block it holds. Only in an action with"scope": "scene".{styles}— the styles picked for this run, each under the label of its type and followed by its description, in the order they were picked. It is how a prompt is built out of parts rather than glued: the assistant is told which sentence is the place and which is the person. A style with nothing written yet goes in by name, marked as not described. The author’s steer never does — it is an instruction about writing the description, not part of one. A template that reads{styles}in a profile naming no style types is refused on save.{canon}— the canon as a work reads it: every card by name and id, then in full — through the work’s lens — the cards the work is about: its own heroes, the cards on its board and the cards its text names.{canon:cover}and{canon:public}— the same, as a picture and as a public text may read it: only public cards are named, only settled public facts of the sections given to that lens are read, and the channel’s root card is read whole as well — its voice, marks and bans are about every work. A release field’s template may read the canon only as{canon:public}: a release goes out in public, and{canon}would carry the internal layer into its description. That is refused when the profile is saved, as{selection}in a release field is.{selection}— the lines selected in the text, word for word. Only in an action with"scope": "selection", which must read it.{register}— the register of repeats: wording grouped by strictness, then the spent images, scenes and devices, each with how many works carry it now and its note, and what the text the action reads already takes from it word for word.{neighbours}— the few works whose words stand closest to the text the action reads — shared words weighed by how rare each is across the works — each with its title, kind, the words it shares and its current text. Where to look for the same thing said twice, not yet a judgement of meaning.{donor}— the first work this one was made from, as “Harbour lights” (song);{donor:lyrics},{donor:style}, … — the latest revision of that role on the donor. A work made from nothing refuses the action and says to link the source first.
Saving a profile checks every template against the vocabulary it reads: a
placeholder nothing fills, a {role:x} not every kind of the action has,
{scenes} on a kind without a storyboard, a scene action that never reads
{scene} — each is named and the save is refused, the way a duplicate axis
key is. This is the check the predecessor lacked: a template edited to lose
the placeholder carrying the text sent critiques of nothing for a month. At
render time an unrecognized placeholder is still left visible rather than
silently blanked, for a stored profile that predates the check.
prompts defaults to an empty list when absent, so a profile written before
the AI panel existed still loads without modification.