-- DB-backed game query scope contract for MCP orchestration. -- -- This script deliberately keeps game facts in database rows, not in application -- code or Select AI instructions. The view combines the active game alias source -- with an operator-maintained registry for known games that currently have no -- approved query object. A zero-row business result is still queryable; only the -- absence of an approved object makes a game scope unavailable. DECLARE v_count PLS_INTEGER; BEGIN SELECT COUNT(*) INTO v_count FROM user_tables WHERE table_name = 'SG_GAME_SCOPE_REGISTRY'; IF v_count = 0 THEN EXECUTE IMMEDIATE q'[ CREATE TABLE sg_game_scope_registry ( game_key VARCHAR2(128) NOT NULL, display_name VARCHAR2(200) NOT NULL, game_alias VARCHAR2(200) NOT NULL, game_prefix VARCHAR2(30), active_yn VARCHAR2(1) DEFAULT 'Y' NOT NULL, alias_priority NUMBER(10) DEFAULT 100 NOT NULL, source_type VARCHAR2(30) DEFAULT 'OPERATOR' NOT NULL, work_dtm TIMESTAMP(6) DEFAULT SYSTIMESTAMP NOT NULL, CONSTRAINT sg_game_scope_registry_pk PRIMARY KEY (game_key, game_alias), CONSTRAINT sg_game_scope_registry_active_ck CHECK (active_yn IN ('Y', 'N')) )]'; END IF; END; / -- Seed only database facts needed to recognise currently unavailable games in -- the customer QA catalogue. Customer game-master synchronization can replace -- these rows without an application deployment. MERGE INTO sg_game_scope_registry t USING ( SELECT 'LORDNINE' AS game_key, utl_i18n.raw_to_char( utl_encode.base64_decode(utl_raw.cast_to_raw('66Gc65Oc64KY7J24')), 'AL32UTF8' ) AS display_name, utl_i18n.raw_to_char( utl_encode.base64_decode(utl_raw.cast_to_raw('66Gc65Oc64KY7J24')), 'AL32UTF8' ) AS game_alias, CAST(NULL AS VARCHAR2(30)) AS game_prefix, 100 AS alias_priority FROM dual UNION ALL SELECT 'BUBBLYZ', 'Bubblyz', 'Bubblyz', CAST(NULL AS VARCHAR2(30)), 100 FROM dual ) s ON (t.game_key = s.game_key AND t.game_alias = s.game_alias) WHEN MATCHED THEN UPDATE SET t.display_name = s.display_name, t.game_prefix = s.game_prefix, t.active_yn = 'Y', t.alias_priority = s.alias_priority, t.source_type = 'OPERATOR', t.work_dtm = SYSTIMESTAMP WHEN NOT MATCHED THEN INSERT ( game_key, display_name, game_alias, game_prefix, active_yn, alias_priority, source_type ) VALUES ( s.game_key, s.display_name, s.game_alias, s.game_prefix, 'Y', s.alias_priority, 'OPERATOR' ); / CREATE OR REPLACE VIEW sg_game_query_scope_v AS WITH existing_query_objects AS ( -- USER_OBJECTS is the authoritative current-schema inventory. The profile -- object list alone is not enough because a stale entry must not make a game -- executable after its table or view has been removed or invalidated. SELECT object_name FROM user_objects WHERE object_type IN ('TABLE', 'VIEW') AND status = 'VALID' ), profile_names AS ( SELECT DISTINCT profile_name FROM user_cloud_ai_profile_attributes WHERE attribute_name = 'object_list' ), profile_objects AS ( SELECT DISTINCT p.profile_name, o.object_name FROM user_cloud_ai_profile_attributes p, JSON_TABLE( p.attribute_value, '$[*]' COLUMNS (object_name VARCHAR2(128) PATH '$.name') ) o INNER JOIN existing_query_objects e ON e.object_name = o.object_name WHERE p.attribute_name = 'object_list' ), source_alias AS ( SELECT game_id AS game_key, game_nm AS display_name, game_alias_nm AS game_alias, game_prefix, use_yn AS active_yn, NVL(sort_order, 100) AS alias_priority, 'GAME_ALIAS' AS source_type FROM comn_game_alias_bas ), all_alias AS ( SELECT game_key, display_name, game_alias, game_prefix, active_yn, alias_priority, source_type FROM source_alias UNION ALL SELECT r.game_key, r.display_name, r.game_alias, r.game_prefix, r.active_yn, r.alias_priority, r.source_type FROM sg_game_scope_registry r WHERE NOT EXISTS ( SELECT 1 FROM source_alias a WHERE a.game_key = r.game_key AND a.game_alias = r.game_alias ) ), scope_object AS ( SELECT n.profile_name, a.game_key, a.game_alias, COUNT(p.object_name) AS approved_object_count FROM profile_names n CROSS JOIN all_alias a LEFT JOIN profile_objects p ON p.profile_name = n.profile_name AND a.game_prefix IS NOT NULL AND SUBSTR(p.object_name, 1, LENGTH(a.game_prefix) + 1) = a.game_prefix || '_' GROUP BY n.profile_name, a.game_key, a.game_alias ) SELECT o.profile_name, a.game_key, a.display_name, a.game_alias, a.game_prefix, a.active_yn, NVL(o.approved_object_count, 0) AS approved_object_count, CASE WHEN a.active_yn <> 'Y' THEN 'N' WHEN NVL(o.approved_object_count, 0) > 0 THEN 'Y' ELSE 'N' END AS query_allowed_yn, CASE WHEN a.active_yn <> 'Y' THEN 'GAME_INACTIVE' WHEN NVL(o.approved_object_count, 0) > 0 THEN 'APPROVED_OBJECT_AVAILABLE' ELSE 'OBJECT_LIST_NOT_AVAILABLE' END AS reason_code, a.alias_priority, a.source_type, TO_CHAR(MAX(a.alias_priority) OVER (PARTITION BY a.game_key), 'FM999999990') AS scope_version FROM all_alias a LEFT JOIN scope_object o ON o.game_key = a.game_key AND o.game_alias = a.game_alias; / COMMENT ON TABLE sg_game_scope_registry IS 'Operator-managed game aliases retained for scope resolution when a game has no approved query object.'; COMMENT ON COLUMN sg_game_scope_registry.game_key IS 'Stable game identifier used only by the DB-backed scope contract.'; COMMENT ON COLUMN sg_game_scope_registry.game_alias IS 'Question text alias matched by the resolver before any SQL worker is called.'; COMMENT ON COLUMN sg_game_scope_registry.game_prefix IS 'Optional data-object prefix. The scope view derives approved object availability from it.'; COMMENT ON COLUMN sg_game_scope_registry.active_yn IS 'Whether the game is eligible for scope resolution; inactive games are never executable.'; COMMENT ON COLUMN sg_game_scope_registry.alias_priority IS 'Database-defined ordering used to resolve overlapping aliases without application constants.'; COMMENT ON COLUMN sg_game_query_scope_v.profile_name IS 'Select AI profile whose current approved object list was used for this scope decision.'; COMMENT ON COLUMN sg_game_query_scope_v.query_allowed_yn IS 'Y only when the active game has at least one current Select AI approved and valid prefix-specific table or view.'; COMMENT ON COLUMN sg_game_query_scope_v.reason_code IS 'Database-derived explanation for scope availability returned to the MCP agent.';