jerryshao commented on code in PR #13382:
URL: https://github.com/apache/gravitino/pull/13382#discussion_r4068263269


##########
docs/migration-guide.md:
##########
@@ -0,0 +1,102 @@
+---
+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.

Review Comment:
   [Important] The enabled flag changed meaning in this release, and the guide 
states the new behavior without flagging it as a change that needs action.
   
   In 1.3 the flag was documentation-only: the text this PR deletes says so 
explicitly (`docs/policies.md:126` on `main`: "Gravitino does not act on it, so 
disabling a policy does not detach it or change what a consumer receives", and 
the same claim in `manage-policies-in-gravitino.md`). After #13355, 
`ObjectPolicyResolver.resolve` ends with `.filter(PolicyEntity::enabled)` 
(`core/src/main/java/org/apache/gravitino/policy/ObjectPolicyResolver.java:128`),
 so a disabled policy silently disappears from every object lookup.
   
   So a deployment that disabled a policy while still relying on a consumer 
acting on it (TMS is the concrete case) loses that behavior at upgrade, 
independent of the tag migration, and nothing in the checklist tells the 
operator to look. Suggest adding an action bullet to "Policies are selected 
through tags" or step 1 of the checklist: inventory disabled policies before 
upgrading and re-enable the ones consumers still need, because disabling is now 
enforced rather than advisory.
   
   Verified by: reading `ObjectPolicyResolver.java:126-128` on this head and 
`git show origin/main:docs/policies.md` lines 120-130 for the previous 
documented behavior.



##########
docs/policies.md:
##########
@@ -1,189 +1,105 @@
 ---
 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.
-
-**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.
+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 one exact 
assignment 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.
 
-**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.
+| `custom`                    | A free-form map that you define      | A 
system that you provide |
 
-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.
+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.
 
-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.
+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 and cannot be changed later. Object policy lookup does not filter by 
this field; each
+consumer decides whether a policy type applies to the object it is processing.
 
-## Working With Policies in the UI
+### Policy-to-Tag Associations
 
-### Managing the Policy Set
+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.
 
-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.
+| Selector     | When it matches                                               
         |
+|--------------|------------------------------------------------------------------------|
+| `ALL_VALUES` | The effective tag is present, including an assignment without 
a value. |
+| `TAG_VALUE`  | The effective tag has the specified exact assignment value.   
         |

Review Comment:
   [Nit] "the specified exact assignment value" reads as if the assignment 
holds a single value. An assignment can hold several 
(`TagAssignment.ofValues(String...)`, and `migration-guide.md:53` in this PR 
says as much), and the match is a containment check: 
`Arrays.asList(assignment.values()).contains(expectedValue)` 
(`core/src/main/java/org/apache/gravitino/policy/ObjectPolicyResolver.java:138`).
 So `data_domain=[finance, risk]` matches both `TAG_VALUE("finance")` and 
`TAG_VALUE("risk")`.
   
   Suggest "one of the effective tag's assignment values equals the specified 
value" here and in the same table at `manage-policies-in-gravitino.md:228`.
   
   Verified by: reading `ObjectPolicyResolver.matches` and 
`api/src/main/java/org/apache/gravitino/tag/TagAssignment.java`.



##########
docs/manage-policies-in-gravitino.md:
##########
@@ -215,35 +217,145 @@ 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`  | The effective tag has the specified exact assignment 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
 to an object, associate a policy with a tag and then assign or remove that tag 
on the object or one
 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. A direct assignment of `data_domain` on the table overrides 
an inherited
+assignment of the same tag. For example, assigning only `data_domain=risk` to 
the table stops
+`retention_30d` from matching there, even if its catalog has 
`data_domain=finance`.
+
+```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 = ...

Review Comment:
   [Nit] `Table customers = ...` replaces what used to be a runnable snippet 
(`client.loadCatalog("catalog1")`), so the Java tab no longer shows how to get 
to the object. `Table.supportsPolicies()` is a default method on the API 
interface (`api/src/main/java/org/apache/gravitino/rel/Table.java:167`), so a 
real line works, e.g. loading the catalog, `asTableCatalog()`, then 
`loadTable(NameIdentifier.of("schema1", "customers"))`.
   
   Verified by: reading 
`api/src/main/java/org/apache/gravitino/rel/Table.java:159-167` and the REST 
example directly above this tab.



##########
docs/policies.md:
##########
@@ -1,189 +1,105 @@
 ---
 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.
-
-**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.
+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 one exact 
assignment 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.
 
-**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.
+| `custom`                    | A free-form map that you define      | A 
system that you provide |
 
-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.
+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.
 
-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.
+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 and cannot be changed later. Object policy lookup does not filter by 
this field; each
+consumer decides whether a policy type applies to the object it is processing.
 
-## Working With Policies in the UI
+### Policy-to-Tag Associations
 
-### Managing the Policy Set
+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.
 
-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.
+| Selector     | When it matches                                               
         |
+|--------------|------------------------------------------------------------------------|
+| `ALL_VALUES` | The effective tag is present, including an assignment without 
a value. |
+| `TAG_VALUE`  | The effective tag has the specified exact assignment value.   
         |
 
-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.
+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.
 
-### Attaching and Detaching
+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.
 
-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.
+### Effective Tags and Inheritance
 
-### Finding Where a Policy Is Used
+An object receives tags assigned directly to it and tags inherited from its 
metadata object
+ancestors. When both the object and an ancestor assign the same tag name, the 
object's direct
+assignment wins, including its assignment values. Tag names themselves are 
flat; tags do not

Review Comment:
   [Important] Rewriting this page dropped the object-type coverage, and under 
the new model the answer changed rather than became irrelevant.
   
   The old page had "What Can Carry a Policy" with the eight object types and 
the sentence "Columns cannot carry a policy" (`docs/policies.md:80` on `main`). 
Now policies come from effective tags, and tags *are* assignable to columns 
(`docs/tags.md:60`). Nothing in the lookup path narrows that: 
`EffectiveTagResolver.resolve` accepts whatever 
`MetadataObjectUtil.checkMetadataObject` accepts, which includes `COLUMN` 
(`core/src/main/java/org/apache/gravitino/utils/MetadataObjectUtil.java:262`), 
the walk up is generic (`EffectiveTagResolver.java:70-71`), 
`ObjectPolicyResolver` applies no object-type filter, and the REST layer parses 
any type name 
(`server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectPolicyOperations.java:94`).
   
   Two consequences a reader cannot get from this page: `GET 
/objects/column/{catalog}.{schema}.{table}.{column}/policies` now returns 
policies, and a migration tag assigned to a catalog (the recipe in 
`migration-guide.md`) reaches columns too, which direct policy association 
never did. Combined with `supportedObjectTypes` no longer filtering, a policy 
created for `TABLE` only will show up in a column lookup.
   
   Suggest restoring one short paragraph or table in this section stating which 
object types object policy lookup covers, explicitly including columns.
   
   Verified by: reading the three resolver/REST files named above on this head, 
plus `docs/tags.md:54-64` for the tag-assignable types.



##########
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
+Gravitino stores the topic's place in the hierarchy, its tag assignments, 
derived policies,

Review Comment:
   [Nit] "Gravitino stores ... derived policies" says the derived result is 
persisted. It is computed per request: `ObjectPolicyResolver.resolve` 
re-resolves effective tags and re-evaluates selectors on every lookup 
(`core/src/main/java/org/apache/gravitino/policy/ObjectPolicyResolver.java:77-128`),
 which is also what `policies.md` describes. What is stored is the tag 
assignment and the policy-to-tag association.
   
   Suggest "its tag assignments and ownership" here, with policies described as 
derived at read time. `docs/catalogs-and-schemas.md:78` has the same phrasing 
("anything associated with those objects such as tags, derived policies, and 
ownership").
   
   Verified by: reading the resolver on this head; no persisted object-policy 
relation is written anywhere in that path.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to