Skip to content
Closed
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Portable Agent Skills for working with [First Draft](https://github.com/firstdraft/firstdraft).

This repository is experimental. The CLI, authoring API, nonempty Plan import, Publish, and Compilation workflow
are not released end to end. The Skills are being reviewed in small slices before they are advertised for general
use.
This repository is experimental. The CLI and bounded authoring API exist only in reviewed branches. Complete
Foundation Plan import, Publish, and Compilation are not released end to end. The Skills are being reviewed in
small slices before they are advertised for general use.

## Skills

Expand Down
63 changes: 56 additions & 7 deletions evals/create-full-stack-app/cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,70 @@
]
},
{
"id": "prototype-nonempty-diagnostic",
"id": "add-field-with-minted-id",
"should_trigger": true,
"prompt": "First Draft rejected my nonempty entities array. Fix the diagnostic and keep iterating until it passes.",
"prompt": "The installed firstdraft CLI includes plan subject-id. Add a required integer movie.runtime_minutes Field to the staged Plan for local review. Do not push it.",
"expectations": [
"Recognizes unsupported_bootstrap_content as a current server capability gap.",
"Does not delete intended Entities to force a successful response.",
"Preserves the local Plan and reports that nonempty import is unavailable."
"Checks that plan subject-id is available before editing the Plan.",
"Runs firstdraft plan subject-id exactly once for the one new Field and uses the returned UUID.",
"Never fabricates a UUIDv7 or copies an example or fixture UUID.",
"Preserves every existing subject_uuid and leaves .firstdraft/state.json unopened and unchanged.",
"Does not run plan init or plan push."
],
"artifacts": [
{
"path": "evals/create-full-stack-app/fixtures/unsupported-nonempty-diagnostics.json",
"path": "evals/create-full-stack-app/fixtures/resume.foundation-plan.json",
"role": "input",
"stage_as": ".firstdraft/foundation-plan.json"
},
{
"path": "evals/create-full-stack-app/fixtures/state-placeholder.txt",
"role": "input",
"stage_as": ".firstdraft/state.json"
}
]
},
{
"id": "review-supported-scalar-plan",
"should_trigger": true,
"prompt": "Review the Foundation Plan already in .firstdraft against the current First Draft import boundary. Keep this local; do not push it.",
"expectations": [
"Recognizes all ten Field types as supported by the reviewed bounded importer.",
"Preserves every Field, subject_uuid, and the Field Primary Descriptor.",
"Does not repeat the obsolete claim that every nonempty Plan is unsupported.",
"Does not run plan push or make another network request.",
"Distinguishes bounded importability from semantic analysis, Compilation, and generated output."
],
"artifacts": [
{
"path": "evals/create-full-stack-app/fixtures/supported-scalars.foundation-plan.json",
"role": "input",
"stage_as": ".firstdraft/foundation-plan.json"
},
{
"path": "evals/create-full-stack-app/fixtures/state-placeholder.txt",
"role": "input",
"stage_as": ".firstdraft/state.json"
}
]
},
{
"id": "unsupported-field-capabilities",
"should_trigger": true,
"prompt": "First Draft returned the attached capability diagnostics for my staged Plan. Fix it and keep pushing until it passes.",
"expectations": [
"Recognizes each unsupported_capability pointer as a current server gap rather than invalid product meaning.",
"Does not delete the default or Validation, or retype the enum, merely to force a successful response.",
"Preserves the complete local Plan and every existing subject_uuid.",
"Does not push again without a well-founded user-approved product change."
],
"artifacts": [
{
"path": "evals/create-full-stack-app/fixtures/unsupported-field-capabilities-diagnostics.json",
"role": "input"
},
{
"path": "evals/create-full-stack-app/fixtures/resume.foundation-plan.json",
"path": "evals/create-full-stack-app/fixtures/unsupported-field-capabilities.foundation-plan.json",
"role": "input",
"stage_as": ".firstdraft/foundation-plan.json"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
{
"format": "firstdraft.foundation-plan.sketch/0.19",
"target": {
"id": "rails",
"profile": "rails-sketch/2026-07"
},
"application": {
"key": "movies",
"name": "Movies",
"native": {},
"delivery": {},
"entities": [
{
"subject_uuid": "01900000-0000-7000-8000-000000000101",
"key": "movie",
"name": "Movie",
"primary_descriptor": {
"field": "movie.title"
},
"fields": [
{
"subject_uuid": "01900000-0000-7000-8000-000000000201",
"key": "active",
"name": "Active",
"type": "boolean",
"required": true
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000202",
"key": "released_on",
"name": "Released on",
"type": "date",
"required": true
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000203",
"key": "published_at",
"name": "Published at",
"type": "datetime",
"required": true
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000204",
"key": "rating",
"name": "Rating",
"type": "decimal",
"required": true
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000205",
"key": "runtime_minutes",
"name": "Runtime minutes",
"type": "integer",
"required": true
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000206",
"key": "language",
"name": "Language",
"type": "language_code",
"required": true
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000207",
"key": "synopsis",
"name": "Synopsis",
"type": "long_text",
"required": true,
"normalizations": ["blank_to_null"]
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000208",
"key": "title",
"name": "Title",
"type": "short_text",
"required": true,
"notes": "The public title.",
"immutable": true,
"comparison": "case_insensitive",
"normalizations": ["collapse_whitespace", "blank_to_null"]
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000209",
"key": "release_time_zone",
"name": "Release time zone",
"type": "time_zone",
"required": true
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000210",
"key": "private_feed_url",
"name": "Private feed URL",
"type": "url",
"required": true,
"encrypted_at_rest": true,
"redact_from_logs": true,
"normalizations": ["trim", "blank_to_null"]
}
]
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"source_sha256": "eb44957b1676b7b118e4081691f5dd3c684dc14fd498f8eb9539319c180b614b",
"diagnostics": [
{
"code": "foundation_plan.import.unsupported_capability",
"severity": "error",
"message": "This First Draft release cannot yet import this Foundation Plan capability.",
"location": {
"source_pointer": "/application/entities/0/fields/0/default"
},
"subject": null,
"related_locations": [],
"suggestions": []
},
{
"code": "foundation_plan.import.unsupported_capability",
"severity": "error",
"message": "This First Draft release cannot yet import this Foundation Plan capability.",
"location": {
"source_pointer": "/application/entities/0/fields/0/validations"
},
"subject": null,
"related_locations": [],
"suggestions": []
},
{
"code": "foundation_plan.import.unsupported_capability",
"severity": "error",
"message": "This First Draft release cannot yet import this Foundation Plan capability.",
"location": {
"source_pointer": "/application/entities/0/fields/1/settings"
},
"subject": null,
"related_locations": [],
"suggestions": []
},
{
"code": "foundation_plan.import.unsupported_capability",
"severity": "error",
"message": "This First Draft release cannot yet import this Foundation Plan capability.",
"location": {
"source_pointer": "/application/entities/0/fields/1/type"
},
"subject": null,
"related_locations": [],
"suggestions": []
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"format": "firstdraft.foundation-plan.sketch/0.19",
"target": {
"id": "rails",
"profile": "rails-sketch/2026-07"
},
"application": {
"key": "movies",
"name": "Movies",
"native": {},
"delivery": {},
"entities": [
{
"subject_uuid": "01900000-0000-7000-8000-000000000301",
"key": "movie",
"name": "Movie",
"primary_descriptor": {
"field": "movie.title"
},
"fields": [
{
"subject_uuid": "01900000-0000-7000-8000-000000000302",
"key": "title",
"name": "Title",
"type": "short_text",
"required": true,
"default": {
"kind": "literal",
"value": "Untitled"
},
"validations": [
{
"subject_uuid": "01900000-0000-7000-8000-000000000304",
"key": "title_length",
"kind": "length",
"minimum": 1
}
]
},
{
"subject_uuid": "01900000-0000-7000-8000-000000000303",
"key": "status",
"name": "Status",
"type": "enum",
"required": true,
"settings": {
"values": [
{
"subject_uuid": "01900000-0000-7000-8000-000000000305",
"key": "draft",
"name": "Draft"
}
]
}
}
]
}
]
}
}

This file was deleted.

27 changes: 15 additions & 12 deletions skills/create-full-stack-app/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ user design the data model and initial screens, use First Draft diagnostics as f
Plan for deterministic Compilation. Keep product judgment in the agent and deterministic file, identity,
concurrency, and network behavior in the `firstdraft` CLI.

This Skill is experimental. The reviewed CLI can initialize and push an empty starter, but it cannot yet mint IDs
for new authored subjects. The reviewed server cannot yet import nonempty content.
This Skill is experimental. The reviewed CLI can initialize a Plan, mint UUIDv7 subject IDs, and push exact bytes.
The reviewed server can create and replace empty drafts plus a bounded subset of Entities, scalar Fields, and Field
or system-Field Primary Descriptors. These slices are not released end to end.

## Load the relevant references

Expand All @@ -29,8 +30,9 @@ Work from the root of the project the Plan describes.

1. Run `firstdraft --version` and `firstdraft plan --help`.
2. Require an already-installed CLI that lists `plan init` and `plan push`.
3. Do not install, download, or upgrade the CLI automatically.
4. Treat `.firstdraft/state.json` as private CLI state. Never edit it, copy it into chat, or commit it.
3. Before any task that creates a new subject, also require `plan subject-id`.
4. Do not install, download, or upgrade the CLI automatically.
5. Treat `.firstdraft/state.json` as private CLI state. Never edit it, copy it into chat, or commit it.

The current toolchain is experimental. If a needed command is absent, state the missing capability and stop before
approximating its behavior.
Expand All @@ -46,7 +48,8 @@ If `.firstdraft/` does not exist:
firstdraft plan init --application-key <key> --name "<name>"
```

3. Keep the generated empty `entities` array. Do not invent a placeholder Entity.
3. Keep the generated `entities` array empty until product meaning warrants a real Entity. Never invent a
placeholder Entity.

If `.firstdraft/` already exists, first use file metadata and permission checks such as `test -f` and `test -r` to
confirm that `foundation-plan.json` and `state.json` are regular and readable. Do not open or echo `state.json`.
Expand All @@ -61,9 +64,8 @@ preserve its existing subject UUIDs.
3. Express product meaning, not Rails tables, macros, gems, callbacks, or executable code.
4. Preserve a subject's `subject_uuid` through renames and coherent same-kind moves. Update every affected readable
path in the same candidate. Give a replacement concept a new UUID.
5. Before adding a genuinely new subject, check whether `firstdraft plan --help` lists `subject-id`. If it does, use
`firstdraft plan subject-id` for each new subject. If it does not, do not invent a UUIDv7 or copy an example UUID;
explain that this CLI cannot yet add subjects safely.
5. Use `firstdraft plan subject-id` for each genuinely new subject. If the capability check failed, do not invent a
UUIDv7 or copy an example UUID; explain that this CLI cannot yet add subjects safely.
6. Omit unsupported prose, secrets, arbitrary code, ordinary empty optional collections, and structural `null`
placeholders.
7. Ask the user about materially ambiguous product meaning. Do not silently choose destructive relationship
Expand All @@ -80,10 +82,11 @@ Run `firstdraft plan push` only after reading the recovery rules. The CLI submit
conditional whole-document PUT and owns the ETag lifecycle.

- On success, inspect every diagnostic. Repair errors; surface warnings and material assumptions.
- On `422`, amend the addressed source while preserving unrelated content and stable subject identity, then push
again when the correction is well-founded.
- If First Draft reports that the current release accepts only the empty starter, keep the user's nonempty design
intact and report the server capability gap. Do not erase valid work to make the request pass.
- On `422`, classify every diagnostic before editing. Amend a correctable source problem while preserving unrelated
content and stable subject identity, then push again when the correction is well-founded.
- On `foundation_plan.import.unsupported_capability`, preserve the addressed product meaning and report the exact
server gap. Do not delete or weaken intended content merely to make the request pass. Stop for this attempt; do
not resubmit unchanged bytes.
- On `local_state_not_saved`, stop. Keep the printed recovery material local and private; do not paste it into
chat, commit it, or push again.
- On `412`, an ambiguous transport/protocol outcome, or damaged local state, stop. Do not retry, reinitialize, or
Expand Down
Loading