Skip to content

Activate the web and iPhone Skill contract - #12

Merged
raghubetina merged 1 commit into
mainfrom
codex/rails-profile-202608
Jul 31, 2026
Merged

Activate the web and iPhone Skill contract#12
raghubetina merged 1 commit into
mainfrom
codex/rails-profile-202608

Conversation

@raghubetina

Copy link
Copy Markdown
Contributor

Summary

  • pin the Skill to the landed rails-sketch/2026-08 server, analyzer, Compiler, CLI, schema, and iOS Core contract
  • teach the authoring guidance and eval corpus about the bounded public web index and owned iPhone project under ios/
  • preserve intentional unsupported content, including enums and private or broader access, instead of weakening Plans to obtain a valid analysis
  • extend the exact CLI contract check through nested iOS artifact materialization, executable mode, analyzer capability-gap fixtures, and the 16 MiB artifact ceiling

Exact contract

  • First Draft server: 35ad070beb36c66dc6480f36b33767caaed160a9
  • CLI: 121272cd592055354d09a4fe90e55c3ca002770c
  • CLI JavaScript-source runtime SHA-256: 205e664df0ed9c7e63651a1c2c01e749a04d8879fe7f62cc4c1e13b66dce738d
  • Foundation Plan: firstdraft.foundation-plan.sketch/0.19, target profile rails-sketch/2026-08
  • schema SHA-256: 1954e5c95d6e6621578202ad4452686b56c150256ffcd75935078d9f4247c568
  • analyzer: foundation-plan-rails/application-2026-08
  • Compiler: foundation-plan-rails/compiler-application-2026-08
  • iOS Core: aa2ac902fa52abab51a4502953b7b962f949a21d, archive SHA-256 0807e76cf02296af27d4eb1aae68e298beef162a7daa8a3da55d83e88ab6d748

This 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 passed
  • PATH="/Users/sandbox2/.asdf/shims:$PATH" node script/check-cli-contract.mjs /Users/sandbox2/code/firstdraft/cli — passed against exact CLI revision, including fresh package installation
  • uv run --with pyyaml python /Users/sandbox2/.codex/skills/.system/skill-creator/scripts/quick_validate.py skills/create-full-stack-appSkill is valid!
  • gh skill publish --dry-run — completed; reported only the existing recommended-license and tag-ruleset warnings
  • git diff --check origin/main...HEAD

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.
@raghubetina

Copy link
Copy Markdown
Contributor Author

Technical review

Eight pinned identities in the Exact contract block, so I checked all of them.

Every pin holds

sh script/check                              → 16 tests pass
node script/check-cli-contract.mjs <cli>     → exit 0
Pin Verified
server 35ad070 in firstdraft main, and contains #262
CLI 121272c is that repository's main tip
CLI runtime SHA-256 205e664d… matches, independently recomputed
schema SHA-256 1954e5c9… matches firstdraft main and the vendored copy
iOS Core aa2ac902 / archive 0807e76c… matches #259

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

35ad070 contains #262, the activation, but not #263, the CLI journey proof, even though #263 has merged. That is not staleness. It matches the body:

This pairs with firstdraft/firstdraft#262. The 2026-08 external CLI-and-Skill journey remains intentionally unclaimed until the successor smoke is run.

Pinning to the commit that establishes the contract, rather than to whatever main happens to be, is the same discipline as #250's profile pin and skills#11's baseline. It also keeps the deferral honest: the journey evidence is not folded in by accident just because it exists now.

The artifact ceiling is now checked against the real CLI

const { 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 assert_equal 16 * 1024 * 1024, ArtifactEnvelope::MAX_SOURCE_BYTES. This PR adds the client half, and does it better than a same-repo assertion would, because it checks the dependency rather than a local copy of the belief.

Preserving unsupported content is the right instruction

preserve intentional unsupported content, including enums and private or broader access, instead of weakening Plans to obtain a valid analysis

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 valid, report success. That produces a Plan the user did not author and a compiled application that does not match their intent.

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 findings

The remaining boundary is stated plainly: the Skill and CLI remain unpublished, and the external journey stays unclaimed pending the successor smoke.

@raghubetina

Copy link
Copy Markdown
Contributor Author

Lesson: do not fix the input to make the check pass

Buried in this PR's summary is a rule about how an AI agent should behave, and it is the most interesting thing here:

preserve intentional unsupported content, including enums and private or broader access, instead of weakening Plans to obtain a valid analysis

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 one

The 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 pending, which is a decision the code made and nobody recorded.

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 instead

Report 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 it

This 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:

  • do not modify user-authored content to satisfy a validator
  • do not retry with different inputs and report the retry as the original request
  • do not summarise a partial result as complete
  • when blocked, say what blocked you and what you did not do

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.

@raghubetina
raghubetina merged commit 5cad5ac into main Jul 31, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant