diff --git a/a3p-integration/proposals/a:upgrade-next/invite-submission/README.md b/a3p-integration/proposals/a:upgrade-next/invite-submission/README.md
new file mode 100644
index 00000000000..345d5cd67f2
--- /dev/null
+++ b/a3p-integration/proposals/a:upgrade-next/invite-submission/README.md
@@ -0,0 +1,8 @@
+These files enable a test of the walletFactory changes, by
+verifying that upgraded wallets that aren't backed by vbanks can still add
+assets, in this case an invitation.
+
+sendInvite is a secondary submission, which is transmitted in ../wallet-repair.test.js.
+Some template values in the `.tjs` file are replaced before submitting the
+core-eval. The test then verifies that the invitation details were written to
+the wallet in vstorage.
diff --git a/a3p-integration/proposals/a:upgrade-next/invite-submission/sendInvite-permit.json b/a3p-integration/proposals/a:upgrade-next/invite-submission/sendInvite-permit.json
new file mode 100644
index 00000000000..27ba77ddaf6
--- /dev/null
+++ b/a3p-integration/proposals/a:upgrade-next/invite-submission/sendInvite-permit.json
@@ -0,0 +1 @@
+true
diff --git a/a3p-integration/proposals/a:upgrade-next/invite-submission/sendInvite.tjs b/a3p-integration/proposals/a:upgrade-next/invite-submission/sendInvite.tjs
new file mode 100644
index 00000000000..e0ac3556255
--- /dev/null
+++ b/a3p-integration/proposals/a:upgrade-next/invite-submission/sendInvite.tjs
@@ -0,0 +1,39 @@
+#! false node --ignore-this-line
+/* global E */
+
+///
+///
+
+// to be replaced before execution
+const addr = '{{ADDRESS}}';
+
+/**
+ * verify that a pre-existing wallet has an invitation purse that is still monitored
+ *
+ * @param {BootstrapPowers} powers
+ */
+const sendInvitation = async powers => {
+ console.log('sendInvitation start');
+ // namesByAddress is broken #8113
+ const {
+ consume: { namesByAddressAdmin, zoe },
+ instance: {
+ consume: { reserve },
+ },
+ } = powers;
+ const pf = E(zoe).getPublicFacet(reserve);
+ const anInvitation = await E(pf).makeAddCollateralInvitation();
+
+ await E(namesByAddressAdmin).reserve(addr);
+ // don't trigger the namesByAddressAdmin.readonly() bug
+ const addressAdmin = E(namesByAddressAdmin).lookupAdmin(addr);
+
+ await E(addressAdmin).reserve('depositFacet');
+ const addressHub = E(addressAdmin).readonly();
+ const addressDepositFacet = E(addressHub).lookup('depositFacet');
+
+ await E(addressDepositFacet).receive(anInvitation);
+ console.log('ADDED an invitation to a purse!');
+};
+
+sendInvitation;
diff --git a/a3p-integration/proposals/a:upgrade-next/wallet-repairs.test.js b/a3p-integration/proposals/a:upgrade-next/wallet-repairs.test.js
new file mode 100755
index 00000000000..6d50acb1d2f
--- /dev/null
+++ b/a3p-integration/proposals/a:upgrade-next/wallet-repairs.test.js
@@ -0,0 +1,46 @@
+import { readFile, writeFile } from 'node:fs/promises';
+
+import test from 'ava';
+
+import { agd, getUser, evalBundles } from '@agoric/synthetic-chain';
+
+const SUBMISSION_DIR = 'invite-submission';
+
+/**
+ * @param {string} fileName base file name without .tjs extension
+ * @param {Record} replacements
+ */
+const replaceTemplateValuesInFile = async (fileName, replacements) => {
+ let script = await readFile(`${fileName}.tjs`, 'utf-8');
+ for (const [template, value] of Object.entries(replacements)) {
+ script = script.replaceAll(`{{${template}}}`, value);
+ }
+ await writeFile(`${fileName}.js`, script);
+};
+
+test('smartWallet repairs', async t => {
+ const gov1Address = await getUser('gov1');
+
+ await replaceTemplateValuesInFile(`${SUBMISSION_DIR}/sendInvite`, {
+ ADDRESS: gov1Address,
+ });
+
+ await evalBundles(SUBMISSION_DIR);
+
+ // agd query vstorage data published.wallet.$GOV1ADDR.current -o json \
+ // |& jq '.value | fromjson | .values[0] | fromjson | .body[1:] \
+ // | fromjson | .purses '
+ const walletCurrent = await agd.query(
+ 'vstorage',
+ 'data',
+ `published.wallet.${gov1Address}.current`,
+ );
+
+ const body = JSON.parse(JSON.parse(walletCurrent.value).values[0]);
+ const bodyTruncated = JSON.parse(body.body.substring(1));
+ const invitePurseBalance = bodyTruncated.purses[0].balance;
+ t.truthy(invitePurseBalance.value[0], 'expecting a non-empty purse');
+ const description = invitePurseBalance.value[0].description;
+
+ t.is(description, 'Add Collateral', 'invitation purse should not be empty');
+});