Repository: riscv-software-src/riscv-unified-db
Pull request #723: fix: display UDB extension dependency information correctly in ISA Explorer
Author: login: james-ball-qualcomm
Created: 2025-05-02T17:33:01Z | Merged: 2025-05-14T13:11:51Z

Fixes #597 and #731 (and a few more bugs found in the process).

This is a work in progress. Issue #730 tracks what needs to happen next to fully fix #597.

Note that I accidentally merged in commits from branch "731 still have 404 errors on artifacts" with PR #739 into this branch. So, you'll commits for both issues #597 and #731 in this PR.

Linked issue #597: ISA Explorer backend missing extension_version functions now
The ExtensionVersion object used to have a transitive_implications and transitive_conflicts methods that are now gone (after the cpp_hart merge into main). What should I use instead? I found implied_by (which I'm not sure is the right thing) and nothing for conflicts.


Here's the relevant code:
```
ext_table = {
    # Array of hashes
    "columns" => [
      {name: "Extension Name", formatter: "link", sorter: "alphanum", headerFilter: true, frozen: true, formatterParams:
        {
        labelField:"Extension Name",
        urlPrefix: "https://risc-v-certification-steering-committee.github.io/riscv-unified-db/manual/html/isa/isa_20240411/exts/"
        }
      },
      {name: "Description", formatter: "textarea", sorter: "alphanum", headerFilter: true},
      {name: "IC", formatter: "textarea", sorter: "alphanum", headerFilter: true},
      {name: "Extensions\nIncluded\n(subsets)", formatter: "textarea", sorter: "alphanum"},
      {name: "Implies\n(and\ntransitives)", formatter: "textarea", sorter: "alphanum"},
      {name: "Incompatible\n(and\ntransitives)", formatter: "textarea", sorter: "alphanum"},
      {name: "Ratified", formatter: "textarea", sorter: "boolean", headerFilter: true},
      {name: "Ratification\nDate", formatter: "textarea", sorter: "alphanum", headerFilter: true},
      sorted_profile_releases.map do |pr|
        {name: "#{pr.name}", formatter: "textarea", sorter: "alphanum", headerFilter: true}
      end
    ].flatten,

    # Will eventually be an array containing arrays.
    "rows" => []
    }

  arch.extensions.sort_by!(&:name).each do |ext|
    row = [
      ext.name,
      ext.long_name,
      ext.compact_priv_type,
      "UDB Missing",
      ext.max_version.transitive_implications.map(&:name),
      ext.max_version.transitive_conflicts.map(&:name),
      ext.ratified,
      if ext.ratified
        if ext.min_ratified_version.ratification_date.nil? || ext.min_ratified_version.ratification_date.empty?
          "UDB MISSING"
        else
          ext.min_ratified_version.ratification_date
        end
      else
        ""
      end
    ]
```
Linked issue comments: user: login: dhower-qc
created_at: 2025-04-17T15:30:52Z
body: I think this is fixed in #584?
author_association: COLLABORATOR

Diff:
diff --git a/arch/ext/Zve32f.yaml b/arch/ext/Zve32f.yaml
new file mode 100644
index 0000000000..a1ecdf9cf5
--- /dev/null
+++ b/arch/ext/Zve32f.yaml
@@ -0,0 +1,14 @@
+# yaml-language-server: $schema=../../schemas/ext_schema.json
+
+$schema: "ext_schema.json#"
+kind: extension
+name: Zve32f
+long_name: Vector Extension for Minimal Single-Precision Embedded Floating-Point
+description: |
+  TBD - See GitHub issue 616
+type: unprivileged
+versions:
+  - version: "1.0.0"
+    state: ratified
+    ratification_date: null
+    requires: F
diff --git a/backends/isa_explorer/isa_explorer.rb b/backends/isa_explorer/isa_explorer.rb
index fadd487d63..d56d58961e 100644
--- a/backends/isa_explorer/isa_explorer.rb
+++ b/backends/isa_explorer/isa_explorer.rb
@@ -39,9 +39,9 @@ def arch2ext_table(arch)
       },
       {name: "Description", formatter: "textarea", sorter: "alphanum", headerFilter: true},
       {name: "IC", formatter: "textarea", sorter: "alphanum", headerFilter: true},
-      {name: "Extensions\nIncluded\n(subsets)", formatter: "textarea", sorter: "alphanum"},
-      {name: "Implied By\n(and\ntransitives)", formatter: "textarea", sorter: "alphanum"},
-      {name: "Incompatible\n(and\ntransitives)", formatter: "textarea", sorter: "alphanum"},
+      {name: "Implied By", formatter: "textarea", sorter: "alphanum"},
+      {name: "Requires", formatter: "textarea", sorter: "alphanum"},
+      {name: "Incompatible", formatter: "textarea", sorter: "alphanum"},
       {name: "Ratified", formatter: "textarea", sorter: "boolean", headerFilter: true},
       {name: "Ratification\nDate", formatter: "textarea", sorter: "alphanum", headerFilter: true},
       sorted_profile_releases.map do |pr|
@@ -55,14 +55,12 @@ def arch2ext_table(arch)
 
   arch.extensions.sort_by!(&:name).each do |ext|
     row = [
-      ext.name,
-      ext.long_name,
-      ext.compact_priv_type,
-      "UDB Missing",
-      # See https://github.com/riscv-software-src/riscv-unified-db/issues/597 for the next 2 columns.
-      ext.max_version.implied_by.map(&:name),
-      # ext.max_version.transitive_conflicts.map(&:name),
-      "UDB MISSING",
+      ext.name,           # Name
+      ext.long_name,      # Description
+      ext.compact_priv_type,  # IC
+      ext.max_version.implied_by.map(&:name),  # Implied By
+      ext.max_version.requirement_condition.empty? ? "" : ext.max_version.requirement_condition.to_logic_tree.to_s, # Requires
+      ext.conflicts_condition.empty? ? "" : ext.conflicts_condition.to_logic_tree.to_s, # Incompatible
       ext.ratified,
       if ext.ratified
         if ext.min_ratified_version.ratification_date.nil? || ext.min_ratified_version.ratification_date.empty?
diff --git a/lib/arch_obj_models/req_expression.rb b/lib/arch_obj_models/req_expression.rb
index d08d4c0b42..1f2dbc06ae 100644
--- a/lib/arch_obj_models/req_expression.rb
+++ b/lib/arch_obj_models/req_expression.rb
@@ -466,32 +466,36 @@ def to_logic_tree(hsh = @hsh, term_idx: [0], expand: true)
         when "oneOf"
           # expand oneOf into AND
           roots = T.let([], T::Array[LogicNode])
-          raise "unexpected" if hsh["oneOf"].size < 2
-
-          hsh["oneOf"].size.times do |k|
-            root =
-              if k.zero?
-                LogicNode.new(TYPES::And, [to_logic_tree(hsh["oneOf"][0], term_idx:, expand:), LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][1], term_idx:, expand:)])])
-              elsif k == 1
-                LogicNode.new(TYPES::And, [LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][0], term_idx:, expand:)]), to_logic_tree(hsh["oneOf"][1], term_idx:, expand:)])
-              else
-                LogicNode.new(TYPES::And, [LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][0], term_idx:, expand:)]), LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][1], term_idx:, expand:)])])
-              end
-            (2...hsh["oneOf"].size).each do |i|
+
+          if hsh["oneOf"].size < 2
+            to_logic_tree(hsh["oneOf"][0], term_idx:, expand:)
+          else
+            hsh["oneOf"].size.times do |k|
               root =
-                if k == i
-                  LogicNode.new(TYPES::And, [root, to_logic_tree(hsh["oneOf"][i], term_idx:, expand:)])
+                if k.zero?
+                  LogicNode.new(TYPES::And, [to_logic_tree(hsh["oneOf"][0], term_idx:, expand:), LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][1], term_idx:, expand:)])])
+                elsif k == 1
+                  LogicNode.new(TYPES::And, [LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][0], term_idx:, expand:)]), to_logic_tree(hsh["oneOf"][1], term_idx:, expand:)])
                 else
-                  LogicNode.new(TYPES::And, [root, LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][i], term_idx:, expand:)])])
+                  LogicNode.new(TYPES::And, [LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][0], term_idx:, expand:)]), LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][1], term_idx:, expand:)])])
                 end
-             end
-            roots << root
-          end
-          root = LogicNode.new(TYPES::Or, [T.must(roots[0]), T.must(roots[1])])
-          (2...roots.size).each do |i|
-            root = LogicNode.new(TYPES::Or, [root, T.must(roots[i])])
+              (2...hsh["oneOf"].size).each do |i|
+                root =
+                  if k == i
+                    LogicNode.new(TYPES::And, [root, to_logic_tree(hsh["oneOf"][i], term_idx:, expand:)])
+                  else
+                    LogicNode.new(TYPES::And, [root, LogicNode.new(TYPES::Not, [to_logic_tree(hsh["oneOf"][i], term_idx:, expand:)])])
+                  end
+               end
+              roots << root
+            end
+
+            root = LogicNode.new(TYPES::Or, [T.must(roots[0]), T.must(roots[1])])
+            (2...roots.size).each do |i|
+              root = LogicNode.new(TYPES::Or, [root, T.must(roots[i])])
+            end
+            root
           end
-          root
         when "not"
           LogicNode.new(TYPES::Not, [to_logic_tree(hsh["not"], term_idx:, expand:)])
         else
diff --git a/lib/deploy.sh b/lib/deploy.sh
index 978d99a59a..0c5d47ee80 100755
--- a/lib/deploy.sh
+++ b/lib/deploy.sh
@@ -2,8 +2,9 @@
 
 # deploy artifacts to a directory, in preparation for GitHub deployment
 
-# Default to success
+# Initialize globals used to track failures.
 exit_status=0
+declare -a failures # Array
 
 ROOT=$(dirname $(dirname $(realpath ${BASH_SOURCE[0]})))
 
@@ -14,30 +15,45 @@ function deploy_log() {
   echo "[DEPLOY] $(date) $*"
 }
 
+# Put "FAIL" between [DEPLOY] and date to make it easier to grep for failures (ie.., "\[DEPLOY\] FAIL" RE does the trick)
+# Don't put "FAIL" in [DEPLOY] so that we can first grep for [DEPLOY] and see all messages from this deploy.sh script.
+# Record failures but don't exit so that we can see which artifacts pass & fail.
+function deploy_fail() {
+  echo "[DEPLOY] FAIL $(date) $*"
+  failures+=("$*")    # Append to array
+  exit_status=1
+}
+
 function deploy_mkdir() {
   [[ $# -ne 1 ]] && {
-    deploy_log "deploy_mkdir(): Passed $# args but it needs 1"
+    deploy_fail "deploy_mkdir(): Passed $# args but it needs 1"
+
+    # Exit if args are wrong.
     exit 1
   }
 
   local dst_dir="$1"
   mkdir -p $dst_dir || {
-    deploy_log "mkdir -p $dst_dir failed"
-    exit_status=1
+    deploy_fail "mkdir -p $dst_dir failed"
   }
 }
 
 function deploy_do() {
-  deploy_log "$@"
+  deploy_log
+  deploy_log 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv'
+  deploy_log "./do $*"
+  deploy_log '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
+  deploy_log
   ./do "$@" || {
-    deploy_log "./do $* failed"
-    exit_status=1
+    deploy_fail "./do $*"
   }
 }
 
 function deploy_cp_recursive() {
   [[ $# -ne 2 ]] && {
-    deploy_log "deploy_cp_recursive(): Passed $# args but it needs 2"
+    deploy_fail "deploy_cp_recursive(): Passed $# args but it needs 2"
+
+    # Exit if args are wrong.
     exit 1
   }
 
@@ -45,14 +61,15 @@ function deploy_cp_recursive() {
   local dst_dir="$2"
 
   cp -R ${src_dir} ${dst_dir} || {
-    deploy_log "cp -R ${src_dir} ${dst_dir} failed"
-    exit_status=1
+    deploy_fail "cp -R ${src_dir} ${dst_dir} failed"
   }
 }
 
 function deploy_cp() {
   [[ $# -ne 2 ]] && {
-    deploy_log "deploy_cp(): Passed $# args but it needs 2"
+    deploy_fail "deploy_cp(): Passed $# args but it needs 2"
+
+    # Exit if args are wrong.
     exit 1
   }
 
@@ -60,12 +77,13 @@ function deploy_cp() {
   local dst_dir="$2"
 
   cp ${src_file} ${dst_dir} || {
-    deploy_log "cp ${src_file} ${dst_dir} failed"
-    exit_status=1
+    deploy_fail "cp ${src_file} ${dst_dir} failed"
   }
 }
 
-deploy_log "Starting"
+deploy_log '***************************************************************'
+deploy_log '*                      DEPLOY STARTING                        *'
+deploy_log '***************************************************************'
 
 deploy_mkdir $DEPLOY_DIR
 deploy_mkdir $DEPLOY_DIR/example_cfg
@@ -174,17 +192,17 @@ cat <<- EOF > $DEPLOY_DIR/index.html
       <li><a href="$PAGES_URL/isa_explorer/browser/ext_table.html">Extensions</a></li>
       <li><a href="$PAGES_URL/isa_explorer/browser/inst_table.html">Instructions</a></li>
       <li><a href="$PAGES_URL/isa_explorer/browser/csr_table.html">CSRs</a></li>
-      <li><a href="$PAGES_URL/isa_explorer.xlsx">Excel version (includes Extensions, Instructions, CSRs)</a></li>
+      <li><a href="$PAGES_URL/isa_explorer/spreadsheet/isa_explorer.xlsx">Excel version (includes Extensions, Instructions, CSRs)</a></li>
     </ul>
 
     <br/>
     <h3>Profile Releases</h3>
     <ul>
-      <li><a href="$PAGES_URL/pdfs/RVI20.pdf">RVI20 Profile Release</a></li>
-      <li><a href="$PAGES_URL/pdfs/RVA20.pdf">RVA20 Profile Release</a></li>
-      <li><a href="$PAGES_URL/pdfs/RVA22.pdf">RVA22 Profile Release</a></li>
-      <li><a href="$PAGES_URL/pdfs/RVA23.pdf">RVA23 Profile Release</a></li>
-      <li><a href="$PAGES_URL/pdfs/RVB23.pdf">RVB23 Profile Release</a></li>
+      <li><a href="$PAGES_URL/pdfs/RVI20ProfileRelease.pdf">RVI20 Profile Release</a></li>
+      <li><a href="$PAGES_URL/pdfs/RVA20ProfileRelease.pdf">RVA20 Profile Release</a></li>
+      <li><a href="$PAGES_URL/pdfs/RVA22ProfileRelease.pdf">RVA22 Profile Release</a></li>
+      <li><a href="$PAGES_URL/pdfs/RVA23ProfileRelease.pdf">RVA23 Profile Release</a></li>
+      <li><a href="$PAGES_URL/pdfs/RVB23ProfileRelease.pdf">RVB23 Profile Release</a></li>
     </ul>
 
     <br/>
@@ -223,6 +241,26 @@ cat <<- EOF > $DEPLOY_DIR/index.html
 </html>
 EOF
 
-deploy_log "Complete"
+[[ $exit_status -eq 1 ]] && {
+  deploy_log
+  deploy_log '***************************************************************'
+  deploy_log '*                      DEPLOY FAILED                          *'
+  deploy_log '***************************************************************'
+  deploy_log
+  deploy_log "LIST OF FAILURES:"
+
+  # Iterate through each failure array element.
+  for f in "${failures[@]}"; do
+    deploy_log "  $f"
+  done
+}
+
+deploy_log
+deploy_log "Overall exit status is $exit_status"
+deploy_log
+
+deploy_log '***************************************************************'
+deploy_log '*                      DEPLOY COMPLETE                        *'
+deploy_log '***************************************************************'
 
 exit $exit_status


Reviews:
user: login: ThinkOpenly
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T21:46:35Z
user: login: james-ball-qualcomm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T12:20:08Z
user: login: ThinkOpenly
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T12:34:04Z
user: login: james-ball-qualcomm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T14:21:19Z
user: login: ThinkOpenly
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T14:58:51Z
user: login: ThinkOpenly
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T15:00:59Z
user: login: james-ball-qualcomm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T15:45:09Z
user: login: ThinkOpenly
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T16:01:19Z
user: login: ThinkOpenly
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T16:02:27Z
user: login: ThinkOpenly
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T16:09:10Z
user: login: ThinkOpenly
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T16:12:53Z
user: login: ThinkOpenly
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T16:14:24Z
user: login: james-ball-qualcomm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T17:46:56Z
user: login: james-ball-qualcomm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T17:48:18Z
user: login: james-ball-qualcomm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T17:51:36Z
user: login: james-ball-qualcomm
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T17:52:50Z
user: login: dhower-qc
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-14T13:02:51Z

Review comments:
path: arch/ext/Zve32f.yaml
user: login: ThinkOpenly
body: unintentional?
created_at: 2025-05-05T21:46:34Z
author_association: COLLABORATOR
path: arch/ext/Zve32f.yaml
user: login: james-ball-qualcomm
body: No, Derek and I added this. It is one of many extensions are that pretty bare but is required to resolve dependencies between extensions.
created_at: 2025-05-07T12:20:08Z
author_association: COLLABORATOR
path: arch/ext/Zve32f.yaml
user: login: ThinkOpenly
body: It's not used anywhere. Why is it here?
created_at: 2025-05-07T12:34:04Z
author_association: COLLABORATOR
path: arch/ext/Zve32f.yaml
user: login: james-ball-qualcomm
body: Zve32f is referenced by several other extensions. We just didn't have test coverage to notice the omission until Derek and I worked on improving the extension dependency information that the ISA Explorer collects from UDB and displays in a table.

Here's screenshot of my search for Zve32f. See, its reference in several extensions. Not sure why you aren't seeing it used anywhere. Maybe you can elaborate?

![image](https://github.com/user-attachments/assets/9d07c781-2286-4f03-88a0-7a986f04d747)

created_at: 2025-05-07T14:21:18Z
author_association: COLLABORATOR
path: arch/ext/Zve32f.yaml
user: login: ThinkOpenly
body: > Zve32f is referenced by several other extensions.

By "not used anywhere", I meant not referenced in any of the other changes in this PR. It was thrown into a commit titled "fix: display implied-by and requires columns from UDB & bugs it exposed" with no explanatory information in the commit message body.

As a reviewer, there is no information that is helpful to understand why this file is here.

Commits titled "fix: bugs" with no explanatory text are not helpful.

From `CONTRIBUTING.adoc`:
> Explain the motivation for the change in the commit message body. This commit message should explain _why_ you are making the change.

Emphasis on "_explain_": The observed behavior, desired behavior, and ideally a synopsis of the changes.
created_at: 2025-05-07T14:58:51Z
author_association: COLLABORATOR
diff_hunk: @@ -55,14 +55,12 @@ def arch2ext_table(arch)
 
   arch.extensions.sort_by!(&:name).each do |ext|
     row = [
-      ext.name,
-      ext.long_name,
-      ext.compact_priv_type,
-      "UDB Missing",
-      # See https://github.com/riscv-software-src/riscv-unified-db/issues/597 for the next 2 columns.
-      ext.max_version.implied_by.map(&:name),
-      # ext.max_version.transitive_conflicts.map(&:name),
-      "UDB MISSING",
+      ext.name,           # Name
+      ext.long_name,      # Description
+      ext.compact_priv_type,  # IC
path: backends/isa_explorer/isa_explorer.rb
user: login: ThinkOpenly
body: What is "IC"?
created_at: 2025-05-07T15:00:59Z
author_association: COLLABORATOR
diff_hunk: @@ -55,14 +55,12 @@ def arch2ext_table(arch)
 
   arch.extensions.sort_by!(&:name).each do |ext|
     row = [
-      ext.name,
-      ext.long_name,
-      ext.compact_priv_type,
-      "UDB Missing",
-      # See https://github.com/riscv-software-src/riscv-unified-db/issues/597 for the next 2 columns.
-      ext.max_version.implied_by.map(&:name),
-      # ext.max_version.transitive_conflicts.map(&:name),
-      "UDB MISSING",
+      ext.name,           # Name
+      ext.long_name,      # Description
+      ext.compact_priv_type,  # IC
path: backends/isa_explorer/isa_explorer.rb
user: login: james-ball-qualcomm
body: IC = ISA Committee. Note that one of my goals of ISA Explorer is to replace the existing https://docs.google.com/spreadsheets/d/1A40dfm0nnn2-tgKIhdi3UYQ1GBr8iRiV2edFowvgp7E/edit?gid=1157775000 spreadsheet with data from UDB (working towards it being accepted by the community as the single source of truth). So, I'm working to match this spreadsheet. I agree IC is vague but not when you look how it is used as in the screenshot below.

Paul, I'm really hoping you or Derek will approve this soon. Having a PR sit around is a time sync for me given that other PRs often completely independent of my changes get merged and then I have to keep merging those changes into my PR. UDB's current structure isn't scaling well to having more people contributing. There must be some best practices used by the Github community to help address this. 

![image](https://github.com/user-attachments/assets/9a0d5bbc-e1f7-4a64-acf4-1e3ee57769ea)

created_at: 2025-05-07T15:45:09Z
author_association: COLLABORATOR
diff_hunk: @@ -55,14 +55,12 @@ def arch2ext_table(arch)
 
   arch.extensions.sort_by!(&:name).each do |ext|
     row = [
-      ext.name,
-      ext.long_name,
-      ext.compact_priv_type,
-      "UDB Missing",
-      # See https://github.com/riscv-software-src/riscv-unified-db/issues/597 for the next 2 columns.
-      ext.max_version.implied_by.map(&:name),
-      # ext.max_version.transitive_conflicts.map(&:name),
-      "UDB MISSING",
+      ext.name,           # Name
+      ext.long_name,      # Description
+      ext.compact_priv_type,  # IC
path: backends/isa_explorer/isa_explorer.rb
user: login: ThinkOpenly
body: I see. (Get it?)

There's a RISC-V Confluence [RISC-V Active ICs, HCs, SIGs and TGs](https://lf-riscv.atlassian.net/wiki/spaces/HOME/pages/16154847/RISC-V+Active+ICs+HCs+SIGs+and+TGs) page where "ICs" is plural.

Please change "IC" to "RISC-V ISA Committees (ICs)" here.
created_at: 2025-05-07T16:01:19Z
author_association: COLLABORATOR
diff_hunk: @@ -55,14 +55,12 @@ def arch2ext_table(arch)
 
   arch.extensions.sort_by!(&:name).each do |ext|
     row = [
-      ext.name,
-      ext.long_name,
-      ext.compact_priv_type,
-      "UDB Missing",
-      # See https://github.com/riscv-software-src/riscv-unified-db/issues/597 for the next 2 columns.
-      ext.max_version.implied_by.map(&:name),
-      # ext.max_version.transitive_conflicts.map(&:name),
-      "UDB MISSING",
+      ext.name,           # Name
+      ext.long_name,      # Description
+      ext.compact_priv_type,  # IC
path: backends/isa_explorer/isa_explorer.rb
user: login: ThinkOpenly
body: > Paul, I'm really hoping you or Derek will approve this soon. Having a PR sit around is a time sync for me given that other PRs often completely independent of my changes get merged and then I have to keep merging those changes into my PR. UDB's current structure isn't scaling well to having more people contributing. There must be some best practices used by the Github community to help address this.

One "best practice" that I tell all of my mentees is to "make the reviewers job as easy as possible". ;-)
created_at: 2025-05-07T16:02:27Z
author_association: COLLABORATOR
diff_hunk: @@ -14,58 +15,75 @@ function deploy_log() {
   echo "[DEPLOY] $(date) $*"
 }
 
+# Put "FAIL" between [DEPLOY] and date to make it easier to grep for failures (ie.., "\[DEPLOY\] FAIL" RE does the trick)
+# Don't put "FAIL" in [DEPLOY] so that we can first grep for [DEPLOY] and see all messages from this deploy.sh script.
+# Record failures but don't exit so that we can see which artifacts pass & fail.
+function deploy_fail() {
+  echo "[DEPLOY] FAIL $(date) $*"
+  failures+=("$*")    # Append to array
+  exit_status=1
+}
+
 function deploy_mkdir() {
   [[ $# -ne 1 ]] && {
-    deploy_log "deploy_mkdir(): Passed $# args but it needs 1"
+    deploy_fail "deploy_mkdir(): Passed $# args but it needs 1"
+
+    # Exit if args are wrong.
     exit 1
   }
 
   local dst_dir="$1"
   mkdir -p $dst_dir || {
-    deploy_log "mkdir -p $dst_dir failed"
-    exit_status=1
+    deploy_fail "mkdir -p $dst_dir failed"
   }
 }
 
 function deploy_do() {
-  deploy_log "$@"
+  deploy_log
+  deploy_log 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv'
+  deploy_log "./do $*"
+  deploy_log '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
+  deploy_log
   ./do "$@" || {
-    deploy_log "./do $* failed"
-    exit_status=1
+    deploy_fail "./do $*"
path: lib/deploy.sh
user: login: ThinkOpenly
body: Why `"$@"` in the command and `"$*"` in the message and log? The log should exactly match the command.
created_at: 2025-05-07T16:09:09Z
author_association: COLLABORATOR
diff_hunk: @@ -93,7 +111,7 @@ deploy_log "Copy isa_explorer_spreadsheet"
 deploy_cp_recursive gen/isa_explorer/spreadsheet $DEPLOY_DIR/isa_explorer
 
 deploy_log "Build manual"
-deploy_do gen:html_manual MANUAL_NAME=isa VERSIONS=all
+deploy_do "gen:html_manual" "MANUAL_NAME=isa" "VERSIONS=all"
path: lib/deploy.sh
user: login: ThinkOpenly
body: What's the point of quoting these strings? They are not used on any of the "deploy_X" invocations.
created_at: 2025-05-07T16:12:53Z
author_association: COLLABORATOR
diff_hunk: @@ -213,6 +226,21 @@ cat <<- EOF > $DEPLOY_DIR/index.html
 </html>
 EOF
 
-deploy_log "Complete"
+[[ $exit_status -eq 1 ]] && {
+  deploy_log
+  deploy_log '***************************************************************'
+  deploy_log '*                      DEPLOY FAILED                          *'
+  deploy_log '***************************************************************'
+  deploy_log
+  deploy_log "LIST OF FAILURES:"
+
+  # Iterate through each failure array element.
+  for f in "${failures[@]}"; do
+    deploy_log "  $f"
+  done
+}
+
+deploy_log
+deploy_log "COMPLETE - Overall exit status is $exit_status"
path: lib/deploy.sh
user: login: ThinkOpenly
body: This should stylistically match the "DEPLOY STARTING" banner above.
created_at: 2025-05-07T16:14:24Z
author_association: COLLABORATOR
diff_hunk: @@ -14,58 +15,75 @@ function deploy_log() {
   echo "[DEPLOY] $(date) $*"
 }
 
+# Put "FAIL" between [DEPLOY] and date to make it easier to grep for failures (ie.., "\[DEPLOY\] FAIL" RE does the trick)
+# Don't put "FAIL" in [DEPLOY] so that we can first grep for [DEPLOY] and see all messages from this deploy.sh script.
+# Record failures but don't exit so that we can see which artifacts pass & fail.
+function deploy_fail() {
+  echo "[DEPLOY] FAIL $(date) $*"
+  failures+=("$*")    # Append to array
+  exit_status=1
+}
+
 function deploy_mkdir() {
   [[ $# -ne 1 ]] && {
-    deploy_log "deploy_mkdir(): Passed $# args but it needs 1"
+    deploy_fail "deploy_mkdir(): Passed $# args but it needs 1"
+
+    # Exit if args are wrong.
     exit 1
   }
 
   local dst_dir="$1"
   mkdir -p $dst_dir || {
-    deploy_log "mkdir -p $dst_dir failed"
-    exit_status=1
+    deploy_fail "mkdir -p $dst_dir failed"
   }
 }
 
 function deploy_do() {
-  deploy_log "$@"
+  deploy_log
+  deploy_log 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv'
+  deploy_log "./do $*"
+  deploy_log '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
+  deploy_log
   ./do "$@" || {
-    deploy_log "./do $* failed"
-    exit_status=1
+    deploy_fail "./do $*"
path: lib/deploy.sh
user: login: james-ball-qualcomm
body: I thought about this and intentionally chose $* in the message since I just want the log message to expand to one string. I chose $@ in the deploy_log call to properly pass multiple arguments. So, there are different needs here hence the $* vs. $@. What I've committed has been tested by me and works as expected and produces the correct log. So, I don't think there is justification to change to $@ for both unless you can explain what is the problem with the $* vs. $@ (i.e., not a personal-style justification).

BTW, Paul, do you think you've found a bug or problem? If not, I'm hoping you can be less stringent in your reviews since it is very discouraging to keep a PR open for such review comments. Doesn't encourage me to continue as a UDB developer. 
created_at: 2025-05-07T17:46:56Z
author_association: COLLABORATOR
diff_hunk: @@ -14,58 +15,75 @@ function deploy_log() {
   echo "[DEPLOY] $(date) $*"
 }
 
+# Put "FAIL" between [DEPLOY] and date to make it easier to grep for failures (ie.., "\[DEPLOY\] FAIL" RE does the trick)
+# Don't put "FAIL" in [DEPLOY] so that we can first grep for [DEPLOY] and see all messages from this deploy.sh script.
+# Record failures but don't exit so that we can see which artifacts pass & fail.
+function deploy_fail() {
+  echo "[DEPLOY] FAIL $(date) $*"
+  failures+=("$*")    # Append to array
+  exit_status=1
+}
+
 function deploy_mkdir() {
   [[ $# -ne 1 ]] && {
-    deploy_log "deploy_mkdir(): Passed $# args but it needs 1"
+    deploy_fail "deploy_mkdir(): Passed $# args but it needs 1"
+
+    # Exit if args are wrong.
     exit 1
   }
 
   local dst_dir="$1"
   mkdir -p $dst_dir || {
-    deploy_log "mkdir -p $dst_dir failed"
-    exit_status=1
+    deploy_fail "mkdir -p $dst_dir failed"
   }
 }
 
 function deploy_do() {
-  deploy_log "$@"
+  deploy_log
+  deploy_log 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv'
+  deploy_log "./do $*"
+  deploy_log '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
+  deploy_log
   ./do "$@" || {
-    deploy_log "./do $* failed"
-    exit_status=1
+    deploy_fail "./do $*"
path: lib/deploy.sh
user: login: james-ball-qualcomm
body: Now my branch is out-of-date again due to keeping this PR open.
created_at: 2025-05-07T17:48:18Z
author_association: COLLABORATOR
diff_hunk: @@ -93,7 +111,7 @@ deploy_log "Copy isa_explorer_spreadsheet"
 deploy_cp_recursive gen/isa_explorer/spreadsheet $DEPLOY_DIR/isa_explorer
 
 deploy_log "Build manual"
-deploy_do gen:html_manual MANUAL_NAME=isa VERSIONS=all
+deploy_do "gen:html_manual" "MANUAL_NAME=isa" "VERSIONS=all"
path: lib/deploy.sh
user: login: james-ball-qualcomm
body: Paul, do you think you've found a bug? Keeping quotes is a personal style and keeps all the deploy_do calls consistent (i.e., don't have some calls to deploy_do with quotes around each individual argument and some without). I did this intentionally after many years of experience in writing shell scripts and what kind of bugs can creep in. In this case, putting each argument in quotes allows arguments with spaces to be passed correctly if needed. So, its a future proofing technique.

BTW, Paul, this is another example of your feedback that is really discouraging me from being a UDB developer.
created_at: 2025-05-07T17:51:36Z
author_association: COLLABORATOR
diff_hunk: @@ -213,6 +226,21 @@ cat <<- EOF > $DEPLOY_DIR/index.html
 </html>
 EOF
 
-deploy_log "Complete"
+[[ $exit_status -eq 1 ]] && {
+  deploy_log
+  deploy_log '***************************************************************'
+  deploy_log '*                      DEPLOY FAILED                          *'
+  deploy_log '***************************************************************'
+  deploy_log
+  deploy_log "LIST OF FAILURES:"
+
+  # Iterate through each failure array element.
+  for f in "${failures[@]}"; do
+    deploy_log "  $f"
+  done
+}
+
+deploy_log
+deploy_log "COMPLETE - Overall exit status is $exit_status"
path: lib/deploy.sh
user: login: james-ball-qualcomm
body: I agree. I'll make the change. This is useful review feedback.
created_at: 2025-05-07T17:52:50Z
author_association: COLLABORATOR

PR comments:
user: login: dhower-qc
created_at: 2025-05-05T19:07:17Z
body: Can you add a note in the PR description mentioning that this is a work in progress, and that the dependency display needs simplified?
author_association: COLLABORATOR
user: login: james-ball-qualcomm
created_at: 2025-05-06T12:35:16Z
body: Created issue #730 assigned to @dhower-qc to create a more legible summary string of extension dependencies for ISA Explorer. Note that Rafael is open to the idea of adding ISA Explorer links to the riscv.org website but only when ISA Explorer output is stable and at least as high quality as the Profiles&Bases&Extensions spreadsheet.
author_association: COLLABORATOR
user: login: james-ball-qualcomm
created_at: 2025-05-07T12:29:39Z
body: Note that I accidentally merged in commits from branch "731 still have 404 errors on artifacts" with PR https://github.com/riscv-software-src/riscv-unified-db/pull/739 into this branch. So, you'll commits for both issues https://github.com/riscv-software-src/riscv-unified-db/issues/597 and https://github.com/riscv-software-src/riscv-unified-db/issues/731 in this PR.

author_association: COLLABORATOR