Skip to content

fix: name custom actions the way FlutterFlow files them - #56

Merged
sgardoll merged 6 commits into
mainfrom
feat/widget-conformance-rules
Aug 14, 2026
Merged

fix: name custom actions the way FlutterFlow files them#56
sgardoll merged 6 commits into
mainfrom
feat/widget-conformance-rules

Conversation

@sgardoll

Copy link
Copy Markdown
Owner

The bug

A deploy failed with:

init_qa_analytics.dart: Action "initQaAnalytics" declaration not found.

FlutterFlow snake_cases an artifact name naively — an underscore before every capital — so initQAAnalytics is filed as init_q_a_analytics.dart. The generated file used the human-idiomatic init_qa_analytics.dart, which the deploy reads back as initQaAnalytics, a name that appears nowhere in the code.

The same bundle shipped logAppError and logOpFailed, which round-tripped fine — no acronym, no loss. That contrast is what identified it, and both are now regression fixtures.

Fixes

getCustomActionFileNameError checks in FlutterFlow's own direction: the identifier derived from the file name must equal the declared function. It reuses deriveIdentifierName (now exported) instead of duplicating the derivation that goes over the wire — that duplication is exactly how a check drifts from reality. Wired into both validateDartFile (deploy gate) and validateArtifactCompatibility (review), because a rule in only one surface labels a finding blocking and then deploys it anyway.

The client validator could not have caught this before: normalizeFunctionName lowercases and strips punctuation, so initQAAnalytics and initQaAnalytics looked identical to it.

The CustomClass file-name rule went the other way and is relaxed. A Code File's path is a free-text field in the FlutterFlow editor, so q_a_service.dart holding class QAService is valid and deploys. The rule was blocking a pairing FlutterFlow accepts. It is now a review warning, removed from the deploy gate, with the false "FlutterFlow expects" wording dropped.

Also in here

  • Summary tab. Manual steps never fed the status computation, yet rendered with the alert triangle in orange-900 rust next to a 95/100 score and all-green per-file ticks. Now an info icon, real amber, and a lead line stating they do not block. Red is reserved for what genuinely stops a push.
  • Review contract asks for manualActions with explicit exclusions, so the review stops volunteering work FlutterFlow already does — creating the action, declaring parameters read off the signature. manualActions is the key the review stage already emits, with title/location/timing/detail.
  • Workflow steps no longer name the model that runs each one.
  • pub.dev version resolution (separate commit, pre-existing work): resolves a package's version against the project's SDK floor instead of pinning ^1.0.0, falling back to an unconstrained dependency rather than stalling a deploy.

Upstream

The root cause was the BuildShip system prompts, which never stated the naming rule. Those are corrected and deployed separately (buildship@1f406a0). This PR is the enforcement backstop: the prompts are guidance, these validators are the gate.

Verification

npm test 192/192 · npm run build clean · node --check app.js

One pre-existing fixture was corrected rather than the rule — a return-type test paired supported.dart with a loadProduct function, a combination FlutterFlow would genuinely reject.

Not visually verified: Chrome tooling failed throughout with a cross-extension error, so the Summary-tab restyle is confirmed by computed values and the emitted bundle, not by eye.

🤖 Generated with Claude Code

sgardoll and others added 3 commits August 12, 2026 22:16
Co-Authored-By: Claude <noreply@anthropic.com>
Writing a fixed constraint like ^1.0.0 pins every newly added package to
whatever shipped under its first major, so a project ends up depending on
1.0.0 of a package with current releases available.

Adds pubVersions (semver compare, constraint containment, prerelease
detection), pubRegistry (pub.dev lookup picking the newest release the
project's own SDK floor can build, with an 8s timeout that falls back to an
unconstrained dependency rather than stalling a deploy the user is watching),
and dependencyResolution (keeps a constraint the project already declares,
rewriting it only when the generated code needs a minimum that constraint
provably cannot resolve). Wired through pubspecSync and bundleDeployPlanner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A commit failed with: init_qa_analytics.dart: Action "initQaAnalytics"
declaration not found. FlutterFlow snake_cases an artifact name naively - an
underscore before every capital - so initQAAnalytics is filed as
init_q_a_analytics.dart. The generated file used the human-idiomatic
init_qa_analytics.dart, which the deploy reads back as initQaAnalytics, a name
nowhere in the code. The two sibling actions in the same bundle round-tripped
fine because they carry no acronym.

getCustomActionFileNameError checks in FlutterFlow's own direction: the
identifier derived from the file name must equal the declared function. It
reuses deriveIdentifierName, now exported, rather than duplicating the
derivation that goes over the wire. Wired into both validateDartFile (deploy
gate) and validateArtifactCompatibility (review), since a rule in only one
place labels a finding blocking and still deploys.

The CustomClass file-name rule goes the other way. A Code File's path is a
free-text field in the FlutterFlow editor, so q_a_service.dart holding
class QAService is valid - the rule was blocking a pairing FlutterFlow
accepts. Downgraded to a review warning, removed from the deploy gate, and
the false "FlutterFlow expects" wording dropped.

Also:
- Summary tab: manual steps are informational, not alerts. They never fed the
  status computation, yet carried the alert triangle in orange-900 rust beside
  a 95/100 score. Now an info icon, real amber, and a lead line saying they do
  not block. Red is reserved for what genuinely stops a push.
- Review contract asks for manualActions with exclusions, so it stops
  volunteering work FlutterFlow already does - creating the action, declaring
  parameters read off the signature. Matches the key the review already emits.
- Workflow steps no longer name the model that runs each one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your organization has used all 50 credits included in the free plan this billing period. To keep receiving reviews, upgrade your plan.

@sgardoll

Copy link
Copy Markdown
Owner Author

@greptileai please review pr and resolve conflicts

@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR aligns Custom Action filenames with FlutterFlow’s identifier derivation, relaxes Code File naming to advisory status, updates review/manual-action presentation, and adds SDK-aware pub.dev dependency resolution.

  • Adds matching review and deploy validation for Custom Action filenames.
  • Resolves missing package versions against project SDK constraints while preserving existing dependencies.
  • Clarifies non-blocking manual steps and removes model names from workflow labels.

Confidence Score: 4/5

The dependency-resolution defect should be fixed before merging because projects using ^0.0.x packages can receive a pubspec that cannot satisfy generated code requirements.

The new caret arithmetic overstates the versions admitted by ^0.0.x constraints, causing dependency planning to preserve an incompatible existing pin instead of raising it to the generated code’s required patch.

Files Needing Attention: src/pubVersions.js, src/dependencyResolution.js

Important Files Changed

Filename Overview
src/flutterFlowArtifactValidation.js Adds a blocking Custom Action filename round-trip check and changes Custom Class filename mismatches to advisory findings.
src/flutterFlowSyncMetadata.js Exports the existing filename-to-identifier derivation so validation and wire metadata share one implementation.
src/dependencyResolution.js Plans dependency additions and overrides from project constraints, but inherits incorrect ^0.0.x reachability from the new constraint parser.
src/pubVersions.js Implements version and constraint arithmetic; its caret upper bound is too broad for 0.0.x versions and can preserve an unusable project constraint.
src/pubRegistry.js Selects non-retracted stable package releases compatible with the project’s declared SDK floors and falls back without throwing.
src/pubspecSync.js Extends textual pubspec parsing and mutation to preserve existing structure while applying planned dependency overrides.
app.js Integrates filename validation, dependency planning, and updated Summary-tab/manual-step presentation into deploy and UI flows.
src/bundleDeployPlanner.js Carries only explicitly required dependency minima and leaves inferred package versions for project-aware resolution.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Generated artifact dependencies] --> B[Bundle deploy plan]
  B --> C[Read project pubspec]
  C --> D[Parse SDK floors and existing constraints]
  D --> E{Dependency already declared?}
  E -- Yes --> F[Check whether constraint reaches required minimum]
  E -- No --> G[Resolve compatible pub.dev release]
  F --> H[Keep or override constraint]
  G --> I[Add resolved or unconstrained dependency]
  H --> J[Merge complete pubspec]
  I --> J
  J --> K[FlutterFlow deploy]
Loading
Prompt To Fix All With AI
### Issue 1
src/pubVersions.js:78-83
**Incorrect zero-major caret bound**

When generated code requires a newer patch than an existing `^0.0.x` dependency, this upper-bound calculation treats the constraint as reaching the next minor version, so dependency planning preserves an incompatible constraint and the FlutterFlow project build can fail.

```suggestion
function caretUpperBound(version) {
  const parsed = parseVersion(version);
  if (!parsed) return null;
  if (parsed.major > 0) return `${parsed.major + 1}.0.0`;
  if (parsed.minor > 0) return `0.${parsed.minor + 1}.0`;
  return `0.0.${parsed.patch + 1}`;
}
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix: name custom actions the way Flutter..." | Re-trigger Greptile

Comment thread src/pubVersions.js
Comment on lines +78 to +83
function caretUpperBound(version) {
const parsed = parseVersion(version);
if (!parsed) return null;
if (parsed.major > 0) return `${parsed.major + 1}.0.0`;
return `0.${parsed.minor + 1}.0`;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Incorrect zero-major caret bound

When generated code requires a newer patch than an existing ^0.0.x dependency, this upper-bound calculation treats the constraint as reaching the next minor version, so dependency planning preserves an incompatible constraint and the FlutterFlow project build can fail.

Suggested change
function caretUpperBound(version) {
const parsed = parseVersion(version);
if (!parsed) return null;
if (parsed.major > 0) return `${parsed.major + 1}.0.0`;
return `0.${parsed.minor + 1}.0`;
}
function caretUpperBound(version) {
const parsed = parseVersion(version);
if (!parsed) return null;
if (parsed.major > 0) return `${parsed.major + 1}.0.0`;
if (parsed.minor > 0) return `0.${parsed.minor + 1}.0`;
return `0.0.${parsed.patch + 1}`;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/pubVersions.js
Line: 78-83

Comment:
**Incorrect zero-major caret bound**

When generated code requires a newer patch than an existing `^0.0.x` dependency, this upper-bound calculation treats the constraint as reaching the next minor version, so dependency planning preserves an incompatible constraint and the FlutterFlow project build can fail.

```suggestion
function caretUpperBound(version) {
  const parsed = parseVersion(version);
  if (!parsed) return null;
  if (parsed.major > 0) return `${parsed.major + 1}.0.0`;
  if (parsed.minor > 0) return `0.${parsed.minor + 1}.0`;
  return `0.0.${parsed.patch + 1}`;
}
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

sgardoll and others added 3 commits August 14, 2026 09:37
Review flagged caretUpperBound as wrong for ^0.0.x, suggesting it should
reach 0.0.4 rather than 0.1.0. That is npm's rule. Pub has no 0.0.x special
case: `^` resolves through pub_semver's Version.nextBreaking, which is
`if (major == 0) return _incrementMinor();`, so ^0.0.3 means >=0.0.3 <0.1.0.

The code was already correct; the tests only covered ^0.19.0, so nothing
pinned the boundary that made it look wrong. Adds that coverage and records
why narrowing it would regress - constraintContains would reject versions the
project can resolve, and dependency resolution would rewrite a constraint that
was never broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
main landed PR #54, which reworked the same review contract this branch
touches. Both sides added manualActions to the review's output requirements,
independently.

Resolution keeps main's shape and this branch's constraint:
- outputRequirements uses main's `bundleReview` key, not this branch's
  `overall`. It mirrors the object the review stage actually emits, so it is
  the better name; the auto-merge had also duplicated manualActions in that
  list, now a single entry.
- The manualActions definition/exclude/preferEmpty block is kept. main asks
  the review FOR manual actions but never bounds them, which is what let the
  review volunteer work FlutterFlow already does.
- Dropped this branch's `assert.doesNotMatch(prompt, /bundleReview/)`. It
  guarded against leaking system-prompt vocabulary while the key was
  `overall`; with main's rename bundleReview is the contract's own key, so the
  assertion is now false by construction.

dist/index.html was regenerated rather than hand-merged.

Suite 196/196.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dist/assets/ is gitignored, so `git add -A` silently skipped the emitted
bundle while still staging the deletion of the previous one. The tracked
dist/index.html referenced /assets/index-DKBhAJWY.js with no such file in the
tree, so serving the committed dist/ verbatim 404s on the module script and
the app never boots.

Every previous "build: rebuild dist" commit force-added the matching bundle -
main tracks index-C8GiNBZX.js and references exactly that - so this restores
the repo's existing invariant rather than introducing one.

Caught by cloud review.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sgardoll
sgardoll merged commit ff6716e into main Aug 14, 2026
5 checks passed
@sgardoll
sgardoll deleted the feat/widget-conformance-rules branch August 14, 2026 00:05
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