@rehearsal-db/core 0.1.0-beta.13 → 0.1.0-beta.14
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.
- package/CHANGELOG.md +76 -0
- package/README.md +3 -1
- package/docs/configuration.md +40 -5
- package/docs/getting-started.md +28 -0
- package/docs/production-source.md +43 -7
- package/docs/releasing.md +5 -3
- package/docs/roadmap.md +3 -2
- package/docs/sanitization.md +415 -14
- package/docs/standalone-workflow.md +12 -6
- package/docs/troubleshooting.md +5 -1
- package/docs/tutorial.md +5 -0
- package/package.json +1 -1
- package/scripts/install/create_root_config.mjs +5 -22
- package/src/baseline/privacy_engine.mjs +660 -213
- package/src/baseline/privacy_structured_json.mjs +554 -0
- package/src/cli/rehearsal.mjs +3 -1
- package/src/cli/renderers.mjs +12 -0
- package/src/cli/source_commands.mjs +11 -3
- package/src/identity/claim.mjs +23 -21
- package/src/project/install_scaffold.mjs +35 -1
- package/src/project/setup.mjs +9 -1
- package/src/source/access.mjs +50 -5
- package/src/source/asset_transfer.mjs +139 -87
- package/src/source/external_reader.mjs +136 -23
- package/src/source/postgresql_access.mjs +80 -23
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,82 @@ a Changelog, and versions will follow Semantic Versioning after the package exis
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## [0.1.0-beta.14] - 2026-10-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Packed-package onboarding is now a required CI job. It covers fresh Supabase and
|
|
13
|
+
PostgreSQL installs, beta.7 configuration preservation, missing credentials, occupied
|
|
14
|
+
ports, malformed configuration, and non-destructive partial-setup recovery.
|
|
15
|
+
- The independent test lab can run the same first-install boundary against an exact
|
|
16
|
+
candidate tarball before either database lifecycle.
|
|
17
|
+
- Installation and setup now report the exact active Rehearsal configuration path and
|
|
18
|
+
explain when a supported existing nested config prevents root-file generation.
|
|
19
|
+
- Packed-package onboarding covers fresh, nested, existing-root, and repeated installs
|
|
20
|
+
while proving reviewed configuration bytes are preserved.
|
|
21
|
+
- Privacy policy version 2 can explicitly treat nullable owners as non-matches and match
|
|
22
|
+
an approved owner through any of a bounded list of pseudonymized identity columns.
|
|
23
|
+
- Bounded JSON unions classify each permitted root type, while JSON dictionaries require
|
|
24
|
+
reviewed runtime-key formats, key actions, value recipes, and item/depth/byte limits.
|
|
25
|
+
- JSON dictionaries can restrict runtime keys to canonical signed integers within an
|
|
26
|
+
explicit safe-integer range, preserving positive, negative, and reviewed zero keys
|
|
27
|
+
without widening numeric maps to arbitrary text.
|
|
28
|
+
- Date shifting can explicitly preserve numeric epoch-millisecond representation,
|
|
29
|
+
including inside structured JSON, while sharing a stable grouped offset with related
|
|
30
|
+
epoch or ISO-string dates.
|
|
31
|
+
- Bounded scalar enums retain only explicitly reviewed structural strings, finite
|
|
32
|
+
numbers, or booleans inside ordinary fields and nested JSON declarations, rejecting
|
|
33
|
+
out-of-domain values without echoing them in diagnostics.
|
|
34
|
+
- Validated strings preserve bounded, case-sensitive structural codes from open domains
|
|
35
|
+
using a package-owned portable alphabet, refusing credential-shaped and malformed
|
|
36
|
+
text without consumer regexes, callbacks, or static identifier lists.
|
|
37
|
+
- Keyed hexadecimal and GTIN pseudonyms preserve reviewed database constraints, while
|
|
38
|
+
fixed-width digest recipes derive fingerprints only from explicitly named sanitized
|
|
39
|
+
columns and reject missing, excluded, duplicate, or cyclic inputs.
|
|
40
|
+
- A single GTIN recipe can declare reviewed `allowedLengths`, preserving each source
|
|
41
|
+
value's 8-, 12-, 13-, or 14-digit shape and check digit even inside structured JSON.
|
|
42
|
+
- Keyed URL pseudonyms replace private HTTPS URLs with deterministic, bounded HTTPS
|
|
43
|
+
loopback paths without retaining source hosts, paths, credentials, queries, or tokens.
|
|
44
|
+
- Bounded binding substitution can retain permitted surrounding text while replacing
|
|
45
|
+
exact occurrences of a reviewed UUID with its corresponding keyed UUID pseudonym.
|
|
46
|
+
- External PostgreSQL readers can declare a bounded exact allowlist of provider-owned
|
|
47
|
+
permission groups. Rehearsal verifies safe membership options, group attributes,
|
|
48
|
+
nesting, view ownership, and the complete effective privilege surface.
|
|
49
|
+
|
|
50
|
+
### Documentation
|
|
51
|
+
|
|
52
|
+
- First-time users now have a short recovery table for interrupted setup, failed
|
|
53
|
+
upgrades, port conflicts, missing OAuth credentials, and low Docker capacity.
|
|
54
|
+
- The configuration guide documents a safe manual move to the project root, separating
|
|
55
|
+
config-file-relative JavaScript imports from project-root-relative Rehearsal paths.
|
|
56
|
+
- The sanitization guide documents nullable and multi-column ownership, heterogeneous
|
|
57
|
+
JSON values, runtime-keyed dictionaries, keyed HTTPS replacements, bounded identity
|
|
58
|
+
substitution, and their fail-closed boundaries.
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
|
|
62
|
+
- Source preparation now stores and reads each target's access receipt and owner-only
|
|
63
|
+
reader credential under its configured baseline artifact directory. Nested and
|
|
64
|
+
multi-target state roots no longer collide, and retirement refuses a receipt from a
|
|
65
|
+
different target root.
|
|
66
|
+
- Supabase Storage source preparation now recursively traverses real folder placeholder
|
|
67
|
+
entries while keeping the approved prefix, path, depth, object-count, per-object, and
|
|
68
|
+
total-byte boundaries fail closed.
|
|
69
|
+
- Approved-owner selection now rejects non-finite numeric values in every declared
|
|
70
|
+
owner column before choosing a privacy branch, including when another column already
|
|
71
|
+
matches the approved owner.
|
|
72
|
+
- Pseudonymized canonical integer dictionary keys now share the same keyed identity
|
|
73
|
+
mapping as numeric columns and nested numeric values using the same namespace, so
|
|
74
|
+
runtime maps keep their reviewed cross-representation joins.
|
|
75
|
+
- External-reader membership mismatches now return a redacted, actionable source
|
|
76
|
+
authorization refusal instead of an internal-failure diagnostic.
|
|
77
|
+
- All external-reader privilege, expiration, readable-surface, and security-definer
|
|
78
|
+
denials now use the same `SOURCE_AUTHORIZATION_REFUSED` diagnostic and exit code 3.
|
|
79
|
+
- Identity claims now sequence PostgreSQL reads on their single transaction client,
|
|
80
|
+
avoiding deprecated overlapping queries and remaining compatible with pg 9.
|
|
81
|
+
|
|
82
|
+
## [0.1.0-beta.13] - 2026-10-05
|
|
83
|
+
|
|
8
84
|
### Added
|
|
9
85
|
|
|
10
86
|
- Runtime and CI compatibility now cover maintained Node.js 22, 24, and 26 releases;
|
package/README.md
CHANGED
|
@@ -45,7 +45,9 @@ For an ordinary npm application install, Rehearsal safely creates a commented
|
|
|
45
45
|
`rehearsal.config.mjs` in the project root, adds only its local-artifact entries to
|
|
46
46
|
`.gitignore`, and prepares the isolated local runtime configuration. It detects the
|
|
47
47
|
project name, database target, migration paths, commands, and free ports. Review lines
|
|
48
|
-
marked `CHECK`, then open the guide. Existing files are never overwritten.
|
|
48
|
+
marked `CHECK`, then open the guide. Existing files are never overwritten. If a supported
|
|
49
|
+
root or nested config already exists, Rehearsal reports that active path and does not
|
|
50
|
+
create a competing file; see [Configuration](docs/configuration.md#workspaces-and-custom-paths).
|
|
49
51
|
|
|
50
52
|
If install scripts are disabled or the workspace root is ambiguous, installation makes
|
|
51
53
|
no guess. Run `npx rehearsal` inside the application and choose **Set the stage** for the
|
package/docs/configuration.md
CHANGED
|
@@ -130,15 +130,20 @@ All paths resolve inside the consuming project. The artifact directory must be n
|
|
|
130
130
|
`.rehearsal`; this is an intentional deletion guard. `runtimeWorkdir` must be its
|
|
131
131
|
`runtime` child, and the generated application environment file must remain inside it.
|
|
132
132
|
|
|
133
|
-
Rehearsal never overwrites this config.
|
|
134
|
-
safe, run setup again, and compare the two files before deleting either one.
|
|
135
|
-
|
|
133
|
+
Rehearsal never overwrites this config. Do not move it merely to inspect a newer template.
|
|
136
134
|
After upgrading, run `npx rehearsal init` to print the current release's generated
|
|
137
135
|
template beside the path of your existing config. This comparison is read-only; adding a
|
|
138
136
|
new optional key remains your decision. `npx rehearsal init --write` still refuses to
|
|
139
137
|
replace an existing config. Rehearsal lists important optional settings that are
|
|
140
138
|
available but not enabled before printing the template, so they are easier to find.
|
|
141
139
|
|
|
140
|
+
If setup was interrupted after creating only some files, rerun
|
|
141
|
+
`npx rehearsal setup --write`. It recreates missing generated files while preserving every
|
|
142
|
+
existing config and local runtime file. When a config itself is invalid, keep it as a
|
|
143
|
+
backup, generate a fresh file, and manually carry over only reviewed settings. See
|
|
144
|
+
[Getting started](getting-started.md#if-setup-or-an-upgrade-is-interrupted) for the short
|
|
145
|
+
recovery table.
|
|
146
|
+
|
|
142
147
|
### Workspaces and custom paths
|
|
143
148
|
|
|
144
149
|
For an npm workspace, automatic creation proceeds only when exactly one application
|
|
@@ -151,8 +156,38 @@ checkout never receive project files.
|
|
|
151
156
|
|
|
152
157
|
Supported existing config paths—including
|
|
153
158
|
`infrastructure/rehearsal/rehearsal.config.mjs`—are preserved, and a second root config
|
|
154
|
-
is not created.
|
|
155
|
-
|
|
159
|
+
is not created. The install hook reports the exact active path when npm displays lifecycle
|
|
160
|
+
output, and setup always prints it. `npx rehearsal init --json` is the package-manager-
|
|
161
|
+
independent way to check the `destination`. Rehearsal checks the supported root filenames
|
|
162
|
+
first and then the supported nested filenames; finding any one of them prevents automatic
|
|
163
|
+
creation of another. Use
|
|
164
|
+
`--config=<project-relative-path>` for other explicit paths when running commands.
|
|
165
|
+
|
|
166
|
+
### Move an existing config to the project root
|
|
167
|
+
|
|
168
|
+
Relocation is deliberately manual because the config may contain reviewed settings and
|
|
169
|
+
JavaScript imports. Rehearsal will never move it or create a competing root file.
|
|
170
|
+
|
|
171
|
+
From the directory containing `package.json`:
|
|
172
|
+
|
|
173
|
+
1. Stop active Rehearsal commands and confirm the current path with
|
|
174
|
+
`npx rehearsal init --json`.
|
|
175
|
+
2. Make a backup outside every supported discovery path, such as
|
|
176
|
+
`.rehearsal/config-backups/rehearsal.config.mjs`.
|
|
177
|
+
3. Move—do not copy—the active file to `./rehearsal.config.mjs`. Use `git mv` when it is
|
|
178
|
+
tracked so review history remains clear.
|
|
179
|
+
4. Update relative JavaScript `import` specifiers inside the moved file. Imports resolve
|
|
180
|
+
from the config file's own folder, so a nested `../../something.mjs` import usually
|
|
181
|
+
changes after moving to the root.
|
|
182
|
+
5. Leave config values such as `migrationDirectory`, `sanitizationPolicy`,
|
|
183
|
+
`runtimePolicy`, `identityPolicy`, and `dependentTargets[].configPath` unchanged unless
|
|
184
|
+
the referenced project file also moved. Rehearsal resolves those values from the
|
|
185
|
+
project root, not from the config file's folder.
|
|
186
|
+
6. Run `npx rehearsal init --json`, confirm its `destination` is
|
|
187
|
+
`rehearsal.config.mjs`, then run `npx rehearsal doctor` and review the Git diff.
|
|
188
|
+
|
|
189
|
+
If validation fails, restore the backup to its original path. Do not run setup while the
|
|
190
|
+
move is half-finished; setup may correctly conclude that no supported config exists.
|
|
156
191
|
|
|
157
192
|
`preparation.privacyKey` must stay inside `.rehearsal`; it is ignored and owner-readable
|
|
158
193
|
only. Source, runtime, and identity policy files are declarations that may be reviewed in
|
package/docs/getting-started.md
CHANGED
|
@@ -48,6 +48,11 @@ especially:
|
|
|
48
48
|
- the application start and proof commands;
|
|
49
49
|
- the local ports.
|
|
50
50
|
|
|
51
|
+
If a supported configuration already exists elsewhere—such as
|
|
52
|
+
`infrastructure/rehearsal/rehearsal.config.mjs`—the installer reports that exact active
|
|
53
|
+
path and does not generate a competing root file. Keep using it, or follow the explicit
|
|
54
|
+
[root relocation guide](configuration.md#move-an-existing-config-to-the-project-root).
|
|
55
|
+
|
|
51
56
|
If lifecycle scripts were disabled, a global/cache install was used, or a workspace has
|
|
52
57
|
more than one possible application, no project files are created automatically. Run
|
|
53
58
|
`npx rehearsal` inside the intended application, choose **Set the stage**, select
|
|
@@ -180,6 +185,29 @@ config during an upgrade. After upgrading, run `npx rehearsal init` to see impor
|
|
|
180
185
|
optional settings you have not enabled and compare your file with the installed
|
|
181
186
|
release's current template.
|
|
182
187
|
|
|
188
|
+
### If setup or an upgrade is interrupted
|
|
189
|
+
|
|
190
|
+
Do not delete `.rehearsal/` or your existing config. Start with:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npx rehearsal doctor
|
|
194
|
+
npx rehearsal init
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Use the reported problem to choose the smallest recovery:
|
|
198
|
+
|
|
199
|
+
| Problem | Safe recovery |
|
|
200
|
+
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
201
|
+
| A generated runtime file is missing | Run `npx rehearsal setup --write`. Existing files stay unchanged; only missing files are created. |
|
|
202
|
+
| A port is already in use | Stop the conflicting local process or choose new dedicated ports, then rerun setup. |
|
|
203
|
+
| The config is malformed | Move it to a backup name, run `npx rehearsal setup --write`, then copy reviewed choices into the new file. |
|
|
204
|
+
| OAuth credentials are missing | Create the ignored file named by `supabase.authentication.environmentFile`, add only the declared keys, and run `chmod 600` on it. Ambient shell credentials are not used. |
|
|
205
|
+
| An npm upgrade fails | Keep the existing config, baseline, and lockfile. Restore dependencies with your package manager, then run `npx rehearsal init` before retrying. |
|
|
206
|
+
| Docker storage is low | Run `npx rehearsal cleanup` for an exact preview. Rehearsal never performs a global Docker prune or silently removes volumes. |
|
|
207
|
+
|
|
208
|
+
`setup --write` and package installation never replace a config. If an existing file is
|
|
209
|
+
invalid, Rehearsal fails closed and leaves it available for comparison.
|
|
210
|
+
|
|
183
211
|
## Files Rehearsal creates
|
|
184
212
|
|
|
185
213
|
- `rehearsal.config.mjs` — reviewed project configuration
|
|
@@ -23,6 +23,15 @@ The privacy key stays ignored under `.rehearsal`. The policy is safe to review i
|
|
|
23
23
|
control because it contains hashes and environment-variable names, not credentials,
|
|
24
24
|
URLs, or raw owner identifiers.
|
|
25
25
|
|
|
26
|
+
`credentialFile` must be inside the selected target's
|
|
27
|
+
`baseline.artifactDirectory`, under its `secrets` folder. With the default artifact
|
|
28
|
+
directory, use `.rehearsal/secrets/source-reader.env`. For a target whose artifact
|
|
29
|
+
directory is `targets/primary/.rehearsal`, use
|
|
30
|
+
`targets/primary/.rehearsal/secrets/source-reader.env` and place `privacyKey` there as
|
|
31
|
+
well. Rehearsal stores that target's access receipt beside its baseline at
|
|
32
|
+
`targets/primary/.rehearsal/source-access-receipt.json`; separate targets never share
|
|
33
|
+
the receipt or credential.
|
|
34
|
+
|
|
26
35
|
## 2. Declare the source boundary
|
|
27
36
|
|
|
28
37
|
`source-access-policy.json` uses this shape:
|
|
@@ -112,6 +121,7 @@ an administrator credential or change the source:
|
|
|
112
121
|
"reader": {
|
|
113
122
|
"mode": "external",
|
|
114
123
|
"role": "provided_rehearsal_reader",
|
|
124
|
+
"allowedMemberships": ["provided_rehearsal_readers"],
|
|
115
125
|
"connectionEnvironmentVariable": "REHEARSAL_SOURCE_READER_URL",
|
|
116
126
|
"credentialFile": ".rehearsal/secrets/source-reader.env",
|
|
117
127
|
"maximumValidForMinutes": 30
|
|
@@ -153,9 +163,25 @@ view hash, columns, scopes, or reader declaration requires a new verification.
|
|
|
153
163
|
|
|
154
164
|
External mode also requires all non-system readable columns to match only the declared
|
|
155
165
|
views and migration-ledger columns. It refuses database, schema, relation, or column
|
|
156
|
-
writes;
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
writes; privileged role attributes; executable non-system security-definer functions;
|
|
167
|
+
a reader-owned view; or a database-enforced expiration beyond
|
|
168
|
+
`maximumValidForMinutes`.
|
|
169
|
+
|
|
170
|
+
This audit is intentionally conservative: an object-level table, column, sequence, or
|
|
171
|
+
function grant remains disallowed even when its schema currently lacks `USAGE`. Do not
|
|
172
|
+
use schema visibility to hide stale grants. Revoke the object grant or move the approved
|
|
173
|
+
surface behind the reviewed export views; this also avoids relying on behavior of an
|
|
174
|
+
already-open or prepared database session.
|
|
175
|
+
|
|
176
|
+
An external login may inherit its narrow permissions from provider-owned group roles.
|
|
177
|
+
List every direct group in `allowedMemberships`; omit the field or use `[]` when the
|
|
178
|
+
login has direct grants only. Rehearsal requires the observed membership set to match
|
|
179
|
+
that list exactly. Every allowed group must be non-login and non-privileged, grant
|
|
180
|
+
inheritance to the login, disable both `SET ROLE` and membership delegation, inherit no
|
|
181
|
+
other role, and own none of the export views. PostgreSQL versions that cannot prove the
|
|
182
|
+
per-membership role-switch setting are refused. Rehearsal still evaluates the login's
|
|
183
|
+
complete effective read, write, sequence, and executable-function surface after those
|
|
184
|
+
checks. Adding, removing, or changing a group changes the reviewed plan digest.
|
|
159
185
|
|
|
160
186
|
`targetSchema` is optional and defaults to `public`. Set it when the restored relation
|
|
161
187
|
belongs elsewhere, and use the same schema in privacy policy version 2. This is relation
|
|
@@ -167,6 +193,9 @@ non-identity prefix may use `prefix`. A private owner prefix uses
|
|
|
167
193
|
the privacy policy below. Its raw value stays out of tracked files and the review plan
|
|
168
194
|
records only its SHA-256. Objects outside the exact bucket/prefix are not listed or
|
|
169
195
|
downloaded. Size, object-count, path, ETag, and exact-byte checks apply.
|
|
196
|
+
Supabase folder entries are traversed recursively beneath that exact prefix. Folder
|
|
197
|
+
entries count toward `maximumObjects`, so the same reviewed limit bounds both the
|
|
198
|
+
directory walk and transferred files. Empty files remain files and are copied normally.
|
|
170
199
|
|
|
171
200
|
## 3. Use executable privacy policy version 2
|
|
172
201
|
|
|
@@ -227,10 +256,12 @@ Every selected column has one action and, where required, a bounded recipe:
|
|
|
227
256
|
}
|
|
228
257
|
```
|
|
229
258
|
|
|
230
|
-
Supported recipes are intentionally small: keyed UUID/email/text/integer
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
259
|
+
Supported recipes are intentionally small: keyed UUID/email/text/integer/hex/GTIN/URL
|
|
260
|
+
pseudonyms, hexadecimal digests of explicitly named sanitized columns, explicit
|
|
261
|
+
constants, bounded or grouped date shifts, identity-aware path mappings, bounded
|
|
262
|
+
reviewed-identity substitution inside retained text, and fully classified JSON
|
|
263
|
+
structures. Unknown tables, columns, digest inputs, JSON keys, formats, and recipes stop
|
|
264
|
+
the refresh. There is no JavaScript or SQL escape hatch. See
|
|
234
265
|
[Sanitization policy](sanitization.md) for complete path and date examples.
|
|
235
266
|
|
|
236
267
|
## 4. Preview, apply, refresh, and retire
|
|
@@ -290,6 +321,11 @@ limits access, and the provider may revoke it sooner. Rehearsal does not claim t
|
|
|
290
321
|
cleanup revoked a provider-owned credential. Unrelated roles, grants, objects, baselines,
|
|
291
322
|
runtimes, and Docker resources are preserved.
|
|
292
323
|
|
|
324
|
+
Apply, refresh, and retire always resolve those local files from the active config's
|
|
325
|
+
`baseline.artifactDirectory`. If you switch configs or target roots, Rehearsal refuses
|
|
326
|
+
to retire a receipt from the other target. It also refuses an existing receipt or
|
|
327
|
+
credential rather than overwriting or adopting it.
|
|
328
|
+
|
|
293
329
|
## Limits
|
|
294
330
|
|
|
295
331
|
Rehearsal does not decide consent, licensing, retention rights, privileged-role meaning,
|
package/docs/releasing.md
CHANGED
|
@@ -32,9 +32,11 @@ publication must change and prove the workflow before narrowing that permission.
|
|
|
32
32
|
1. Start from protected `main` on a dedicated ticketed release branch.
|
|
33
33
|
2. Verify the exact version, changelog, compatibility notes, and packed file list.
|
|
34
34
|
3. Run unit and contract tests, formatting, package-content and secret audits,
|
|
35
|
-
dependency audit, clean tarball installation, and the installed
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
dependency audit, packed onboarding, clean tarball installation, and the installed
|
|
36
|
+
Docker fixtures.
|
|
37
|
+
4. When an unfamiliar developer is available, have them follow clean-project onboarding.
|
|
38
|
+
Correct and retest the first confusing, missing, or wrong instruction. Record when
|
|
39
|
+
this human observation was unavailable; automated checks must not impersonate it.
|
|
38
40
|
5. Change `private` to `false` only in the reviewed release change.
|
|
39
41
|
6. Record the exact tarball filename, SHA-1, SHA-256, allowlisted files, unpacked size,
|
|
40
42
|
executable, declared runtime dependency inventory, and dependency audit result.
|
package/docs/roadmap.md
CHANGED
|
@@ -29,8 +29,9 @@ Track the full breakdown in
|
|
|
29
29
|
invalid migrations, identities, images, persistence, reset, and cleanup.
|
|
30
30
|
- Run the released package in the real reporting consumer and record positives and
|
|
31
31
|
negatives.
|
|
32
|
-
- Directly observe any claimed Google account-picker/callback
|
|
33
|
-
onboarding behavior; automation cannot
|
|
32
|
+
- Directly observe any claimed Google account-picker/callback. Record
|
|
33
|
+
unfamiliar-developer onboarding behavior when a tester is available; automation cannot
|
|
34
|
+
invent either kind of human evidence.
|
|
34
35
|
- Remove consumer integration code only after its replacement passes and that deletion
|
|
35
36
|
is separately approved.
|
|
36
37
|
|