Skip to content

feat(tools): add table manifest command - #7970

Open
bugwz wants to merge 4 commits into
lance-format:mainfrom
bugwz:lance-tools-for-manifest
Open

bugwz wants to merge 4 commits into
lance-format:mainfrom
bugwz:lance-tools-for-manifest

Conversation

@bugwz

@bugwz bugwz commented Jul 24, 2026 •

Copy link
Copy Markdown

feat(tools): add table manifest command

Which issue does this PR close?

None.

What changes are included in this PR?

This PR adds a lance-tools table manifest command for inspecting Lance table manifest files from local or object-store paths.

Included changes:

  • Add the table manifest --source <manifest> CLI entry point to lance-tools.
  • Render manifest-level details including version, branch, writer version, timestamp, feature flags, row id state, schema, fragments, base paths, config, table metadata, index metadata, and transaction metadata.
  • Load optional manifest sections such as index and transaction sections when present, while reporting section read errors in the rendered output.
  • Expose read_manifest_proto from lance-table so tools can read the raw protobuf manifest before conversion to the semantic Manifest type.
  • Document the new lance-tools table manifest command in the tool README.
  • Add test coverage that parses checked-in test_data manifests and verifies the rendered output contains the expected top-level sections.

Are there any user-facing changes?

Yes. Users can now inspect a table manifest with:

lance-tools table manifest --source path/to/table/_versions/1.manifest

This is an additive CLI feature and does not change existing commands or public dataset behavior.

Testing

  • git diff --cached --check

Targeted Rust tests were not run locally for this temporary PR description.

@github-actions github-actions Bot added A-deps Dependency updates enhancement New feature or request labels Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds raw manifest protobuf reading and separates semantic conversion. Extends lance-tools with a table manifest command that loads manifest sections and renders metadata, fragments, indices, transactions, and related details.

Changes

Manifest inspection

Layer / File(s) Summary
Manifest protobuf reader
rust/lance-table/src/io/manifest.rs
Separates byte retrieval and validation from protobuf decoding, exposes read_manifest_proto, and converts decoded protobufs through Manifest::try_from.
Manifest source loading
rust/lance-tools/src/manifest.rs
Infers dataset bases, loads manifests from object storage, and reads inline or external index and transaction sections.
Manifest report rendering
rust/lance-tools/src/manifest.rs
Renders manifest fields, summaries, schema, fragments, files, indices, transactions, maps, timestamps, and feature flags.
CLI integration and validation
rust/lance-tools/src/cli.rs, rust/lance-tools/src/lib.rs, rust/lance-tools/Cargo.toml, rust/lance-tools/README.md, rust/lance-tools/src/manifest.rs
Adds table manifest --source, wires command dispatch, adds dependencies and documentation, and tests manifest loading and rendering across repository test data.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant LanceToolsArgs
  participant LanceToolManifest
  participant ObjectStore
  participant Writer
  User->>LanceToolsArgs: run table manifest --source
  LanceToolsArgs->>LanceToolManifest: show_table_manifest
  LanceToolManifest->>ObjectStore: read manifest and sections
  ObjectStore-->>LanceToolManifest: return manifest data
  LanceToolManifest->>Writer: write formatted report
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a table manifest command to lance-tools.
Description check ✅ Passed The description matches the changeset and clearly explains the new manifest command, docs, and tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bugwz
bugwz force-pushed the lance-tools-for-manifest branch from b50c8d0 to a20868d Compare July 24, 2026 10:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
rust/lance-tools/src/manifest.rs (1)

34-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tighten LanceToolManifest visibility to pub(crate).

LanceToolManifest is only used internally (via the pub(crate) show_table_manifest and tests); its fields are private and it is not part of an intended public API. Since lib.rs re-exports this module (pub mod manifest;), leaving it pub leaks an internal type onto the crate's public surface.

As per coding guidelines: "Prefer pub(crate) over pub for crate-internal items, and use pub use re-exports for the actual public API surface."

♻️ Proposed change
-pub struct LanceToolManifest {
+pub(crate) struct LanceToolManifest {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-tools/src/manifest.rs` around lines 34 - 41, Change the visibility
of the LanceToolManifest struct from pub to pub(crate), keeping its fields and
implementation unchanged so the internally used type is no longer exposed as
part of the public API.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@rust/lance-tools/src/manifest.rs`:
- Around line 34-41: Change the visibility of the LanceToolManifest struct from
pub to pub(crate), keeping its fields and implementation unchanged so the
internally used type is no longer exposed as part of the public API.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: d8f30995-630a-4605-9599-87d5700ab8d4

📥 Commits

Reviewing files that changed from the base of the PR and between b50c8d0 and a20868d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • rust/lance-table/src/io/manifest.rs
  • rust/lance-tools/Cargo.toml
  • rust/lance-tools/README.md
  • rust/lance-tools/src/cli.rs
  • rust/lance-tools/src/lib.rs
  • rust/lance-tools/src/manifest.rs

@bugwz
bugwz force-pushed the lance-tools-for-manifest branch from a20868d to fecb66a Compare July 25, 2026 04:38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
rust/lance-tools/src/manifest.rs-608-618 (1)

608-618: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

uuid_bytes prints a length, not bytes.

In the parse-failure branch — the one path an operator uses to diagnose a corrupt index entry — this emits the byte count (e.g. 16), which conveys nothing about the malformed uuid. Rename the key to reflect a count, and consider hex-dumping the raw bytes instead.

♻️ Proposed change
             write_kv(
                 f,
                 8,
-                "uuid_bytes",
+                "uuid_byte_len",
                 &index
                     .raw
                     .uuid
                     .as_ref()
                     .map(|uuid| uuid.uuid.len().to_string())
                     .unwrap_or_else(|| "absent".to_string()),
             )?;

As per coding guidelines: "for numeric parameters state whether the value is an id, count, index, etc." and "Use precise domain terminology."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-tools/src/manifest.rs` around lines 608 - 618, Update the write_kv
call in the parse-failure manifest formatting path to rename the uuid_bytes key
to a precise byte-count name such as uuid_byte_count, matching the existing
length value; if retaining diagnostic detail, add a separate hex representation
of the raw UUID bytes rather than labeling the count as bytes.

Source: Coding guidelines

rust/lance-tools/src/manifest.rs-855-855 (1)

855-855: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Render update_mode as the enum variant name.

update.update_mode is a prost open enum field (i32), so .to_string() writes 0/1, which obscures the update mode in manifest dumps. Use the generated accessor/enumerator and only fall back to the numeric value for unknown discriminators.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-tools/src/manifest.rs` at line 855, Update the manifest rendering
in the write_kv call for update.update_mode to resolve the prost enum
discriminator through its generated accessor or enumerator, emitting the enum
variant name for known values and retaining the numeric discriminator only for
unknown values.

Source: Coding guidelines

🧹 Nitpick comments (5)
rust/lance-tools/src/manifest.rs (5)

111-133: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the recognized manifest layouts and the None case.

The inference rules (<base>/_latest.manifest and <base>/_versions/*.manifest) and the meaning of None (base not inferable → external transaction files cannot be resolved, per Lines 173-178) are non-obvious to a reader. Also worth noting that a root-level manifest yields None because path_from_parts rejects an empty prefix.

📝 Proposed doc comments
+/// Infers the dataset base directory from a manifest path.
+///
+/// Recognizes two layouts: `<base>/_latest.manifest` and `<base>/_versions/<n>.manifest`.
+/// Returns `None` when the path matches neither layout, or when the inferred base would be
+/// empty (a manifest sitting at the store root). Callers must then report external
+/// transaction files as unresolvable rather than guessing a base.
 fn infer_dataset_base_from_manifest_path(path: &Path) -> Option<Path> {

As per coding guidelines: "Add doc comments to magic constants, thresholds, and non-obvious transformation functions" and "Comment fallback or guard code paths with when they trigger and why they exist."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-tools/src/manifest.rs` around lines 111 - 133, Add doc comments
for infer_dataset_base_from_manifest_path and path_from_parts documenting the
recognized <base>/_latest.manifest and <base>/_versions/*.manifest layouts, that
None means the dataset base cannot be inferred and external transaction files
cannot be resolved, and that root-level manifests return None because
path_from_parts rejects an empty prefix.

Source: Coding guidelines


73-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid cloning the entire manifest protobuf for two presence checks.

raw_manifest is retained solely to test timestamp.is_some() (Line 239) and data_format.is_some() (Line 249). The clone at Line 75 duplicates the whole proto — schema fields plus every fragment — doubling peak memory on large tables. Capture the two booleans instead and drop the field.

♻️ Proposed refactor
-        let raw_manifest =
-            read_manifest_proto(&source.object_store, &source.manifest_path, None).await?;
-        let manifest = Manifest::try_from(raw_manifest.clone())?;
+        let raw_manifest =
+            read_manifest_proto(&source.object_store, &source.manifest_path, None).await?;
+        let has_timestamp = raw_manifest.timestamp.is_some();
+        let has_data_format = raw_manifest.data_format.is_some();
+        let manifest = Manifest::try_from(raw_manifest)?;

Then replace the raw_manifest: pb::Manifest field with has_timestamp: bool / has_data_format: bool and update Lines 239 and 249.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-tools/src/manifest.rs` around lines 73 - 97, In the manifest
construction flow, remove the full protobuf clone and replace retained
raw_manifest presence checks with boolean flags captured from the original raw
manifest before conversion. Add has_timestamp and has_data_format fields to the
containing struct, initialize them in this constructor, and update the
timestamp/data-format checks in the relevant methods to use those flags.

1269-1285: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the None inference case and the error-rendering branches.

Both existing unit tests cover only successful inference. Uncovered: paths that match neither layout (a bare foo.manifest, a *.manifest not under _versions, a root-level _latest.manifest), and the indices_error: / transaction_error: output branches at Lines 548 and 683, which no test reaches.

💚 Proposed additional test
#[test]
fn test_infer_dataset_base_returns_none_for_unrecognized_paths() {
    for path in [
        "foo.manifest",
        "dataset/other/1.manifest",
        "dataset/_versions/1.txt",
        "_latest.manifest",
    ] {
        assert!(
            infer_dataset_base_from_manifest_path(&Path::from(path)).is_none(),
            "expected no dataset base for {path}"
        );
    }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-tools/src/manifest.rs` around lines 1269 - 1285, Add a unit test
alongside test_infer_dataset_base_from_version_manifest_path and
test_infer_dataset_base_from_latest_manifest_path covering unrecognized paths,
including bare, wrong-directory, wrong-extension, and root-level latest
manifests, and assert infer_dataset_base_from_manifest_path returns None. Also
add test coverage that exercises the indices_error and transaction_error
rendering branches at their respective error-formatting sites.

179-195: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

External transaction failures lose the attempted path.

Lines 190-194 surface bare object_store/prost messages, so a missing or corrupt transaction file renders as e.g. NotFound with no indication of which _transactions/<file> was tried. Include path in each message; extracting a small helper also flattens the four-level match pyramid.

♻️ Proposed refactor
-        match object_store.inner.get(&path).await {
-            Ok(result) => match result.bytes().await {
-                Ok(data) => match pb::Transaction::decode(data) {
-                    Ok(transaction) => OptionalSection::Loaded(TransactionView {
-                        source: TransactionSource::External { path },
-                        transaction,
-                    }),
-                    Err(e) => OptionalSection::Error(e.to_string()),
-                },
-                Err(e) => OptionalSection::Error(e.to_string()),
-            },
-            Err(e) => OptionalSection::Error(e.to_string()),
-        }
+        match read_external_transaction(object_store, &path).await {
+            Ok(transaction) => OptionalSection::Loaded(TransactionView {
+                source: TransactionSource::External { path },
+                transaction,
+            }),
+            Err(e) => OptionalSection::Error(format!(
+                "failed to read transaction file {}: {}",
+                path, e
+            )),
+        }

with:

async fn read_external_transaction(
    object_store: &ObjectStore,
    path: &Path,
) -> Result<pb::Transaction> {
    let data = object_store.inner.get(path).await?.bytes().await?;
    Ok(pb::Transaction::decode(data)?)
}

As per coding guidelines: "Include full error context, including variable names, values, sizes, and types."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-tools/src/manifest.rs` around lines 179 - 195, Update the external
transaction loading flow around TransactionSource::External to include the
attempted path in every object-store, byte-reading, and protobuf decode error
message. Extract the nested reads and decode into a small
read_external_transaction helper if appropriate, then map its errors at the
caller while preserving OptionalSection::Loaded, OptionalSection::Error, and the
existing path value.

Source: Coding guidelines


1183-1187: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the FLAG_UNKNOWN mask invariant.

FLAG_UNKNOWN - 1 is only the mask of known feature flags because FLAG_UNKNOWN is defined as the first bit after all known flags. Add a comment near this calculation so future flag additions preserve the invariant.

📝 Proposed change
-    let known_mask = FLAG_UNKNOWN - 1;
-    let unknown = flags & !known_mask;
+    // FLAG_UNKNOWN is the first bit above all defined feature flags, so FLAG_UNKNOWN - 1
+    // masks exactly the known bits. Any bit at or above it is reported as unknown.
+    let known_mask = FLAG_UNKNOWN - 1;
+    let unknown = flags & !known_mask;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-tools/src/manifest.rs` around lines 1183 - 1187, Add a concise
comment immediately above the known_mask calculation in the manifest
flag-processing code, documenting that FLAG_UNKNOWN is the first bit after all
known feature flags and that FLAG_UNKNOWN - 1 therefore masks known flags.
Ensure future flag additions preserve this ordering invariant.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/lance-table/src/io/manifest.rs`:
- Around line 30-33: Add synchronized, compiling Rustdoc examples to the public
readers read_manifest_proto (rust/lance-table/src/io/manifest.rs:30-33) and
read_manifest (rust/lance-table/src/io/manifest.rs:120-122), using their current
signatures and documenting links to the relevant protobuf and semantic Manifest
types or methods. Ensure both examples remain valid if the APIs change.
- Around line 35-41: Add direct tests for read_manifest_proto covering normal
round-trip decoding to the expected pb::Manifest and a stale known_size value
that successfully retries and returns the correct protobuf. Extend or add
focused manifest tests alongside test_roundtrip_manifest, reusing existing
object-store and manifest setup.

In `@rust/lance-tools/src/lib.rs`:
- Line 5: Change the manifest module declaration to crate visibility by updating
pub mod manifest to pub(crate) mod manifest; in the crate root. Preserve
internal access from the CLI entrypoint through
crate::manifest::show_table_manifest without exposing lance_tools::manifest
publicly.

In `@rust/lance-tools/src/manifest.rs`:
- Around line 1287-1338: Strengthen test_parse_all_test_data_manifests so it
validates rendered manifest values rather than only the manifest:, schema:, and
fragments: headings. For at least one known checked-in manifest, assert a stable
value such as the version, fragment count, or data-file path, or compare the
rendered output against an approved snapshot while preserving the existing
compatibility checks.
- Around line 34-41: Change the visibility of the crate-internal
LanceToolManifest struct from pub to pub(crate). Keep show_table_manifest as the
crate entry point and leave the struct’s fields and behavior unchanged.
- Around line 1105-1115: Rename format_option_usize and format_optional_usize to
explicitly identify their None sentinels, such as unknown- and absent-specific
names, then update callers at the noted locations so unknown values use the
“unknown” formatter and absent values use the “absent” formatter.

---

Other comments:
In `@rust/lance-tools/src/manifest.rs`:
- Around line 608-618: Update the write_kv call in the parse-failure manifest
formatting path to rename the uuid_bytes key to a precise byte-count name such
as uuid_byte_count, matching the existing length value; if retaining diagnostic
detail, add a separate hex representation of the raw UUID bytes rather than
labeling the count as bytes.
- Line 855: Update the manifest rendering in the write_kv call for
update.update_mode to resolve the prost enum discriminator through its generated
accessor or enumerator, emitting the enum variant name for known values and
retaining the numeric discriminator only for unknown values.

---

Nitpick comments:
In `@rust/lance-tools/src/manifest.rs`:
- Around line 111-133: Add doc comments for
infer_dataset_base_from_manifest_path and path_from_parts documenting the
recognized <base>/_latest.manifest and <base>/_versions/*.manifest layouts, that
None means the dataset base cannot be inferred and external transaction files
cannot be resolved, and that root-level manifests return None because
path_from_parts rejects an empty prefix.
- Around line 73-97: In the manifest construction flow, remove the full protobuf
clone and replace retained raw_manifest presence checks with boolean flags
captured from the original raw manifest before conversion. Add has_timestamp and
has_data_format fields to the containing struct, initialize them in this
constructor, and update the timestamp/data-format checks in the relevant methods
to use those flags.
- Around line 1269-1285: Add a unit test alongside
test_infer_dataset_base_from_version_manifest_path and
test_infer_dataset_base_from_latest_manifest_path covering unrecognized paths,
including bare, wrong-directory, wrong-extension, and root-level latest
manifests, and assert infer_dataset_base_from_manifest_path returns None. Also
add test coverage that exercises the indices_error and transaction_error
rendering branches at their respective error-formatting sites.
- Around line 179-195: Update the external transaction loading flow around
TransactionSource::External to include the attempted path in every object-store,
byte-reading, and protobuf decode error message. Extract the nested reads and
decode into a small read_external_transaction helper if appropriate, then map
its errors at the caller while preserving OptionalSection::Loaded,
OptionalSection::Error, and the existing path value.
- Around line 1183-1187: Add a concise comment immediately above the known_mask
calculation in the manifest flag-processing code, documenting that FLAG_UNKNOWN
is the first bit after all known feature flags and that FLAG_UNKNOWN - 1
therefore masks known flags. Ensure future flag additions preserve this ordering
invariant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 8e392696-b046-4c02-aefc-2dea9f9573bb

📥 Commits

Reviewing files that changed from the base of the PR and between a20868d and fecb66a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • rust/lance-table/src/io/manifest.rs
  • rust/lance-tools/Cargo.toml
  • rust/lance-tools/README.md
  • rust/lance-tools/src/cli.rs
  • rust/lance-tools/src/lib.rs
  • rust/lance-tools/src/manifest.rs

Comment thread rust/lance-table/src/io/manifest.rs
Comment thread rust/lance-table/src/io/manifest.rs
Comment thread rust/lance-tools/src/lib.rs Outdated
Comment thread rust/lance-tools/src/manifest.rs Outdated
Comment thread rust/lance-tools/src/manifest.rs Outdated
Comment thread rust/lance-tools/src/manifest.rs
@bugwz

bugwz commented Aug 5, 2026

Copy link
Copy Markdown
Author

Hi @Xuanwo @westonpace, sorry for the ping. Could you help take a look at this PR when you have a chance?

This adds lance-tools table manifest --source for inspecting table manifests, and exposes read_manifest_proto so the tool can render raw manifest protobuf details before semantic conversion. I also followed up on the review comments with Rustdoc examples, focused tests, visibility cleanup, and manifest rendering fixes.

The targeted checks passed locally:

  • cargo fmt --all
  • git diff --check
  • cargo test -p lance-table io::manifest::test
  • cargo test -p lance-tools manifest::tests
  • cargo clippy -p lance-table -p lance-tools --all-targets -- -D warnings

Thanks!

lance-gatekeeper[bot]

This comment was marked as outdated.

@lance-gatekeeper lance-gatekeeper Bot added the K-changes Latest Gatekeeper recommendation requests changes. label Sep 17, 2026
@lance-gatekeeper lance-gatekeeper Bot removed the K-changes Latest Gatekeeper recommendation requests changes. label Sep 17, 2026

@lance-gatekeeper lance-gatekeeper Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Gate recommendation: approve.

Both transaction renderer branches now match the current compacted_sstables protobuf contract. The command is additive and read-only, preserves the semantic manifest and index read paths, and I found no remaining decision-relevant risk.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Sep 17, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-deps Dependency updates enhancement New feature or request K-approved Latest Gatekeeper recommendation permits acceptance.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant