Activate the web and iPhone Skill contract - #12
Conversation
Pin the landed server, CLI, schema, and iOS Core contracts so the Skill can drive the bounded August web-and-iPhone journey without provisional activation markers.
Technical reviewEight pinned identities in the Exact contract block, so I checked all of them. Every pin holds
Two of those I had already reproduced from the other side. The CLI runtime digest is the same value I recomputed from firstdraft#263's harness algorithm yesterday, and the iOS Core archive is the one I regenerated byte-for-byte from upstream on #259. Independent paths, same numbers. The cross-repo binding also still bites: pointed at the previous merged CLI commit, the contract check exits 1. The server pin excludes #263 on purpose
Pinning to the commit that establishes the contract, rather than to whatever The artifact ceiling is now checked against the real CLIconst { MAX_ARTIFACT_BYTES } = await import(
pathToFileURL(path.join(cliDirectory, "src", "compilation-artifact.js")).href
);
assert.equal(MAX_ARTIFACT_BYTES, 16 * 1024 * 1024);This reads the constant out of the pinned CLI source and asserts its value, so a silent change to 8 MiB fails this repository's CI. I raised the same gap on firstdraft/firstdraft#242, where the only test stubbed the equivalent Ruby constant away. That side has since gained Preserving unsupported content is the right instruction
Worth calling out because the opposite behavior is what an agent optimising for a green result would naturally do: strip the enum, widen the access, get Telling the Skill to keep the unsupported content and report the gap means a blocked analysis stays a true statement about the current release rather than becoming a silent edit to the user's design. No findingsThe remaining boundary is stated plainly: the Skill and CLI remain unpublished, and the external journey stays unclaimed pending the successor smoke. |
Lesson: do not fix the input to make the check passBuried in this PR's summary is a rule about how an AI agent should behave, and it is the most interesting thing here:
Read that as a description of a bug it prevents. An agent is helping someone design an application. The user wants a field with a fixed set of choices, and a screen only signed-in people can see. The agent submits the design, and the server says: this release cannot handle enums or private access yet. What now? The unhelpful-looking answer is to report the gap and stop. The helpful-looking answer is to quietly change the enum to a plain text field, make the screen public, resubmit, and announce success. The second is worse, and not by a little. The user gets a green result describing something they did not ask for, and the difference is invisible unless they reread their own design carefully. Why this is a general failure, not an AI oneThe same shape shows up in ordinary code whenever a validation failure is "handled" by editing the input. # the import "succeeds"
row[:email] = "unknown@example.com" if row[:email].blank?
row[:quantity] = 1 if row[:quantity].to_i <= 0
Order.create!(row)Every row imports. The report says zero failures. Somebody now has an order for one unit that should have been forty, and a customer record pointing at a fake address. Or in a form object: self.status = "pending" unless VALID_STATUSES.include?(status)An unrecognised status silently becomes When input does not satisfy a rule, that is information. Changing the input destroys it. The tell is a "fix" that makes an error go away without anybody being told what changed. Coercion, defaulting, truncation, silent dropping. Each one converts a question into an answer nobody asked for. What to do insteadReport the gap, keep the input. The Skill keeps the enum, keeps the private access, and says the current release cannot compile them. The user's design is intact, and the limitation is attributed correctly, to the tool rather than to their choice. Separate "cannot yet" from "must not." Those need different words. "This release does not support enums" invites waiting or asking. "Enums are not allowed here" invites redesigning. Conflating them makes users redesign around temporary limits. Let partial success be a state. The Plan is saved. It is not compilable. Both true, both reported. Systems that only model success and failure force everything into one bucket, and the usual casualty is data that was fine but not yet processable. For anything you build with an LLM in itThis rule generalises to any agent that can both act and report on its actions. Optimising for a green outcome is the failure mode, because the agent controls both the work and the summary of it. The instructions that help are the ones that say what not to do when blocked:
Every one of those is a rule against making the output look better than the work. The version worth remembering, for code and agents alike: a failing check is a fact about the input. Report the fact. Do not edit the input until the check agrees with you. |
Summary
rails-sketch/2026-08server, analyzer, Compiler, CLI, schema, and iOS Core contractios/Exact contract
35ad070beb36c66dc6480f36b33767caaed160a9121272cd592055354d09a4fe90e55c3ca002770c205e664df0ed9c7e63651a1c2c01e749a04d8879fe7f62cc4c1e13b66dce738dfirstdraft.foundation-plan.sketch/0.19, target profilerails-sketch/2026-081954e5c95d6e6621578202ad4452686b56c150256ffcd75935078d9f4247c568foundation-plan-rails/application-2026-08foundation-plan-rails/compiler-application-2026-08aa2ac902fa52abab51a4502953b7b962f949a21d, archive SHA-2560807e76cf02296af27d4eb1aae68e298beef162a7daa8a3da55d83e88ab6d748This pairs with firstdraft/firstdraft#262. The 2026-08 external CLI-and-Skill journey remains intentionally unclaimed until the successor smoke is run; the Skill and CLI remain unpublished.
Verification
PATH="/Users/sandbox2/.asdf/shims:$PATH" sh script/check— 16 tests, 16 passedPATH="/Users/sandbox2/.asdf/shims:$PATH" node script/check-cli-contract.mjs /Users/sandbox2/code/firstdraft/cli— passed against exact CLI revision, including fresh package installationuv run --with pyyaml python /Users/sandbox2/.codex/skills/.system/skill-creator/scripts/quick_validate.py skills/create-full-stack-app—Skill is valid!gh skill publish --dry-run— completed; reported only the existing recommended-license and tag-ruleset warningsgit diff --check origin/main...HEAD