fix(schema): a fresh install of ffc_reregistration_submissions lacked auth_code and magic_token (#1087 passo 5) - #1093
Merged
Conversation
… auth_code and magic_token (#1087 passo 5) Mesma classe da #1091, na outra tabela que declara schema em dois lugares. O CREATE TABLE tinha onze colunas; auth_code e magic_token so chegavam pelo add_columns_if_missing(), na primeira mudanca de FFC_VERSION. Agora sao treze colunas e seis chaves, com os dois indices que aquele caminho tambem cria. A guarda 'if ( table_exists() ) return;' fica, pelo mesmo motivo da #1091: remove-la para deixar o dbDelta cuidar do upgrade e o cenario que o CLAUDE.md nomeia como gatilho dos defeitos latentes, e mudaria o comportamento de toda instalacao a cada bump. O guarda de acordo passa a varrer os activators em vez de mirar uma tabela: SubmissionsSchemaAgreementTest vira SchemaAgreementTest, que procura os add_columns_if_missing() existentes e confere cada um contra o CREATE TABLE do mesmo arquivo. Verificado nos dois casos — remover auth_code do reregistration e ticket_hash do submissions falham, cada uma nomeando arquivo e coluna. NAO construi framework, e isso e deliberado: a populacao real e de dois add_columns_if_missing() no repositorio inteiro. Um motor declarativo de schema sobre dois sites seria a indirecao que nao estreita nada — a armadilha que o CLAUDE.md nomeia, e a mesma recusa que fiz no #1079 contra um OptionValue::int para tres sites. Um terceiro caso fica coberto no dia em que for escrito. RETIFICACAO. Ao desenhar este passo eu afirmei, na #1087 e no corpo da #1091, que tres tabelas rodavam ORDER BY created_at sem indice numa instalacao nova. E falso: ffc_recruitment_notice, ffc_recruitment_candidate e ffc_reregistration_submissions ja declaram KEY idx_created no proprio CREATE TABLE. O maybe_add_perf_indexes() e rede defensiva para instalacoes antigas. Eu deduzi da existencia do add_index_if_missing() sem abrir os CREATE TABLE, e generalizei a partir do caso do ffc_submissions, onde os dois indices compostos de fato faltavam. Retificado nos dois lugares. Refs #1087, #1091, #1079 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU
Coverage Report for CI Build 34072045854Warning No base build found for commit Coverage: 89.971%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
15 tasks
rpgmem
added a commit
that referenced
this pull request
Sep 7, 2026
… nothing measured it (#1087 passo 7) (#1095) * test(schema): dbDelta re-ALTERs forever when a CREATE TABLE lies, and nothing measured it (#1087 passo 7) `dbDelta()` ALTERs away the difference between a `CREATE TABLE` and what MySQL actually stored. When the two disagree in a way the statement can never satisfy, it re-ALTERs on every single run, forever, in silence. No gate saw that class. `ActivatorSqlTest` reads the statement as text, with no database. The `fresh-install` job starts from an empty schema, so it only ever watches the CREATE succeed — never the second pass. The post-deploy smoke asserts the tables exist, not that the schema still matches. #997 measured it exactly once, by hand, against a real MariaDB (41 database errors on the self-scheduling tables, 1 after the cleanup) and never again — while two CREATE statements changed in #1091 and #1093. The new step runs in `fresh-install`, the only job with a real MariaDB AND a freshly created schema, and replays every CREATE through `dbDelta( $sql, false )` against the table it just built. The change list must be empty; anything in it is an ALTER on every future activation. The extraction moves to `.github/scripts/ffc-create-statements.php` so this gate and `ActivatorSqlTest` measure the same 37 statements — the rule `uninstall.php` already follows as one manifest, and the direct lesson of steps 3 and 6: two private scans of one thing is how a denominator goes wrong unnoticed (the row ruler saw 45 of 53 classes). The gate fails on blindness rather than passing: an empty scan, a count that diverges from a wider net (single quotes, heredoc), or a table name it cannot resolve all abort instead of counting as clean. Recorded along the way: `ffc_device_signals` is the only table whose dbDelta is deliberately not guarded by `table_exists()`, so it already runs against an existing table on every activation in every install — the #997 class, live and never measured. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU * fix(ci): `wp eval-file` eval()s the script, where declare(strict_types=1) is fatal The gate failed on its first run for a reason that has nothing to do with the schema: `wp eval-file` reads the file and `eval()`s it, and a `declare(strict_types=1)` is only legal as the first statement of a script — inside an eval it is a fatal error, so the step died before reaching a single CREATE TABLE. Load WordPress the way `fresh-install-check.php` does in this same job instead: take the wp root as an argument and `require` its `wp-load.php`. Same invocation shape as its sibling, the declaration stays, and the command that eval()s files is out of the loop. Verified: the original failure reproduces exactly (`eval()` of this file before the change → "strict_types declaration must be the very first statement in the script"), and after it the file parses whole and stops at its own usage guard. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU * test(schema): the first measurement since #997 finds 14 statements dbDelta re-ALTERs (#1087 passo 7) The gate ran for real and did not come back empty. Against tables it had just created from those very statements, `dbDelta()` wanted 33 changes across 14 `CREATE TABLE` literals — an ALTER on every future activation, in every install. Three families, and only one is a schema question: 1. Display width the server supplies. The statement writes `int unsigned` and MariaDB stores `int(10) unsigned`; dbDelta compares text and ALTERs the difference forever. 9 statements. 2. `json`, which MariaDB implements as `longtext` plus a CHECK, so the statement asks for `json` and `SHOW CREATE TABLE` answers `longtext`. 6 statements. 3. A column or index the live table does not have — `ffc_reregistrations.audience_id` and a `KEY auth_code` on two tables. Not cosmetic: statement and table genuinely disagree, and three files declare `ffc_custom_fields` while three declare `ffc_reregistration_submissions`. So the gate blocks against a frozen baseline rather than against zero, in the shape this repository already uses four times over (module boundary, vacuous tests, superglobal casts, the row ruler): a change not listed fails, and a listed change that stops happening also fails, so a fix is locked in the moment it lands. Fixing the 14 is follow-up work on #1087, not something to fold into the pull request that adds the measurement. Two things the run settled. `ffc_device_signals` — the only table whose dbDelta is not guarded by `table_exists()`, so it re-runs on every activation in every install — is NOT among the 14. It was the obvious suspect and it is clean; the drift is in tables where the repeated ALTER is latent instead. And there is no local way to regenerate the baseline, since it needs a live MariaDB, so the gate prints the exact block to paste and dumps `SHOW CREATE TABLE` for the statements whose disagreement is not a type spelling — the data family 3 needs. `CreateStatementsParserTest` gains a check that every baseline key still names a file that declares that table: an entry whose statement moved would otherwise match nothing and fail nothing, which is the same silent exemption this issue has spent three steps on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU --------- Co-authored-by: Claude <noreply@anthropic.com>
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Passo 5, o último da #1087. Mesma classe da #1091, na outra tabela que declara schema em dois lugares — e muito menor do que eu havia desenhado, porque a medição derrubou metade da premissa antes de eu escrever qualquer linha.
O que é real
O
CREATE TABLEdeffc_reregistration_submissionstinha 11 colunas.auth_codeemagic_tokennão estavam entre elas: chegavam só peloadd_columns_if_missing(), na primeira mudança deFFC_VERSION. Agora são 13 colunas e 6 chaves, com os dois índices que aquele caminho também cria.A retificação, que veio antes do código
Ao desenhar este passo eu afirmei — na #1087, no corpo da #1091 e duas vezes ao mantenedor — que três tabelas rodavam
ORDER BY created_atsem índice numa instalação nova, porquemaybe_add_perf_indexes()só roda quandoFFC_VERSIONmuda.É falso. As três já declaram
KEY idx_createdno próprioCREATE TABLE:ffc_recruitment_noticeclass-ffc-recruitment-activator.php:581ffc_recruitment_candidateclass-ffc-recruitment-activator.php:669ffc_reregistration_submissionsCREATEde 11 colunas do seu activatormaybe_add_perf_indexes()é rede defensiva para instalações antigas. Eu deduzi da existência doadd_index_if_missing()sem abrir osCREATE TABLE, e generalizei a partir do caso doffc_submissions, onde os dois índices compostos de fato faltavam. Retificado na #1087 e na #1091 antes de começar.Então o passo 5 encolheu de "2 colunas + 3 índices, com guarda generalizado" para 2 colunas em uma tabela.
O guarda: varredura, não framework — e isso é deliberado
O plano previa "generalizar o guarda para a classe inteira". Com a população real sendo dois
add_columns_if_missing()no repositório inteiro, um motor declarativo de schema seria a indireção que não estreita nada — a armadilha que oCLAUDE.mdnomeia, e a mesma recusa que fiz no #1079 contra umCore\OptionValue::int()para três sites.O que o
SchemaAgreementTestfaz (substituindo oSubmissionsSchemaAgreementTest, que ele subsume) é procurar osadd_columns_if_missing()que existem e conferir cada um contra oCREATE TABLEdo mesmo arquivo. Um terceiro caso fica coberto no dia em que for escrito, sem abstração nova.Verificado nos dois casos, cada um nomeando arquivo e coluna:
Um terceiro teste garante que a varredura não devolve vazio — sem ele, no dia em que o padrão quebrar, as outras asserções passariam vazias. É o defeito do #1071 um nível acima, e o mesmo cuidado que o guarda anterior já tomava.
Ele compara nomes, não tipos, pelo mesmo motivo da #1091: as duas fontes escrevem tipos diferente por construção, e normalizar isso seria reimplementar a comparação do
dbDeltadentro de um teste.A guarda
table_exists()ficaPelo mesmo motivo da #1091: removê-la para deixar o
dbDeltacuidar do upgrade é o cenário que oCLAUDE.mdnomeia como gatilho dos defeitos latentes, e mudaria o comportamento de toda instalação a cada bump. Consolidar as colunas não precisa disso.Validação
ActivatorSqlTest: verde — as duas regras dedbDeltavalem para oCREATEalteradoSchemaAgreementTest: 3 testes, a asserção principal verificada falhando contra remoção proposital em ambas as tabelasphpcs-tests.xml.dist: limposdbDeltanão rodou. Quem exercita o caminho alterado é o gatefresh-installdo CI, contra MariaDB 11.8 num banco vazioRefs #1087, #1091, #1079, #994
🤖 Generated with Claude Code
https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU
Generated by Claude Code