закрыты известные остатки

- документ самоуправления объявляется ключом governance, а не угадывается
  по «он один и без ключей оси»: конвенция, потерявшая topic, была от него
  неотличима и тихо теряла все проверки об отъезде к потребителю
- проверка путей канона больше не ловит README.md и READING.md — эти два
  имени значат что-то и на стороне потребителя
- lang.Recognize требует совпадения и слов, и номера версии; директории
  компонентов сверяются на вложенность, а не только на равенство
- у обеих проверок появился --json, а convy sync называет ссылки на темы,
  которых компонент не взял
This commit is contained in:
av
2026-07-28 10:16:27 +03:00
parent 29d29740f5
commit 600aba5ee3
16 changed files with 413 additions and 70 deletions
+27 -18
View File
@@ -131,31 +131,40 @@ func checkTopicNames(s *suite.Suite, rep *Report) {
// checkSelfGoverning guards the document without a topic.
//
// A topic-less document is the one the suite governs itself by: it travels
// nowhere and cannot be subscribed to, so it gets no spread checks. Nothing in
// the manifest tells it from a convention that lost its topic key, and the
// silent loss is the expensive one — the file keeps every check of form while
// quietly dropping every check about travelling to a consumer. Two markers make
// that loss visible: such a document is one per suite, and it has no layer of
// its own, hence neither axis keys nor a base.
// nowhere and cannot be subscribed to, so it gets no spread checks. A
// convention that lost its topic key looks exactly the same, and that loss is
// the expensive one — the file keeps every check of form while quietly dropping
// every check about travelling to a consumer.
//
// Which file it is, the manifest says. Guessing was tried and does not reach:
// "there is one of them" and "it has no axis keys" both hold for a suite whose
// only topic-less file is a convention with the key knocked out.
func checkSelfGoverning(s *suite.Suite, rep *Report) {
var topicless []string
declared := s.Manifest.Governance
if declared != "" && !s.Exists(declared) {
rep.Errorf(Manifest, manifest.Name, 0,
"governance names %q, and the file is missing", declared)
}
for _, d := range s.Docs {
if d.Front.Topic != "" {
if s.Manifest.Governs(d.Path) {
rep.Errorf(Manifest, d.Path, d.Front.At["topic"],
"the manifest names this file the one the suite governs itself by, and it declares topic %q: such a document belongs to no topic, because nobody may subscribe to it", d.Front.Topic)
}
continue
}
topicless = append(topicless, d.Path)
if d.Front.Axis() || d.Front.Extends != "" {
switch {
case declared == "":
rep.Errorf(Manifest, d.Path, 1,
"the file declares no topic, and the manifest names no document the suite governs itself by: either the topic key is lost, or the manifest has to say governance = %q", d.Path)
case !s.Manifest.Governs(d.Path):
rep.Errorf(Manifest, d.Path, 1,
"the file declares no topic, while the manifest names %q as the one the suite governs itself by: a convention without a topic has lost the key", declared)
case d.Front.Axis() || d.Front.Extends != "":
rep.Errorf(Manifest, d.Path, d.Front.At["prefix"],
"the file declares no topic yet carries the keys of a layer: a document without a topic is the one the suite governs itself by, and it is nobody's layer — the topic key looks lost")
}
}
if len(topicless) > 1 {
sort.Strings(topicless)
for _, path := range topicless[1:] {
rep.Errorf(Manifest, path, 1,
"the suite holds more than one document without a topic (%v): only the one the suite governs itself by may lack a topic, so the rest have lost the key",
topicless)
"the document the suite governs itself by carries the keys of a layer: it is nobody's layer, having no topic to be a layer of")
}
}
}