Judge every manifest against the list of predefined keys of its tree, and carry the list from a build
Mesh and node config may hold values only for keys on a fixed, reviewed list (hq ADR 0348 clause 7, design 57 package K2, issue 661). A build reads predefined.json at the root of the repository at the commit it builds and carries it with the commit's generation; module check and the merge gate read it from the tree. (Part 1 of 2: the changes to existing files.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -61,18 +61,6 @@ const (
|
||||
// accountFacts are what the mesh places for a module's account, never config values (ADR 0348 §11).
|
||||
var accountFacts = map[string]bool{"puid": true, "pgid": true}
|
||||
|
||||
// PredefinedKey is one key of the catalogue's list of predefined keys, as far as a module's value falling back
|
||||
// to it is judged here: its type, and whether it is trusted (ADR 0348 §7, §8).
|
||||
type PredefinedKey struct {
|
||||
Type string
|
||||
Trusted bool
|
||||
}
|
||||
|
||||
// predefinedKeys is the list of predefined keys, nil until it is present. The list (`predefined.json` at the
|
||||
// catalogue's root) arrives with design 57's package K2 (issue 661); until then a value naming one is refused
|
||||
// saying the list is not yet present.
|
||||
var predefinedKeys map[string]PredefinedKey
|
||||
|
||||
// configForms is which of the two forms a manifest was written in, by the words of each it uses.
|
||||
type configForms struct {
|
||||
config, settings []string
|
||||
@@ -259,12 +247,13 @@ func ConfigProblems(m Manifest) []string {
|
||||
"(novox/hq ADR 0348 §4, ADR 0112)", word)
|
||||
}
|
||||
if d.Predefined != "" {
|
||||
p, listed := predefinedKeys[d.Predefined]
|
||||
p, listed := m.predefined[d.Predefined]
|
||||
switch {
|
||||
case predefinedKeys == nil:
|
||||
say("falls back to the predefined key %q, and the list of predefined keys (the catalogue's "+
|
||||
"predefined.json, design 57 package K2) is not yet present: no value can fall back to one "+
|
||||
"until it is; leave \"predefined\" out for now (novox/hq ADR 0348 §7)", d.Predefined)
|
||||
case m.predefined == nil:
|
||||
say("falls back to the predefined key %q, and the list of predefined keys (%s at the root of "+
|
||||
"the catalogue's repository) is not present where it is judged: a value falls back only to a "+
|
||||
"key on that list; judge it in the catalogue, or leave \"predefined\" out (novox/hq ADR 0348 §7)",
|
||||
d.Predefined, PredefinedFile)
|
||||
case !listed:
|
||||
say("falls back to %q, which is not on the list of predefined keys: name a key on it, or leave "+
|
||||
"\"predefined\" out (novox/hq ADR 0348 §7)", d.Predefined)
|
||||
|
||||
@@ -157,20 +157,20 @@ func TestConfigRefusesAnObjectOrListNotTrusted(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// withPredefined is the list of predefined keys as package K2 will give it, for the length of a test.
|
||||
func withPredefined(t *testing.T, keys map[string]PredefinedKey) {
|
||||
// withPredefined is the list of predefined keys the mesh published, for the length of a test.
|
||||
func withPredefined(t *testing.T, keys PredefinedList) {
|
||||
t.Helper()
|
||||
was := predefinedKeys
|
||||
predefinedKeys = keys
|
||||
t.Cleanup(func() { predefinedKeys = was })
|
||||
was := ReplacePublished(keys)
|
||||
t.Cleanup(func() { ReplacePublished(was) })
|
||||
}
|
||||
|
||||
// Rule 5: a predefined key not on the list, or of another type. Until the list is present (package K2), a value
|
||||
// naming one is refused saying so.
|
||||
// Rule 5: a predefined key not on the list, or of another type. Where no list is present, a value naming one is
|
||||
// refused saying so.
|
||||
func TestConfigRefusesAPredefinedKeyNotOnTheListOrOfAnotherType(t *testing.T) {
|
||||
withPredefined(t, nil)
|
||||
m := inConfigForm()
|
||||
declaration(m, "font-size")["predefined"] = "font-size"
|
||||
refused(t, m, `"font-size"`, "the list of predefined keys", "not yet present")
|
||||
refused(t, m, `"font-size"`, "the list of predefined keys", "is not present")
|
||||
|
||||
withPredefined(t, map[string]PredefinedKey{"font-size": {Type: "number"}, "accent": {Type: "string"}})
|
||||
if _, err := parseRaw(t, m); err != nil {
|
||||
@@ -186,9 +186,10 @@ func TestConfigRefusesAPredefinedKeyNotOnTheListOrOfAnotherType(t *testing.T) {
|
||||
|
||||
// Rule 6: an untrusted value falling back to a trusted predefined key.
|
||||
func TestConfigRefusesAnUntrustedValueFallingBackToATrustedKey(t *testing.T) {
|
||||
withPredefined(t, nil)
|
||||
m := inConfigForm()
|
||||
declaration(m, "theme")["predefined"] = "theme"
|
||||
refused(t, m, `"theme"`, "not yet present")
|
||||
refused(t, m, `"theme"`, "is not present")
|
||||
|
||||
withPredefined(t, map[string]PredefinedKey{"theme": {Type: "string", Trusted: true}})
|
||||
refused(t, m, `"theme"`, "trusted", "is not")
|
||||
|
||||
@@ -541,6 +541,10 @@ type Manifest struct {
|
||||
// machine's declaration by name until the controller is updated (LeftOut).
|
||||
unknown string
|
||||
|
||||
// predefined is the list of predefined keys it was judged against (predefined.go, novox/hq ADR 0348 §7):
|
||||
// read again — its templates filled from the tree — it is judged against the same list.
|
||||
predefined PredefinedList
|
||||
|
||||
// bestEffort marks the view of a left-out module that only its backup lines are made from
|
||||
// (backupView, novox/hq ADR 0262): a line of it that cannot be placed is said in the plan's list of
|
||||
// what could not be placed, never an error that would cost the whole machine its declaration.
|
||||
@@ -1600,6 +1604,17 @@ func SecretLocal(to, local string) string {
|
||||
func (m Manifest) UnknownField() string { return m.unknown }
|
||||
|
||||
func ParseManifest(raw []byte) (Manifest, error) {
|
||||
return parseManifest(raw, PublishedPredefined())
|
||||
}
|
||||
|
||||
// ParseManifestWith is ParseManifest judging every value that falls back to a predefined key against the list
|
||||
// given — the one read from the tree the manifest is in (predefined.go, novox/hq ADR 0348 §7) — where
|
||||
// ParseManifest judges against the list the mesh published. Nil is no list: a value naming a key is refused.
|
||||
func ParseManifestWith(raw []byte, predefined PredefinedList) (Manifest, error) {
|
||||
return parseManifest(raw, predefined)
|
||||
}
|
||||
|
||||
func parseManifest(raw []byte, predefined PredefinedList) (Manifest, error) {
|
||||
var m Manifest
|
||||
// Strictly. **An unknown key is refused**, which is the discipline the host's declaration
|
||||
// parser has and manifests lacked (novox/hq 04-ISSUES/003): a `scope:` key survived in five
|
||||
@@ -1627,6 +1642,7 @@ func ParseManifest(raw []byte) (Manifest, error) {
|
||||
}
|
||||
return Manifest{}, fmt.Errorf("this is not a module manifest: %w", err)
|
||||
}
|
||||
m.predefined = predefined
|
||||
|
||||
var problems []string
|
||||
if !name.MatchString(m.Module) {
|
||||
|
||||
@@ -312,7 +312,7 @@ func WithTemplatesFrom(m Manifest, dir string) (Manifest, error) {
|
||||
if err != nil {
|
||||
return Manifest{}, err
|
||||
}
|
||||
return ParseManifest(raw)
|
||||
return parseManifest(raw, m.predefined)
|
||||
}
|
||||
|
||||
// HeldTemplateProblem is why a manifest cannot be held by the mesh because it still names a template, or "": the
|
||||
|
||||
Reference in New Issue
Block a user