Skip to content
Closed
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
1 change: 1 addition & 0 deletions evals/create-full-stack-app/cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
"Models Like as its own Entity with required immutable References to Recipe and to the Account Entity, plus one-like-per-member uniqueness over both References.",
"Gives Like's scaffold.create no inputs, binds its Account Reference to current_account, and authorizes it with a Like Policy or the signed-in gate.",
"Selects destroy in Like's resource_routes, authorized by a Like Policy that allows only when its Account Reference equals current_account.",
"Sets Like's destroy.return_to to the Recipe show route with record from mutation_record through Like's Recipe Association, so Unlike stays on the Recipe page.",
"Adds create_form {} to the displayed likes Association on the Recipe details page and leaves new and create out of Like's resource_routes.",
"Models each cooking as its own no-input record created from the Recipe page, with no uniqueness rule over Recipe and the member and no destroy, so a member can record the same recipe more than once.",
"Says plainly in the read-back \"one tap to like, one tap to unlike\" and that each Cooked it tap adds another record.",
Expand Down
115 changes: 84 additions & 31 deletions skills/create-full-stack-app/references/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,8 @@ GapSet consequences.

## Signed-in gate and one-tap records

These fragments come from a private family social network where every page requires sign-in. The Account Entity
These fragments come from a private family social network where every page requires sign-in. A guest who opens
any page is sent to sign in and, after signing in, returns to the page they asked for. The Account Entity
`member` already has `read_self` and `update_self` Policies for its profile, like the example above. It adds one gate
Policy with its own operation; any signed-in member passes because the gate record is the current Account. An Entity
may have only one Policy per `operation`, so the gate must not also use `read`: two `read` Policies on `member`
Expand Down Expand Up @@ -612,74 +613,126 @@ would use `"public"`, since the page's gate already applies.
}
```

This Like fragment is a copy of the example in the Foundation Plan Guide's No-input records section, which defines
the pattern and what Rails generates for it. A like is made by a tap: the post comes from the page and the member
from the signed-in Account, so Like's create has a binding and no `inputs`, and Like selects neither `new` nor
`create`. A member can take a like back, so Like is a toggle. Its uniqueness rule over `like.post` and `like.member`
allows one like per member per post, and its `destroy` is authorized by `like.manage_own`, which only the like's
member passes. With both, the post page shows one button that reads Like or Unlike: one tap to like, one tap to
unlike. Follows, RSVPs, upvotes, and bookmarks are toggles too. A record that may repeat, such as a "mark as read"
event or a check-in, keeps the no-input create but gets no uniqueness rule or `destroy`; a required time on it uses
a `current_time` default. Today's deployed Compiler may still show a separate Create page until the one-tap change
ships.
This Like fragment is a copy of the example in the public Guide's [No-input records](https://firstdraft.github.io/firstdraft/docs/architecture/design/foundation-plan.html#no-input-records)
section, which defines the pattern and what Rails generates for it. A like is made by a tap: the post comes from
the page and the member from the signed-in Account, so Like's create has a binding and no `inputs`, and Like selects
neither `new` nor `create`. A tap on Like creates the like and returns to the post page. A member can take a like
back, so Like is a toggle. Its uniqueness rule over `like.post` and `like.member` allows one like per member per
post, and its `destroy` is authorized by `like.manage_own`, which only the like's member passes. With both, the post
page shows one button that reads Like or Unlike: one tap to like, one tap to unlike. A toggle also authors
`destroy.return_to` through the parent Reference, here `like.post`, so Unlike stays on the post page; without it,
Unlike goes to the ordinary delete destination, such as Home. Follows, RSVPs, upvotes, and bookmarks are toggles
too. A record that may repeat, such as a "mark as read" event or a check-in, keeps the no-input create but gets no
uniqueness rule or `destroy`; a required time on it uses a `current_time` default.

```jsonc
{
"subject_uuid": "019fb300-0000-7000-8000-000000000110",
"subject_uuid": "01a10369-3055-7edd-bb07-1d3835cd140b",
"key": "like",
"name": "Like",
"primary_descriptor": { "association": "like.member" },
"primary_descriptor": {
"association": "like.member"
},
"references": [
{
"subject_uuid": "019fb300-0000-7000-8000-000000000111",
"subject_uuid": "01a10369-3055-7fdf-b8c1-89fa2e048fbe",
"key": "post",
"name": "Post",
"targets": ["post"],
"targets": [
"post"
],
"required": true,
"immutable": true,
"on_referenced_deleted": "delete_referencing_record"
"one_to_one": false,
"on_referenced_deleted": "delete_referencing_record",
"immutable": true
},
{
"subject_uuid": "019fb300-0000-7000-8000-000000000112",
"subject_uuid": "01a10369-3055-77bf-9eb6-72e155679db8",
"key": "member",
"name": "Member",
"targets": ["member"],
"targets": [
"member"
],
"required": true,
"immutable": true,
"on_referenced_deleted": "delete_referencing_record"
"one_to_one": false,
"on_referenced_deleted": "delete_referencing_record",
"immutable": true
}
],
"validations": [
{
"subject_uuid": "019fb300-0000-7000-8000-000000000113",
"subject_uuid": "01a10369-3055-79e4-a51d-1d7ef064798c",
"key": "once_per_member",
"kind": "uniqueness",
"targets": [{ "reference": "like.post" }, { "reference": "like.member" }],
"targets": [
{
"reference": "like.post"
},
{
"reference": "like.member"
}
],
"nulls": "distinct",
"error_target": { "reference": "like.member" }
"error_target": {
"reference": "like.member"
}
}
],
"policies": [
{
"subject_uuid": "019fb300-0000-7000-8000-000000000114",
"subject_uuid": "01a10369-3055-7254-8716-e913cc529b55",
"key": "manage_own",
"operation": "manage",
"allow_when": {
"kind": "comparison",
"left": { "target": { "association": "like.member" } },
"left": {
"target": {
"association": "like.member"
}
},
"operator": "equals",
"right": { "kind": "environment", "name": "current_account" }
"right": {
"kind": "environment",
"name": "current_account"
}
}
}
],
"scaffold": {
"resource_routes": ["destroy"],
"resource_routes": [
"destroy"
],
"create": {
"bindings": [{ "reference": "like.member", "value": { "kind": "environment", "name": "current_account" } }],
"authorization": { "policy": "like.manage_own" }
"bindings": [
{
"reference": "like.member",
"value": {
"kind": "environment",
"name": "current_account"
}
}
],
"authorization": {
"policy": "like.manage_own"
}
},
"destroy": {
"authorization": { "policy": "like.manage_own" }
"authorization": {
"policy": "like.manage_own"
},
"return_to": {
"kind": "resource",
"entity": "post",
"route": "show",
"record": {
"from": "mutation_record",
"through": [
{
"association": "like.post"
}
]
}
}
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions skills/create-full-stack-app/references/foundation-plan-023.md
Original file line number Diff line number Diff line change
Expand Up @@ -584,10 +584,11 @@ the required application form-entry work into [implementation notes](modeling-gu
Qualifying fixtures do not fix that application behavior; add successful New/browser coverage after repairing it.

Omit `return_to` when the conventional interaction is intended: standalone New/Edit returns to the saved record,
scoped associated create returns to its collection, destroy returns to the record's collection, and profile updates
return to Account. If a preferred record or collection route is unavailable, Rails uses the admitted collection
or home fallback. An explicit override remains authored meaning and must itself be lowerable; it does not silently
become a default. The browser supplies no destination URL, hidden return input, or history-based redirect.
scoped associated create returns to its collection, a no-input associated create returns to its parent's page,
destroy returns to the record's collection, and profile updates return to Account. If a preferred record or
collection route is unavailable, Rails uses the admitted collection or home fallback. An explicit override remains
authored meaning and must itself be lowerable; it does not silently become a default. The browser supplies no
destination URL, hidden return input, or history-based redirect.

A selected standalone create without New or an explicit return uses an empty `201 Created` response on success
and `422` for validation failure, even when its definition also serves associated forms. Authorization denials keep
Expand All @@ -599,6 +600,8 @@ Admitted details collections show a bounded preview with a separate paginated fu
attributes remain under `params[:credit]`. The authorized parent association builds the child, with no hidden
parent input; a competing submitted parent does not replace it. Shared target new/create actions select the form
for that context and retain its other inputs and validation errors.
A [no-input create](modeling-guide.md#add-behavior-deliberately) shows a one-tap button in place of the Add link,
with no scoped New page.
Ordinary child edit/update/destroy routes stay flat. Preview rows contain selected properties and an optional
authorized details link, without inline mutation controls. If no target show route is selected, its flat mutation
routes can remain unlinked starter code. Do not invent a show route or discard authored properties to fill that gap.
Expand Down
16 changes: 9 additions & 7 deletions skills/create-full-stack-app/references/modeling-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ consumers, cursor pagination, Field and Association inputs, server bindings, ass
optional return overrides. Omit `return_to` for conventional interaction defaults; discuss navigation only when
product intent needs an exception. The [worked return examples](examples.md#deliberate-return-overrides) show the
complete record operand and independent associated-success override. A supported associated `create_form` supplies
a scoped New page, not an inline form in the details card.
a scoped New page, or a one-tap button for a no-input record, not an inline form in the details card.
Every request and displayed Association declares public access or a Policy binding. The exact
Web Account/Policy slice can protect supported surfaces and provide a Web-only Account profile; unsupported Policies
and dependent consumers remain exact gaps. Read the Foundation Plan reference for the current prerequisites. Do not
Expand All @@ -334,12 +334,14 @@ Name each list's fields in the read-back as a delegated choice unless the user c

When the user describes something people do with a click, such as a like, follow, RSVP, bookmark, or upvote, use
the Foundation Plan's no-input record pattern: a create with no `inputs` under the parent's `"create_form": {}`,
with every value bound from context. The Foundation Plan Guide's No-input records section defines it, and the
[Like example](examples.md#signed-in-gate-and-one-tap-records) copies its example. When each person does it once
and can take it back, it is a toggle: also add a uniqueness rule over the parent Reference and the Account
Reference, and select `destroy` authorized by an owner Policy. Records that may repeat, such as "mark as read"
events or check-ins, get neither. Say it plainly in the read-back, such as "one tap to like, one tap to unlike".
Today's deployed Compiler may still show a separate Create page until the one-tap change ships.
with every value bound from context. The parent page shows a one-tap button, and a tap returns to the parent page.
The public Guide's [No-input records](https://firstdraft.github.io/firstdraft/docs/architecture/design/foundation-plan.html#no-input-records)
section defines the pattern, and the [Like example](examples.md#signed-in-gate-and-one-tap-records) copies its
example. When each person does it once and can take it back, it is a toggle: also add a uniqueness rule over the
parent Reference and the Account Reference, select `destroy` authorized by an owner Policy, and author
`destroy.return_to` through the parent Reference so Unlike stays on the parent page. Records that may repeat, such
as "mark as read" events or check-ins, get no uniqueness rule or `destroy`. Say it plainly in the read-back, such as
"one tap to like, one tap to unlike".

Select `native.ios` and `native.android` independently when the user wants those owned projects. Ordinary
Compilation emits each with at least one admitted public navigation entry and an identity that fits its
Expand Down
Loading