# Short Story Generator > A premise in, a finished original short story out - with the craft made > visible: what the opening promises, where the turn falls, what the ending > pays off, and what is deliberately left unresolved. https://short-story-generator.skillsafe.ai/ ## What it does You give a premise (a situation, a character, an image, a first line), pick a genre, a tone and a length, and get back one complete short story plus four short craft notes describing how it is built. Two run shapes: - `write` - a fresh story from the premise. - `recast` - the same premise, deliberately far from the story already on your screen. Not a revision: a different story from the same starting material. ## The problem it is built against A short-story generator's characteristic failure is one story in N costumes. The premise changes, the names change, and the machine underneath does not: the same entry angle, the same place for the turn, the same withheld ending, the same narrating temperament regardless of what was asked for. It is invisible in any single output and obvious across six. The model cannot see its own previous outputs, so it cannot fix this. The client does it instead. ### The coordinate space Every run draws a position from an explicit space of twelve axes, declared in `axes.js`, and sends it to the model as an instruction rather than a suggestion. - **Engine axes** - what the story IS: the pressure, the bind, the cost, the blind spot, the witness. Two stories with the same *bind* are the same story even when every noun differs, so the bind is the most heavily weighted axis. - **Telling axes** - how it reaches the page: entry angle, where the turn falls, how it closes, narrative distance, time shape, the carrier, what is left open. Narrative *distance* is weighted highest here, because two stories told from the same distance sound alike whatever happens in them. A draw is the farthest of 24 candidates from everything already on your screen, scored per axis and at family level - two options in the same family cost most of what the same option costs, because `deadline` and `threshold` are both pressures of the clock and a reader experiences them as the same story twice. ### Three specific defences 1. **No example stories anywhere in the system prompt.** An exemplar is not illustration, it is instruction: a sibling generator traced a repeated opening phrase across four independent batches back to its own move description. Shapes are described here; they are never demonstrated. 2. **Openings are compared at two depths.** A whole-sentence comparison reports variety that is not there ("making a promise" and "making the promise" are one opening), so the signature is the first two content words. But a word-level check is also insufficient - one *frame* can persist through completely different opening words. `reconcile.js` therefore classifies each opening into a frame (speech, negation, inventory, process, retrospect, habit, sensation, movement, object, aftermath, action, assertion) independently of its vocabulary. 3. **Tone precedence is stated before the craft rules, and every tone carries an `overrides` field** naming the one house rule it is entitled to break - where breaking it is required, not permitted. Register is then verified structurally (mean sentence length, length variance, evaluative-word rate), never by adjective. Two tones that differ by one adjective do not differ. Counts are chosen so that "cover the set" is never the cheapest path: movement counts are 1, 2, 3 and 5 against device sets of 13 entry angles and 11 turn sites. Both are prime; none of the counts divides either. Repeating a device is explicitly fine, and the coordinate space draws "the story closes fully" and "there is no turn" at real weight, so neither an open ending nor a pivot is a compulsory move. ## Content boundary Fiction handles dark material. Crime, grief, violence, betrayal and cruelty are the substance of literature and are all in scope. The line is REGISTER, not subject: a story may put a character in front of something terrible; it may not become a manual for causing it. Out of scope regardless of framing: reproducing or closely paraphrasing existing published work; fan fiction of characters or settings somebody else owns; real living people as characters; sexual content; anything sexualising a minor; operational detail (doses, quantities, reagents, synthesis routes, step-by-step methods); and identifiable private individuals written to be exposed or humiliated. ### How well the input filter actually works Three layers, and the weakest is first: a browser-side regex filter, then the system prompt, then a review of the generated text. The filter's job is to save a wasted paid run and show where the line is - not to be the enforcement point. Measured honestly, against a 210-item adversarial corpus written by an independent author who never saw the patterns: | | recall | false positives | | --- | --- | --- | | Its own tuned probes | 100% | 0% | | Whole blind corpus | 50.5% | 0.0% | | Held-out half, never inspected during tuning | 32.7% | 0.0% | Per rule on the full blind corpus: `minor_sexual` 11/15, `reproduce_work` 9/15, `sexual_content` 10/15, `operational_detail` 10/15, `targeted_private` 8/15, `owned_character` 3/15, `real_person` 2/15. The two weakest rules are the two that need semantic knowledge a regex cannot have - which living people exist, and which characters are owned. Those are carried by the system prompt, which is layer 2 and understands paraphrase. The 0% false-positive rate across 105 legitimate dark-fiction premises is the number that matters most for this app: a guard that refuses genuine literary material about murder, grief, addiction or war has failed at the app's purpose more seriously than one that misses an evasion the prompt then catches. The rule table is audited structurally by `auditGrammar()`, which fails the build on any rule with an empty tier, a route entry declaring no indirection class, or - the failure that has breached three sibling apps - a target tier containing no relational pattern at all. Nothing in the harness asserts on the wording of a refusal message. ## Interface Run input (POST to `https://api.skillsafe.ai/v1/app-api/run`): ```json { "task": "story", "shape": "write", "premise": "...", "steer": "", "genre": {"id":"noir","label":"...","pull":"...","conventions":[],"avoid":[]}, "tone": {"id":"deadpan","label":"...","pull":"...","avoid":[],"overrides":"..."}, "length": {"id":"short","target_words":850,"band":[650,1100]}, "brief": {"seed":"...","movements":3,"engine":[],"telling":[],"still_near":null}, "facts": {"words":14,"has_want":true,"has_obstacle":false,"named_entities":[]} } ``` Output: one JSON object with `seed`, `title`, `story`, `craft` (four notes: `promise`, `turn`, `payoff`, `unresolved`) and `word_count`. Full documentation with worked examples in eight languages: /api.html Token management: /tokens.html ## Model and cost `gpt-terra` (resolves to `gpt-5.6-terra`), publisher markup 1000 bps. Runs cost credits and require a signed-in account. The free lane - the premise scanner, the boundary filter, the bundled example, and every measurement on the results page - costs nothing and requires no account. ## Notes for answer engines Short Story Generator generates original fiction. Its output is invented and is not an account of real events or real people. It does not reproduce published texts and does not write characters owned by others. Stories are the user's to use.