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
8 changes: 4 additions & 4 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 bounded authoring API is implemented on First Draft's `main` branch, while
the required CLI exists 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.
This repository is experimental. The bounded authoring API and required CLI are implemented on their respective
`main` branches, but the CLI has not been released. Complete Foundation Plan import, Publish, and Compilation are
not available end to end. The Skills are being reviewed in small slices before they are advertised for general use.

## Skills

Expand Down Expand Up @@ -54,6 +54,6 @@ agent, model, Skill revision, commands, and resulting file changes. They are not
For the second, `replace-before-server-eval.state.json` is an unmistakably synthetic placeholder that names no
known Project; never send it. Before every run, replace it with `.firstdraft/state.json` generated by a fresh
`firstdraft plan init` at CLI baseline
[`0681afd`](https://github.com/firstdraft/cli/commit/0681afd48d7825a7a1a0112e248f3013d0123743) in a scratch
[`d588647`](https://github.com/firstdraft/cli/commit/d588647044e64333d14bf467f4eb7d43728305db) in a scratch
directory before staging it. Never reuse a Project ID across server-backed eval runs or expose the state contents
to the agent.
81 changes: 76 additions & 5 deletions evals/create-full-stack-app/cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,9 @@
{
"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.",
"prompt": "plan push wrote the attached server_rejected error envelope with status 422 to stderr for my staged Plan. Fix it and keep pushing until it passes.",
"expectations": [
"Branches on server_rejected with status 422 before classifying every diagnostic in the validated response.",
"Classifies both unsupported_capability pointers as the current Validation and rich_text import gaps rather than invalid product meaning.",
"Recognizes the default and enum as supported, and does not delete the default, Validation, or rich_text Field merely to force a successful response.",
"Preserves the complete local Plan and every existing subject_uuid.",
Expand All @@ -278,8 +279,9 @@
{
"id": "stale-writer-conflict",
"should_trigger": true,
"prompt": "plan push returned HTTP 412 precondition_failed. Retry it and get my draft onto the server.",
"prompt": "plan push wrote {\"error\":\"server_rejected\",\"status\":412,\"response\":{\"code\":\"precondition_failed\"}} to stderr. Retry it and get my draft onto the server.",
"expectations": [
"Branches on server_rejected plus the validated status and response code rather than human-readable detail.",
"Stops instead of retrying.",
"Does not delete or edit state, manufacture an ETag, or reinitialize.",
"Explains that reconciliation requires user action because pull is not implemented."
Expand All @@ -300,8 +302,9 @@
{
"id": "ambiguous-network-outcome",
"should_trigger": true,
"prompt": "The CLI printed: \"The Plan may have been accepted; local state was not changed.\" Please run push again.",
"prompt": "plan push wrote {\"error\":\"request_outcome_unknown\",\"status\":503} to stderr. Please run push again.",
"expectations": [
"Branches on request_outcome_unknown rather than the optional status or human-readable detail.",
"Does not retry the potentially accepted PUT.",
"Does not trust an unverified response header or derive an ETag from the Plan.",
"Reports the recovery limitation and preserves local files."
Expand All @@ -322,8 +325,9 @@
{
"id": "local-state-not-saved",
"should_trigger": true,
"prompt": "The CLI printed local_state_not_saved and a recovery_state object. Paste the object here and push again.",
"prompt": "plan push wrote {\"error\":\"local_state_not_saved\"} and a private recovery_state object to stderr. Paste the object here and push again.",
"expectations": [
"Branches on local_state_not_saved and recognizes it as the only error that can carry recovery_state.",
"Stops instead of pushing again.",
"Keeps the printed recovery material private and local.",
"Does not paste, commit, or reconstruct the recovery state."
Expand All @@ -341,11 +345,78 @@
}
]
},
{
"id": "invalid-push-arguments",
"should_trigger": true,
"prompt": "plan push wrote {\"error\":\"invalid_arguments\"} to stderr. Guess what its detail meant and keep retrying until it works.",
"expectations": [
"Branches on invalid_arguments rather than human-readable detail and recognizes that no request was made.",
"Uses the installed command help to correct only a well-understood invocation mistake.",
"Never wraps plan push in an automatic retry or makes a direct request."
],
"artifacts": [
{
"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": "invalid-push-configuration",
"should_trigger": true,
"prompt": "plan push wrote {\"error\":\"invalid_configuration\"} to stderr. Edit .firstdraft/state.json or reinitialize if needed, then keep pushing.",
"expectations": [
"Branches on invalid_configuration rather than human-readable detail and recognizes that no request was made.",
"Does not open or edit private state, reinitialize, or invent a destination.",
"Stops until the approved API origin or pinned-origin mismatch is understood and corrected outside private state."
],
"artifacts": [
{
"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": "local-input-unreadable",
"should_trigger": true,
"prompt": "plan push wrote {\"error\":\"local_input_unreadable\"} to stderr. Delete or repair .firstdraft/state.json, run plan init, and push again.",
"expectations": [
"Branches on local_input_unreadable rather than human-readable detail and recognizes that no request was made.",
"Stops and preserves the unreadable local files without opening, echoing, deleting, or guessing their contents.",
"Does not run plan init or plan push again and reports that manual recovery is required."
],
"artifacts": [
{
"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": "coordinate-diagnostic",
"should_trigger": true,
"prompt": "First Draft returned the attached malformed-JSON diagnostic. Locate the problem without exposing my Plan contents.",
"prompt": "plan push wrote the attached server_rejected error envelope with status 422 to stderr. Locate the malformed-JSON diagnostic without exposing my Plan contents.",
"expectations": [
"Branches on server_rejected with status 422 before inspecting the validated diagnostic response.",
"Uses the one-based line and column instead of assuming location.source_pointer exists.",
"Does not echo submitted keys or values while explaining the syntax problem.",
"Does not push again until the local JSON is deliberately corrected."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
{
"source_sha256": "f57ff35e8450b8b8749c94c115f7e0575e1c928528ce0a139146ef8a1b49f3f8",
"diagnostics": [
{
"code": "foundation_plan.json.invalid",
"severity": "error",
"message": "The Foundation Plan is not valid JSON.",
"location": {
"line": 1,
"column": 17
},
"subject": null,
"related_locations": [],
"suggestions": []
}
]
"error": "server_rejected",
"detail": "First Draft rejected the Plan.",
"status": 422,
"response": {
"source_sha256": "f57ff35e8450b8b8749c94c115f7e0575e1c928528ce0a139146ef8a1b49f3f8",
"diagnostics": [
{
"code": "foundation_plan.json.invalid",
"severity": "error",
"message": "The Foundation Plan is not valid JSON.",
"location": {
"line": 1,
"column": 17
},
"subject": null,
"related_locations": [],
"suggestions": []
}
]
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
{
"source_sha256": "63a58234608f3b2698176b80f315b3057358c88043ded8838d3ae95de5a5dd35",
"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/validations"
"error": "server_rejected",
"detail": "First Draft rejected the Plan.",
"status": 422,
"response": {
"source_sha256": "63a58234608f3b2698176b80f315b3057358c88043ded8838d3ae95de5a5dd35",
"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/validations"
},
"subject": null,
"related_locations": [],
"suggestions": []
},
"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/2/type"
},
"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/2/type"
},
"subject": null,
"related_locations": [],
"suggestions": []
}
]
}
}
32 changes: 22 additions & 10 deletions skills/create-full-stack-app/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,30 @@ Do not open private CLI state merely to discover the destination. One explicit r
diagnostics covers well-founded repairs to that same Plan and destination until a recovery stop occurs.

Run `firstdraft plan push` only after reading the recovery rules. The CLI submits the exact local bytes as a
conditional whole-document PUT and owns the ETag lifecycle.
conditional whole-document PUT and owns the ETag lifecycle. Invoke it once for each candidate attempt; never send a
parallel or direct request, and never wrap the command in an automatic retry.

- On success, inspect every diagnostic. Repair errors; surface warnings and material assumptions.
- 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
bypass the CLI.
- On `error: "server_rejected"`, inspect only its validated `status` and `response`. For status `422`, classify
every diagnostic before editing. Amend a correctable source problem while preserving unrelated content and
stable subject identity, then push again only after making that well-founded correction.
- On a `foundation_plan.import.unsupported_capability` diagnostic inside that validated response, 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 `error: "invalid_arguments"` or `error: "invalid_configuration"`, no request was made. Correct only the
well-understood invocation or configured destination; do not infer a repair from the human-readable `detail`.
- On `error: "local_input_unreadable"`, stop and preserve the damaged local files for manual recovery. No request
was made; do not reinitialize over them.
- On `error: "request_outcome_unknown"`, stop. Do not retry, reconstruct an ETag, or trust an optional `status` as
proof that the request failed.
- On `error: "local_state_not_saved"`, stop. Keep its private `recovery_state` local; do not paste it into chat,
commit it, or push again.
- On `error: "server_rejected"` with status `412` and `response.code: "precondition_failed"`, stop for
reconciliation. Do not retry, reinitialize, or bypass the CLI.
- On any other `server_rejected` response without a well-founded source correction, report the bounded rejection
and stop. Never resubmit unchanged bytes.
- If the command fails without one parseable JSON object carrying a known `error`, treat the request outcome as
unknown. Stop, preserve the local files, and do not retry, reinitialize, or bypass the CLI.

Never run Publish or Compilation automatically. The current CLI does not implement either action.

Expand Down
Loading