Spaces:
Running
Running
CI: sync Gap-Finder Space (d1507c1)
Browse files- README.md +12 -7
- space/DEPLOY.md +37 -13
- space/README.md +12 -7
- space/app.py +21 -4
- space/pipeline.py +8 -0
- space/requirements.txt +13 -1
- space/reviewer.py +13 -3
README.md
CHANGED
|
@@ -30,17 +30,23 @@ manifest, public key, and instructions beside it are convenience copies.
|
|
| 30 |
|
| 31 |
```
|
| 32 |
unzip uofa-pack-*.zip
|
| 33 |
-
uofa verify uofa.jsonld
|
| 34 |
-
--pubkey keys/uofa-issuer.pub \
|
| 35 |
-
--decision-pubkey keys/demo-reviewer.pub
|
| 36 |
```
|
| 37 |
|
| 38 |
**What a valid signature means here.** Only that the file is unmodified since
|
| 39 |
this demo produced it. It is not a review and not an acceptance decision. The
|
| 40 |
signing key is a *demonstration issuer key* held by the demo, not a research or
|
| 41 |
-
production key
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
The public key travels inside the zip so verification works offline. A trust
|
| 46 |
anchor shipped inside the artifact it validates only proves self-consistency, so
|
|
@@ -48,7 +54,6 @@ compare it against this independent copy:
|
|
| 48 |
|
| 49 |
```
|
| 50 |
keys/uofa-issuer.pub sha256:ead2e1e1068f8c6da14b2c9c384e4d00d8900308ad2e406fe294330ce0edd81d
|
| 51 |
-
keys/demo-reviewer.pub sha256:3605a146f4880d9f7a29db6ef5629655091d2ecd0c2b9919cbe49d90d65d83c8
|
| 52 |
```
|
| 53 |
|
| 54 |
## Privacy
|
|
|
|
| 30 |
|
| 31 |
```
|
| 32 |
unzip uofa-pack-*.zip
|
| 33 |
+
uofa verify uofa.jsonld --pubkey keys/uofa-issuer.pub
|
|
|
|
|
|
|
| 34 |
```
|
| 35 |
|
| 36 |
**What a valid signature means here.** Only that the file is unmodified since
|
| 37 |
this demo produced it. It is not a review and not an acceptance decision. The
|
| 38 |
signing key is a *demonstration issuer key* held by the demo, not a research or
|
| 39 |
+
production key — so a demo package can never be mistaken for a formally issued
|
| 40 |
+
one (it does not verify against the default trust anchor; `--pubkey` is
|
| 41 |
+
required, deliberately).
|
| 42 |
+
|
| 43 |
+
**The Space applies an issuer seal only, and signs no decision.** A service key
|
| 44 |
+
cannot stand in for a human reviewer, so the pack carries no decision block and
|
| 45 |
+
`--decision-pubkey` has nothing here to check. Until 2026-09-08 this file
|
| 46 |
+
documented that flag against `keys/demo-reviewer.pub`, which is not one of the
|
| 47 |
+
pack's five members, so the command failed on a missing file and implied an
|
| 48 |
+
attestation the Space deliberately does not make. See
|
| 49 |
+
`docs/UofA_Spec_Unified_Signing_Surface_v1_0.md`.
|
| 50 |
|
| 51 |
The public key travels inside the zip so verification works offline. A trust
|
| 52 |
anchor shipped inside the artifact it validates only proves self-consistency, so
|
|
|
|
| 54 |
|
| 55 |
```
|
| 56 |
keys/uofa-issuer.pub sha256:ead2e1e1068f8c6da14b2c9c384e4d00d8900308ad2e406fe294330ce0edd81d
|
|
|
|
| 57 |
```
|
| 58 |
|
| 59 |
## Privacy
|
space/DEPLOY.md
CHANGED
|
@@ -84,34 +84,58 @@ never evidence content.
|
|
| 84 |
|
| 85 |
---
|
| 86 |
|
| 87 |
-
## 3b. Package signing key (`
|
| 88 |
-
|
| 89 |
-
The "Download UofA package" control
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
is its own identity.
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
**Setup (one-time).** Generate the pair *outside the repo* and install the
|
| 98 |
private half as a Space secret:
|
| 99 |
|
| 100 |
```bash
|
| 101 |
-
uofa keygen ~/secure/uofa-
|
| 102 |
-
cp ~/secure/uofa-
|
| 103 |
```
|
| 104 |
|
| 105 |
Space → **Settings → Variables and secrets → New secret**:
|
| 106 |
|
| 107 |
| Name | Value |
|
| 108 |
|---|---|
|
| 109 |
-
| `
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
The PEM is read into memory at request time and never written to the container
|
| 112 |
filesystem — the process serves user downloads out of a temp directory, and a
|
| 113 |
private key on that filesystem is one path bug away from being one of them. For
|
| 114 |
-
local development, `
|
|
|
|
| 115 |
|
| 116 |
**The key can never travel as a file.** `space/deploy_to_hf.py` filters `.key`,
|
| 117 |
`.pem`, and `.env` out of the upload set *and* hard-refuses the deploy if one
|
|
|
|
| 84 |
|
| 85 |
---
|
| 86 |
|
| 87 |
+
## 3b. Package signing key (`UOFA_ISSUER_SIGNING_KEY`)
|
| 88 |
+
|
| 89 |
+
The "Download UofA package" control seals each package with a **dedicated issuer
|
| 90 |
+
key**. Deliberately *not* `keys/research.key`: a demo artifact must never be
|
| 91 |
+
cryptographically indistinguishable from a research package, so the issuer key
|
| 92 |
+
is its own identity.
|
| 93 |
+
|
| 94 |
+
**One key, one scope: an issuer seal.** `UOFA_ISSUER_SIGNING_KEY` attests
|
| 95 |
+
integrity and origin — this package came from this service and has not been
|
| 96 |
+
altered since. Anchor: `keys/uofa-issuer.pub`. The signer renders as
|
| 97 |
+
`UofA issuer (keys/uofa-issuer.pub)`.
|
| 98 |
+
|
| 99 |
+
**The Space does not sign a decision, and must not.** A service key cannot stand
|
| 100 |
+
in for a human reviewer, so there is no second signature and no decision
|
| 101 |
+
attestor. Per `docs/UofA_Spec_Unified_Signing_Surface_v1_0.md`, which supersedes
|
| 102 |
+
the earlier two-attestor design, the web path applies an issuer seal only. A
|
| 103 |
+
valid signature here means *this artifact is intact and came from us*. It does
|
| 104 |
+
not mean anyone accepted the model.
|
| 105 |
+
|
| 106 |
+
> **Corrected 2026-09-08.** This section previously described a second secret,
|
| 107 |
+
> `UOFA_DEMO_SIGNING_KEY`, said to sign a decision against
|
| 108 |
+
> `keys/demo-reviewer.pub`. **No code has ever read that variable.**
|
| 109 |
+
> `space/pipeline.py` defines `UOFA_ISSUER_SIGNING_KEY` and
|
| 110 |
+
> `UOFA_ISSUER_SIGNING_KEY_FILE` and nothing else, and the Space's own tests
|
| 111 |
+
> already assert the emitted package carries no decision block. The document
|
| 112 |
+
> described a capability the implementation deliberately does not have, on the
|
| 113 |
+
> most sensitive question the tool answers. Pinned by
|
| 114 |
+
> `tests/space/test_deploy_doc_env_names.py` so the names cannot drift again.
|
| 115 |
|
| 116 |
**Setup (one-time).** Generate the pair *outside the repo* and install the
|
| 117 |
private half as a Space secret:
|
| 118 |
|
| 119 |
```bash
|
| 120 |
+
uofa keygen ~/secure/uofa-issuer.key # writes uofa-issuer.key + uofa-issuer.pub
|
| 121 |
+
cp ~/secure/uofa-issuer.pub keys/uofa-issuer.pub # public half is committed
|
| 122 |
```
|
| 123 |
|
| 124 |
Space → **Settings → Variables and secrets → New secret**:
|
| 125 |
|
| 126 |
| Name | Value |
|
| 127 |
|---|---|
|
| 128 |
+
| `UOFA_ISSUER_SIGNING_KEY` | the full PEM contents of `uofa-issuer.key` |
|
| 129 |
+
|
| 130 |
+
Without that secret the Space still runs, but the result reports
|
| 131 |
+
`Authenticity: Unverified (demo)` and **no download control appears** — an
|
| 132 |
+
unsigned package is not offered rather than offered unsigned.
|
| 133 |
|
| 134 |
The PEM is read into memory at request time and never written to the container
|
| 135 |
filesystem — the process serves user downloads out of a temp directory, and a
|
| 136 |
private key on that filesystem is one path bug away from being one of them. For
|
| 137 |
+
local development, `UOFA_ISSUER_SIGNING_KEY_FILE=/path/to/issuer.key` works
|
| 138 |
+
instead.
|
| 139 |
|
| 140 |
**The key can never travel as a file.** `space/deploy_to_hf.py` filters `.key`,
|
| 141 |
`.pem`, and `.env` out of the upload set *and* hard-refuses the deploy if one
|
space/README.md
CHANGED
|
@@ -30,17 +30,23 @@ manifest, public key, and instructions beside it are convenience copies.
|
|
| 30 |
|
| 31 |
```
|
| 32 |
unzip uofa-pack-*.zip
|
| 33 |
-
uofa verify uofa.jsonld
|
| 34 |
-
--pubkey keys/uofa-issuer.pub \
|
| 35 |
-
--decision-pubkey keys/demo-reviewer.pub
|
| 36 |
```
|
| 37 |
|
| 38 |
**What a valid signature means here.** Only that the file is unmodified since
|
| 39 |
this demo produced it. It is not a review and not an acceptance decision. The
|
| 40 |
signing key is a *demonstration issuer key* held by the demo, not a research or
|
| 41 |
-
production key
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
The public key travels inside the zip so verification works offline. A trust
|
| 46 |
anchor shipped inside the artifact it validates only proves self-consistency, so
|
|
@@ -48,7 +54,6 @@ compare it against this independent copy:
|
|
| 48 |
|
| 49 |
```
|
| 50 |
keys/uofa-issuer.pub sha256:ead2e1e1068f8c6da14b2c9c384e4d00d8900308ad2e406fe294330ce0edd81d
|
| 51 |
-
keys/demo-reviewer.pub sha256:3605a146f4880d9f7a29db6ef5629655091d2ecd0c2b9919cbe49d90d65d83c8
|
| 52 |
```
|
| 53 |
|
| 54 |
## Privacy
|
|
|
|
| 30 |
|
| 31 |
```
|
| 32 |
unzip uofa-pack-*.zip
|
| 33 |
+
uofa verify uofa.jsonld --pubkey keys/uofa-issuer.pub
|
|
|
|
|
|
|
| 34 |
```
|
| 35 |
|
| 36 |
**What a valid signature means here.** Only that the file is unmodified since
|
| 37 |
this demo produced it. It is not a review and not an acceptance decision. The
|
| 38 |
signing key is a *demonstration issuer key* held by the demo, not a research or
|
| 39 |
+
production key — so a demo package can never be mistaken for a formally issued
|
| 40 |
+
one (it does not verify against the default trust anchor; `--pubkey` is
|
| 41 |
+
required, deliberately).
|
| 42 |
+
|
| 43 |
+
**The Space applies an issuer seal only, and signs no decision.** A service key
|
| 44 |
+
cannot stand in for a human reviewer, so the pack carries no decision block and
|
| 45 |
+
`--decision-pubkey` has nothing here to check. Until 2026-09-08 this file
|
| 46 |
+
documented that flag against `keys/demo-reviewer.pub`, which is not one of the
|
| 47 |
+
pack's five members, so the command failed on a missing file and implied an
|
| 48 |
+
attestation the Space deliberately does not make. See
|
| 49 |
+
`docs/UofA_Spec_Unified_Signing_Surface_v1_0.md`.
|
| 50 |
|
| 51 |
The public key travels inside the zip so verification works offline. A trust
|
| 52 |
anchor shipped inside the artifact it validates only proves self-consistency, so
|
|
|
|
| 54 |
|
| 55 |
```
|
| 56 |
keys/uofa-issuer.pub sha256:ead2e1e1068f8c6da14b2c9c384e4d00d8900308ad2e406fe294330ce0edd81d
|
|
|
|
| 57 |
```
|
| 58 |
|
| 59 |
## Privacy
|
space/app.py
CHANGED
|
@@ -20,7 +20,7 @@ import gradio as gr
|
|
| 20 |
from space import (curated, leadcapture, llm_env, pipeline, ratelimit, reviewer,
|
| 21 |
solver_panel, wizard)
|
| 22 |
from space.gloss import gloss_for, load_gloss
|
| 23 |
-
from uofa_cli import paths
|
| 24 |
|
| 25 |
PACK_LABELS = {"vv40": "ASME V&V 40", "nasa-7009b": "NASA-STD-7009B"}
|
| 26 |
PACK_CHOICES = [(label, pid) for pid, label in PACK_LABELS.items()]
|
|
@@ -370,14 +370,25 @@ def _render_results(p):
|
|
| 370 |
gaps.append("**Weakeners fired:**")
|
| 371 |
for w in p["weakeners"]:
|
| 372 |
fac = f", {', '.join(w['factors'])}" if w.get("factors") else ""
|
| 373 |
-
|
|
|
|
|
|
|
|
|
|
| 374 |
else:
|
| 375 |
gaps.append("**Weakeners:** none fired. 🎉")
|
| 376 |
if c["missing"]:
|
| 377 |
gaps.append("\n**Not assessed:** " + ", ".join(c["missing"]))
|
| 378 |
gaps_md = "\n".join(gaps)
|
| 379 |
|
| 380 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
if c["excluded"]:
|
| 382 |
tail.append("**Excluded (scoped-out / N/A):** " + ", ".join(c["excluded"]))
|
| 383 |
struct = p["structural"]
|
|
@@ -651,8 +662,14 @@ def build() -> gr.Blocks:
|
|
| 651 |
def render_factors(result):
|
| 652 |
rows = pipeline.factor_rows(result) if result else []
|
| 653 |
for row in rows:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 654 |
rad = gr.Radio(choices=STATUS_CHOICES, value=row["status"],
|
| 655 |
-
label=_factor_label(row))
|
| 656 |
# The factor name is already in the radio label above, so the
|
| 657 |
# accordion just says "what we read" (no redundant name echo).
|
| 658 |
# Lead with the shared gloss so a non-expert understands the factor.
|
|
|
|
| 20 |
from space import (curated, leadcapture, llm_env, pipeline, ratelimit, reviewer,
|
| 21 |
solver_panel, wizard)
|
| 22 |
from space.gloss import gloss_for, load_gloss
|
| 23 |
+
from uofa_cli import paths, report_state
|
| 24 |
|
| 25 |
PACK_LABELS = {"vv40": "ASME V&V 40", "nasa-7009b": "NASA-STD-7009B"}
|
| 26 |
PACK_CHOICES = [(label, pid) for pid, label in PACK_LABELS.items()]
|
|
|
|
| 370 |
gaps.append("**Weakeners fired:**")
|
| 371 |
for w in p["weakeners"]:
|
| 372 |
fac = f", {', '.join(w['factors'])}" if w.get("factors") else ""
|
| 373 |
+
# sev_label, not the raw key: a reader must never see "Medium" here
|
| 374 |
+
# and "Moderate" in the Reviewer view for the same weakener.
|
| 375 |
+
sev = report_state.sev_label(w.get("severity"))
|
| 376 |
+
gaps.append(f"- `{w['patternId']}` [{sev}] ×{w.get('hits')}{fac}")
|
| 377 |
else:
|
| 378 |
gaps.append("**Weakeners:** none fired. 🎉")
|
| 379 |
if c["missing"]:
|
| 380 |
gaps.append("\n**Not assessed:** " + ", ".join(c["missing"]))
|
| 381 |
gaps_md = "\n".join(gaps)
|
| 382 |
|
| 383 |
+
# Say WHICH count this is. The Reviewer view reports a different, also
|
| 384 |
+
# correct number (it demotes any factor an open High/Moderate concern
|
| 385 |
+
# disputes), and side by side the two read as a contradiction unless each
|
| 386 |
+
# names its own basis. Observed live 2026-09-08: Author "13 of 13" against
|
| 387 |
+
# Reviewer "11 of 13" on the same run, with nothing explaining the gap.
|
| 388 |
+
tail = [f"**Completeness (raw extracted statuses):** {c['n_assessed']} of "
|
| 389 |
+
f"{c['n_expected']} factors assessed. The Reviewer view reports a "
|
| 390 |
+
"lower count because it does not treat a factor as evidenced while "
|
| 391 |
+
"an open High or Moderate concern disputes it."]
|
| 392 |
if c["excluded"]:
|
| 393 |
tail.append("**Excluded (scoped-out / N/A):** " + ", ".join(c["excluded"]))
|
| 394 |
struct = p["structural"]
|
|
|
|
| 662 |
def render_factors(result):
|
| 663 |
rows = pipeline.factor_rows(result) if result else []
|
| 664 |
for row in rows:
|
| 665 |
+
# interactive=True is explicit, not decorative. Gradio infers
|
| 666 |
+
# interactivity from whether a component is an input to some
|
| 667 |
+
# event, and that inference does not reach components built
|
| 668 |
+
# inside @gr.render: every factor radio shipped DISABLED, so
|
| 669 |
+
# the confirm step -- the only surface a human may correct --
|
| 670 |
+
# could not be corrected. Pinned by test_confirm_step_editable.
|
| 671 |
rad = gr.Radio(choices=STATUS_CHOICES, value=row["status"],
|
| 672 |
+
label=_factor_label(row), interactive=True)
|
| 673 |
# The factor name is already in the radio label above, so the
|
| 674 |
# accordion just says "what we read" (no redundant name echo).
|
| 675 |
# Lead with the shared gloss so a non-expert understands the factor.
|
space/pipeline.py
CHANGED
|
@@ -398,6 +398,14 @@ def _authenticity_block(*, signed: bool = False, package_hash: str | None = None
|
|
| 398 |
"package_hash": package_hash,
|
| 399 |
"signer": signer or SIGNER_LABEL,
|
| 400 |
"statement": _SIGNED_STATEMENT,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
}
|
| 402 |
|
| 403 |
|
|
|
|
| 398 |
"package_hash": package_hash,
|
| 399 |
"signer": signer or SIGNER_LABEL,
|
| 400 |
"statement": _SIGNED_STATEMENT,
|
| 401 |
+
# Built from the same constants that name the zip's members, so the
|
| 402 |
+
# command a reader is shown cannot drift from the files they receive.
|
| 403 |
+
# The reviewer view previously hardcoded `keys/demo.pub`, which is not
|
| 404 |
+
# a member of the pack, so copying the on-screen command failed on a
|
| 405 |
+
# missing file. Carried in the payload rather than assembled in the
|
| 406 |
+
# renderer because that module renders state and interprets nothing.
|
| 407 |
+
"verify_command": (f"uofa verify {PACK_MEMBER_JSONLD} "
|
| 408 |
+
f"--pubkey {PACK_MEMBER_PUBKEY}"),
|
| 409 |
}
|
| 410 |
|
| 411 |
|
space/requirements.txt
CHANGED
|
@@ -1,5 +1,17 @@
|
|
| 1 |
# App-only deps installed on top of the uofa[extract] wheel (see space/Dockerfile).
|
| 2 |
# The uofa package itself and its extract extras (litellm, pdfplumber, etc.) come
|
| 3 |
# from the wheel, not from here.
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
huggingface_hub>=0.23 # lead capture -> private HF Dataset (S4)
|
|
|
|
| 1 |
# App-only deps installed on top of the uofa[extract] wheel (see space/Dockerfile).
|
| 2 |
# The uofa package itself and its extract extras (litellm, pdfplumber, etc.) come
|
| 3 |
# from the wheel, not from here.
|
| 4 |
+
# Upper bound is deliberate. This was `>=6.0` with no cap, so the Space
|
| 5 |
+
# installed whatever 6.x existed at build time and the UI could change with no
|
| 6 |
+
# commit in this repo. It did: Gradio infers a component's interactivity from
|
| 7 |
+
# whether it feeds an event, that inference does not reach components built
|
| 8 |
+
# inside `@gr.render`, and every credibility-factor radio on the deployed tool
|
| 9 |
+
# shipped DISABLED. The confirm step, the one surface a human may correct, was
|
| 10 |
+
# read-only in production. See the fix in space/app.py (interactive=True) and
|
| 11 |
+
# the browser test that pins it, tests/space/test_ui_e2e.py.
|
| 12 |
+
#
|
| 13 |
+
# 6.24.0 is the version the browser tests were run against on 2026-09-08. Raise
|
| 14 |
+
# this cap only alongside a run of those tests, since they are the only thing
|
| 15 |
+
# that exercises rendered interactivity.
|
| 16 |
+
gradio>=6.0,<6.25
|
| 17 |
huggingface_hub>=0.23 # lead capture -> private HF Dataset (S4)
|
space/reviewer.py
CHANGED
|
@@ -109,7 +109,13 @@ def _reconcile_clause(s: ReviewerState) -> str:
|
|
| 109 |
bits.append(f"{m} high-severity {_plural(m, 'concern', 'concerns')} "
|
| 110 |
f"{_plural(m, 'remains', 'remain')} open")
|
| 111 |
tail = "; ".join(bits) + " before this is review-ready."
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
|
| 115 |
def _section_glance(s: ReviewerState) -> str:
|
|
@@ -127,7 +133,7 @@ def _section_glance(s: ReviewerState) -> str:
|
|
| 127 |
<h2>At a glance</h2>
|
| 128 |
<dl class="ri-glance">
|
| 129 |
<div><dt>Completeness</dt><dd>{s.completeness_pct}%</dd></div>
|
| 130 |
-
<div><dt>
|
| 131 |
<div><dt>Concerns (weakeners)</dt><dd>{_e(sev_txt)}</dd></div>
|
| 132 |
<div><dt>Authenticity verified</dt><dd>{auth_txt}</dd></div>
|
| 133 |
<div><dt>Gate checks passed</dt><dd>{s.gates['passed']} of {s.gates['total']}</dd></div>
|
|
@@ -220,7 +226,11 @@ def _section_authenticity(s: ReviewerState) -> str:
|
|
| 220 |
detail = (f"<ul><li><b>Signed by:</b> {_e(auth.get('signer'))}</li>"
|
| 221 |
f"<li><b>Content hash:</b> <code>{_e(auth.get('package_hash'))}</code></li></ul>\n"
|
| 222 |
f" <p>{_e(auth.get('statement'))}</p>")
|
| 223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
howto = ("Download the package below and re-check it yourself. The public "
|
| 225 |
"key and these instructions travel inside the zip:")
|
| 226 |
else:
|
|
|
|
| 109 |
bits.append(f"{m} high-severity {_plural(m, 'concern', 'concerns')} "
|
| 110 |
f"{_plural(m, 'remains', 'remain')} open")
|
| 111 |
tail = "; ".join(bits) + " before this is review-ready."
|
| 112 |
+
# "after open concerns" is load-bearing. This percentage counts a factor as
|
| 113 |
+
# evidenced only while no open High or Moderate concern disputes it, so it
|
| 114 |
+
# can sit below the Author view's raw count of assessed statuses. Both are
|
| 115 |
+
# correct; unlabelled they read as a contradiction. Observed live
|
| 116 |
+
# 2026-09-08: this view said 11 of 13 while the Author view said 13 of 13.
|
| 117 |
+
return (f"{s.completeness_pct}% of all factors evidenced after open "
|
| 118 |
+
f"concerns; {tail}")
|
| 119 |
|
| 120 |
|
| 121 |
def _section_glance(s: ReviewerState) -> str:
|
|
|
|
| 133 |
<h2>At a glance</h2>
|
| 134 |
<dl class="ri-glance">
|
| 135 |
<div><dt>Completeness</dt><dd>{s.completeness_pct}%</dd></div>
|
| 136 |
+
<div><dt>Evidenced, after concerns</dt><dd>{s.n_evidenced} of {s.n_expected}</dd></div>
|
| 137 |
<div><dt>Concerns (weakeners)</dt><dd>{_e(sev_txt)}</dd></div>
|
| 138 |
<div><dt>Authenticity verified</dt><dd>{auth_txt}</dd></div>
|
| 139 |
<div><dt>Gate checks passed</dt><dd>{s.gates['passed']} of {s.gates['total']}</dd></div>
|
|
|
|
| 226 |
detail = (f"<ul><li><b>Signed by:</b> {_e(auth.get('signer'))}</li>"
|
| 227 |
f"<li><b>Content hash:</b> <code>{_e(auth.get('package_hash'))}</code></li></ul>\n"
|
| 228 |
f" <p>{_e(auth.get('statement'))}</p>")
|
| 229 |
+
# From the payload, not typed here. The packed pubkey member is named by
|
| 230 |
+
# pipeline.PACK_MEMBER_PUBKEY, and a second hardcoded copy of that name
|
| 231 |
+
# in this file drifted: it read `keys/demo.pub`, which the zip does not
|
| 232 |
+
# contain, so a reader who copied this command hit a missing file.
|
| 233 |
+
cmd = auth.get("verify_command") or "uofa verify --help"
|
| 234 |
howto = ("Download the package below and re-check it yourself. The public "
|
| 235 |
"key and these instructions travel inside the zip:")
|
| 236 |
else:
|