geenforce: gate publishing on the library version, not on the manifest's own

The publish gate exists because a minor version bump that NARROWS behaviour is
not backward compatible: _CmmVersion arrived in lib 2.6, and an older lib does
not know the field, so every gated entry looks unfiltered and it installs every
PC-DMIS version it cannot detect, on every CMM, in one cycle.

It was comparing the fleet's reported library versions against manifestversion.
That is the manifest's own 'Version' field. For a share-imported manifest the
two numbering schemes happen to coincide; for a scope authored in code they do
not, and seed_display_scope writes '2.0' - which every kiosk exceeds. So the
gate passed on the scope that most needed it.

A scope now declares minlibversion. Unset, the requirement is DERIVED from what
the manifest actually uses, so a scope written before this column existed is
still judged on its contents rather than on a number that says nothing about the
library. Only features that narrow behaviour belong in that table; an addition
an old lib ignores harmlessly needs no floor. manifestversion remains the last
fallback, which preserves what share-imported manifests already relied on.
This commit is contained in:
cproudlock
2026-08-14 13:47:11 -04:00
parent 2df5028883
commit 838932a72d
5 changed files with 148 additions and 5 deletions

View File

@@ -352,6 +352,8 @@ def list_scopes():
'scopename': scope.scopename,
'phase': scope.phase,
'manifestversion': scope.manifestversion,
'minlibversion': scope.minlibversion,
'minlibversion': scope.minlibversion,
'computertypeid': scope.computertypeid,
'measuringtooltypeid': scope.measuringtooltypeid,
'iscommon': scope.iscommon,
@@ -462,6 +464,7 @@ def create_scope():
scope = ManifestScope(
scopename=scopename, phase=phase,
manifestversion=str(payload.get('manifestversion', '1.0')),
minlibversion=(payload.get('minlibversion') or None),
description=payload.get('description'),
computertypeid=payload.get('computertypeid'),
measuringtooltypeid=payload.get('measuringtooltypeid'),
@@ -496,6 +499,10 @@ def update_scope(scopeid):
setattr(scope, field, payload[field])
if 'manifestversion' in payload:
scope.manifestversion = str(payload['manifestversion'])
if 'minlibversion' in payload:
# Blank clears it, which returns the scope to a derived floor rather
# than pinning it at the empty string.
scope.minlibversion = (payload['minlibversion'] or None)
if 'iscommon' in payload:
scope.iscommon = bool(payload['iscommon'])
db.session.commit()
@@ -764,9 +771,9 @@ def publish_preflight(scopeid):
if not scope:
return error_response(ErrorCodes.NOT_FOUND, 'No such scope', http_code=404)
hosts, floor = service.hosts_below_libversion(
scope.scopename, scope.phase, scope.manifestversion)
scope.scopename, scope.phase, service.required_libversion(scope))
return success_response({
'required': scope.manifestversion,
'required': service.required_libversion(scope),
'floor': floor,
'hostsbehind': hosts,
'canpublish': not hosts,