Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,34 @@ Entries are newest first. Keep their headings stable. Publish material correctio
new entries so readers who advanced their checkpoint see them. Entries explain what changed, why, when it applies,
and any useful small example or verification; they need not reproduce commit logs.

## 2026-10-12: State Machine events First Draft cannot generate are stubs

**Skills — released in plugin 0.8.14.** Once the First Draft service deploys the matching Compiler, a required,
unconditional State Machine alone on its Entity stays in AASM when an event has effects the Compiler cannot generate.
Before, one such event made the whole machine storage only: a string column with no events, AASM initialization, or
direct-assignment guard, even for the events the Compiler could generate. Now the `aasm` block lists the generated
events, and each other event is emitted as three methods, `<m>!`, `<m>`, and `may_<m>?`, under the names AASM would
give it, each raising `NotImplementedError` until it is written. An event is generated when it is effect-free or sets
one optional, mutable, non-derived `datetime` Field on the same record to the current time or `nil`. An event with
two effects, or one that sets a Field on another Entity, a required Field, or a `date` Field, is a stub. A Ticket
`status` machine whose `assign` event sets `assigned_at` to the current time, and whose `resolve` event sets
`desk.last_resolved_at`, emits `event :assign` in the `aasm :status` block and `Ticket#resolve!`, `#resolve`, and
`#may_resolve?` stubs. The README's Behavior to write lists each stubbed event with its states, effects, and Plan
pointer, and says to add the event to the `aasm` block and delete its three stubs. The State Machine spec's allowed
and rejected examples for that event fail through the stub until then. No generated page fires an event, so no page
raises. Seeds and spec setup never fire a stub: a development record whose state needs one, such as a `resolved`
Ticket, is left out of `db/seeds/development.rb`, and an example whose setup needs one is listed under Test coverage
to complete. The GapSet keeps each stubbed event's `foundation_plan.gap.state_transition.effects_not_generated`
record, whose consequence now names the stubs, and drops the machine's
`foundation_plan.gap.state_machine.partially_generated` record and the generated events' records. Optional,
conditional, and multiple-per-Entity machines still keep only storage.

The Skill's rule for a failing spec now says a stubbed scope or event raises `NotImplementedError`, as the generated
`AGENTS.md` does. The Plan reference describes the event stub instead of a storage-only machine.

Until the service deploys that Compiler, such a machine is still storage only, as in plugin 0.8.13, and no README
lists an event under Behavior to write.

## 2026-10-12: Policies First Draft cannot lower are stubs

**Skills — released in plugin 0.8.13.** Once the First Draft service deploys the matching Compiler, a Policy whose
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firstdraft/skills",
"version": "0.8.13",
"version": "0.8.14",
"description": "Portable Agent Skills for First Draft",
"license": "MIT",
"private": true,
Expand Down
2 changes: 1 addition & 1 deletion packages/claude-plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "firstdraft",
"displayName": "First Draft",
"version": "0.8.13",
"version": "0.8.14",
"description": "Author a Foundation Plan and compile a bounded Rails, iPhone, and Android application",
"author": {
"name": "First Draft",
Expand Down
2 changes: 1 addition & 1 deletion packages/claude-plugin/package.template.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firstdraft.com/claude-code",
"version": "0.8.13",
"version": "0.8.14",
"description": "First Draft Foundation Plan authoring and Compilation for Claude Code and Codex",
"license": "MIT",
"type": "module",
Expand Down
4 changes: 2 additions & 2 deletions release/compatibility.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"format": "firstdraft.release-compatibility/1",
"component": "skills",
"version": "0.8.13",
"version": "0.8.14",
"plugin_source": {
"package": "@firstdraft.com/claude-code",
"tarball_sha256": "1b57b73ab21d0bb5b51e41d650ce3761994a4412998e2924406643dbb4e1a6cb"
"tarball_sha256": "9a00700f9c83c1fc919dac8c7f209a5904754b5910f9a4eeb9d086ca20920964"
},
"requires": {
"api_contract": [
Expand Down
2 changes: 1 addition & 1 deletion skills/create-full-stack-app/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ explicit handoff. Follow [writing notes](references/modeling-guide.md#retain-imp

## Current boundary

Targets plugin 0.8.13, CLI 0.8.2, API 0.7, Plan 0.23; catalog selection is separate.
Targets plugin 0.8.14, CLI 0.8.2, API 0.7, Plan 0.23; catalog selection is separate.

- Bounded generation includes Web Accounts/Policies/Scaffolds, development data, and selected iPhone/Android
clients. [Appearance](references/foundation-plan-023.md#application-and-clients) controls theme and native colors,
Expand Down
29 changes: 15 additions & 14 deletions skills/create-full-stack-app/references/diagnostics-and-recovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interleaved output fail closed. Branch on the object's stable `error` and struct
human-readable `detail` or broad process exit status.

This plugin bundles `@firstdraft.com/[email protected]`. Its exact reviewed revision and runtime digest are owned by
[the CLI contract configuration](https://github.com/firstdraft/skills/blob/claude-v0.8.13/script/cli-contract/config.mjs)
[the CLI contract configuration](https://github.com/firstdraft/skills/blob/claude-v0.8.14/script/cli-contract/config.mjs)
at this plugin's protected release tag. Check the command surface rather than assuming the version alone
establishes compatibility. These source checks do not prove plugin/catalog publication, service authentication,
staging compatibility, or a complete user journey.
Expand Down Expand Up @@ -317,12 +317,12 @@ First Draft defect: report it and ask before installing anything. With the prere
is a defect.

A spec that fails through behavior the README's Behavior to write lists is work to do: First Draft did not write
that behavior. Write it until its specs pass. A stubbed scope raises `NotImplementedError`, a stubbed Policy rule
returns `false`, and a deletion outcome First Draft left out fails its spec with `RecordNotDestroyed`. Write every
item that section lists, including a denying rule no failing spec names. Any other failure is a First Draft defect.
Report RSpec's summary line and each such failure's rerun line (`rspec ./spec/...:LINE`) and error; the user decides
how to proceed. Never edit or delete a generated spec, skip examples, or change the commands to make the run pass.
Change a generated spec only when the owner changes the behavior it pins.
that behavior. Write it until its specs pass. A stubbed scope or event raises `NotImplementedError`, a stubbed Policy
rule returns `false`, and a deletion outcome First Draft left out fails its spec with `RecordNotDestroyed`. Write
every item that section lists, including a denying rule no failing spec names. Any other failure is a First Draft
defect. Report RSpec's summary line and each such failure's rerun line (`rspec ./spec/...:LINE`) and error; the user
decides how to proceed. Never edit or delete a generated spec, skip examples, or change the commands to make the run
pass. Change a generated spec only when the owner changes the behavior it pins.

When the run passes and every item Behavior to write lists is written, continue to the preview. Otherwise ask before
the preview, even one requested earlier. If the user still wants it, first name the pages or flows the failing specs
Expand Down Expand Up @@ -384,13 +384,14 @@ server Publication carried the notes.

During continuation, read the app's `AGENTS.md`, README, and retained implementation notes before selecting work.
Run the specs before other work. A spec that fails through behavior the README's Behavior to write lists is work to
do: write that behavior until its specs pass. A stubbed scope raises `NotImplementedError`, a stubbed Policy rule
returns `false`, and a deletion outcome First Draft left out fails its spec with `RecordNotDestroyed`. Write every
item that section lists, including a denying rule no failing spec names. Report any other failure as a First Draft
defect. Never edit or delete a generated spec to make it pass; change one only when the owner changes the behavior
it pins. Then implement the agreed behavior and its acceptance examples in ordinary source; keep unresolved product
questions open. Existing app feature work does not require another Plan submission, Compile, or CLI capability
probe. Update the notes to reflect remaining behavior without making app setup, runtime, or tests depend on them.
do: write that behavior until its specs pass. A stubbed scope or event raises `NotImplementedError`, a stubbed Policy
rule returns `false`, and a deletion outcome First Draft left out fails its spec with `RecordNotDestroyed`. Write
every item that section lists, including a denying rule no failing spec names. Report any other failure as a First
Draft defect. Never edit or delete a generated spec to make it pass; change one only when the owner changes the
behavior it pins. Then implement the agreed behavior and its acceptance examples in ordinary source; keep unresolved
product questions open. Existing app feature work does not require another Plan submission, Compile, or CLI
capability probe. Update the notes to reflect remaining behavior without making app setup, runtime, or tests depend
on them.

#### UI continuation

Expand Down
25 changes: 17 additions & 8 deletions skills/create-full-stack-app/references/foundation-plan-023.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ The bundled schema was copied byte-for-byte from
release or execution evidence.

This plugin's bundled CLI and pinned contract check use the exact reviewed CLI revision and runtime digest in
[the CLI contract configuration](https://github.com/firstdraft/skills/blob/claude-v0.8.13/script/cli-contract/config.mjs)
[the CLI contract configuration](https://github.com/firstdraft/skills/blob/claude-v0.8.14/script/cli-contract/config.mjs)
at this plugin's protected release tag, as contract provenance rather than release or execution evidence. The CLI
exposes `generate uuid`, `generate application-key`, `plan init`, `plan push`,
`plan status`, local `plan compile` (equivalent to `--output .`), explicit `plan compile --github`,
Expand Down Expand Up @@ -381,14 +381,23 @@ or otherwise unsupported Association descriptors remain exact gaps. A Field may

That is the conditional import list, not the complete schema vocabulary. A State Machine Field retains its states,
initial state, transitions, and transition effects. The current target realizes exactly one required unconditional
State Machine per Entity when the AASM helper surface is safe, with effect-free transitions or one bounded local
datetime `set_field` effect per transition. AASM owns initialization, validation, named events, state scopes, and the
admitted effects; behavior-emitted state columns keep `NOT NULL` without a SQL default. Native naming options
resolve supported helper collisions. Required storage-only states retain the initial-state SQL default and
State Machine per Entity when the AASM helper surface is safe. AASM owns initialization, validation, named events,
state scopes, and the generated effects; behavior-emitted state columns keep `NOT NULL` without a SQL default. Native
naming options resolve supported helper collisions. An event is generated when it is effect-free or has one
`set_field` effect that sets an optional, mutable, non-derived `datetime` Field on the same record to the current
time or `nil`. Any other event, such as one with two effects or one that sets a Field on another Entity, a required
Field, or a `date` Field, is emitted as a stub: its bang form, plain form, and `may_` predicate keep the names AASM
would give it and raise `NotImplementedError` until it is written. The machine keeps its AASM states, initialization,
validation, and direct-assignment guard even when every event is a stub. The README's Behavior to write lists each
stubbed event, and its State Machine examples fail until then; no generated page fires an event. Development seeds
and spec setup never fire a stub, so a development record whose authored state needs one is left out of
`db/seeds/development.rb` with a development-data gap, and an example whose setup needs one is listed under Test
coverage to complete. The GapSet keeps the event's `foundation_plan.gap.state_transition.effects_not_generated`
record, whose consequence names the stubs. Required storage-only states retain the initial-state SQL default and
inclusion validation; optional machines keep nullable storage without a default. Optional, conditional,
multiple-per-Entity, guarded, cross-Entity, multi-effect, unresolved helper collisions, and broader effect shapes
retain storage with behavior gaps. Failed transitions retain ordinary AASM/Rails object and transaction behavior;
an application may need to reload or reset a failed object before reusing it.
multiple-per-Entity, and guarded machines, and those with an unresolved helper collision, retain storage with
behavior gaps. Failed transitions retain ordinary AASM/Rails object and transaction behavior; an application may need
to reload or reset a failed object before reusing it.
AASM 6.0.0 namespaced state scopes can query a prefixed value instead of the stored state: `status_active` can
query `status_active` when the stored state is `active`. Check an affected application query against its stored
state; no generated workaround or namespace-based scope suppression is supplied. With `no_direct_assignment: true`,
Expand Down
Loading