feat: scaffold the feature standard the platform actually enforces - #6
Merged
Merged
Conversation
A scaffolded feature named an image nothing publishes (fn-<name>, while constructive-functions pushes ghcr.io/constructive-io/features/<name>), and the gql template declared no inputs at all — so the runtime had nothing to enforce and the author's first instinct was the presence check the declaration exists to make unnecessary. Every method now declares one, the sql handler stops re-checking a port the runtime already 400s on, and both queue suites pass featureDir so a locally run image holds a request to the same declaration a deployed one does. Two drifts a fresh scaffold hit before it could run: createFunctionContext now requires the job every invocation carries, and annotating methods as one common payload type erased each handler's declared params. scripts/check-templates.mjs pins the invariants on every push: what fun init emits is the standard, whatever the docs say. Verified by scaffolding both surfaces into constructive-functions and running them: 4 tests each, green, no manual repair.
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Whatever
fun initemits is the standard, whatever the docs say — and it wasemitting four things the platform disagrees with.
The image named nothing. Both feature templates declared
"image": "fn-____name____", while constructive-functions publishesghcr.io/constructive-io/features/<name>. A scaffold therefore named an imagenothing builds; it is
features/____name____now, in the manifests and in theIMAGEconstants beside them.The gql manifest declared
"inputs": []— the one thing the manifest existsfor. The runtime compiles declared inputs into a JSON Schema and answers a bad
request with a 400 before the handler is entered, so a scaffold declaring none
taught its author to hand-check payloads, which
AGENTS.mdspends a sectionforbidding. Every method now declares one, and the sql handler stops re-checking
a port the platform already rejected:
The
sqlsurface no longer owns a schema. It scaffolded a pgpm module —pgpm.plan,____name____.control,Makefile,deploy/ revert/ verify/, a____schema____prompt — whichconstructive-functions/AGENTS.mdforbids in asmany words, and which is how eight features end up inventing eight
documentstables that nothing in production has. All of it is deleted, along with the
language teaching it (both READMEs and the root one). What remains is the only
thing the surface ever was: a database connection.
The example query is now the one statement true of every database a feature might
run against — what the transaction is, rather than a table the template had to
invent to have something to read:
Its behaviour suite is the point of the surface as a fixture: the same handler on
a job carrying an actor reads
authenticated, and without one readsanonymous.roleis a declared output, so the manifest carries it too.Two drifts a fresh scaffold hit before it could run, found by scaffolding into
constructive-functions and running it rather than by reading:
createFunctionContext()now requires thejobevery invocation carries;export const methods: Record<string, FunctionHandler<Record<string, unknown>>>erased each handler's declared params, so a typed
Paramsfailed to assign.The map infers its own type now, as
features/greetingdoes.scripts/check-templates.mjspins the invariants on every push (node builtinsonly, no package to install): feature images are
features/<name>, every templatedeclares a method, every method declares at least one input, task identifiers stay
____name____:____method____,IMAGEagrees with the manifest, invalid JSON is ahard failure — and no feature template carries
pgpm.plan, aMakefile, a.control, ordeploy/ revert/ verify/, so the module cannot come back the wayit arrived. It reads both manifest shapes (
methods[]and the top-levelsingle-method form a platform handler uses), so the two are not forced to converge
to satisfy the checker.
Verified by scaffolding both surfaces into constructive-functions with
pnpm fun init <name> --surface gql|sqlagainst this branch and running them: 4tests each, green, no manual repair.
Companions: constructive-io/constructive-db#2970 (
funroot/port resolution, and--schemaretired fromfun init) and constructive-io/constructive-functions#52.Planning: constructive-io/constructive-planning#1535.
Link to Devin session: https://app.devin.ai/sessions/12ca02ecefab4a49a7d375452a86efdb
Requested by: @pyramation