Oracle DBA - Multitenant (CDB / PDB)
Day-to-day operation of Oracle multitenant containers for 19c and 23ai (EE;
single-instance and Data Guard; on-prem): PDB status monitoring, open/close, save
state, local clone / snapshot copy, unplug-to-manifest, CDB-level Resource Manager,
and lockdown profiles. Everything self-executing is T1 (observe) or T2
(reversible, dry-run-default). The structural / cross-CDB operations — PDB
relocation, application-container lifecycle, plug-from-manifest, drop, and
lockdown-profile drop — are T3 and live only in references/runbooks.md; they print
exact commands for a human and never self-execute.
In-DB SQL/PL-SQL runs through the SQLcl MCP saved connection dba_ai_conn
(wallet-backed). Host-level steps (none are required by the T1/T2 scripts here beyond
invoking SQLcl) and all T3 structural DDL are operator-driven. Never SYS/SYSTEM
except inside a T3 runbook that explicitly states AS SYSDBA, supplied interactively.
Scope & risk map
| Task |
Level |
Tier |
Mechanism (script / sql / runbook path) |
Idempotent? |
| PDB status monitoring |
L1 |
T1 |
scripts/checks/check_pdb_status.sh / .ps1 + sql/pdb_status.sql |
yes — read-only (emits KEYVAL metrics + RESTRICTED / violation findings, never mutates) |
| PDB open / close |
L2 |
T2 |
scripts/pdb_open_close.sh / .ps1 + sql/pdb_open_close.sql |
yes — already in target mode -> noop; OPEN <-> CLOSE inverses; --pdb ALL skips seed/root |
| Save / discard PDB state |
L2 |
T2 |
scripts/save_pdb_state.sh / .ps1 + sql/pdb_save_state.sql |
yes — SAVE when already saved / DISCARD when none -> noop; --ensure-open opens first then saves |
| PDB clone / snapshot copy |
L2 |
T2 |
scripts/pdb_clone.sh / .ps1 + sql/pdb_clone.sql |
yes — existing target PDB -> noop; CREATE only (drop is T3) |
| PDB unplug (to manifest) |
L2 |
T2 |
scripts/pdb_unplug.sh / .ps1 + sql/pdb_unplug.sql |
yes — already UNPLUGGED -> noop; closes + writes manifest, never drops |
| CDB-level Resource Manager — configure / activate |
L2 |
T2 |
scripts/cdb_resource_manager.sh --action configure / .ps1 + sql/cdb_resource_manager.sql |
yes — plan + per-PDB SHARES upserted; unchanged -> noop; must run from CDB$ROOT |
| CDB-level Resource Manager — deactivate |
L2 |
T2 |
scripts/cdb_resource_manager.sh --action deactivate / .ps1 + sql/cdb_resource_manager_deactivate.sql |
yes — no active plan -> noop; definitions retained |
| Lockdown profile — apply / drop-rule |
L2 |
T2 |
scripts/lockdown_profile.sh --action apply|drop-rule / .ps1 + sql/lockdown_profile.sql |
yes — present rule on apply / absent on drop-rule -> noop; from CDB$ROOT |
| Lockdown profile — assign to PDB |
L2 |
T2 |
scripts/lockdown_profile.sh --action assign / .ps1 + sql/lockdown_assign.sql |
yes — PDB_LOCKDOWN already = target -> noop; --profile NONE clears |
| PDB plug (from manifest) |
L2-struct |
T3 |
references/runbooks.md#pdb-plug |
partial — CREATE ... USING is one-shot; retry after DROP ... KEEP DATAFILES |
| PDB relocation (online, across CDBs) |
L3 |
T3 |
references/runbooks.md#pdb-relocate |
no — source PDB dropped at cut-over; abort only before OPEN |
| Application container management |
L3 |
T3 |
references/runbooks.md#app-container |
partial — versioned INSTALL/UPGRADE/PATCH + SYNC; re-runnable per block, not a free rollback |
| Drop a PDB |
struct |
T3 |
references/runbooks.md#pdb-drop |
no — INCLUDING DATAFILES irreversible; KEEP DATAFILES re-pluggable |
| Drop a lockdown profile (escape hatch) |
struct |
T3 |
references/runbooks.md#lockdown-drop |
n/a — refused by the T2 lockdown wrapper |
Levels map to oracle_dba_task_level_map.md section 12. Tasks 1-9 there
(monitoring, open/close, save state, clone/snapshot, unplug/plug, CDB Resource
Manager, lockdown, relocation = L3, application container = L3) are all
covered above; relocation and application containers are the two L3 tasks and are
given expert-grade Tier-3 runbooks.
Preconditions
- SQLcl MCP connection
dba_ai_conn is reachable and wallet-backed. All in-DB
prechecks, postchecks, idempotency probes, and sql/*.sql run through it via
connect_db / Connect-Db. Never SYS/SYSTEM — except inside a T3 runbook that
explicitly states AS SYSDBA, supplied interactively by the operator. The
dba_ai_conn common user needs the relevant privileges granted out of band per
site policy: ALTER PLUGGABLE DATABASE / CREATE PLUGGABLE DATABASE (open/close,
clone, unplug), ADMINISTER RESOURCE MANAGER (CDB plan), ALTER SYSTEM +
CREATE LOCKDOWN PROFILE from CDB$ROOT and ALTER SESSION SET CONTAINER
(lockdown). T3 structural DDL needs SYSDBA-class privilege (operator-supplied).
- Container scope matters. CDB-wide tasks (resource plan, lockdown profile
create/edit) must run connected to
CDB$ROOT — the wrappers verify
SYS_CONTEXT('USERENV','CON_NAME')='CDB$ROOT' in their precheck and abort
otherwise. Per-PDB tasks act on the named PDB; --pdb ALL loops user PDBs only
(skips PDB$SEED and con_id<=2).
- Secrets resolve from the Oracle Wallet / external password store. No plaintext
anywhere — not in argv, env, files, or SQL. The helper libraries reject
credential-looking arguments with
ERR_SECRET (8). Cross-CDB DB links (relocate /
plug) use wallet-backed link credentials only.
- Helpers are sourced from
_common (scripts/lib.sh / lib.ps1): connect_db,
log_event, emit_metric, require_dry_run, guard_blocked_dry_run,
require_approval_token, precheck, postcheck, print_banner. Not reinvented.
- Logging: structured
key=value lines to the canonical paths
/var/log/oracle-dba/multitenant/multitenant.log (Linux) /
C:/ProgramData/oracle-dba/logs/multitenant/multitenant.log (Windows); skill
segment multitenant throughout (T1/T2 scripts and T3 runbooks).
- T2 dry-run default: every T2 script previews and changes nothing unless given
--execute. Architecture floor: 19c is CDB-by-default; 23ai is CDB-only.
All work targets the container dba_ai_conn selects; no non-CDB path exists.
- Storage / option prerequisites (operational, not syntax): SNAPSHOT COPY needs
copy-on-write storage (ACFS / ASM / supported NFS); hot clone & online relocate
need ARCHIVELOG + LOCAL UNDO on the relevant CDBs.
Procedures
Scripts ship as behaviour-equivalent .sh (Linux) / .ps1 (Windows) pairs. T2
scripts default to --dry-run; pass --execute to act. See each script's --help.
PDB status monitoring (T1)
- Trigger: scheduled (e.g. every 15 min) or investigating a closed / RESTRICTED PDB.
- Precheck: none beyond
dba_ai_conn reachable (read-only).
- Action:
scripts/checks/check_pdb_status.sh [--max-restricted N --max-violations N] / .ps1 + sql/pdb_status.sql.
- Postcheck: n/a — emits KEYVAL metrics (
total_pdbs, pdbs_read_write, pdbs_mounted, pdbs_restricted, unresolved_violations) + non-fatal findings.
- Rollback note: none — read-only.
PDB open / close
- Trigger: bring a PDB online READ WRITE / READ ONLY / UPGRADE, or close it for maintenance.
- Precheck:
dba_ai_conn reachable; idempotency probe reads current open_mode.
- Action:
scripts/pdb_open_close.sh --pdb NAME|ALL --action open|close [--open-mode "READ WRITE"|"READ ONLY"|UPGRADE] [--force] [--close-mode immediate|normal] --execute / .ps1 -> sql/pdb_open_close.sql. --force only to switch an already-open PDB's mode without closing.
- Postcheck: OPEN -> PDB not MOUNTED; CLOSE -> PDB MOUNTED (verified in
v$pdbs).
- Rollback note: OPEN <-> CLOSE are inverses. Does NOT persist across CDB restart — use save state for that.
Save / discard PDB state
- Trigger: make a PDB auto-open to its current mode after a CDB restart (SAVE), or revert to default MOUNTED-on-restart (DISCARD).
- Precheck:
dba_ai_conn reachable; reads DBA_PDB_SAVED_STATES.
- Action:
scripts/save_pdb_state.sh --pdb NAME|ALL --action save|discard [--ensure-open "READ WRITE"] --execute / .ps1 -> sql/pdb_save_state.sql. --ensure-open opens to the desired mode FIRST (delegates to pdb_open_close) then saves the current mode.
- Postcheck: SAVE -> saved-state row present; DISCARD -> absent.
- Rollback note: SAVE <-> DISCARD are inverses. On Data Guard, run on the PRIMARY.
PDB clone / snapshot copy
- Trigger: spin up a copy of an existing PDB (test/dev refresh, pre-change snapshot).
- Precheck: ARCHIVELOG (hot clone) +
dba_ai_conn reachable; idempotency probe checks the target name in CDB_PDBS.
- Action:
scripts/pdb_clone.sh --src SRC --tgt NEW [--kind full|snapshot] [--file-convert "from,to,..."] [--storage-max 50G] [--no-open] --execute / .ps1 -> sql/pdb_clone.sql. Creates MOUNTED, then opens READ WRITE (separate logged step) unless --no-open.
- Postcheck: target present in
CDB_PDBS (and open unless --no-open).
- Rollback note: a clone is undone by
DROP PLUGGABLE DATABASE <tgt> INCLUDING DATAFILES — that is T3 (references/runbooks.md#pdb-drop). This wrapper never drops.
PDB unplug (to manifest)
- Trigger: prepare a PDB to be moved/archived by writing its manifest; first half of an unplug/plug move.
- Precheck: manifest directory exists/writable (where visible) +
dba_ai_conn reachable; idempotency probe reads CDB_PDBS.STATUS.
- Action:
scripts/pdb_unplug.sh --pdb NAME --manifest /path/pdb.xml [--archive xml|pdb] --execute / .ps1 -> sql/pdb_unplug.sql. Closes the PDB then UNPLUG INTO; leaves it UNPLUGGED (datafiles on disk). Never drops.
- Postcheck: PDB
STATUS='UNPLUGGED' in CDB_PDBS.
- Rollback note: re-create from the manifest, or change your mind before dropping. The destructive
DROP ... KEEP/INCLUDING DATAFILES + plug elsewhere is T3 (#pdb-plug, #pdb-drop).
CDB-level Resource Manager (configure / activate / deactivate)
- Trigger: govern CPU/parallelism BETWEEN PDBs via per-PDB SHARES; or clear the active plan.
- Precheck: connected to
CDB$ROOT (enforced); diff existing plan/directives.
- Action:
scripts/cdb_resource_manager.sh --action configure --plan ODB_CDB_PLAN --shares "SALES:3,HR:1" [--activate] --execute / .ps1 -> sql/cdb_resource_manager.sql; deactivate via --action deactivate --execute -> sql/cdb_resource_manager_deactivate.sql.
- Postcheck: plan has directives (
DBA_CDB_RSRC_PLAN_DIRECTIVES); deactivate -> no active RESOURCE_MANAGER_PLAN.
- Rollback note: re-run configure with prior SHARES, or deactivate. Definitions are retained on deactivate (re-activation is a one-liner). Deleting a plan entirely is operator-driven.
Lockdown profile (apply / drop-rule / assign)
- Trigger: harden PDBs by DISABLEing features (apply), remove a rule (drop-rule), or bind/unbind a profile to a PDB (assign).
- Precheck: apply/drop-rule require
CDB$ROOT (enforced); assign requires dba_ai_conn reachable.
- Action:
scripts/lockdown_profile.sh --action apply|drop-rule --profile NAME --rules "ALTER_SYSTEM,OS_ACCESS" --execute / --action assign --profile NAME|NONE --pdb SALES --execute / .ps1 -> sql/lockdown_profile.sql / sql/lockdown_assign.sql.
- Postcheck: apply -> profile present in
DBA_LOCKDOWN_PROFILES; assign -> PDB_LOCKDOWN equals target inside the PDB (or empty for NONE).
- Rollback note: drop-rule / re-apply restore rules; assign
--profile NONE clears. Dropping the whole profile is T3 (#lockdown-drop).
Tier-3 structural operations (relocation, app containers, plug, drop)
- Trigger: move a PDB across CDBs, manage an application container, plug an unplugged PDB, or drop a PDB / lockdown profile.
- Precheck: the runbook's pre-flight checklist (right env, backup, space, compatibility, LOCAL UNDO + ARCHIVELOG, connectivity, Data Guard awareness).
- Action: none auto-executes. Print
references/runbooks.md#<anchor> and stop. Gate with require_approval_token multitenant <task> ...#<anchor> / Require-ApprovalToken.
- Postcheck: per-runbook verification queries (
V$PDBS, PDB_PLUG_IN_VIOLATIONS, DBA_APP_PDB_STATUS, etc.).
- Rollback note: per-runbook abort/rollback guidance (abort relocate before OPEN; re-plug
KEEP DATAFILES; restore from backup; compensating application UPGRADE).
Tier-3 runbooks
All structural / cross-CDB operations are Tier 3 and live in
references/runbooks.md: #pdb-relocate (L3), #app-container (L3),
#pdb-plug, #pdb-drop, #lockdown-drop.
These runbooks PRINT exact SQL/DDL for a human operator and NEVER self-execute.
They are gated by require_approval_token / Require-ApprovalToken: an automated
agent's only sanctioned action is to print the relevant section and stop with
ERR_APPROVAL (6) until a human supplies a real change ticket/token (--token <TICKET> or ODB_APPROVAL_TOKEN; placeholders rejected). The T2 scripts deliberately
refuse the destructive operations (clone/unplug never drop; lockdown wrapper never
drops the profile) and point to the runbook instead. SYSDBA is a T3-only,
interactively-supplied exception. When in doubt, runbook.
See references/version-notes.md for 19c-vs-23ai behavioural deltas, and
cron/crontab.d + cron/task-scheduler.xml for the schedulable T1/T2 jobs (PDB
status monitoring; optional ensure-open + save-state self-healing).
1---2name: oracle-dba-multitenant-cdb-pdb3description: USE THIS SKILL WHENEVER the user mentions multitenant, container database / CDB, pluggable database / PDB, CDB$ROOT, PDB$SEED, open/close a PDB, PDB open mode (READ WRITE / READ ONLY / MOUNTED / RESTRICTED), SAVE STATE / DISCARD STATE, PDB clone or snapshot copy, PDB unplug / plug / manifest (.xml/.pdb), PDB relocation, CDB resource plan / inter-PDB shares, lockdown profile / PDB_LOCKDOWN, application container / application root / application PDB, PDB_PLUG_IN_VIOLATIONS, or the related views V$PDBS / CDB_PDBS / DBA_PDB_SAVED_STATES — EVEN IF they do not name the specific task. Covers PDB status monitoring, PDB open/close, save/discard state, local clone & snapshot copy, unplug-to-manifest, CDB-level Resource Manager configuration, lockdown profile rules + assignment, and the plan-only Tier-3 runbooks for PDB relocation, application-container management, plug, drop, and lockdown-profile drop. Follows the oracle-dba-common contracts: SQLcl MCP saved connection dba_ai_conn, Oracle Wallet secrets (never plaintex4---5# Oracle DBA - Multitenant (CDB / PDB)67Day-to-day operation of Oracle multitenant containers for **19c and 23ai** (EE;8single-instance and Data Guard; on-prem): PDB status monitoring, open/close, save9state, local clone / snapshot copy, unplug-to-manifest, CDB-level Resource Manager,10and lockdown profiles. Everything self-executing is **T1 (observe)** or **T211(reversible, dry-run-default)**. The **structural / cross-CDB** operations — **PDB12relocation, application-container lifecycle, plug-from-manifest, drop, and13lockdown-profile drop — are T3** and live only in `references/runbooks.md`; they print14exact commands for a human and never self-execute.1516In-DB SQL/PL-SQL runs through the **SQLcl MCP** saved connection **`dba_ai_conn`**17(wallet-backed). Host-level steps (none are required by the T1/T2 scripts here beyond18invoking SQLcl) and all T3 structural DDL are operator-driven. **Never SYS/SYSTEM**19except inside a T3 runbook that explicitly states `AS SYSDBA`, supplied interactively.2021## Scope & risk map2223| Task | Level | Tier | Mechanism (script / sql / runbook path) | Idempotent? |24|------|-------|------|------------------------------------------|-------------|25| PDB status monitoring | L1 | T1 | `scripts/checks/check_pdb_status.sh` / `.ps1` + `sql/pdb_status.sql` | yes — read-only (emits KEYVAL metrics + RESTRICTED / violation findings, never mutates) |26| PDB open / close | L2 | T2 | `scripts/pdb_open_close.sh` / `.ps1` + `sql/pdb_open_close.sql` | yes — already in target mode -> noop; OPEN <-> CLOSE inverses; `--pdb ALL` skips seed/root |27| Save / discard PDB state | L2 | T2 | `scripts/save_pdb_state.sh` / `.ps1` + `sql/pdb_save_state.sql` | yes — SAVE when already saved / DISCARD when none -> noop; `--ensure-open` opens first then saves |28| PDB clone / snapshot copy | L2 | T2 | `scripts/pdb_clone.sh` / `.ps1` + `sql/pdb_clone.sql` | yes — existing target PDB -> noop; CREATE only (drop is T3) |29| PDB unplug (to manifest) | L2 | T2 | `scripts/pdb_unplug.sh` / `.ps1` + `sql/pdb_unplug.sql` | yes — already UNPLUGGED -> noop; closes + writes manifest, never drops |30| CDB-level Resource Manager — configure / activate | L2 | T2 | `scripts/cdb_resource_manager.sh --action configure` / `.ps1` + `sql/cdb_resource_manager.sql` | yes — plan + per-PDB SHARES upserted; unchanged -> noop; must run from CDB$ROOT |31| CDB-level Resource Manager — deactivate | L2 | T2 | `scripts/cdb_resource_manager.sh --action deactivate` / `.ps1` + `sql/cdb_resource_manager_deactivate.sql` | yes — no active plan -> noop; definitions retained |32| Lockdown profile — apply / drop-rule | L2 | T2 | `scripts/lockdown_profile.sh --action apply\|drop-rule` / `.ps1` + `sql/lockdown_profile.sql` | yes — present rule on apply / absent on drop-rule -> noop; from CDB$ROOT |33| Lockdown profile — assign to PDB | L2 | T2 | `scripts/lockdown_profile.sh --action assign` / `.ps1` + `sql/lockdown_assign.sql` | yes — PDB_LOCKDOWN already = target -> noop; `--profile NONE` clears |34| PDB plug (from manifest) | L2-struct | **T3** | `references/runbooks.md#pdb-plug` | partial — `CREATE ... USING` is one-shot; retry after `DROP ... KEEP DATAFILES` |35| PDB relocation (online, across CDBs) | **L3** | **T3** | `references/runbooks.md#pdb-relocate` | no — source PDB dropped at cut-over; abort only before OPEN |36| Application container management | **L3** | **T3** | `references/runbooks.md#app-container` | partial — versioned INSTALL/UPGRADE/PATCH + SYNC; re-runnable per block, not a free rollback |37| Drop a PDB | struct | **T3** | `references/runbooks.md#pdb-drop` | no — `INCLUDING DATAFILES` irreversible; `KEEP DATAFILES` re-pluggable |38| Drop a lockdown profile (escape hatch) | struct | **T3** | `references/runbooks.md#lockdown-drop` | n/a — refused by the T2 lockdown wrapper |3940Levels map to `oracle_dba_task_level_map.md` section 12. Tasks 1-9 there41(monitoring, open/close, save state, clone/snapshot, unplug/plug, CDB Resource42Manager, lockdown, **relocation = L3**, **application container = L3**) are all43covered above; relocation and application containers are the two L3 tasks and are44given expert-grade Tier-3 runbooks.4546## Preconditions4748- **SQLcl MCP connection `dba_ai_conn`** is reachable and wallet-backed. All in-DB49 prechecks, postchecks, idempotency probes, and `sql/*.sql` run through it via50 `connect_db` / `Connect-Db`. **Never SYS/SYSTEM** — except inside a T3 runbook that51 explicitly states `AS SYSDBA`, supplied interactively by the operator. The52 `dba_ai_conn` common user needs the relevant privileges granted out of band per53 site policy: `ALTER PLUGGABLE DATABASE` / `CREATE PLUGGABLE DATABASE` (open/close,54 clone, unplug), `ADMINISTER RESOURCE MANAGER` (CDB plan), `ALTER SYSTEM` +55 `CREATE LOCKDOWN PROFILE` from CDB$ROOT and `ALTER SESSION SET CONTAINER`56 (lockdown). T3 structural DDL needs SYSDBA-class privilege (operator-supplied).57- **Container scope matters.** CDB-wide tasks (resource plan, lockdown profile58 create/edit) must run **connected to `CDB$ROOT`** — the wrappers verify59 `SYS_CONTEXT('USERENV','CON_NAME')='CDB$ROOT'` in their precheck and abort60 otherwise. Per-PDB tasks act on the named PDB; `--pdb ALL` loops user PDBs only61 (skips `PDB$SEED` and `con_id<=2`).62- **Secrets resolve from the Oracle Wallet / external password store. No plaintext63 anywhere** — not in argv, env, files, or SQL. The helper libraries reject64 credential-looking arguments with `ERR_SECRET` (8). Cross-CDB DB links (relocate /65 plug) use wallet-backed link credentials only.66- **Helpers are sourced from `_common`** (`scripts/lib.sh` / `lib.ps1`): `connect_db`,67 `log_event`, `emit_metric`, `require_dry_run`, `guard_blocked_dry_run`,68 `require_approval_token`, `precheck`, `postcheck`, `print_banner`. Not reinvented.69- **Logging**: structured `key=value` lines to the canonical paths70 `/var/log/oracle-dba/multitenant/multitenant.log` (Linux) /71 `C:/ProgramData/oracle-dba/logs/multitenant/multitenant.log` (Windows); skill72 segment `multitenant` throughout (T1/T2 scripts and T3 runbooks).73- **T2 dry-run default**: every T2 script previews and changes nothing unless given74 `--execute`. **Architecture floor**: 19c is CDB-by-default; **23ai is CDB-only**.75 All work targets the container `dba_ai_conn` selects; no non-CDB path exists.76- **Storage / option prerequisites** (operational, not syntax): SNAPSHOT COPY needs77 copy-on-write storage (ACFS / ASM / supported NFS); hot clone & online relocate78 need ARCHIVELOG + LOCAL UNDO on the relevant CDBs.7980## Procedures8182Scripts ship as behaviour-equivalent `.sh` (Linux) / `.ps1` (Windows) pairs. T283scripts default to `--dry-run`; pass `--execute` to act. See each script's `--help`.8485### PDB status monitoring (T1)86- **Trigger**: scheduled (e.g. every 15 min) or investigating a closed / RESTRICTED PDB.87- **Precheck**: none beyond `dba_ai_conn` reachable (read-only).88- **Action**: `scripts/checks/check_pdb_status.sh [--max-restricted N --max-violations N]` / `.ps1` + `sql/pdb_status.sql`.89- **Postcheck**: n/a — emits KEYVAL metrics (`total_pdbs`, `pdbs_read_write`, `pdbs_mounted`, `pdbs_restricted`, `unresolved_violations`) + non-fatal findings.90- **Rollback note**: none — read-only.9192### PDB open / close93- **Trigger**: bring a PDB online READ WRITE / READ ONLY / UPGRADE, or close it for maintenance.94- **Precheck**: `dba_ai_conn` reachable; idempotency probe reads current `open_mode`.95- **Action**: `scripts/pdb_open_close.sh --pdb NAME|ALL --action open|close [--open-mode "READ WRITE"|"READ ONLY"|UPGRADE] [--force] [--close-mode immediate|normal] --execute` / `.ps1` -> `sql/pdb_open_close.sql`. `--force` only to switch an already-open PDB's mode without closing.96- **Postcheck**: OPEN -> PDB not MOUNTED; CLOSE -> PDB MOUNTED (verified in `v$pdbs`).97- **Rollback note**: OPEN <-> CLOSE are inverses. Does NOT persist across CDB restart — use save state for that.9899### Save / discard PDB state100- **Trigger**: make a PDB auto-open to its current mode after a CDB restart (SAVE), or revert to default MOUNTED-on-restart (DISCARD).101- **Precheck**: `dba_ai_conn` reachable; reads `DBA_PDB_SAVED_STATES`.102- **Action**: `scripts/save_pdb_state.sh --pdb NAME|ALL --action save|discard [--ensure-open "READ WRITE"] --execute` / `.ps1` -> `sql/pdb_save_state.sql`. `--ensure-open` opens to the desired mode FIRST (delegates to `pdb_open_close`) then saves the current mode.103- **Postcheck**: SAVE -> saved-state row present; DISCARD -> absent.104- **Rollback note**: SAVE <-> DISCARD are inverses. On Data Guard, run on the PRIMARY.105106### PDB clone / snapshot copy107- **Trigger**: spin up a copy of an existing PDB (test/dev refresh, pre-change snapshot).108- **Precheck**: ARCHIVELOG (hot clone) + `dba_ai_conn` reachable; idempotency probe checks the target name in `CDB_PDBS`.109- **Action**: `scripts/pdb_clone.sh --src SRC --tgt NEW [--kind full|snapshot] [--file-convert "from,to,..."] [--storage-max 50G] [--no-open] --execute` / `.ps1` -> `sql/pdb_clone.sql`. Creates MOUNTED, then opens READ WRITE (separate logged step) unless `--no-open`.110- **Postcheck**: target present in `CDB_PDBS` (and open unless `--no-open`).111- **Rollback note**: a clone is undone by `DROP PLUGGABLE DATABASE <tgt> INCLUDING DATAFILES` — that is **T3** (`references/runbooks.md#pdb-drop`). This wrapper never drops.112113### PDB unplug (to manifest)114- **Trigger**: prepare a PDB to be moved/archived by writing its manifest; first half of an unplug/plug move.115- **Precheck**: manifest directory exists/writable (where visible) + `dba_ai_conn` reachable; idempotency probe reads `CDB_PDBS.STATUS`.116- **Action**: `scripts/pdb_unplug.sh --pdb NAME --manifest /path/pdb.xml [--archive xml|pdb] --execute` / `.ps1` -> `sql/pdb_unplug.sql`. Closes the PDB then `UNPLUG INTO`; leaves it UNPLUGGED (datafiles on disk). Never drops.117- **Postcheck**: PDB `STATUS='UNPLUGGED'` in `CDB_PDBS`.118- **Rollback note**: re-create from the manifest, or change your mind before dropping. The destructive `DROP ... KEEP/INCLUDING DATAFILES` + plug elsewhere is **T3** (`#pdb-plug`, `#pdb-drop`).119120### CDB-level Resource Manager (configure / activate / deactivate)121- **Trigger**: govern CPU/parallelism BETWEEN PDBs via per-PDB SHARES; or clear the active plan.122- **Precheck**: connected to `CDB$ROOT` (enforced); diff existing plan/directives.123- **Action**: `scripts/cdb_resource_manager.sh --action configure --plan ODB_CDB_PLAN --shares "SALES:3,HR:1" [--activate] --execute` / `.ps1` -> `sql/cdb_resource_manager.sql`; deactivate via `--action deactivate --execute` -> `sql/cdb_resource_manager_deactivate.sql`.124- **Postcheck**: plan has directives (`DBA_CDB_RSRC_PLAN_DIRECTIVES`); deactivate -> no active `RESOURCE_MANAGER_PLAN`.125- **Rollback note**: re-run configure with prior SHARES, or deactivate. Definitions are retained on deactivate (re-activation is a one-liner). Deleting a plan entirely is operator-driven.126127### Lockdown profile (apply / drop-rule / assign)128- **Trigger**: harden PDBs by DISABLEing features (apply), remove a rule (drop-rule), or bind/unbind a profile to a PDB (assign).129- **Precheck**: apply/drop-rule require `CDB$ROOT` (enforced); assign requires `dba_ai_conn` reachable.130- **Action**: `scripts/lockdown_profile.sh --action apply|drop-rule --profile NAME --rules "ALTER_SYSTEM,OS_ACCESS" --execute` / `--action assign --profile NAME|NONE --pdb SALES --execute` / `.ps1` -> `sql/lockdown_profile.sql` / `sql/lockdown_assign.sql`.131- **Postcheck**: apply -> profile present in `DBA_LOCKDOWN_PROFILES`; assign -> `PDB_LOCKDOWN` equals target inside the PDB (or empty for NONE).132- **Rollback note**: drop-rule / re-apply restore rules; assign `--profile NONE` clears. Dropping the whole profile is **T3** (`#lockdown-drop`).133134### Tier-3 structural operations (relocation, app containers, plug, drop)135- **Trigger**: move a PDB across CDBs, manage an application container, plug an unplugged PDB, or drop a PDB / lockdown profile.136- **Precheck**: the runbook's pre-flight checklist (right env, backup, space, compatibility, LOCAL UNDO + ARCHIVELOG, connectivity, Data Guard awareness).137- **Action**: **none auto-executes.** Print `references/runbooks.md#<anchor>` and stop. Gate with `require_approval_token multitenant <task> ...#<anchor>` / `Require-ApprovalToken`.138- **Postcheck**: per-runbook verification queries (`V$PDBS`, `PDB_PLUG_IN_VIOLATIONS`, `DBA_APP_PDB_STATUS`, etc.).139- **Rollback note**: per-runbook abort/rollback guidance (abort relocate before OPEN; re-plug `KEEP DATAFILES`; restore from backup; compensating application UPGRADE).140141## Tier-3 runbooks142143All **structural / cross-CDB** operations are Tier 3 and live in144**`references/runbooks.md`**: `#pdb-relocate` (**L3**), `#app-container` (**L3**),145`#pdb-plug`, `#pdb-drop`, `#lockdown-drop`.146147These runbooks **PRINT exact SQL/DDL for a human operator and NEVER self-execute.**148They are gated by `require_approval_token` / `Require-ApprovalToken`: an automated149agent's only sanctioned action is to print the relevant section and stop with150`ERR_APPROVAL` (6) until a human supplies a real change ticket/token (`--token151<TICKET>` or `ODB_APPROVAL_TOKEN`; placeholders rejected). The T2 scripts deliberately152refuse the destructive operations (clone/unplug never drop; lockdown wrapper never153drops the profile) and point to the runbook instead. SYSDBA is a T3-only,154interactively-supplied exception. When in doubt, runbook.155156See `references/version-notes.md` for 19c-vs-23ai behavioural deltas, and157`cron/crontab.d` + `cron/task-scheduler.xml` for the schedulable T1/T2 jobs (PDB158status monitoring; optional ensure-open + save-state self-healing).