This runbook is for replacing the ENSJobPages contract used by AGIJobManager on Ethereum mainnet, without changing AGIJobManager protocol behavior.
Canonical cutover flow:
- Deploy new
ENSJobPages(Hardhat script). - Wrapped-root owner manually calls
NameWrapper.setApprovalForAll(newEnsJobPages, true). - AGIJobManager owner manually calls
AGIJobManager.setEnsJobPages(newEnsJobPages). - Migrate legacy jobs with historical labels if needed (
migrateLegacyWrappedJobPage). - Lock configuration only after validation is complete.
| Action | Automated by script? | Required caller |
|---|---|---|
Deploy new ENSJobPages |
Yes | deployer key |
setJobManager(JOB_MANAGER) on new ENSJobPages |
Yes | deployer key |
NameWrapper setApprovalForAll(newEnsJobPages, true) |
No (manual) | wrapped-root owner |
AGIJobManager.setEnsJobPages(newEnsJobPages) |
No (manual) | AGIJobManager owner |
migrateLegacyWrappedJobPage(jobId, exactLabel) |
No (manual, if needed) | ENSJobPages owner |
lockConfiguration() |
Optional/manual | ENSJobPages owner |
ENSJobPages manages ENS job page naming and metadata writes for AGIJobManager hooks.
It determines:
- job label prefix (
jobLabelPrefix, defaultagijob), - job root suffix (
jobsRootName, default in deploy script:alpha.jobs.agi.eth), - and stores snapshotted exact labels for each job.
AGIJobManager contributes the numeric jobId; ENSJobPages builds names from that jobId.
Typical replacement/migration drivers from current contract behavior:
- You need newer
ENSJobPagesbehavior for label snapshotting and legacy migration support. - Old jobs may not have label snapshots in the new contract, causing post-create writes to revert with
JobLabelNotSnapshotteduntil migrated. - Wrapped-root operations require explicit NameWrapper approval to the active
ENSJobPages; missing approval blocks wrapped-root writes.
- Name format:
<prefix><jobId>.<jobsRootName>. AGIJobManagerdecides numericjobIdand protocol settlement state.ENSJobPagesdecidesprefix,jobsRootName, label snapshotting, and ENS write behavior.
With script defaults + contract defaults:
jobLabelPrefix = "agijob"jobsRootName = "alpha.jobs.agi.eth"
So names are:
agijob0.alpha.jobs.agi.ethagijob1.alpha.jobs.agi.eth- ...
Prefix changes apply only to unsnapshotted/future jobs. Already snapshotted labels stay unchanged.
- Mainnet deploy scripts require:
DEPLOY_CONFIRM_MAINNET=I_UNDERSTAND_MAINNET_DEPLOYMENT. lockConfiguration()on ENSJobPages is irreversible.- Wiring the wrong ENSJobPages address into AGIJobManager changes hook target for all future calls.
- If NameWrapper approval is missing on wrapped root, create/adopt/write paths can fail best-effort.
- You control deployer key and owner key(s) needed for manual wiring.
hardhat/.envis configured.- You know the intended AGIJobManager address for
JOB_MANAGER. - You have identified whether your jobs root is wrapped or unwrapped.
cd hardhat
npm ci
cp .env.example .env
npm run compile
DRY_RUN=1 DEPLOY_CONFIRM_MAINNET=I_UNDERSTAND_MAINNET_DEPLOYMENT npm run deploy:ens-job-pages:mainnet
DEPLOY_CONFIRM_MAINNET=I_UNDERSTAND_MAINNET_DEPLOYMENT VERIFY=1 NEW_OWNER=0xa9eD0539c2fbc5C6BC15a2E168bd9BCd07c01201 npm run deploy:ens-job-pages:mainnetOptional overrides (via .env):
JOB_MANAGERJOBS_ROOT_NAMEJOBS_ROOT_NODE(must matchnamehash(JOBS_ROOT_NAME))ENS_REGISTRYNAME_WRAPPERPUBLIC_RESOLVERLOCK_CONFIG=1
Expected result:
- New ENSJobPages address deployed.
setJobManager(JOB_MANAGER)already executed by script.- Optional verification submitted.
- Performing only deploy, but forgetting manual NameWrapper approval.
- Performing NameWrapper approval, but forgetting
setEnsJobPages(newAddress). - Locking configuration before validating at least one future job hook and any required legacy migration.
- Supplying an inexact
exactLabelin legacy migration calls.
What is automated vs manual:
- Automated by deploy script: deploy contract, set
jobManager, optional ownership transfer/verification. - Manual on mainnet: NameWrapper approval + AGIJobManager
setEnsJobPages.
Caller: wrapped-root owner account.
On NameWrapper:
setApprovalForAll(newEnsJobPages, true)
Why this matters:
- ENSJobPages checks wrapper authorization before wrapped-root create/adopt operations.
Caller: AGIJobManager owner account.
On AGIJobManager:
setEnsJobPages(newEnsJobPages)
Why this matters:
- AGIJobManager calls ENS hooks on the configured
ensJobPagestarget only.
Expected result after wiring:
- New hook calls route to the new ENSJobPages contract.
- On AGIJobManager
Read Contract,ensJobPagesequalsnewEnsJobPages. - On NameWrapper
Read Contract,isApprovedForAll(rootOwner, newEnsJobPages)is true (or token-level approval exists).
- AGIJobManager
ensJobPages()equalsnewEnsJobPages. - NameWrapper
isApprovedForAll(rootOwner, newEnsJobPages)istrue(or token-level approval equivalent). - New job hook transaction shows protocol success (
status=1). - ENSJobPages events include
ENSHookProcessed(or explicit skip/failure reason).
If a legacy job page exists under a historical exact label, migrate by importing the exact label:
migrateLegacyWrappedJobPage(jobId, exactLabel)on ENSJobPages owner account.
Use this when post-create write hooks fail because label was never snapshotted in the current ENSJobPages.
Important:
exactLabelmust match the real label for thatjobId(including numeric suffix).- Migration snapshots/adopts/creates as needed, then best-effort updates resolver/auth/text.
- If a wrapped child is no longer parent-controllable (for example, emancipated), migration adoption can fail and revert (
ENSNotAuthorized).
Expected result:
LegacyJobPageMigrated(jobId, node, label, adopted, created)emitted.- Subsequent write hooks for that job can resolve node from snapshotted label.
- If wrapped-child adoption is blocked (for example, child no longer parent-controllable), migration reverts and no
LegacyJobPageMigratedevent is emitted; treat this as a failed migration that needs operator remediation before retry.
- Future/unsnapshotted jobs: new creates use
<prefix><jobId>.<jobsRootName>(default prefixagijob) and should proceed once wiring is complete. - Legacy snapshotted jobs: keep their historical label; they do not auto-rename on prefix changes.
- Legacy unsnapshotted jobs: may need
migrateLegacyWrappedJobPage(jobId, exactLabel)before deterministic write hooks succeed.
On new ENSJobPages (Read Contract):
jobManagerequals target AGIJobManager.jobsRootNameandjobsRootNodeare expected values.jobLabelPrefixexpected default or configured value.
On AGIJobManager (Read Contract):
ensJobPagesequals new ENSJobPages address.
On NameWrapper (Read Contract):
isApprovedForAll(rootOwner, newEnsJobPages)istrue(or token-level approval exists).
Event checks:
- ENSJobPages deployment tx + ownership transfer (if used).
- AGIJobManager
EnsJobPagesUpdated(old,new)event.
- If AGIJobManager was wired to the wrong ENSJobPages, owner can call
setEnsJobPages(previousAddress)(if identity config still configurable). - If NameWrapper approval is incorrect, correct with
setApprovalForAll(correctEnsJobPages, true). - If legacy writes fail for specific jobs, run
migrateLegacyWrappedJobPage(jobId, exactLabel)per affected job. - If verification API fails, use deployment artifact
solc-input.jsonfor manual standard-json verify.
- Dry run reviewed and approved.
- ENSJobPages deployed and (if required) verified.
- NameWrapper approval granted for wrapped root.
- AGIJobManager
setEnsJobPages(new)executed. - Etherscan read checks pass on all key fields.
- At least one new job hook observed successfully.
- Legacy jobs requiring migration identified and migrated.
- All addresses (
ens,nameWrapper,publicResolver,jobManager) are final. -
jobsRootName/jobsRootNodeare final and validated. - Wrapped-root approval already works.
- Migration backlog is complete or explicitly tracked.
- You acknowledge
lockConfiguration()is irreversible.
- Do not assume deploy scripts perform NameWrapper approval.
- Do not forget
setEnsJobPages(newEnsJobPages)on AGIJobManager owner account. - Do not change prefix expecting old snapshotted labels to rename automatically.
- Do not lock configuration before validating future-job hooks and legacy-job migration needs.
- Do not treat ENS hook best-effort failures as proof that settlement failed; check AGIJobManager settlement events separately.
- Calling
setEnsJobPages(new)before wrapped-root approval is in place, then misreading hook failures as total protocol failure. - Locking ENSJobPages configuration before future-job hook validation and legacy migration checks.
- Using approximate labels for migration;
exactLabelmust match historical on-chain label.