How to Resolve Conflicts During kcmd Push When Metadata Changed Remotely

When kcmd push detects that remote metadata has changed, it aborts with a conflict error and requires you to run kcmd pull to reconcile differences, though you can override this with the --force flag.

The kcmd CLI (Metadata-as-Code command line tool) in the GoogleCloudPlatform/knowledge-catalog repository synchronizes local YAML snapshot files with Google Knowledge Catalog. When multiple users edit metadata simultaneously, resolving conflicts during kcmd push when metadata changed remotely requires understanding the tool's checksum-based validation system to prevent accidental overwrites.

How kcmd Detects Push Conflicts

The conflict detection in kcmd follows a fast-fail strategy designed to prevent data loss. Before modifying any remote entry, the tool validates the current state against your local snapshot to ensure the remote version has not diverged since your last pull.

Checksum Validation Against .catalog.state

Each entry and aspect tracks a cryptographic checksum stored in the hidden state file .catalog.state located in your snapshot root. According to the specification in toolbox/mdcode/docs/spec.md, this checksum represents the state of the metadata at the time of your last successful pull.

Before sending an update, kcmd looks up the current remote entry using lookupEntry() and compares your stored checksum against the remote version's checksum. If they differ, the push operation aborts immediately with a clear error message instructing you to run kcmd pull first.

The Fast-Fail Mechanism in CatalogSync.push()

The conflict-handling logic resides in CatalogSync.push() within toolbox/mdcode/src/libts/sync.ts (lines 82-115). The implementation performs the following validation:

const exist = await this._catalog.lookupEntry(project, location, entry.name);
if (exist.status != 200 || !exist.result) { … }   // create new entry
// … build updateMask …
const res = await this._catalog.modifyEntry(project, location, entry, updateMask, aspectKeys);
if (res.status !== 200) {
  return { success: false, details: `Failed to update entry ${name}: ${res.message || res.status}` };
}

If modifyEntry returns a non-200 status—indicating the remote entry was modified since your last pull—the push fails and surfaces the conflict error to the user.

The Conflict Resolution Workflow

When kcmd detects a checksum mismatch, you must reconcile the differences before pushing again. Follow this standard workflow:

  1. Run kcmd pull to merge remote modifications into your local snapshot files.
  2. Review and edit the merged YAML files (e.g., tables/my_dataset.my_table.yaml) to resolve any semantic conflicts.
  3. Execute kcmd push again, which will now succeed because your local checksum matches the remote state.

Example Workflow


# 1️⃣ Pull the latest remote state into your local snapshot

kcmd pull

# 2️⃣ Edit a YAML file (e.g. tables/my_dataset.my_table.yaml) locally

# 3️⃣ Attempt to push changes

kcmd push

# → If the entry was modified remotely, you’ll see:

#   “Failed to update entry …: conflict detected – run `kcmd pull` first”

# 4️⃣ Resolve the conflict:

kcmd pull          # merges remote changes into the local file

# (edit the file again if needed)

kcmd push          # now succeeds

# 5️⃣ Force an update, ignoring a detected conflict

kcmd push --force

Force Pushing and Dry Run Options

The CLI provides flags to either override conflict detection or preview changes before applying them.

Overriding Conflicts with --force

The --force flag, defined in toolbox/mdcode/src/tool/commands.ts, allows you to apply local changes regardless of remote modifications. This bypasses the checksum validation in CatalogSync.push():

kcmd push --force

Use this option with caution, as it will overwrite the remote metadata with your local version, potentially discarding changes made by other users.

Previewing Changes with --dry-run

The --dry-run flag executes the entire validation logic—including checksum comparison and conflict detection—but stops short of calling modifyEntry() to apply changes. As documented in toolbox/mdcode/docs/plan.md, this mode allows you to preview which entries would be affected:

kcmd push --dry-run

This simulates the push operation and reports which entries would be created, updated, or rejected due to conflicts, allowing you to verify your changes safely before risking a conflict error.

Key Implementation Files

The conflict resolution system spans several critical files in the repository:

Summary

  • Conflict detection relies on checksum comparison between your local .catalog.state file and the remote entry state via lookupEntry().
  • Fast-fail behavior: When checksums mismatch, kcmd push aborts with an error instructing you to run kcmd pull.
  • Resolution workflow: Always pull remote changes, resolve conflicts locally, then push again.
  • Override options: Use --force to bypass conflicts or --dry-run to preview changes without modifying the catalog.
  • Core implementation: The logic resides in CatalogSync.push() at lines 82-115 of toolbox/mdcode/src/libts/sync.ts.

Frequently Asked Questions

What triggers a conflict error during kcmd push?

A conflict occurs when the checksum stored in your local .catalog.state file does not match the current checksum of the remote entry in Google Knowledge Catalog. This indicates that someone else modified the metadata after your last pull, causing the modifyEntry() call in toolbox/mdcode/src/libts/sync.ts to return a non-200 status and abort the push operation.

Can I push local changes without pulling first?

Yes, but only by using the --force flag. This overrides the checksum validation in CatalogSync.push() and allows modifyEntry() to proceed regardless of remote changes. However, forcing a push will overwrite the remote metadata with your local version, potentially losing changes made by other users.

What is the .catalog.state file used for?

The .catalog.state file is a hidden metadata file generated in your snapshot root that stores cryptographic checksums for each entry and aspect. It represents the "known good" state of the remote catalog at the time of your last successful pull, enabling kcmd to detect when remote changes have occurred by comparing these stored values against current remote checksums retrieved via lookupEntry().

How can I preview what kcmd push will do before running it?

Use the --dry-run flag when executing kcmd push. This mode runs through the entire validation logic—including checksum comparison and conflict detection—but stops before actually calling modifyEntry() to apply changes. It reports which entries would be created, updated, or rejected due to conflicts, allowing you to verify your changes safely without modifying the remote catalog.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →