This is an automated email from the ASF dual-hosted git repository.

jerryshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 84fa299c5c [MINOR] docs: Add policy-on-tag migration guide (#13382)
84fa299c5c is described below

commit 84fa299c5c7ce42f5e757d1fec715cddc8e2e1af
Author: roryqi <[email protected]>
AuthorDate: Thu Sep 24 18:00:52 2026 +0800

    [MINOR] docs: Add policy-on-tag migration guide (#13382)
    
    ### What changes were proposed in this pull request?
    
    - Add a versioned Migration Guide covering the move from direct policy
    associations to policy-on-tag, including API replacements, a migration
    checklist, and rollback preparation.
    - Update policy, tag, access control, UI, and maintenance service
    documentation to describe the current policy-on-tag behavior.
    
    ### Why are the changes needed?
    
    Direct object-policy associations were removed in #13355. Users need a
    migration path and documentation that reflects the current APIs and
    behavior.
    
    Related: #13355
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes. It adds migration instructions and updates user documentation. This
    PR does not change APIs or properties.
    
    ### How was this patch tested?
    
    - `./gradlew :docs:build`
    - Checked links and anchors in all 15 changed Markdown files (0 broken
    links).
    - `git diff --check apache/main...HEAD`
---
 docs/assets/webui-v2/policy-metadata-objects.png   | Bin 125448 -> 0 bytes
 docs/catalogs-and-schemas.md                       |  14 +-
 docs/how-to-upgrade.md                             |   5 +-
 docs/iceberg-compaction-policy.md                  |  12 +-
 docs/index.md                                      |   5 +
 docs/manage-policies-in-gravitino.md               | 140 ++++++++++-
 docs/manage-tags-in-gravitino.md                   |   3 +
 docs/metalakes.md                                  |   6 +-
 docs/migration-guide.md                            | 108 +++++++++
 docs/policies.md                                   | 267 ++++++++-------------
 docs/security/access-control.md                    |  41 ++--
 .../optimizer-configuration.md                     |   2 +-
 docs/table-maintenance-service/optimizer.md        |   6 +-
 docs/tables-and-views.md                           |   4 +-
 docs/topics.md                                     |   9 +-
 docs/webui-v2.md                                   |  15 +-
 16 files changed, 407 insertions(+), 230 deletions(-)

diff --git a/docs/assets/webui-v2/policy-metadata-objects.png 
b/docs/assets/webui-v2/policy-metadata-objects.png
deleted file mode 100644
index 3d218048d4..0000000000
Binary files a/docs/assets/webui-v2/policy-metadata-objects.png and /dev/null 
differ
diff --git a/docs/catalogs-and-schemas.md b/docs/catalogs-and-schemas.md
index e987795753..1b5505b423 100644
--- a/docs/catalogs-and-schemas.md
+++ b/docs/catalogs-and-schemas.md
@@ -20,8 +20,8 @@ A schema is the middle level, and what it means depends on 
the system underneath
 catalog it is a database. In a fileset catalog it groups filesets under a 
location. In a model
 catalog it is a namespace for models with no physical counterpart at all.
 
-Catalogs and schemas both carry tags and policies, and both are the level to 
attach them at when
-something should apply broadly. A tag on a catalog reaches every object 
beneath it.
+Catalogs and schemas can carry tag assignments. Assign a tag at either level 
when its derived
+policies should apply broadly. A tag on a catalog reaches every object beneath 
it.
 
 ## Quick Start
 
@@ -74,9 +74,9 @@ where they are.
 
 ### What Gravitino Stores
 
-Gravitino stores the catalog registration, the schemas and objects created 
through it, and anything
-attached to those objects such as tags, policies, and ownership. It does not 
store a copy of the
-source system's contents.
+Gravitino stores the catalog registration, the schemas and objects created 
through it, and their tag
+assignments and ownership. Policies are derived from effective tags when they 
are read. Gravitino
+does not store a copy of the source system's contents.
 
 Listing tables in a schema reaches the source system at request time, so a 
table created directly in
 Hive appears the next time Gravitino is asked. The consequence worth knowing 
is that Gravitino
@@ -88,8 +88,8 @@ The catalog list holds every catalog in the current metalake. 
A catalog can be c
 enabled or disabled, and deleted from there, and each one expands into its 
schemas and their
 contents.
 
-Tags and policies attach from the catalog and schema rows, which is the 
fastest way to classify a
-whole subtree.
+Assign tags from the catalog and schema rows to classify a whole subtree. 
Policies associated
+with those tags appear in object policy lookups.
 
 ## Deleting Catalogs and Schemas
 
diff --git a/docs/how-to-upgrade.md b/docs/how-to-upgrade.md
index 26b63ba3c9..b8b465d5b5 100644
--- a/docs/how-to-upgrade.md
+++ b/docs/how-to-upgrade.md
@@ -14,6 +14,9 @@ Gravitino 0.7.0 schema. Before attempting this project we
 strongly recommend that you read through all of the steps in this
 document and familiarize yourself with the required tools.
 
+For version-specific behavior and API changes, see the
+[Migration Guide](./migration-guide.md) before upgrading.
+
 ## Upgrade Steps
 
 ### Step 1: Shut Down the Gravitino Instance
@@ -171,7 +174,7 @@ you will want to compare your schema dump against the 
contents of
 
 :::note
 The Gravitino Helm chart does not currently support automatic schema 
migration. Before running
-`helm upgrade`, you must manually back up your database (see [Step 
2](#step-2-backup-your-gravitino-instance))
+`helm upgrade`, you must manually back up your database (see [Step 
2](#step-2-back-up-the-gravitino-instance))
 and apply the appropriate SQL upgrade scripts (see [Step 
5](#step-5-apply-the-upgrade-scripts)).
 :::
 
diff --git a/docs/iceberg-compaction-policy.md 
b/docs/iceberg-compaction-policy.md
index 86b492ee64..25a5520213 100644
--- a/docs/iceberg-compaction-policy.md
+++ b/docs/iceberg-compaction-policy.md
@@ -128,7 +128,11 @@ Policy policy =
 </TabItem>
 </Tabs>
 
-## Attach Policy to Metadata Objects
-
-After the policy is created, associate it with a catalog, schema, or table 
through standard policy association APIs.
-The optimizer will read the generated rules and properties to evaluate 
strategy triggering and job submission context.
+## Apply the Policy Through a Tag
+
+After creating the policy, create or reuse a tag, associate the policy with 
that tag using the
+`ALL_VALUES` selector, and assign the tag to a table or one of its ancestors. 
The optimizer reads
+the table's derived policies to evaluate strategy triggering and job 
submission context. See
+[Manage 
Policies](./manage-policies-in-gravitino.md#policy-to-tag-associations) for 
association
+operations and the [Table maintenance service 
walkthrough](./table-maintenance-service/optimizer.md#step-4-configure-a-compaction-policy-through-a-tag)
+for a complete REST example.
diff --git a/docs/index.md b/docs/index.md
index 017a084e99..d8e5a6fcbd 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -32,6 +32,11 @@ Gravitino also provides a playground to experience the whole 
Gravitino system wi
 See the [Gravitino playground 
repository](https://github.com/apache/gravitino-playground)
 and [How to use the playground](./how-to-use-the-playground.md).
 
+## Migration Guide
+
+Use the [Migration Guide](./migration-guide.md) for version-specific behavior 
and API changes.
+For database schema upgrades, see [Upgrade Gravitino](./how-to-upgrade.md).
+
 ## Getting Started
 
 To get started with Gravitino, see [Getting 
started](./getting-started/index.md) for the details.
diff --git a/docs/manage-policies-in-gravitino.md 
b/docs/manage-policies-in-gravitino.md
index 8654a89545..110f9498db 100644
--- a/docs/manage-policies-in-gravitino.md
+++ b/docs/manage-policies-in-gravitino.md
@@ -10,9 +10,10 @@ import TabItem from '@theme/TabItem';
 
 ## Introduction
 
-This page covers the Gravitino API for policies. For what a policy is, which 
object types can carry one, what goes in
-policy content, how inheritance resolves, and how to work with policies in the 
UI, see
-[Policies](./policies.md).
+This page covers the Gravitino API for policies and policy-to-tag 
associations. For the policy
+model and how object policy lookup works, see
+[Policies](./policies.md). To move from direct object policy associations, 
follow
+[Migration Guide](./migration-guide.md).
 
 The Python client does not cover policies, so the examples below are REST and 
Java only.
 
@@ -21,7 +22,10 @@ The Python client does not cover policies, so the examples 
below are REST and Ja
 ### Create a Policy
 
 A policy needs a name and a type. Content carries the rules, the object types 
the policy supports,
-and optional properties. `supportedObjectTypes` cannot be changed after 
creation.
+and optional properties. For a built-in policy, `supportedObjectTypes` cannot 
be changed after
+creation. Updating custom policy content replaces the whole content and can 
change this field.
+Object policy lookup does not filter by this field; consumers decide which 
policy types they can
+use.
 
 <Tabs groupId='language' queryString>
 <TabItem value="shell" label="REST">
@@ -170,8 +174,8 @@ Policy policy = client.alterPolicy(
 
 ### Enable or Disable a Policy
 
-The flag is a marker for readers. Gravitino does not act on it, and disabling 
a policy neither
-detaches it nor changes what a consumer receives.
+Disabling a policy keeps its tag associations, but removes it from object 
policy lookup results.
+It remains available through policy and association listing APIs.
 
 <Tabs groupId='language' queryString>
 <TabItem value="shell" label="REST">
@@ -215,6 +219,104 @@ client.deletePolicy("retention_30d");
 </TabItem>
 </Tabs>
 
+## Policy-to-Tag Associations
+
+Create a policy and a tag in the same metalake before associating them. Each 
policy-to-tag
+association has a selector:
+
+| Selector     | Match condition                                               
         |
+|--------------|------------------------------------------------------------------------|
+| `ALL_VALUES` | The effective tag is present, with or without assignment 
values.       |
+| `TAG_VALUE`  | One effective tag assignment value equals the specified 
value.         |
+
+The selector belongs to the association, not to the policy or the tag. An 
existing association
+cannot be replaced by another add request. Remove it and add it again to 
change its selector.
+
+### Associate a Policy with a Tag
+
+This example applies `retention_30d` when the effective `data_domain` tag has 
the value `finance`.
+Create the tag first if it does not exist; see
+[Manage 
tags](./manage-tags-in-gravitino.md#create-a-tag-with-a-value-constraint).
+
+<Tabs groupId='language' queryString>
+<TabItem value="shell" label="REST">
+
+```shell
+curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
+  -H "Content-Type: application/json" \
+  -d '{"selector": {"type": "TAG_VALUE", "value": "finance"}}' \
+  
http://localhost:8090/api/metalakes/test/tags/data_domain/policies/retention_30d
+```
+
+Use `{"selector": {"type": "ALL_VALUES"}}` to match any assignment of 
`data_domain`.
+
+</TabItem>
+<TabItem value="java" label="Java">
+
+```java
+PolicyTagAssociation association = client.addPolicyForTag(
+    "data_domain", "retention_30d", TagValueSelector.of("finance"));
+
+// Match any assignment of data_domain instead:
+// client.addPolicyForTag("data_domain", "retention_30d");
+```
+
+</TabItem>
+</Tabs>
+
+The request fails with a conflict if the policy is already associated with the 
tag.
+
+### List Associations
+
+List associations from either side. By default the response contains names. 
Set `details=true`
+to get policy or tag details together with each association's selector. These 
lists show direct
+associations even when their selectors do not match any object's current tag 
values.
+
+<Tabs groupId='language' queryString>
+<TabItem value="shell" label="REST">
+
+```shell
+curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
+  
"http://localhost:8090/api/metalakes/test/tags/data_domain/policies?details=true";
+
+curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
+  
"http://localhost:8090/api/metalakes/test/policies/retention_30d/tags?details=true";
+```
+
+</TabItem>
+<TabItem value="java" label="Java">
+
+```java
+PolicyTagAssociation[] policies = 
client.listPolicyAssociationsForTag("data_domain");
+PolicyTagAssociation[] tags = 
client.listTagAssociationsForPolicy("retention_30d");
+```
+
+</TabItem>
+</Tabs>
+
+### Remove an Association
+
+Removing the association stops this policy from being selected through the 
tag. It leaves the
+policy, tag, and tag assignments in place.
+
+<Tabs groupId='language' queryString>
+<TabItem value="shell" label="REST">
+
+```shell
+curl -X DELETE -H "Accept: application/vnd.gravitino.v1+json" \
+  
http://localhost:8090/api/metalakes/test/tags/data_domain/policies/retention_30d
+```
+
+</TabItem>
+<TabItem value="java" label="Java">
+
+```java
+client.removePolicyFromTag("data_domain", "retention_30d");
+```
+
+</TabItem>
+</Tabs>
+
 ## Object Operations
 
 Object policies are read-only results derived from effective tags. To change 
the policies that apply
@@ -222,28 +324,44 @@ to an object, associate a policy with a tag and then 
assign or remove that tag o
 of its ancestors. See [Manage tags in 
Gravitino](./manage-tags-in-gravitino.md) for tag assignment
 operations.
 
+For the `TAG_VALUE("finance")` association above, assign `data_domain=finance` 
to a table or one
+of its ancestors. The nearest assignment of `data_domain` overrides farther 
assignments of the
+same tag. For example, a schema assignment of `data_domain=risk` overrides
+`data_domain=finance` on its catalog for every table in the schema. A direct 
table assignment
+overrides both.
+
+```shell
+curl -X POST -H "Accept: application/vnd.gravitino.v2+json" \
+  -H "Content-Type: application/vnd.gravitino.v2+json" \
+  -d '{"tagsToAdd": [{"name": "data_domain", "value": "finance"}]}' \
+  
http://localhost:8090/api/metalakes/test/objects/table/catalog1.schema1.customers/tags
+```
+
 ### List Policies on an Object
 
 The response includes policies derived from effective tags assigned to the 
object or its ancestors.
 With `details=true`, the response returns full policy objects instead of 
policy names.
 Each policy includes an `inherited` field, which is `true` when it matches 
only through a tag
-assigned to an ancestor of the object.
+assigned to an ancestor of the object. Disabled policies do not appear in this 
result.
 
 <Tabs groupId='language' queryString>
 <TabItem value="shell" label="REST">
 
 ```shell
 curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-  
"http://localhost:8090/api/metalakes/test/objects/catalog/catalog1/policies?details=true";
+  
"http://localhost:8090/api/metalakes/test/objects/table/catalog1.schema1.customers/policies?details=true";
 ```
 
 </TabItem>
 <TabItem value="java" label="Java">
 
 ```java
-Catalog catalog = client.loadCatalog("catalog1");
-String[] policyNames = catalog.supportsPolicies().listPolicies();
-Policy[] policies = catalog.supportsPolicies().listPolicyInfos();
+Table customers =
+    client.loadCatalog("catalog1")
+        .asTableCatalog()
+        .loadTable(NameIdentifier.of("schema1", "customers"));
+String[] policyNames = customers.supportsPolicies().listPolicies();
+Policy[] policies = customers.supportsPolicies().listPolicyInfos();
 ```
 
 </TabItem>
diff --git a/docs/manage-tags-in-gravitino.md b/docs/manage-tags-in-gravitino.md
index f5308db080..5254582a1e 100644
--- a/docs/manage-tags-in-gravitino.md
+++ b/docs/manage-tags-in-gravitino.md
@@ -13,6 +13,9 @@ import TabItem from '@theme/TabItem';
 This page covers the Gravitino API for tags. For what a tag is, which object 
types can carry one, how inheritance
 resolves, and how to work with tags in the UI, see [Tags](./tags.md).
 
+Policies can be associated with tags to govern objects carrying those tags. See
+[Policy-to-Tag 
Associations](./manage-policies-in-gravitino.md#policy-to-tag-associations).
+
 ## Tag Operations
 
 ### Create a Tag
diff --git a/docs/metalakes.md b/docs/metalakes.md
index d0e33de413..52b5c02888 100644
--- a/docs/metalakes.md
+++ b/docs/metalakes.md
@@ -17,7 +17,7 @@ Nothing crosses that boundary:
   user in each, and provisioning them is done per metalake
 - Roles are defined and granted within one metalake, so a role held in one 
carries no privilege in
   another
-- A tag or policy created in one metalake cannot be attached to an object in 
another
+- A tag can only be assigned to objects in its metalake, and a policy can only 
be associated with tags in its metalake
 - Names only have to be unique within one metalake
 
 That makes a metalake the unit to reach for when separating environments, 
business units, or tenants
@@ -50,8 +50,8 @@ Properties are free-form key and value pairs, with one 
reserved key. `in-use` re
 metalake is available, defaults to `true`, and is set through the enable and 
disable operations
 rather than by writing the property directly.
 
-A metalake cannot carry a tag or a policy, so there is no way to classify or 
govern everything at
-once from the top. The widest attachment point is a catalog.
+A metalake cannot carry a tag assignment, so there is no way to classify or 
govern everything at
+once from the top. The widest tag assignment point is a catalog. Policies 
apply through tags.
 
 ### In Use and Not In Use
 
diff --git a/docs/migration-guide.md b/docs/migration-guide.md
new file mode 100644
index 0000000000..9bbecb698c
--- /dev/null
+++ b/docs/migration-guide.md
@@ -0,0 +1,108 @@
+---
+title: "Migration Guide"
+slug: "/migration-guide"
+keyword: "migration, upgrade, compatibility, Gravitino"
+license: "This software is licensed under the Apache License version 2."
+---
+
+This guide lists user-visible behavior changes by upgrade version and the 
action needed for
+existing deployments. For database backup, schema scripts, and rollback 
commands, see
+[Upgrade Gravitino](./how-to-upgrade.md).
+
+## Upgrading from Gravitino 1.3 to 2.0
+
+### Policies are selected through tags
+
+- Direct policy associations with metadata objects are no longer read or 
written. The upgrade
+  does not convert existing direct associations. Before upgrading, export each 
policy's directly
+  associated objects from `GET 
/api/metalakes/{metalake}/policies/{policy}/objects`. Save the
+  policy name, object type, and full name, including direct associations on 
ancestor objects.
+  Back up the metadata database before the schema upgrade.
+- To preserve the old scope, create one dedicated tag per policy, associate 
the policy with that
+  tag using the `ALL_VALUES` selector, and assign the tag to every object that 
had a direct
+  policy association. A tag on a catalog or schema reaches its descendants. If 
a policy was
+  directly associated with both a parent and a child, assign the tag to both.
+- Object policy lookup at `GET 
/api/metalakes/{metalake}/objects/{type}/{fullName}/policies`
+  remains available, but now returns enabled policies matching the object's 
effective tags.
+  A policy matching through multiple tags appears once. With `details=true`, 
`inherited` is
+  true only if the matching tag assignments are inherited. Disabled policies 
remain associated
+  with tags but do not appear in object policy results.
+- In Gravitino 1.3, disabling a policy was advisory and did not change what 
consumers received.
+  Object policy lookup now excludes disabled policies. Before upgrading, 
inventory disabled
+  policies and re-enable any that consumers must continue to receive.
+- The legacy `supportedObjectTypes` field no longer filters object policy 
lookup. Consumers
+  must decide which policy types they can use. For example, TMS should consume 
the built-in
+  compaction policy type from each table's resolved policies.
+
+### REST and client API changes
+
+| Previous API or call                                                  | 
Migration                                                                       
                                                                         |
+|-----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `POST /objects/{type}/{fullName}/policies`                            | 
Associate the policy with a tag using `POST /tags/{tag}/policies/{policy}`, 
then assign the tag using `POST /objects/{type}/{fullName}/tags`.            |
+| `GET /objects/{type}/{fullName}/policies/{policy}`                    | List 
resolved policies with `GET /objects/{type}/{fullName}/policies?details=true` 
and select the policy by name.                                        |
+| `GET /policies/{policy}/objects`                                      | Use 
`GET /policies/{policy}/tags` to inspect tag associations. It does not list 
every object reached through those tags.                                 |
+| Java `supportsPolicies().associatePolicies(...)` and `getPolicy(...)` | Use 
`GravitinoClient.addPolicyForTag(...)` and tag assignment APIs for writes; use 
`supportsPolicies().listPolicies()` or `listPolicyInfos()` for reads. |
+| Java `Policy.associatedObjects()`                                     | List 
tag associations with `GravitinoClient.listTagAssociationsForPolicy(...)`, then 
inspect affected objects separately.                                |
+
+Paths in this table are relative to `/api/metalakes/{metalake}`. A 
policy-to-tag association
+requires a selector. `ALL_VALUES` matches tag presence, including an 
assignment without a
+value; `TAG_VALUE` matches when one effective tag assignment value equals the 
selector value.
+To change an existing selector, remove the association and add it again. See
+[Manage 
Policies](./manage-policies-in-gravitino.md#policy-to-tag-associations) for 
REST and Java
+examples.
+
+The Python client can manage tag assignments but does not provide policy APIs. 
Use REST for
+policy-to-tag association changes.
+
+### Tag assignment values and inheritance
+
+- Existing string-based tag assignments remain supported. Use the v2
+  `application/vnd.gravitino.v2+json` request when adding or removing a 
tag-value pair.
+  A tag can have no value, one value, or multiple values.
+- Allowed values are chosen when a tag is created and cannot be changed later. 
Plan the
+  constraint before using a `TAG_VALUE` selector.
+- Resolution uses the nearest assignment for a tag name. A direct assignment 
replaces all
+  inherited values, and a nearer ancestor overrides a farther one. For example,
+  `data_domain=risk` on a schema overrides `data_domain=finance` on its 
catalog for every table
+  in that schema, so a `TAG_VALUE("finance")` association no longer matches 
those tables. A
+  dedicated migration tag with `ALL_VALUES` avoids this interaction.
+
+See [Manage 
Tags](./manage-tags-in-gravitino.md#create-a-tag-with-a-value-constraint)
+for tag creation and value assignment examples.
+
+### Authorization, UI, and consumers
+
+- Creating a migration tag requires `CREATE_TAG` on the metalake or ownership. 
Associating
+  a policy with a tag requires access to both: `APPLY_POLICY` on the policy 
and `APPLY_TAG`
+  on the tag, or the corresponding ownership. Assigning a tag also requires 
access to the
+  target metadata object.
+- Read-only inspection requires `VIEW_TAG` or `APPLY_TAG` for tags and 
`VIEW_POLICY` or
+  `APPLY_POLICY` for policies. Object policy results include only policies 
visible to the caller;
+  use an account with the same policy visibility when comparing results before 
and after migration.
+- The current web UI still displays direct policy controls that call the 
removed
+  object-policy write API. Use REST or Java for policy-to-tag associations 
during migration.
+- Update TMS and other policy consumers to read the resolved object policy 
list. Confirm the
+  expected policy appears on representative tables before resuming maintenance 
jobs.
+
+### Migration checklist
+
+1. On the old runtime, back up the database. Export direct policy associations 
for **each
+   metalake**, inventory disabled policies, and record policy results on 
representative
+   descendants. Re-enable disabled policies that consumers must continue to 
receive. The old
+   `/policies/{policy}/objects` endpoint is unavailable after the upgrade.
+2. Map each direct relation to a tag assignment. A dedicated tag per policy 
with
+   `ALL_VALUES` preserves the original scope; review existing tag values and 
child overrides
+   before choosing `TAG_VALUE`.
+3. Stop direct policy writes and pause consumers that require complete policy 
results. Upgrade
+   the server and schema using [Upgrade Gravitino](./how-to-upgrade.md).
+4. Create the tags, add policy-to-tag associations, and assign tags to every 
object from the
+   exported direct-relation inventory.
+5. Compare resolved policy names and `inherited` values for every inventoried 
object and
+   representative descendants. Check enabled and disabled policies, direct 
child assignments,
+   and value selectors. Use the same policy visibility for before and after 
comparisons.
+6. Resume consumers after verification. Keep the inventory and database backup 
until the
+   deployment is stable. If rollback is needed, stop the new server and follow 
the
+   [database rollback 
procedure](./how-to-upgrade.md#rollback-if-upgrade-fails).
+
+There is no automatic converter or verification command for direct policy 
relations. Do not
+edit relation tables directly as part of this migration.
diff --git a/docs/policies.md b/docs/policies.md
index b48f24f64b..fa87ebfaed 100644
--- a/docs/policies.md
+++ b/docs/policies.md
@@ -1,189 +1,116 @@
 ---
 title: "Policies"
 slug: "/policies"
-keyword: "policy, policies, governance, metadata object, Gravitino"
+keyword: "policy, policies, governance, tags, Gravitino"
 license: "This software is licensed under the Apache License version 2."
 ---
 
 ## Introduction
 
-A policy is a named set of rules that you create once in a metalake and attach 
to metadata objects.
-Attaching a policy to a catalog or schema applies it to everything beneath, so 
a setting that varies
-by table can be expressed once at the level where it holds and overridden 
where it does not.
+A policy is a named set of rules in a metalake. Associate it with a tag, then 
assign that tag to
+metadata objects. When a client reads an object's policies, Gravitino finds 
its effective tags and
+returns the enabled policies whose association selectors match. The policy 
remains a separate
+object: changing its rules updates every object where it applies.
 
-Tags and policies are close cousins, and the difference is what they carry. A 
tag classifies, and
-its content is its name. A policy prescribes, and its content is a set of 
rules something acts on.
-
-Policies come in two kinds. A built-in policy has a type that Gravitino 
defines and a consumer that
-acts on it. A custom policy carries rules of your own, which Gravitino stores, 
inherits, and serves
-back to whatever system you build around it.
-
-Common uses:
-
-- Setting table maintenance behavior for a whole catalog rather than table by 
table, and letting new
-  tables pick it up without further work
-- Recording a rule once against metadata that lives in several catalogs, so 
every engine reaching
-  those objects through Gravitino sees the same rule
-- Feeding an external enforcement or scheduling system that reads policies 
from Gravitino rather
-  than keeping its own copy of what applies where
+Policies come in two kinds. Built-in policy types have rules and consumers 
defined by Gravitino.
+Custom policies carry rules that your own system interprets. For example, the 
table maintenance
+service consumes the built-in Iceberg compaction policy.
 
 ## Quick Start
 
-**1. Create the policy.** Policies are created from the policy list in the UI, 
which creates custom
-policies. A policy needs a name, the object types it supports, and its rules. 
Built-in policies are
-created over REST.
+1. [Create a policy](./manage-policies-in-gravitino.md#create-a-policy) and
+   [create a tag](./manage-tags-in-gravitino.md#create-a-tag) in the same 
metalake.
+2. [Associate the policy with the 
tag](./manage-policies-in-gravitino.md#associate-a-policy-with-a-tag).
+   Choose `ALL_VALUES` to match tag presence or `TAG_VALUE` to match when an 
assignment contains
+   one specified value.
+3. [Assign the tag](./manage-tags-in-gravitino.md#object-operations) to an 
object or its ancestor.
+4. [List the object's 
policies](./manage-policies-in-gravitino.md#list-policies-on-an-object) to
+   confirm the result.
 
-**2. Attach it to an object.** Open the catalog, schema, table, fileset, 
topic, model, view, or function you want to
-govern and add the policy from its policy control. Only policies that already 
exist in the metalake
-are offered.
-
-**3. See where the policy is attached.** Selecting a policy name in the policy 
list shows the
-objects it is attached to directly.
+For example, associate `retention_30d` with `data_domain` using
+`TAG_VALUE("finance")`. A table with an effective `data_domain=finance` 
assignment receives the
+policy. A table with only `data_domain=risk` does not.
 
 ## The Policy Model
 
-### Policy Types
+### Policy Types and Content
 
-| Type                        | Rules                                | 
Consumed by               |
+| Type                        | Rules                                | 
Consumer                  |
 
|-----------------------------|--------------------------------------|---------------------------|
 | `system_iceberg_compaction` | Compaction thresholds and scheduling | Table 
maintenance service |
-| `custom`                    | A free-form map you define           | A 
system you provide      |
-
-A built-in type has a name beginning with `system_` and a content shape 
Gravitino defines. The
-compaction policy is documented in [Iceberg compaction 
policy](./iceberg-compaction-policy.md), and
-the service that acts on it in
-[Table maintenance service](./table-maintenance-service/optimizer.md).
-
-A custom policy has type `custom`, and Gravitino makes no attempt to interpret 
what is inside
-`customRules`. The rules are stored, inherited down the hierarchy, and 
returned to any client that
-asks.
-
-The UI creates custom policies only. A built-in policy is created over REST 
with its own content
-shape.
-
-### What Can Carry a Policy
-
-A metadata object is identified by a type and a name, with each level below 
the catalog separated by
-a dot. Eight object types can carry a policy.
-
-| Object type | Name form                                     |
-|-------------|-----------------------------------------------|
-| `CATALOG`   | `{catalog_name}`                              |
-| `SCHEMA`    | `{catalog_name}.{schema_name}`                |
-| `TABLE`     | `{catalog_name}.{schema_name}.{table_name}`   |
-| `FILESET`   | `{catalog_name}.{schema_name}.{fileset_name}` |
-| `TOPIC`     | `{catalog_name}.{schema_name}.{topic_name}`   |
-| `MODEL`     | `{catalog_name}.{schema_name}.{model_name}`   |
-| `VIEW`      | `{catalog_name}.{schema_name}.{view_name}`    |
-| `FUNCTION`  | `{catalog_name}.{schema_name}.{function_name}`|
-
-Columns cannot carry a policy, which is narrower than
-[tags](./tags.md). A metalake cannot carry one either, so to reach every object
-in a catalog, attach the policy to the catalog.
-
-Each policy also declares its own `supportedObjectTypes`, which narrows the 
list further for that
-policy.
-
-### Content
-
-Policy content has three parts: the `supportedObjectTypes` list, the rules, 
and properties.
-
-`supportedObjectTypes` is fixed when the policy is created and cannot be 
changed afterward, so a
-policy meant for tables only stays that way for its lifetime.
-
-The rules are what a consumer evaluates. For a custom policy they live under 
`customRules` as a map
-you define, where the name is yours and the value is any JSON value.
-
-```json
-"customRules": {
-  "retentionDays": 30,
-  "maxTableSizeGb": 500,
-  "requiresApproval": true
-}
-```
-
-Gravitino does not interpret those names or values. Whatever consumes the 
policy decides what
-`retentionDays` means and what to do about it.
-
-A built-in policy has a rule set Gravitino defines, and the service that 
consumes it documents how
-those rules are applied. The compaction policy carries `minDataFileMse`, 
`minDeleteFileNumber`,
-`dataFileMseWeight`, `deleteFileNumberWeight`, `max-partition-num`, and a 
trigger and score
-expression, plus any `job.options.` entries passed through to the job. Those 
names and their
-meanings are covered in [Iceberg compaction 
policy](./iceberg-compaction-policy.md).
-
-Properties describe the policy itself rather than the behavior it asks for. 
Rules change as you
-adjust thresholds, and properties stay stable. The compaction policy uses 
properties for its
-strategy type and job template name, which tell the table maintenance service 
what to run, and those
-are set by Gravitino rather than by you. For a custom policy, properties are 
yours, and suit facts
-such as which team owns the policy, which system consumes it, or which version 
of a rule set it
-represents. Anything evaluated against an object belongs in rules instead.
-
-Properties sit on the policy rather than on an attachment, so every object 
carrying the policy sees
-the same values.
-
-### The Enabled Flag
-
-The `enabled` flag marks a policy as active or inactive for readers. Gravitino 
does not act on it,
-so disabling a policy does not detach it or change what a consumer receives. 
Treat it as a signal to
-whoever reads the policy, useful for holding a policy through review without 
deleting it.
-
-### Inheritance
-
-An object shows the policies attached to it plus the policies attached to each 
of its ancestors, so
-a policy on a catalog applies to every schema, table, fileset, topic, model, 
view, and function beneath it. For
-catalogs that support multi-level schemas, the intermediate schemas are 
ancestors too.
-
-Each policy appears once, whether it reaches the object through one ancestor 
or several. A policy
-attached directly to the object counts as direct even when an ancestor carries 
it too.
-
-Direct and inherited attachments are distinguishable. In the UI an inherited 
policy is marked with a
-lock icon. Over REST, a policy listing requested with `details=true` carries 
an `inherited` field on
-each policy, which a plain listing of names does not.
-
-A policy that reaches an object only by inheritance cannot be removed there. 
Detach it from the
-ancestor that carries it, which affects every other object beneath that 
ancestor as well.
-
-Inheritance is resolved when the object is read rather than stored on the 
object, so attaching a
-policy to a catalog takes effect immediately for tables created afterward.
-
-## Working With Policies in the UI
-
-### Managing the Policy Set
-
-The policy list holds every policy in the metalake and can be searched. A 
policy can be renamed, its
-comment and rules edited, and its enabled flag switched from there. Policies 
created over REST,
-including built-in ones, appear in the list alongside the rest.
-
-Deleting a policy removes it from every object it was attached to, with no 
warning about how many
-objects that affects and no way to recover the attachments.
-
-### Attaching and Detaching
-
-Policies attach from the object rather than from the policy, so open the 
object and use the policy
-control there. Inherited policies carry no remove control. Detaching removes 
the direct attachment
-only, so an object still shows a policy it inherits from an ancestor.
-
-### Finding Where a Policy Is Used
-
-Selecting a policy name opens a view listing the objects the policy is 
attached to directly.
-Inherited reach is not included, so a policy attached to one catalog lists 
that catalog rather than
-the tables under it.
-
-## Permissions
-
-Policy permissions are held on the policy, and apply in addition to 
permissions on the objects being
-governed.
-
-| Privilege       | Grantable on                 | What it allows              
                   |
-|-----------------|------------------------------|------------------------------------------------|
-| `CREATE_POLICY` | Metalake                     | Creating policies in the 
metalake              |
-| `APPLY_POLICY`  | Metalake, or a single policy | Reading a policy and 
attaching or detaching it |
-
-Altering and deleting a policy are reserved for the metalake owner and the 
policy owner. Attaching a
-policy also requires access to the object being governed. Policy listings show 
only the policies
-that user is allowed to read.
-
-## Using the API
-
-Policies can be created, attached, and read over REST and through the Java 
client. Endpoints, payload
-shapes, and worked examples are in [Manage 
Policies](./manage-policies-in-gravitino.md).
+| `custom`                    | A free-form map that you define      | A 
system that you provide |
+
+A custom policy's rules live in `customRules`. Gravitino stores them and 
returns them to clients;
+it does not interpret their names or values. Built-in types have a defined 
content shape. See
+[Iceberg compaction policy](./iceberg-compaction-policy.md) for the compaction 
rules and
+[Table maintenance service](./table-maintenance-service/optimizer.md) for a 
worked example.
+
+Policy content also has `properties` and `supportedObjectTypes`. Properties 
describe the policy
+itself, such as its owner or consumer. `supportedObjectTypes` is required when 
creating a custom
+policy. A custom policy content update replaces the whole content and can 
change this field; for a
+built-in policy, the field cannot be changed after creation. Object policy 
lookup does not filter
+by this field; each consumer decides whether a policy type applies to the 
object it is processing.
+
+### Policy-to-Tag Associations
+
+Each association connects one policy to one tag and stores a selector. The 
selector determines
+whether that association contributes the policy to an object's lookup result.
+
+| Selector     | When it matches                                               
           |
+|--------------|--------------------------------------------------------------------------|
+| `ALL_VALUES` | The effective tag is present, including an assignment without 
a value.   |
+| `TAG_VALUE`  | One of the effective tag assignment values equals the 
specified value.   |
+
+A policy may be associated with multiple tags. Association listings show those 
direct relations
+and their selectors, even if no object currently matches them. An object 
policy lookup returns each
+matching policy once.
+
+A selector cannot be changed in place. Remove the policy-to-tag association 
and add it again with
+the new selector. Removing an association leaves the policy, tag, and tag 
assignments intact.
+Deleting a policy removes its associations.
+
+Object policy lookup covers `CATALOG`, `SCHEMA`, `TABLE`, `VIEW`, `COLUMN`, 
`FILESET`, `TOPIC`,
+`MODEL`, `MODEL_VERSION`, and `FUNCTION`. A model version cannot carry a tag 
directly, but it can
+inherit tags from its model and higher ancestors, so their policies appear in 
model version lookups.
+This also includes columns, which did not support direct policy associations.
+A tag assigned to a catalog or schema can therefore make its policies appear 
in descendant column
+lookups. Because lookup does not filter by `supportedObjectTypes`, a policy 
whose content lists
+only `TABLE` can still appear in a column lookup; consumers must enforce the 
intended scope.
+
+### Effective Tags and Inheritance
+
+An object receives tags assigned directly to it and tags inherited from its 
metadata object
+ancestors. Resolution starts at the object and walks upward, so the nearest 
assignment of a tag
+name wins, including its assignment values. A direct assignment therefore 
overrides every
+ancestor, and a schema assignment overrides the same tag assigned on its 
catalog for the schema's
+descendants. Tag names themselves are flat; tags do not inherit from other 
tags.
+
+For example, a catalog with `data_domain=finance` gives its tables that 
effective assignment.
+A table assigned `data_domain=risk` instead uses `risk`, so a policy 
associated with
+`TAG_VALUE("finance")` no longer matches the table. `ALL_VALUES` still matches 
because the
+tag is present.
+
+Object policy lookup is read-only. To change its result, update the policy or 
its enabled state,
+change a policy-to-tag association, or change a tag assignment on the object 
or an ancestor.
+With `details=true`, the lookup includes an `inherited` field. It is `true` 
when the policy
+matches only through an inherited tag.
+
+### Enabled State
+
+Disabling a policy preserves the policy and its tag associations, but excludes 
it from object
+policy lookup. Policy and association listings still show it. Enabling it 
makes matching object
+lookups include it again.
+
+## Managing Policies
+
+The UI supports policy lifecycle operations such as creating custom policies, 
editing their
+content, and changing their enabled state. Use the REST API or Java client to 
manage
+policy-to-tag associations and to read the resulting object policies. The
+[Manage Policies](./manage-policies-in-gravitino.md) guide has requests and 
examples.
+For existing direct object policy associations, see
+[Migration Guide](./migration-guide.md).
+
+To add or remove an association, a user must own the metalake or have the 
required access to both
+the tag and the policy (`APPLY_TAG` and `APPLY_POLICY`, or ownership of each). 
Object policy
+reads also respect the caller's access to the returned policies. `VIEW_TAG` 
and `VIEW_POLICY`
+grant read-only access to tags and policies; `APPLY_TAG` and `APPLY_POLICY` 
also allow reads.
diff --git a/docs/security/access-control.md b/docs/security/access-control.md
index deb942227f..99f23ac2df 100755
--- a/docs/security/access-control.md
+++ b/docs/security/access-control.md
@@ -224,9 +224,11 @@ they will be removed in a future release. Use the current 
names in new roles.
 | `CREATE_ROLE`           | Metalake                                           
                     | Create roles                                       |
 | `MANAGE_GRANTS`         | Metalake, Catalog, Schema, Table, View, Topic, 
Fileset, Model, Function | Grant and revoke privileges on any object in scope |
 | `CREATE_TAG`            | Metalake                                           
                     | Create tags                                        |
+| `VIEW_TAG`              | Metalake, Tag                                      
                     | Read tag metadata                                  |
 | `APPLY_TAG`             | Metalake, Tag                                      
                     | Attach tags to metadata objects                    |
 | `CREATE_POLICY`         | Metalake                                           
                     | Create policies                                    |
-| `APPLY_POLICY`          | Metalake, Policy                                   
                     | Attach policies to metadata objects                |
+| `VIEW_POLICY`           | Metalake, Policy                                   
                     | Read policy metadata                               |
+| `APPLY_POLICY`          | Metalake, Policy                                   
                     | Associate policies with tags                       |
 | `VIEW_SECRET_PROVIDERS` | Metalake                                           
                     | List configured secrets providers                  |
 | `REGISTER_JOB_TEMPLATE` | Metalake                                           
                     | Register job templates                             |
 | `USE_JOB_TEMPLATE`      | Metalake, JobTemplate                              
                     | Run jobs from a job template                       |
@@ -238,12 +240,17 @@ object and its descendants.
 
 `APPLY_TAG`, `APPLY_POLICY`, and `USE_JOB_TEMPLATE` scope differently from 
every other privilege on
 this page. The object they bind to is the instrument the holder may use, not 
the object the operation
-acts on. Granting `APPLY_POLICY` on the policy `pii_masking` lets the holder 
attach that one policy
-and no other, while granting it on the metalake lets them attach any policy in 
the metalake.
+acts on. Granting `APPLY_POLICY` on the policy `pii_masking` lets the holder 
associate that
+policy with tags, provided they also have `APPLY_TAG` on each tag. Granting it 
on the metalake
+covers any policy in that metalake.
 
-Attaching a tag or a policy is checked twice: the holder needs `APPLY_TAG` or 
`APPLY_POLICY` for the
-tag or policy in question, and separately needs access to the metadata object 
being tagged. A user
-cannot tag an object they could not otherwise reach.
+Assigning a tag to a metadata object requires `APPLY_TAG` on the tag and 
access to the object.
+Associating a policy with a tag requires access to both: `APPLY_POLICY` on the 
policy and
+`APPLY_TAG` on the tag. Ownership can satisfy either check.
+
+Reading a tag requires `VIEW_TAG` or `APPLY_TAG`; reading a policy requires 
`VIEW_POLICY` or
+`APPLY_POLICY`. The view privileges do not allow tag assignment or 
policy-to-tag association.
+List results include only tags and policies the caller can read.
 
 ### Required Privileges
 
@@ -297,17 +304,17 @@ owner-only; it does not accept a target schema.
 
 #### Metalake Objects
 
-| Object           | Create                  | Read                            
       | Alter or delete | Use                                             |
-|------------------|-------------------------|----------------------------------------|-----------------|-------------------------------------------------|
-| Metalake         | Service administrator   | Membership                      
       | Owner           |                                                 |
-| User             | `MANAGE_USERS`          | `MANAGE_USERS`, or the user 
themselves | `MANAGE_USERS`  |                                                 |
-| Group            | `MANAGE_GROUPS`         | `MANAGE_GROUPS`, or a member    
       | `MANAGE_GROUPS` |                                                 |
-| Role             | `CREATE_ROLE`           | `MANAGE_GRANTS`, or a holder or 
owner  | Owner           | Grant or revoke: `MANAGE_GRANTS`                |
-| Tag              | `CREATE_TAG`            | `APPLY_TAG`                     
       | Owner           | Attach: `APPLY_TAG` and access to the object    |
-| Policy           | `CREATE_POLICY`         | `APPLY_POLICY`                  
       | Owner           | Attach: `APPLY_POLICY` and access to the object |
-| Job template     | `REGISTER_JOB_TEMPLATE` | `USE_JOB_TEMPLATE`              
       | Owner           | Run a job: `RUN_JOB` and `USE_JOB_TEMPLATE`     |
-| Job              |                         | Owner                           
       | Owner           |                                                 |
-| Secret providers |                         | Owner or 
`VIEW_SECRET_PROVIDERS`       |                 |                               
                  |
+| Object           | Create                  | Read                            
       | Alter or delete | Use                                                |
+|------------------|-------------------------|----------------------------------------|-----------------|----------------------------------------------------|
+| Metalake         | Service administrator   | Membership                      
       | Owner           |                                                    |
+| User             | `MANAGE_USERS`          | `MANAGE_USERS`, or the user 
themselves | `MANAGE_USERS`  |                                                  
  |
+| Group            | `MANAGE_GROUPS`         | `MANAGE_GROUPS`, or a member    
       | `MANAGE_GROUPS` |                                                    |
+| Role             | `CREATE_ROLE`           | `MANAGE_GRANTS`, or a holder or 
owner  | Owner           | Grant or revoke: `MANAGE_GRANTS`                   |
+| Tag              | `CREATE_TAG`            | `VIEW_TAG` or `APPLY_TAG`       
       | Owner           | Assign: `APPLY_TAG` and access to the object       |
+| Policy           | `CREATE_POLICY`         | `VIEW_POLICY` or `APPLY_POLICY` 
       | Owner           | Associate with tag: `APPLY_POLICY` and `APPLY_TAG` |
+| Job template     | `REGISTER_JOB_TEMPLATE` | `USE_JOB_TEMPLATE`              
       | Owner           | Run a job: `RUN_JOB` and `USE_JOB_TEMPLATE`        |
+| Job              |                         | Owner                           
       | Owner           |                                                    |
+| Secret providers |                         | Owner or 
`VIEW_SECRET_PROVIDERS`       |                 |                               
                     |
 
 The secrets-provider registry is process-global server configuration; the 
metalake path only scopes
 authorization. Listing providers does not return secret material.
diff --git a/docs/table-maintenance-service/optimizer-configuration.md 
b/docs/table-maintenance-service/optimizer-configuration.md
index f356f97cf7..5ce59539e6 100644
--- a/docs/table-maintenance-service/optimizer-configuration.md
+++ b/docs/table-maintenance-service/optimizer-configuration.md
@@ -125,7 +125,7 @@ spark.hadoop.fs.defaultFS=file:///
 Four things are worth confirming before assuming a configuration problem is a 
code problem.
 
 - `builtin-iceberg-update-stats` and `builtin-iceberg-rewrite-data-files` 
appear in the job template list.
-- The policy is attached to the target table, not merely created.
+- The policy is associated with a tag assigned to the target table or one of 
its ancestors.
 - `submit-strategy-jobs` prints `SUBMIT` lines rather than nothing.
 - The rewrite log shows `Rewritten data files: N` with `N` greater than zero 
for a non-empty table.
 
diff --git a/docs/table-maintenance-service/optimizer.md 
b/docs/table-maintenance-service/optimizer.md
index 774591d55a..193f7f8c9b 100644
--- a/docs/table-maintenance-service/optimizer.md
+++ b/docs/table-maintenance-service/optimizer.md
@@ -11,7 +11,7 @@ license: "This software is licensed under the Apache License 
version 2."
 
 ## Overview
 
-The table maintenance service keeps tables healthy without anyone watching 
them. You attach a policy to a catalog, schema, or table; the service collects 
statistics, evaluates them against that policy, and submits a job when the 
policy says work is needed.
+The table maintenance service keeps tables healthy without anyone watching 
them. Associate a policy with a tag and assign that tag to a table or one of 
its ancestors. The service collects statistics, evaluates them against the 
table's derived policy, and submits a job when the policy says work is needed.
 
 The framework is generic. Metrics collection, policy evaluation, and job 
submission are not tied to any particular table format, and each is a Java 
ServiceLoader extension point. What ships built in is deliberately narrower, 
and in alpha that means Iceberg data file compaction on identity-partitioned 
tables.
 
@@ -41,7 +41,7 @@ Maintenance runs as four steps. Each is a separate command, 
so you can stop afte
 
 There are two ways in, and they differ in where the numbers come from rather 
than in what they do.
 
-The built-in workflow drives everything through the Gravitino server and its 
job templates, using the policy attached to a table to decide what runs. Use it 
for server-side operational runs.
+The built-in workflow drives everything through the Gravitino server and its 
job templates, using the policy derived from a table's effective tags to decide 
what runs. Use it for server-side operational runs.
 
 The local calculator reads a JSONL file you supply and updates statistics and 
metrics directly from it. Use it for testing and batch scripts, where you 
already have the numbers and want to feed them in without the server computing 
them.
 
@@ -72,7 +72,7 @@ for the per-job options.
 
 ## Walkthrough
 
-This takes one Iceberg table through the whole workflow: create it, fill it 
with small files, attach a compaction policy, collect statistics, and let the 
service decide to compact it. It runs against a local Spark and takes about 
fifteen minutes.
+This takes one Iceberg table through the whole workflow: create it, fill it 
with small files, apply a compaction policy through a tag, collect statistics, 
and let the service decide to compact it. It runs against a local Spark and 
takes about fifteen minutes.
 
 Each step ends with a check. If a check fails, stop there, since every step 
depends on the one before it.
 
diff --git a/docs/tables-and-views.md b/docs/tables-and-views.md
index f2f3b44a72..108a4431e0 100644
--- a/docs/tables-and-views.md
+++ b/docs/tables-and-views.md
@@ -184,8 +184,8 @@ Views can carry tags, and appear in listings alongside 
tables.
 Opening a schema lists its tables and views. Selecting a table shows its 
columns with their types,
 its properties, and its tags.
 
-Tags attach from the table row and from individual column rows, which is the 
fastest way to classify
-a specific field rather than a whole table. Policies attach at the table level.
+Assign tags from the table row and from individual column rows to classify a 
whole table or
+a specific field. Object policies are derived from those tag assignments.
 
 ## Permissions
 
diff --git a/docs/topics.md b/docs/topics.md
index 53dfa9f926..185fb644b3 100644
--- a/docs/topics.md
+++ b/docs/topics.md
@@ -57,13 +57,14 @@ Leaving either unset takes the broker's own default, from 
`num.partition` and
 
 ### What Gravitino Stores and What It Does Not
 
-Gravitino stores the topic's place in the hierarchy and anything attached to 
it, including tags,
-policies, and ownership. Message content, offsets, consumer groups, and lag 
stay entirely in the
-cluster.
+Gravitino stores the topic's place in the hierarchy, its tag assignments, and 
ownership. Policies
+are derived from effective tags when they are read. Message content, offsets, 
consumer groups, and
+lag stay entirely in the cluster.
 
 Message schemas are also outside the catalog. Gravitino does not integrate 
with a schema registry,
 so the structure of the messages in a topic is not described here and cannot 
be classified per field
-the way table columns can. Tags and policies attach to the topic as a whole.
+the way table columns can. Tags are assigned to the topic as a whole, and 
matching policies
+are derived from those tags.
 
 ## Working With Topics in the UI
 
diff --git a/docs/webui-v2.md b/docs/webui-v2.md
index 5b82863979..73d4842925 100644
--- a/docs/webui-v2.md
+++ b/docs/webui-v2.md
@@ -125,9 +125,9 @@ Overview for Catalog in the Web V2.
 On the catalogs page, use the catalog type selector at the top-left to switch 
between `relational`, `messaging`, `fileset`, and `model`. The list updates to 
show catalogs of the selected type.
 
 
-#### Tags and Policies Association
+#### Tags and Derived Policies
 
-The catalog list shows basic catalog information along with associated 
**Tags** and **Policies**. Use **Associate Tag** and **Associate Policy** in 
the list to add associations. Click the **X** on a tag to remove it.
+The catalog list shows basic catalog information along with **Tags** and 
policies derived from those tags. Use **Associate Tag** to assign a tag to a 
catalog. To associate a policy with a tag, use the [Manage 
Policies](./manage-policies-in-gravitino.md#policy-to-tag-associations) API. 
Click the **X** on a tag to remove its catalog assignment.
 
 ![catalogs-list](./assets/webui-v2/catalogs-list.png)
 
@@ -313,11 +313,12 @@ Click **Create Policy** to open the create form. Fill in 
the required fields and
 
 ![policies-create](./assets/webui-v2/policies-create.png)
 
-#### Policy Metadata Objects
+To find the tags associated with a policy and inspect their selectors, use the
+[policy-to-tag association 
API](./manage-policies-in-gravitino.md#list-associations).
 
-Click a policy tag to navigate to the **Metadata Objects** page, which lists 
all metadata objects associated with the selected policy.
-
-![policy-metadata-objects](./assets/webui-v2/policy-metadata-objects.png)
+The current **Metadata Objects** view and direct policy controls still call 
the removed direct
+object-policy association APIs and do not work. Use the REST API or Java 
client for policy-to-tag
+associations until these UI controls are updated.
 
 ### Access
 
@@ -367,6 +368,6 @@ Overview for Access Roles in the Web V2.
 
 #### Create Role
 
-Click **Create Role** to open the create form. A role can include multiple 
securable objects. Different securable object types have different available 
privileges. For details, see [Securable 
Objects](security/access-control.md#securable-objects) and [Privilege 
Types](security/access-control.md#privilege-types).
+Click **Create Role** to open the create form. A role can include multiple 
securable objects. Different securable object types have different available 
privileges. For details, see [Access control](security/access-control.md).
 
 ![roles-create](./assets/webui-v2/roles-create.png)

Reply via email to