toga-ai 1.0.686 → 1.0.687
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.
|
@@ -5,13 +5,15 @@ project: _Underscore
|
|
|
5
5
|
client: shared
|
|
6
6
|
type: standard
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-
|
|
9
|
-
owners: [jcardinal, ajean]
|
|
8
|
+
updated: 2026-08-28
|
|
9
|
+
owners: [jcardinal, ajean, apeterson]
|
|
10
10
|
files: []
|
|
11
11
|
related:
|
|
12
12
|
- ../apps/_underscore/architecture.md
|
|
13
13
|
- ../apps/worker2/architecture.md
|
|
14
14
|
- ../apps/api2/architecture.md
|
|
15
|
+
- ../apps/dbchanges2/architecture.md
|
|
16
|
+
- ../apps/dbchanges2/features/surface-layer-schema.md
|
|
15
17
|
---
|
|
16
18
|
|
|
17
19
|
# Framework 2.0 (_underscore) Rules
|
|
@@ -137,6 +139,14 @@ Enforced mechanically by the `PreToolUse` hook
|
|
|
137
139
|
examples, and the approved rewrites: `2.0/apps/dbchanges2/architecture.md` → *Database
|
|
138
140
|
isolation*. **Applies to `dbchanges2` only — the 1.0 `dbchanges` repo is exempt.**
|
|
139
141
|
|
|
142
|
+
**Verified empirically in production, 2026-08-28:** the Core reader exposes only `Core`,
|
|
143
|
+
`Forecast`, `Forecast_Archive` and `Team`; the client reader exposes only `Client_*`. This is a
|
|
144
|
+
**physical** boundary, not a permissions setting to be widened — a `Client_*` file naming `Core.*`
|
|
145
|
+
does not fail an authorization check, it cannot resolve at all. Corollary for **reviewers**: "it
|
|
146
|
+
ran locally" and "it ran on beta" are both worthless as evidence of production-runnability, because
|
|
147
|
+
every non-prod environment is all-in-one. The only meaningful checks are the hook, or a
|
|
148
|
+
connection-scoped run against a user granted just the one database.
|
|
149
|
+
|
|
140
150
|
### `Core.Records` / `Core.RecordFields` — the only hardcoded `id`s on the platform
|
|
141
151
|
|
|
142
152
|
**These two tables, and only these two, have team-maintained primary keys.** Their `id` values are
|
|
@@ -161,6 +171,58 @@ This is also what makes the `dbchanges2` isolation rule above workable: a `Clien
|
|
|
161
171
|
never has to read `Core` to resolve a `recordFieldId`. Full detail and examples:
|
|
162
172
|
`2.0/apps/dbchanges2/architecture.md` → *`Core.Records` / `Core.RecordFields`*.
|
|
163
173
|
|
|
174
|
+
#### ⚠ Bounded exemption — the Surface tables
|
|
175
|
+
|
|
176
|
+
**`Core.Surfaces`, `Core.SurfaceElements` and `Core.Messages` also carry team-reserved literal `id`
|
|
177
|
+
blocks.** This is a **specific exemption for the Surface layer, not a general precedent** — do not
|
|
178
|
+
cite it to justify hardcoding ids for any other feature. Reserving them was forced by the cluster
|
|
179
|
+
split above: `SurfaceOverrides` lives in the **Client** DB while the elements it overrides live in
|
|
180
|
+
**Core**, so a `Client_*` override file must reference a Core id it is forbidden to look up at run
|
|
181
|
+
time. Without a stable literal there is nothing for that file to hardcode.
|
|
182
|
+
|
|
183
|
+
When seeding into these three tables:
|
|
184
|
+
|
|
185
|
+
- **Verify the block is actually free** — run both `SELECT MAX(id)` *and*
|
|
186
|
+
`SELECT id FROM … WHERE id BETWEEN <lo> AND <hi>`. Confirm whether a number you were handed means
|
|
187
|
+
**"next free"** or **"highest used"**; reading one as the other has already caused a live
|
|
188
|
+
collision.
|
|
189
|
+
- **Use a plain `INSERT`, never `INSERT IGNORE`.** `IGNORE` turns a primary-key collision — the one
|
|
190
|
+
error that must halt the migration — into a silent mis-wiring. Put idempotency in a `NOT EXISTS`
|
|
191
|
+
guard on a *semantic* key, or in a paired `UPDATE`.
|
|
192
|
+
|
|
193
|
+
Blocks in use, the authoring house style, and the collision incident:
|
|
194
|
+
[surface-layer-schema](../apps/dbchanges2/features/surface-layer-schema.md).
|
|
195
|
+
|
|
196
|
+
### A committed migration records INTENT, not deployed state
|
|
197
|
+
|
|
198
|
+
**Never infer what an environment contains by reading `dbchanges2`. Query the environment.**
|
|
199
|
+
|
|
200
|
+
There is no automated executor. A human applies each `.sql` per environment, and where the
|
|
201
|
+
isolation rule bites — a client-side file that needs a Core id — the operator resolves the id
|
|
202
|
+
against the target's Core and **hand-writes the literal statement at run time**. Those
|
|
203
|
+
transpositions have historically **not been written back to the repo**. The consequence is
|
|
204
|
+
permanent and structural, not a backlog item:
|
|
205
|
+
|
|
206
|
+
- A file in the repo may never have run anywhere.
|
|
207
|
+
- A file that *did* run may have run in an edited form that exists nowhere in version control.
|
|
208
|
+
- Deployed rows can therefore have no committed file that would reproduce them.
|
|
209
|
+
|
|
210
|
+
So before authoring anything whose correctness depends on current state — an override, a
|
|
211
|
+
correction, an `INSERT … NOT EXISTS` guard, an id block — **establish that state with read-only
|
|
212
|
+
`SELECT`s against the target environment's read replicas.** Connect using the `readhost` keys of
|
|
213
|
+
the `[database]` and `[databaseClient]` groups in `api2/Config/production.ini`; never copy
|
|
214
|
+
credential values into a doc, a ticket, a commit or a chat message.
|
|
215
|
+
|
|
216
|
+
**The hook cannot save you here.** `dbchanges2-cluster-isolation.js` is a static check on the text
|
|
217
|
+
of a file, so it catches a cross-database *reference* at authoring time. It cannot know whether a
|
|
218
|
+
file ever ran, ran in edited form, or matches the target environment. Cluster isolation is
|
|
219
|
+
mechanically enforced; state verification is **on you**.
|
|
220
|
+
|
|
221
|
+
Worked examples of both failure modes — a file unrunnable in prod, and prod rows with no
|
|
222
|
+
corresponding committed file — are in
|
|
223
|
+
[surface-layer-schema](../apps/dbchanges2/features/surface-layer-schema.md) and
|
|
224
|
+
[surface-resolver](../apps/_underscore/features/surface-resolver.md).
|
|
225
|
+
|
|
164
226
|
## Checking dependencies before touching shared code
|
|
165
227
|
|
|
166
228
|
`dependsOn` in `knowledge/registry.json` means a repo extends or depends on another repo's classes. Before modifying a class in a dependency repo (e.g. `_underscore` core):
|
|
@@ -230,6 +292,22 @@ and not how the convention works. Use a non-`c_` column when you *do* want the f
|
|
|
230
292
|
ACL-governed field treatment.
|
|
231
293
|
|
|
232
294
|
## Change history
|
|
295
|
+
- 2026-08-28 — Extended § Schema migrations with the production-state rules. Recorded that cluster
|
|
296
|
+
isolation is **physical** (verified: the prod Core reader exposes only Core/Forecast/
|
|
297
|
+
Forecast_Archive/Team, the client reader only `Client_*`), so a cross-database reference cannot
|
|
298
|
+
resolve rather than merely being denied — and that neither a local nor a beta run is evidence of
|
|
299
|
+
prod-runnability, since every non-prod environment is all-in-one. Added a **bounded exemption**
|
|
300
|
+
to the hardcoded-id rule for the Surface tables only (`Core.Surfaces`/`SurfaceElements`/
|
|
301
|
+
`Messages` carry reserved literal blocks because `Client_*` override files must reference Core
|
|
302
|
+
ids across the cluster split and cannot resolve them at run time) — **explicitly not a general
|
|
303
|
+
precedent**; the platform rule that only `Core.Records`/`Core.RecordFields` have team-maintained
|
|
304
|
+
ids is unchanged. Attached the seeding safeguards to that exemption (verify the block is free
|
|
305
|
+
with `MAX(id)` *and* a `BETWEEN` check; "next free" vs "highest used"; plain `INSERT`, never
|
|
306
|
+
`INSERT IGNORE`). New subsection: **a committed migration records intent, not deployed state** —
|
|
307
|
+
there is no automated executor, operators hand-transpose Core ids at run time and historically
|
|
308
|
+
did not write them back, so current state must be established with read-only queries against the
|
|
309
|
+
target environment, and the cluster-isolation hook catches cross-database references but
|
|
310
|
+
**cannot** catch state drift. (apeterson)
|
|
233
311
|
- 2026-07-29 — Documented the `c_`-prefixed framework-dynamic column convention (schema-only,
|
|
234
312
|
no model-class/RecordFields/ACL declaration), generalized from the transcript AI-model routing
|
|
235
313
|
work. (ajean)
|
package/package.json
CHANGED