@aotter/mantle 0.2.0-alpha.9 → 0.2.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +15 -5
  2. package/dist/auth/adapter.js +1 -28
  3. package/dist/auth/admin.js +2 -5
  4. package/dist/auth/caller.js +0 -27
  5. package/dist/auth/consent.js +100 -0
  6. package/dist/auth/hardening.js +0 -7
  7. package/dist/auth/identity.js +0 -11
  8. package/dist/auth/oauth.js +0 -32
  9. package/dist/auth/resource-seeds.js +0 -2
  10. package/dist/auth/route-http.js +33 -0
  11. package/dist/auth/routes.js +5 -151
  12. package/dist/build/compiler.d.ts +19 -609
  13. package/dist/build/compiler.js +6 -6896
  14. package/dist/build/spec/domain/model/ManifestGrammar.d.ts +39 -1
  15. package/dist/build/spec/domain/model/RuntimePlan.d.ts +16 -3
  16. package/dist/build/spec/domain/model/SqlIr.d.ts +32 -0
  17. package/dist/build/spec/domain/model/UserFields.d.ts +1 -0
  18. package/dist/build/spec/kernel/diagnostic.d.ts +1 -1
  19. package/dist/bun/assets.js +0 -2
  20. package/dist/domain/adminOrder.js +0 -2
  21. package/dist/domain/columns.js +7 -20
  22. package/dist/domain/describe/chunk-P6PP6VVV.js +1 -0
  23. package/dist/domain/describe/index.d.ts +1 -0
  24. package/dist/domain/describe/index.js +1 -115
  25. package/dist/domain/describe/match.d.ts +19 -0
  26. package/dist/domain/describe/target-hash.js +1 -12
  27. package/dist/domain/entry.js +0 -14
  28. package/dist/domain/generated-tools.js +0 -1
  29. package/dist/domain/input.js +0 -2
  30. package/dist/domain/paging.js +0 -17
  31. package/dist/domain/recipes.js +10 -7
  32. package/dist/domain/userReferences.js +0 -1
  33. package/dist/domain/wireScalars.js +0 -6
  34. package/dist/infrastructure/adminEntries.js +27 -8
  35. package/dist/infrastructure/adminPaging.js +55 -57
  36. package/dist/infrastructure/artifacts.js +0 -4
  37. package/dist/infrastructure/authSchema.js +0 -9
  38. package/dist/infrastructure/bundle.js +45 -38
  39. package/dist/infrastructure/cli.js +26 -12
  40. package/dist/infrastructure/generate.js +26 -19
  41. package/dist/infrastructure/generateAuth.js +1 -9
  42. package/dist/infrastructure/help.js +55 -0
  43. package/dist/infrastructure/init.js +50 -0
  44. package/dist/infrastructure/inputs.js +0 -9
  45. package/dist/infrastructure/mounts.js +22 -0
  46. package/dist/infrastructure/wrangler.js +0 -6
  47. package/dist/kernel/authorization.js +0 -1
  48. package/dist/kernel/binds.d.ts +34 -7
  49. package/dist/kernel/binds.js +5 -11
  50. package/dist/kernel/caller-identity.d.ts +0 -3
  51. package/dist/kernel/caller-identity.js +0 -15
  52. package/dist/kernel/caller-mode.d.ts +4 -0
  53. package/dist/kernel/caller-mode.js +14 -0
  54. package/dist/kernel/contracts.d.ts +13 -10
  55. package/dist/kernel/contracts.js +14 -41
  56. package/dist/kernel/cron.js +0 -4
  57. package/dist/kernel/dialect/common.js +0 -3
  58. package/dist/kernel/dialect/postgres.js +0 -2
  59. package/dist/kernel/dialect/sqlite.js +1 -6
  60. package/dist/kernel/drain.js +2 -8
  61. package/dist/kernel/driver/bun-sqlite.js +3 -7
  62. package/dist/kernel/driver/d1.d.ts +1 -1
  63. package/dist/kernel/driver/d1.js +6 -4
  64. package/dist/kernel/driver/postgres.d.ts +2 -1
  65. package/dist/kernel/driver/postgres.js +12 -22
  66. package/dist/kernel/driver/transaction-context.js +0 -1
  67. package/dist/kernel/email.js +0 -4
  68. package/dist/kernel/host-access.js +2 -3
  69. package/dist/kernel/host-caller.d.ts +1 -1
  70. package/dist/kernel/host-caller.js +3 -6
  71. package/dist/kernel/json-body.js +0 -2
  72. package/dist/kernel/mcp-apps.js +0 -5
  73. package/dist/kernel/mcp-call.js +0 -1
  74. package/dist/kernel/mcp-handler.js +4 -27
  75. package/dist/kernel/mcp.js +0 -5
  76. package/dist/kernel/media.js +0 -18
  77. package/dist/kernel/paging.js +1 -9
  78. package/dist/kernel/publishing.js +3 -4
  79. package/dist/kernel/r2-media.js +0 -7
  80. package/dist/kernel/rest-dispatch.js +0 -1
  81. package/dist/kernel/rest-router.js +5 -8
  82. package/dist/kernel/rest.d.ts +2 -1
  83. package/dist/kernel/rest.js +1 -3
  84. package/dist/kernel/route-pattern.d.ts +26 -0
  85. package/dist/kernel/route-pattern.js +34 -0
  86. package/dist/kernel/scalars.js +1 -9
  87. package/dist/kernel/search.js +0 -1
  88. package/dist/kernel/site.js +0 -2
  89. package/dist/kernel/store.d.ts +26 -2
  90. package/dist/kernel/store.js +69 -49
  91. package/dist/kernel/surface-failure.d.ts +10 -0
  92. package/dist/kernel/surface-failure.js +29 -0
  93. package/dist/kernel/surface.js +0 -3
  94. package/dist/kernel/sweep.js +0 -1
  95. package/dist/kernel/translations.js +0 -1
  96. package/dist/kernel/user-caller.js +2 -8
  97. package/dist/kernel/user-fields.d.ts +3 -2
  98. package/dist/kernel/user-fields.js +9 -9
  99. package/dist/kernel/validation.js +0 -8
  100. package/dist/kernel/web-gateway.js +0 -2
  101. package/dist/kernel/web-head.js +0 -1
  102. package/dist/kernel/web-index.js +0 -1
  103. package/dist/kernel/web-response.js +0 -1
  104. package/dist/kernel/web-serve.js +97 -0
  105. package/dist/kernel/web.d.ts +1 -1
  106. package/dist/kernel/web.js +10 -91
  107. package/dist/kernel/where.js +0 -6
  108. package/dist/usecase/adminBindings.js +32 -23
  109. package/dist/usecase/adminClient.js +4 -22
  110. package/dist/usecase/adminDescriptors.js +2 -9
  111. package/dist/usecase/generate.js +14 -17
  112. package/dist/usecase/handlers.js +1 -4
  113. package/dist/usecase/mcp-media.js +0 -1
  114. package/dist/usecase/mcp.js +0 -4
  115. package/dist/usecase/paging.js +1 -3
  116. package/dist/usecase/publishing.js +20 -11
  117. package/dist/usecase/rest.js +5 -6
  118. package/dist/usecase/site.js +0 -1
  119. package/dist/usecase/stores.js +12 -17
  120. package/dist/usecase/tool-version.js +0 -3
  121. package/dist/usecase/translations.js +0 -2
  122. package/dist/usecase/validation.js +0 -20
  123. package/docs/examples/README.md +13 -5
  124. package/docs/examples/app-shell/README.md +91 -12
  125. package/docs/examples/app-shell/accept.mjs +91 -0
  126. package/docs/examples/app-shell/gitignore +7 -0
  127. package/docs/examples/app-shell/package.json +10 -9
  128. package/docs/examples/app-shell/smoke-kit.mjs +29 -15
  129. package/docs/examples/app-shell/src/routes.ts +6 -3
  130. package/docs/examples/app-shell/src/service.ts +2 -1
  131. package/docs/examples/club-events/README.md +7 -2
  132. package/docs/examples/club-events/manifests/club-events.yaml +17 -0
  133. package/docs/examples/club-events/smoke.mjs +7 -1
  134. package/docs/examples/generated-web/seed.sql +5 -4
  135. package/docs/examples/purchasing/README.md +12 -3
  136. package/docs/examples/purchasing/manifests/purchasing.yaml +24 -4
  137. package/docs/examples/purchasing/smoke.mjs +14 -0
  138. package/docs/examples/recipes/README.md +5 -3
  139. package/docs/handbook/cloudflare/media-r2.md +6 -3
  140. package/docs/handbook/cloudflare/service-entry.md +5 -0
  141. package/docs/handbook/concepts/lifecycle-and-locales.md +21 -1
  142. package/docs/handbook/examples/hub.md +1 -1
  143. package/docs/handbook/guides/admin-ui.md +1 -1
  144. package/docs/handbook/guides/agent-setup.md +20 -10
  145. package/docs/handbook/guides/agent-workflow-acceptance.md +225 -105
  146. package/docs/handbook/guides/local-testing.md +85 -0
  147. package/docs/handbook/guides/native-admin.md +242 -16
  148. package/docs/handbook/guides/native-execution.md +10 -8
  149. package/docs/handbook/guides/public-pages.md +44 -4
  150. package/docs/handbook/guides/revisions.md +584 -0
  151. package/docs/handbook/guides/size-and-startup.md +140 -0
  152. package/docs/handbook/guides/sql-patterns.md +312 -0
  153. package/docs/handbook/guides/typed-queries.md +146 -3
  154. package/docs/handbook/navigation.json +20 -0
  155. package/docs/handbook/reference/admin-descriptors.md +4 -1
  156. package/docs/handbook/reference/authorization.md +3 -1
  157. package/docs/handbook/reference/describe.md +42 -0
  158. package/docs/handbook/reference/diagnostics.md +16 -2
  159. package/docs/handbook/reference/procedure.md +6 -6
  160. package/docs/handbook/reference/schema.md +79 -9
  161. package/docs/handbook/reference/sql.md +65 -0
  162. package/docs/handbook/reference/surface.md +8 -4
  163. package/docs/handbook/reference/trigger.md +12 -0
  164. package/docs/handbook/reference/view.md +11 -3
  165. package/docs/handbook/releases/index.md +71 -7
  166. package/docs/handbook/start/agents.md +49 -0
  167. package/docs/handbook/start/project-and-cli.md +10 -0
  168. package/docs/handbook/start/quickstart-worker.md +1 -1
  169. package/docs/skills/develop/SKILL.md +19 -6
  170. package/docs/skills/integrate/SKILL.md +3 -1
  171. package/docs/skills/performance/SKILL.md +169 -0
  172. package/docs/skills/update/SKILL.md +10 -4
  173. package/docs/upgrade-0.1-to-0.2.md +151 -39
  174. package/package.json +31 -11
  175. package/skills/README.md +6 -2
  176. package/skills/mantle/SKILL.md +53 -120
  177. package/dist/domain/describe/chunk-ZVXBGP7Y.js +0 -424
  178. package/skills/mantle/references/local-testing.md +0 -20
  179. package/skills/mantle/references/web.md +0 -41
  180. package/skills/mantle/references/website.md +0 -51
@@ -13,21 +13,20 @@ carries.
13
13
  The Mantle plugin carries one OSS authoring skill, `mantle`, and local
14
14
  acceptance references. It does not configure a hosted-service connection.
15
15
  Install from the Git tag matching the exact SDK version you selected (currently
16
- `v0.2.0-alpha.9`). Alpha releases integrate on `develop`; an unqualified
17
- `aotter/mantle` source follows `main` and can install older, incompatible
18
- instructions. A skill install does not upgrade the application's SDK.
16
+ `v0.2.1-alpha.1`). Pin the tag to keep instructions aligned with the application's
17
+ SDK. A skill install does not upgrade the application's SDK.
19
18
 
20
19
  For Codex, pin the marketplace checkout:
21
20
 
22
21
  ```sh
23
- codex plugin marketplace add aotter/mantle --ref v0.2.0-alpha.9
22
+ codex plugin marketplace add aotter/mantle --ref v0.2.1-alpha.1
24
23
  codex plugin add mantle@mantle
25
24
  ```
26
25
 
27
26
  For Claude Code, first clone the release into a directory you will keep:
28
27
 
29
28
  ```sh
30
- git clone --depth 1 --branch v0.2.0-alpha.9 https://github.com/aotter/mantle.git mantle-plugin
29
+ git clone --depth 1 --branch v0.2.1-alpha.1 https://github.com/aotter/mantle.git mantle-plugin
31
30
  ```
32
31
 
33
32
  Then, in Claude Code, use that checkout's absolute path:
@@ -41,9 +40,12 @@ Cursor and GitHub Copilot use their plugin manifests from the same tagged
41
40
  checkout. Without a plugin host, install only the consumer skill by name:
42
41
 
43
42
  ```sh
44
- npx skills add https://github.com/aotter/mantle/tree/v0.2.0-alpha.9 --skill mantle
43
+ npx skills add aotter/mantle@mantle
45
44
  ```
46
45
 
46
+ Without a version it follows `main`, the latest stable release. To match the
47
+ application's SDK exactly, add its release tag: `npx skills add aotter/mantle#v0.2.1-alpha.1@mantle`.
48
+
47
49
  When upgrading the SDK, update the marketplace ref or local tagged checkout and
48
50
  refresh/reinstall the plugin using the host's native workflow. Verify the loaded
49
51
  manifest version and skill source. Do not reset a checkout containing local edits.
@@ -54,10 +56,17 @@ project and copies no handbook.
54
56
 
55
57
  ## 2. Install the SDK, then read its own docs
56
58
 
57
- Use the version the project already pins. For a new project, choose a 0.2.x
58
- version with `npm view @aotter/mantle dist-tags` and install it with an exact
59
- version; every `@aotter/mantle*` package stays at that same version. Then
60
- read, from the application:
59
+ Use the version the project already pins. For a new project, install this
60
+ handbook's version exactly. 0.2.1-alpha.1 is a prerelease; stable 0.2.0 remains the
61
+ production line, and its own tagged handbook describes it.
62
+
63
+ ```sh
64
+ pnpm add --save-exact @aotter/mantle@0.2.1-alpha.1
65
+ ```
66
+
67
+ Add optional `@aotter/mantle-ui@0.2.1-alpha.1` and `@aotter/mantle-admin@0.2.1-alpha.1` only
68
+ when the selected surfaces need them. Every selected `@aotter/mantle*` package
69
+ stays at the same exact version. Read, from the application:
61
70
 
62
71
  ```text
63
72
  node_modules/@aotter/mantle/README.md
@@ -76,6 +85,7 @@ the skill's GitHub copy and over any online handbook.
76
85
  | `docs/skills/update/SKILL.md` | upgrading `@aotter/mantle`, including from 0.1.x |
77
86
  | `docs/skills/provision/SKILL.md` | deploying, with production sign-in |
78
87
  | `docs/skills/media-gc/SKILL.md` | auditing or removing stale R2 uploads |
88
+ | `docs/skills/performance/SKILL.md` | after acceptance converges, or when a page, first request or query is slow |
79
89
 
80
90
  0.2.0 has no `mantle skills` command: nothing is copied into the project.
81
91
  An agent reads these files from `node_modules` when the task calls for them.
@@ -6,12 +6,15 @@ customisations, application pages and web routes, REST APIs used by clients,
6
6
  MCP tools and MCP Apps. Generation, typechecking, scripted smoke tests and an
7
7
  author's replay remain useful; they do not establish independent acceptance. A
8
8
  fresh context is not enough if the author still chooses a workflow that mirrors
9
- the implementation. Every acceptance round gives the evaluator responsibility
10
- for choosing realistic tasks and judging the result, not just following a
11
- prescribed sequence.
9
+ the implementation. Every acceptance round gives the evaluator only a
10
+ role and the entry points that role uses; it derives realistic tasks itself
11
+ and the author scores them against a private rubric. The first delivery gets
12
+ full acceptance; after a repair, the change decides what
13
+ [re-runs](#re-verify-what-a-repair-touches).
12
14
  If independent acceptance is not run, report `UNVERIFIED`; if a fresh evaluator
13
15
  or authorized test transport is unavailable, report `BLOCKED`; neither outcome
14
- is done.
16
+ is done. Either way, report the evidence you do have in the
17
+ [layered handoff](#report-done-ness-in-layers).
15
18
 
16
19
  Use the surface the intended user actually uses: a real browser session for
17
20
  Admin and application pages; for REST, an HTTP client plus the public API
@@ -29,8 +32,8 @@ the exercise pass.
29
32
  Start with the original user brief, business role and explicit requirements,
30
33
  not a list of CRUD endpoints or a task derived from what you implemented.
31
34
  Before writing manifests or handlers, record the intended jobs and assumptions
32
- in the existing design note. Keep the author's capability map separate from
33
- the brief an evaluator receives: the evaluator chooses its own tasks and
35
+ in the existing design note. The capability map feeds the private rubric
36
+ (below); an evaluator receives only its role and chooses its own tasks and
34
37
  observable success criteria. Do not expand application scope or authority
35
38
  merely to satisfy an evaluator's newly inferred product wish.
36
39
 
@@ -82,101 +85,170 @@ rounds below before claiming the user-facing work is done. Keep the author's
82
85
  capability map and worked call sequence out of the fresh agent's prompt: an MCP
83
86
  evaluator must discover the workflow from public tool descriptions and results.
84
87
 
85
- ## Run independent acceptance rounds until they converge
86
-
87
- 1. **Freeze scope and revision, not the evaluator's itinerary.** Supply the
88
- original user brief, business role, explicit requirements and exclusions,
89
- exact application/SDK revision, authorized transport, permitted fake-data
90
- actions, per-round interaction/time/write budgets, a user-set total round cap of at
91
- least three (default four) and a fixture cleanup owner. Seed fixtures with
92
- realistic volume and variety: other owners' records, several states and more
93
- than one page of results, so evaluators' own goals reach discovery, paging
94
- and authority. Explicitly authorize synthetic writes only where the user has
95
- authorized testing. Production writes and external side effects need their
96
- own authorization.
97
- 2. **Use a new end-user subagent in every round.** Start without inherited
98
- implementation history. Withhold source, SQL, internal docs, capability maps,
99
- precomputed IDs, hidden routes or tool names, prescribed interaction order
100
- and previous reports or fixes. Ask
101
- the agent to state its own realistic goals and observable success criteria
102
- before execution, use or discover the user-facing surface and assess whether
103
- it supports the work. Ask whether it can make the decisions needed for its
104
- goals, not merely issue successful calls. Do not prescribe the author's
105
- happy path or require a minimum number of defects. Zero findings is a valid
106
- observation. Run the evaluator outside the project directory, without Mantle
107
- skills or project instruction files, with only the intended browser, HTTP
108
- client, MCP client or MCP App host/AppBridge available, and record that
109
- isolation in the evidence. For a multi-role workflow, run one
110
- fresh agent per role in the same round on shared live data in the brief's
111
- natural order; the later role finds earlier records through the service, and
112
- the first agent may take a resumed turn to observe the outcome. Each agent
113
- still chooses its own goals.
114
- 3. **Keep evidence and scope judgments.** Require PASS/FAIL/BLOCKED per chosen
115
- goal, actual readbacks, rejected writes, recovery, interaction costs and
116
- design findings with materiality, evidence and the smallest suggested repair.
117
- Separate a violated explicit requirement, a demonstrated defect and a newly
118
- inferred product need. Missing capability is a finding, not permission to
119
- invent interfaces or automatically expand the product. A rendered page,
120
- HTTP 200, successful tool result or creation ID alone does not prove a
121
- complete business result. Preserve setup assistance
122
- and label any coached attempt; do not count it as a clean independent round.
123
- 4. **Repair outside the evaluator's context.** The author retains the full
124
- findings ledger, classifies application design, SDK/runtime, docs, transport
125
- setup and host issues separately, then makes the smallest in-scope repair.
126
- Turn reproduced failures into focused regression checks and run them with
127
- relevant authorization, OCC and atomicity checks. Keep native guards; do not
128
- shim away errors or retry uncertain writes. A runtime change needs a minimal
129
- supported-contract reproduction. Record optional product suggestions and
130
- unresolved scope questions rather than silently dropping findings.
131
- 5. **Repeat the whole evaluation with a fresh agent.** Freeze the new revision
132
- and supply the same original brief and authority, with new fixtures. Let
133
- this evaluator choose its own goals again; do not pass the ledger or steer
134
- it toward the previous repair. Replaying a known failure remains an author's
135
- regression check and does not replace independent design evaluation.
136
- 6. **Judge convergence across rounds.** Converge only after two consecutive
137
- unassisted fresh rounds on the same revision with no new material in-scope
138
- finding and no unresolved material defect. A finding is material when it
139
- violates an explicit requirement, makes an evaluator's goal FAIL or BLOCKED,
140
- or exposes an authority, data-integrity or uncertain-write defect; record
141
- why any finding was judged non-material. Every repair creates a new revision
142
- and resets the streak, so hold non-material repairs until after the verdict.
143
- Each explicit requirement observable from the evaluator's role needs
144
- evaluator-observed evidence; requirements outside that role's view (another
145
- caller's isolation or hidden state) are verified by author regressions and
146
- reported under that label. A role-observable requirement no evaluator reached
147
- is UNVERIFIED and blocks CONVERGED. If fresh evaluators never reach a required
148
- path, vary the fixtures, not the brief or the steps, or report that path as
149
- UNVERIFIED. Repeated success on one narrow path cannot establish broader
150
- readiness. Keep optional wishes, non-material suggestions and external
151
- BLOCKED gates visible; no finding count proves absence of defects. When the
152
- round cap or an external blocker stops the loop, report NOT CONVERGED or
153
- BLOCKED with the remaining work, not acceptance. Setup cleans isolated
154
- fixtures in `finally` after every round, including failed/aborted rounds.
88
+ ## Run role-only acceptance rounds until they converge
89
+
90
+ The evaluator gets a role, not a task list. It works out what that role needs
91
+ to get done from the interface itself; what it derives, and what it misses, is
92
+ the evidence.
93
+
94
+ 1. **Write a private rubric first.** From the original brief, the author lists
95
+ what each role must be able to do on each surface (Admin, application
96
+ pages, REST, MCP), with observable success criteria, explicit exclusions
97
+ and the authority each role must not have. The rubric stays with the author;
98
+ no evaluator ever sees it, its wording or its order.
99
+ 2. **Hand each evaluator only its role.** Supply the business role and context
100
+ in one or two sentences, the entry points that role really uses (Admin URL,
101
+ site URL, REST base URL and API reference, MCP endpoint), a signed-in browser
102
+ session or client token from setup, the interaction/time/write budget,
103
+ permitted fake-data actions and what is forbidden (source, SQL, internal
104
+ docs, the database, other roles' credentials, blind retries of uncertain
105
+ writes). Withhold the brief's requirement list, capability maps, tool names,
106
+ precomputed IDs and earlier reports. Seed realistic fixtures: other owners'
107
+ records, several states and more than one page of results. Run the evaluator
108
+ outside the project directory, without Mantle skills or project instruction
109
+ files, and record that isolation.
110
+ 3. **Let it derive the work, then do it.** Before any interaction the evaluator
111
+ writes down what this role would need to accomplish and how it would know
112
+ each one succeeded. It then refines that list as it discovers the interface
113
+ (at least once after a first look, again after its first attempts), executes
114
+ it, and reports PASS/FAIL/BLOCKED per goal with readbacks, rejected writes,
115
+ recovery attempts and interaction counts. Zero findings is a valid result.
116
+ 4. **One fresh agent per role, every surface.** In the first round, and in any
117
+ round the [re-verification rule](#re-verify-what-a-repair-touches) widens
118
+ to all of them, cover every role and surface. For the default two roles, run
119
+ an `admin` evaluator and a `user` evaluator in the same round on shared live
120
+ data, in the brief's natural order. Between them they cover every surface
121
+ the app exposes to each role: Admin for `admin`, pages for visitors and
122
+ members, REST for client developers, MCP for both roles' AI clients. A
123
+ surface no evaluator reached stays UNVERIFIED.
124
+ 5. **Score against the rubric outside the evaluator.** The author compares the
125
+ evaluator's derived goals and results with the rubric. A rubric item the
126
+ evaluator never thought of is itself a finding: the interface did not make
127
+ that job discoverable. Classify every finding P0 (data loss, authority or
128
+ integrity defect, uncertain write), P1 (a rubric item FAIL, BLOCKED or not
129
+ discoverable), P2 (friction) or P3 (suggestion), with evidence and the
130
+ smallest repair. Separate a violated explicit requirement, a demonstrated
131
+ defect and a newly inferred product wish; a wish is not permission to grow
132
+ the product. A goal the evaluator's own environment stopped (its tool
133
+ approval, sandbox or network, not an app refusal) is not a finding: record
134
+ it as an [environment block](#report-done-ness-in-layers) and rerun that
135
+ role once it is lifted.
136
+ 6. **Repair outside the evaluator's context, then re-verify what it touches.**
137
+ The author keeps the findings ledger, makes the smallest in-scope repair,
138
+ adds focused regressions (authorization, OCC, atomicity), and freezes a new
139
+ revision. The next round re-runs the roles and surfaces the
140
+ [re-verification rule](#re-verify-what-a-repair-touches) names, with new
141
+ agents and new fixtures, and passes them nothing from earlier rounds.
142
+ 7. **Converge or stop.** A round converges when it has no P0 or P1 and
143
+ evaluator-observed results cover every rubric item visible to their roles:
144
+ from this round, or reused from an earlier round under the
145
+ re-verification rule. Items outside any role's view (another caller's
146
+ isolation, hidden state) need author regressions, reported under that
147
+ label. Run at most three
148
+ rounds. If the third still has a P0 or P1 or an uncovered rubric item, report
149
+ BLOCKED with the remaining ledger, not acceptance. Setup cleans fixtures in
150
+ `finally` after every round, including aborted ones.
155
151
 
156
152
  If a fresh agent or authorized test transport is unavailable, record independent
157
- acceptance as BLOCKED and report the scripted checks separately. Do not invent
153
+ acceptance as BLOCKED and report the scripted checks on their own
154
+ [layers](#report-done-ness-in-layers). Do not invent
158
155
  an independent result. Do not deploy or publish merely to obtain acceptance.
159
156
 
160
- ## Give the agent a role and the user's brief
161
-
162
- Fill in a brief like this using the actual user's request and authorized
163
- scope. Do not replace it with a sequence tailored to existing tools:
164
-
165
- > You are a [business role] working in [business context]. The user requested:
166
- > [original brief, explicit requirements and exclusions]. You are authorized
167
- > to test with fictional data through [user-facing surface and entry point]
168
- > within [allowed actions and interaction/time/write budgets]; [owner] will clean up
169
- > the fixtures.
170
- > Independently define realistic acceptance goals and observable success
171
- > criteria before testing. Use or discover the supplied interface and assess
172
- > whether it lets you make the decisions your goals require, not merely perform
173
- > successful interactions. Report per-goal PASS/FAIL/BLOCKED with readback
174
- > evidence, design shortcomings, interaction costs and minimal improvements.
175
- > Distinguish failures within the requested scope from newly inferred product
176
- > needs; do not invent unsupported interfaces or change the product. Do not inspect
177
- > source, internal docs or a database, blindly retry uncertain writes, or act
178
- > outside the supplied authority. Finding no defects is acceptable; report
179
- > what you actually covered and what remains unverified.
157
+ Acceptance proves the behavior, not its speed. Once a round converges, the
158
+ [performance skill](../../skills/performance/SKILL.md) decides which performance
159
+ checks apply and holds them to their targets; a performance repair that changes
160
+ what a role sees or can do returns here, scoped by the
161
+ [re-verification rule](#re-verify-what-a-repair-touches).
162
+
163
+ ## Re-verify what a repair touches
164
+
165
+ The first delivery always gets full acceptance: every role, every surface.
166
+ After a repair, the change itself decides what runs again, not the finding
167
+ that prompted it. Rounds before convergence are not accepted, so diff against
168
+ evidence, not rounds: for each item you want to reuse, diff the current
169
+ revision against the revision it was observed at, and pick the widest row that
170
+ diff matches. A chain of repairs adds up; a page-only repair followed by a
171
+ handler repair has to re-run what the handler repair reaches.
172
+
173
+ | The repair changes | Re-run with fresh evaluators | May reuse |
174
+ | --- | --- | --- |
175
+ | Only presentation: page or Admin layout, CSS, copy, client-side display | Blind acceptance of the affected UI, for each role that uses it. A shared component or layout affects every page that renders it. | REST and MCP evidence from the same business-rule version |
176
+ | A public contract: a tool or route description, input, output or error shape; the generated client or other `generated/` output it changes | That surface for each role that uses it, plus every UI that consumes it | Other surfaces at the same business-rule version |
177
+ | Authentication, sessions, roles, `requires`, row or tenant scope | Every surface that the changed rule guards, for every affected role | Nothing those surfaces depend on |
178
+ | A SQL Procedure or View, a `uniqueIndexes` entry, a check or lock, a migration that changes results, a handler's business rule | Every surface that reads or writes that data | Nothing those surfaces depend on |
179
+ | A shared transport: the service entry, routing, the HTTP or MCP mount, a shared error envelope | Every surface on that transport | Nothing on that transport |
180
+
181
+ The business-rule version is the revision of the manifests, SQL, migrations,
182
+ handlers, authorization and service entry. A repair that changes none of
183
+ them keeps that version, so evidence collected at it still stands for
184
+ surfaces the repair did not reach. A repair that changes any of them
185
+ starts a new business-rule version, and looking at the UI alone is not
186
+ enough. One exception: a repair that only adds or drops a non-unique Schema
187
+ `indexes` entry and its migration keeps the version. It changes speed, not
188
+ which rows a read returns or which writes are refused. A `uniqueIndexes`
189
+ entry, a check or a lock does change which writes succeed, so it falls in the
190
+ SQL row. When the diff is unclear, widen.
191
+
192
+ Every row still runs the author's regressions on the new revision (layers 1
193
+ and 2 of the [layered report](#report-done-ness-in-layers)): `pnpm check`,
194
+ `pnpm smoke`, `pnpm test` and the focused regressions for the repair.
195
+
196
+ A re-run evaluator is as fresh as the first: a new agent, new fixtures, only
197
+ its role and the entry points under re-test. Do not tell it what was
198
+ repaired, what to check or what an earlier evaluator did. No previous
199
+ answers, rubric or precomputed steps.
200
+
201
+ For every rubric item that was not re-run, record the round and revision its
202
+ evidence comes from and why the changes since that revision could not affect
203
+ it. Never report a
204
+ reused item as observed in a later round.
205
+
206
+ ## Hand each role to its evaluator
207
+
208
+ Setup signs each role in; the evaluator receives the role's access, never a credential it could reuse or widen.
209
+ This is the authorized test transport the BLOCKED rule above refers to.
210
+
211
+ - **Local session URL (the app shell's default).** `pnpm accept` in an app written by `mantle init` starts the app
212
+ on a fresh local D1, signs the admin in, runs an optional fixtures module, signs the user in and prints one local
213
+ URL per role. Each URL forwards every request with that role's session and refuses `/api/auth/*`. Give the
214
+ evaluator only its URL: MCP at `<url>/mcp` (user) or `<url>/mcp/admin` (admin) for any streamable HTTP MCP client,
215
+ Admin at `<url>/admin` and member pages at `<url>/` in a browser, REST at `<url>/` with no credential. An evaluator
216
+ without an MCP client uses `node accept.mjs mcp <endpoint> [tool [json]]`, which prints the raw result including
217
+ `isError`. The app README's "Independent acceptance" section shows the fixtures module.
218
+ - **An external MCP host over OAuth.** To accept the connection a real client uses (Claude, ChatGPT or another host
219
+ that supports MCP OAuth), deploy or tunnel the app as you would for that host, add `<origin>/mcp` or
220
+ `<origin>/mcp/admin` to the host in setup, and complete sign-in and consent as the role's fictional identity. The
221
+ evaluator then works in that already-connected host. Its token stays in the host; the evaluator neither sees nor
222
+ requests it. Record this as OAuth evidence, separate from the local session URL (see
223
+ [OAuth](oauth.md)).
224
+
225
+ Either way, the evaluator's prompt names the URL or the connected host, never a cookie, token or sign-in step.
226
+
227
+ If you cannot start a fresh agent yourself (for example, you are a subagent without that tool), prepare everything
228
+ else: the rubric, the fixtures module, `pnpm accept` running, and one filled-in role prompt per role (below) naming
229
+ its URL. Hand the prompts to whoever can start agents (the person or the orchestrating agent), keep the app running,
230
+ and report acceptance as BLOCKED with the prompts attached until their reports come back. Score those reports as
231
+ usual.
232
+
233
+ ## Give the agent a role
234
+
235
+ Fill this in per role. It names the role and the doors, never the jobs:
236
+
237
+ > You are [role, for example "a purchasing manager"] at [business context in
238
+ > one sentence]. You use [entry points for this role from `pnpm accept`: Admin
239
+ > URL, site URL, REST base URL and reference, MCP endpoint]; setup has already
240
+ > signed you in as [fictional identity]. Budget: [interactions/time/writes]. You may create and
241
+ > change fictional records; [owner] cleans up. Do not read source, internal docs
242
+ > or a database, use anyone else's access, or retry a write whose outcome you
243
+ > do not know.
244
+ > First, write down what someone in this role needs to get done with this
245
+ > service and how you would tell each one worked. Refine that list after a
246
+ > first look at the interface and again after your first attempts. Then do the
247
+ > work and report each goal as PASS/FAIL/BLOCKED with what you read back,
248
+ > what was refused, what it cost you, and the smallest change that would have
249
+ > helped. For BLOCKED, say whether the service refused you or your own tools
250
+ > or permissions stopped you. Report what you covered and what you could not reach; finding nothing
251
+ > wrong is acceptable.
180
252
 
181
253
  The author separately verifies isolation between callers, stale-version
182
254
  refusal, unknown-outcome handling and effects hidden from the evaluator's role
@@ -184,23 +256,71 @@ through authorized test fixtures. Do not grant the evaluator extra authority
184
256
  just to observe hidden state. Keep author regressions distinct from what the
185
257
  fresh agent actually observed.
186
258
 
259
+ ## Report done-ness in layers
260
+
261
+ Report a handoff in four layers, in this order, each for the exact commit. Each
262
+ layer proves something the others do not; a passing layer neither replaces nor
263
+ is invalidated by another layer's result.
264
+
265
+ | Layer | What it proves | Evidence |
266
+ | --- | --- | --- |
267
+ | 1. Generation and types | Manifests compile, `generated/` is current, the app typechecks and builds. No handler runs. | `pnpm check` (includes `generate --check`), `pnpm build` |
268
+ | 2. Business rules and permissions | Handlers on a fresh local D1 over HTTP: each operation at its edges, refusals, caller isolation, visitor pages, MCP discovery, the Admin MCP App loading. Author's evidence. | `pnpm smoke`, `pnpm test`; [Local HTTP checks](local-testing.md) |
269
+ | 3. Independent acceptance, per role | A fresh agent given only its role and entry points finds and finishes that role's work on each surface it uses. | Per role: the [rounds](#run-role-only-acceptance-rounds-until-they-converge) verdict and surfaces covered, naming the round and revision of any [reused](#re-verify-what-a-repair-touches) evidence |
270
+ | 4. External platform | A real MCP host connects over OAuth; the production origin serves the deployed app. Local cookie sessions prove neither. | Host and version, identity, origin, observed result |
271
+
272
+ Give each layer, and each role on layer 3, one status:
273
+
274
+ - **verified**: the evidence above, with its command or host and result.
275
+ - **failed**: the product is wrong. Name the failing check or finding.
276
+ - **BLOCKED**: the layer could not run. Give the reason, what refused or is
277
+ missing, and what would unblock it.
278
+ - **not applicable**: with the reason. Layer 4 is not applicable unless the
279
+ requested work includes an external host connection or a deployment.
280
+
281
+ A layer nobody attempted is UNVERIFIED, as above. A block from the execution
282
+ environment (an agent's approval policy refusing fictional writes, no browser,
283
+ no network, a sandbox) is BLOCKED on that layer only. It is never a failed
284
+ layer or a product finding, and it does not invalidate the evidence on other
285
+ layers. Do not work around it by widening the evaluator's authority, faking a
286
+ result or deploying; ask whoever controls the environment and rerun only that
287
+ layer on the same revision.
288
+
289
+ The work is done only when layers 1 to 3 are verified for every role and
290
+ surface, and layer 4 is verified or not applicable. Partial evidence is still
291
+ reported, layer by layer; it is not done:
292
+
293
+ ```markdown
294
+ | Layer | Status | Evidence |
295
+ | --- | --- | --- |
296
+ | 1. Generation and types | verified | `pnpm check`, `pnpm build` at <commit> |
297
+ | 2. Business rules and permissions | verified | `pnpm smoke` (<n> checks), `pnpm test` |
298
+ | 3. Acceptance: admin | BLOCKED: evaluator's approval policy refused to create fictional records; needs <permission> | round 1 stopped at goal <n> |
299
+ | 3. Acceptance: user | verified: CONVERGED in round <n> | member pages in round <n>; MCP reused from round <m> at <revision> (page-only repair) |
300
+ | 4. External platform | not applicable: no deployment or external host requested | |
301
+ ```
302
+
187
303
  ## Record evidence in the existing issue or handoff
188
304
 
189
305
  Use ordinary Markdown; no new receipt schema or framework is required:
190
306
 
191
- - Original brief, explicit requirements and exclusions, role, environment,
192
- per-round budgets and round cap.
307
+ - Original brief, the private rubric, roles, environment, per-round budgets
308
+ and round cap.
193
309
  - Setup assistance and what each agent could access; identify distinct rounds.
194
- - Per round: revision, the evaluator's own goals and criteria, per-goal
310
+ - Per round: revision, business-rule version, the roles and surfaces re-run
311
+ and why (the [re-verification rule](#re-verify-what-a-repair-touches) row),
312
+ each evaluator's derived goals (first list and
313
+ refinements) and criteria, the gaps between them and the rubric, per-goal
195
314
  PASS/FAIL/BLOCKED, actual readbacks and interaction references,
196
315
  request/page/tool counts
197
316
  and negative/recovery attempts, with their differing scope stated.
198
- - Findings ledger with materiality, owner, disposition, minimal repair and
317
+ - Findings ledger with P0–P3, owner, disposition, minimal repair and
199
318
  regression commands/results.
200
- - Streak and reset history; coverage of each explicit requirement
201
- (evaluator-observed, author regression only, or UNVERIFIED); the final
202
- CONVERGED / NOT CONVERGED / BLOCKED / UNVERIFIED verdict and rounds used. Retain every
203
- failed report.
319
+ - Coverage of each rubric item (evaluator-observed in this round,
320
+ evaluator-observed and reused from round <n> at <revision> with the reason,
321
+ author regression only, or UNVERIFIED); the final CONVERGED / BLOCKED / UNVERIFIED verdict and rounds
322
+ used, which is layer 3 of the [layered report](#report-done-ness-in-layers).
323
+ Retain every failed report.
204
324
  - Fixture cleanup result and every remaining limitation.
205
325
 
206
326
  Keep credentials, raw auth logs, full resource HTML and private operational
@@ -0,0 +1,85 @@
1
+ ---
2
+ description: Scripted local checks before acceptance - handlers over real HTTP, public pages without JavaScript, MCP discovery, Admin's MCP App, and how to account for every surface.
3
+ ---
4
+ # Local HTTP checks
5
+
6
+ `pnpm check` proves that the manifests compile and the app typechecks; it runs no handler. Before you report an app
7
+ or a feature ready, run it on a fresh local D1 and drive it over HTTP. In an app written by `mantle init`, that is
8
+ `smoke.mjs` on the shell's `smoke-kit.mjs` (`pnpm smoke`): it applies `generated/schema.sql` and `seed.sql`, starts
9
+ `wrangler dev --local`, signs users in with the console email code and deletes everything afterwards.
10
+ These checks are the author's evidence: layers 1 and 2 of the
11
+ [layered handoff report](agent-workflow-acceptance.md#report-done-ness-in-layers). [Independent
12
+ acceptance](agent-workflow-acceptance.md) comes after them and does not replace them, nor they it.
13
+
14
+ ## What smoke covers
15
+
16
+ - **Every Procedure and HTTP Trigger the app declares:** valid input with the expected status and body (a status
17
+ below 500 is not success), invalid input, an anonymous call to a protected operation, the other role, and the
18
+ stored result read back. Use only the generated routes, inputs and permissions.
19
+ - **Business rules at their edges:** the oversell, the stale `expectedVersion`, the second approval, a refusal that
20
+ must leave nothing behind (check the rows with the kit's `sql`), and two concurrent calls where a race matters.
21
+ - **Each caller sees only its own rows:** a second member's records are absent from lists and refused by id.
22
+ - **Visitor pages:** the checks in [public pages](public-pages.md#check-before-you-report-done), requested without
23
+ JavaScript.
24
+ - **MCP:** `assertMcpDiscovery(endpoint, { tools, headers })` from `smoke-kit.mjs` for `/mcp` and `/mcp/admin`, with
25
+ the expected tools taken from generated code (the app-shell README shows it), plus a separate assertion that an
26
+ anonymous call and a member calling `/mcp/admin` are refused. Discovery needs no business `tools/call`.
27
+ - **Admin:** `pnpm build`, then `pnpm test` (`generated/admin-mcp-app.check.mjs`) loads the Admin MCP App in an MCP
28
+ host and fails on a network error, console error or exception. It needs `playwright-core` and Chrome
29
+ (`npx playwright-core install chromium`, or `MANTLE_TEST_CHROMIUM=<executable>`). With no browser it fails: report
30
+ that check as BLOCKED on layer 2, not skipped and not as a product failure.
31
+
32
+ Better Auth allows 3 one-time-code requests per minute per client IP; the kit gives each user its own
33
+ `cf-connecting-ip`. Credentials come only from the kit's sign-in: a check never scrapes another session or builds a
34
+ Caller by hand.
35
+
36
+ ## Seed data
37
+
38
+ Create test records through the app itself: in `smoke.mjs` or the `pnpm accept` fixtures module, call the app's own
39
+ REST, MCP or Admin operations as a signed-in user (`call("POST", "/admin/api/operations/create-event", { cookie: admin,
40
+ body })`), or insert through the Store (`store(db, caller).collection("events").insert(...)`). Mantle then fills every
41
+ system column (`id`, `version`, `created_at`, `updated_at`, `author_id`, and on a publishing Schema `status` and
42
+ `published_at` through its publish Procedure) and enforces the Schema's checks, scope and references, exactly as for
43
+ real data.
44
+
45
+ Keep `seed.sql` for static reference data that no surface writes, such as categories or department budgets.
46
+ `pnpm db:seed` and `smoke-kit.mjs` apply it as native SQL, which bypasses all of the above, so the seed sets each
47
+ system column itself:
48
+
49
+ | Column | SQLite and D1 (`cloudflare-d1`, `bun-sqlite`) | PostgreSQL | If the seed omits it |
50
+ |---|---|---|---|
51
+ | `id` | text, unique | text, unique | the insert fails |
52
+ | `version` | `1` | `1` | `1` |
53
+ | `created_at` | integer microseconds since the Unix epoch | `timestamptz` | the insert fails |
54
+ | `updated_at` | integer microseconds; the same as `created_at` | `timestamptz` | SQLite and D1 store `0`: Admin shows 1970-01-01. PostgreSQL stores `now()` |
55
+ | `author_id` | the creator's user id, or `NULL` for system rows | the same | `NULL`: under `restrict: { <schema>: "author_id = auth.uid()" }` no member sees the row |
56
+ | `status` (publishing) | `'draft'`, `'published'` or `'archived'` | the same | `'draft'` |
57
+ | `published_at` (publishing) | microseconds when `status` is `'published'`, otherwise `NULL` | `timestamptz`, otherwise `NULL` | `NULL` |
58
+
59
+ A field- or owner-scoped Schema also needs its scope column. Use fixed instants for repeatable fixtures
60
+ (`1767225600000000` is 2026-01-01T00:00:00Z), or the moment the seed runs (`unixepoch() * 1000000` on SQLite and
61
+ D1, `now()` on PostgreSQL). For a publishing `posts` Schema on D1:
62
+
63
+ ```sql
64
+ INSERT INTO posts (id, version, created_at, updated_at, author_id, status, published_at, title, body) VALUES
65
+ ('welcome', 1, 1767225600000000, 1767225600000000, NULL, 'published', 1767225600000000, 'Welcome', '# Welcome'),
66
+ ('draft-note', 1, unixepoch() * 1000000, unixepoch() * 1000000, NULL, 'draft', NULL, 'Draft', '# Draft');
67
+ ```
68
+
69
+ The same rows on PostgreSQL:
70
+
71
+ ```sql
72
+ INSERT INTO posts (id, version, created_at, updated_at, author_id, status, published_at, title, body) VALUES
73
+ ('welcome', 1, '2026-01-01T00:00:00Z', '2026-01-01T00:00:00Z', NULL, 'published', '2026-01-01T00:00:00Z', 'Welcome', '# Welcome'),
74
+ ('draft-note', 1, now(), now(), NULL, 'draft', NULL, 'Draft', '# Draft');
75
+ ```
76
+
77
+ A seed is local test data; never apply it to an existing production database.
78
+
79
+ ## Account for every surface
80
+
81
+ In the app's README or handoff, list each declared surface (Admin, each MCP endpoint, REST, each visitor page type)
82
+ as verified (command, identity and result), intentionally not exposed, or UNVERIFIED with a reason. Local cookie
83
+ sessions do not prove an external MCP host's OAuth connection or a production origin; those are layer 4. Record the
84
+ commands and their results for the exact commit in the layered report, fix failures and rerun the affected checks. A
85
+ check the environment prevented (no browser, no network, a refused approval) is BLOCKED with its reason, not failed.