122 —AI
Magento 1 AI product copy: a staging shape that holds
A read-only staging mirror is the cheapest insurance against AI-generated product copy that overwrites the wrong SKU in a 2014-vintage Magento store.
The 04:12 Loom
The Loom landed at 04:12 on a Tuesday. A Rotterdam agency lead, mid-sentence, screen-sharing a Google Sheet of 8,400 product descriptions a client wanted "rewritten with AI" and pushed live to a Magento 1.9 store by Friday. The store was last patched in 2021. The SKUs were tied to a Snelstart accounting bridge that re-read short_description on every sync. Two devs. A holiday weekend. A spreadsheet that, in its current form, sat one bad UPDATE away from breaking 8,400 invoices and the indexer along with them.
This is the most common AI-on-legacy site job we see right now, and the part the client almost never sees is the part that decides whether their store is up on Monday: not the prompt, not the model, not even the prose quality. The shape of the staging environment.
If you are about to pour AI-generated product copy into a Magento 1 catalog, the only opinion that matters is this one: it does not touch the live database until a read-only mirror has absorbed the same writes first.
The EAV catalog's brittle seams
Magento 1's EAV catalog is brittle in ways that AI bulk-writes specifically aggravate. A single product description lives across at least four tables: catalog_product_entity, catalog_product_entity_text for description, catalog_product_entity_varchar for short_description, and catalog_product_flat_1 if flat catalog is enabled. Each store view has its own row. Each attribute change demands a reindex of catalog_product_flat, catalog_product_attribute and catalogsearch_fulltext, or the new copy will not appear on the storefront and will not be searchable.
There is no version history in the Magento 1 admin. There is no soft delete on attribute values. If your UPDATE ran with the wrong store_id filter, the original copy is gone. Magento 1 hit official end-of-life in June 2020 per Adobe's product page, which means no security backports and, more relevant here, no friendly migration path off bad data. Anything on PHP 8.x is already running unsupported, and the deprecation notes in the PHP 8.2 migration guide bite the EAV loader in three places we have logged so far.
So the rule that follows from the shape of the data: bulk AI-written copy never enters production until a copy of production has eaten the same writes and survived a full reindex.
The mirror that absorbs the blast
The read-only staging shape has three parts. None of them are clever.
1. A frozen snapshot of the catalog tables
Take the snapshot with --single-transaction so the live store is not locked. You only need the catalog EAV tables, the EAV attribute tables, and the flat tables if flat catalog is on. Skip sales_flat_*, customers, and logs. The dump should land in seconds.
mysqldump \
--single-transaction \
--skip-lock-tables \
--no-tablespaces \
-h db.live.internal -u readonly -p \
magento_live \
catalog_product_entity \
catalog_product_entity_varchar \
catalog_product_entity_text \
catalog_product_entity_int \
catalog_product_flat_1 \
eav_attribute \
eav_entity_type \
> /tmp/catalog-snap.sql
2. A staging MySQL that refuses writes by default
Spin up the snapshot inside a MariaDB container, then bind the credentials your tooling uses to a user that holds SELECT only. Reserve a second user with INSERT and UPDATE for the AI-pour step. The application layer never sees the second user.
CREATE USER 'reviewer'@'%' IDENTIFIED BY '...';
GRANT SELECT ON magento_staging.* TO 'reviewer'@'%';
CREATE USER 'pour'@'%' IDENTIFIED BY '...';
GRANT INSERT, UPDATE ON magento_staging.catalog_product_entity_text
TO 'pour'@'%';
GRANT INSERT, UPDATE ON magento_staging.catalog_product_entity_varchar
TO 'pour'@'%';
FLUSH PRIVILEGES;
This is the part most teams skip. Two users, two intents, two audit trails. When a junior dev runs the AI pour against the wrong host at 23:00, the credentials they hold do not let them touch the catalog of the live store, full stop.
3. The pour, then a real reindex
Map the AI copy to (entity_id, store_id, attribute_id) before you write anything. store_id = 0 is the admin and default scope; per-store-view copy needs the matching view id. Resolve the attribute_id for description and short_description by name, not by a hard-coded integer, because the ids vary across installs.
SELECT attribute_id, attribute_code, backend_type
FROM eav_attribute
WHERE entity_type_id = (
SELECT entity_type_id
FROM eav_entity_type
WHERE entity_type_code = 'catalog_product'
)
AND attribute_code IN ('description', 'short_description');
Then, on the staging copy only, run the pour inside a single transaction and reindex with php shell/indexer.php reindexall. If the reindex fails, the catalog never reached the storefront, and the staging instance is the only blast radius.
The diff that decides what ships
Once staging has eaten the writes, the question is no longer "is the copy good." It is "what actually changed, and does the change match what was approved." A single diff query carries the load.
SELECT live.entity_id,
live.value AS old_value,
stage.value AS new_value
FROM live.catalog_product_entity_text live
JOIN stage.catalog_product_entity_text stage
USING (entity_id, attribute_id, store_id)
WHERE live.value <> stage.value
AND stage.attribute_id = @desc_attr
ORDER BY live.entity_id;
Export to CSV, send to the client for approval, then ship only the approved rows back into live with a parameterised UPDATE ... WHERE entity_id IN (...) inside a single transaction. The same pour script with one flag change. No second model run, no second prompt, no chance of drift between what was approved and what was written.
The Sunday-evening payoff
The agency from the 04:12 Loom shipped the 8,400 descriptions on Sunday evening. The pour ran on staging Friday night, the diff went to the client at 09:00 Saturday, they approved 7,612 rows and rejected 788, and the production write was a five-minute job on Sunday because the only thing it had to do was replay an approved diff. No re-prompting. No "can we tweak the tone." That was already settled in staging.
The point of the read-only staging shape is not to slow the work down. It is to move the irreversible part (writes against a 2014-vintage EAV catalog with a live accounting bridge attached) to a place where irreversibility costs nothing.
When we built Pier, the FTP and MySQL editor we use on legacy site work, this exact pattern is why the database side ships with two connection modes and a row-level version history on every change. The MySQL editor defaults to staging when both hosts are configured, and the production switch is a deliberate keystroke, not an accidental one. The shape is the safety.
The smallest thing you can do today: open your staging MySQL, run SHOW GRANTS FOR CURRENT_USER();, and check whether the user your AI pour script connects with holds UPDATE on the live database. If it does, that is tomorrow's first config change.
— Questions —
Can I skip staging if I only have 50 descriptions to rewrite?
The risk is the same shape, just smaller. A staging mirror takes ten minutes to spin up and turns an irreversible write into a reversible one. At 50 rows, still do it.
Will a reindex on staging really catch what production would catch?
Yes, if the snapshot includes the EAV attribute and flat tables and matches the live store-view count. Mismatched store-view counts are the usual reason staging passes and production fails.
Does this pattern translate to Magento 2?
Same shape, different table names. Magento 2 uses catalog_product_entity_text and catalog_product_entity_varchar with EAV value tables instead of flat tables by default. The two-user staging rule still applies.
What about the AI prompt itself, does it matter?
It matters for prose quality, but it does not change the database risk. A perfect prompt can still write the wrong attribute_id into the wrong store_id. The staging mirror is what catches that.