-
Notifications
You must be signed in to change notification settings - Fork 1
Close kernel conformance freshness and authority gaps #207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d42dcc6
cf15aa7
71c7937
fbd8679
cd211c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ type metadata struct { | |
| Path string `json:"path"` | ||
| Imports []string `json:"imports"` | ||
| RuntimeConsumers []string `json:"runtime_consumers"` | ||
| Vocabulary []string `json:"vocabulary"` | ||
| } | ||
|
|
||
| func main() { | ||
|
|
@@ -35,11 +36,11 @@ func main() { | |
| if filename == "--" { | ||
| continue | ||
| } | ||
| parsed, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.AllErrors) | ||
| parsed, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.AllErrors|parser.ParseComments) | ||
| if err != nil { | ||
| fatalf("parse %s: %v", filename, err) | ||
| } | ||
| item := metadata{Path: filename, Imports: []string{}, RuntimeConsumers: []string{}} | ||
| item := metadata{Path: filename, Imports: []string{}, RuntimeConsumers: []string{}, Vocabulary: []string{}} | ||
| kernelAliases := map[string]bool{} | ||
| for _, spec := range parsed.Imports { | ||
| importPath, err := strconv.Unquote(spec.Path.Value) | ||
|
|
@@ -58,20 +59,33 @@ func main() { | |
| } | ||
| ast.Inspect(parsed, func(node ast.Node) bool { | ||
| switch value := node.(type) { | ||
| case *ast.ImportSpec: | ||
| return false | ||
|
Comment on lines
+62
to
+63
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P3] Import-attached comments bypass the vocabulary guard Invariant: every forbidden domain token in kernel source comments must be detected. A production file containing Confidence: 0.99 |
||
| case *ast.SelectorExpr: | ||
| alias, ok := value.X.(*ast.Ident) | ||
| if ok && kernelAliases[alias.Name] && runtimeSymbols[value.Sel.Name] { | ||
| item.RuntimeConsumers = append(item.RuntimeConsumers, value.Sel.Name) | ||
| } | ||
| case *ast.Ident: | ||
| item.Vocabulary = append(item.Vocabulary, value.Name) | ||
| if kernelAliases["."] && runtimeSymbols[value.Name] { | ||
| item.RuntimeConsumers = append(item.RuntimeConsumers, value.Name) | ||
| } | ||
| case *ast.BasicLit: | ||
| if value.Kind == token.STRING { | ||
| decoded, err := strconv.Unquote(value.Value) | ||
| if err == nil { | ||
| item.Vocabulary = append(item.Vocabulary, decoded) | ||
| } | ||
| } | ||
| case *ast.Comment: | ||
| item.Vocabulary = append(item.Vocabulary, value.Text) | ||
| } | ||
| return true | ||
| }) | ||
| sort.Strings(item.Imports) | ||
| sort.Strings(item.RuntimeConsumers) | ||
| sort.Strings(item.Vocabulary) | ||
| results = append(results, item) | ||
| } | ||
| if err := json.NewEncoder(os.Stdout).Encode(results); err != nil { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,19 @@ | |
| REPO = Path(__file__).resolve().parents[2] | ||
| RUNTIME = REPO / "boatstack" | ||
| CONFIG = REPO / "project.example.json" | ||
| DOMAIN_NEUTRAL_ROOTS = ( | ||
| ("pullrequest", "pull request"), | ||
| ("codingagent", "coding agent"), | ||
| ("github", "github"), | ||
| ("repository", "repository"), | ||
| ("worktree", "worktree"), | ||
| ("publication", "publication"), | ||
| ("branch", "branch"), | ||
| ("git", "git"), | ||
| ) | ||
| DOMAIN_NEUTRAL_PHRASE = re.compile(r"\b(?:pull\s+request|coding\s+agent)\b", re.IGNORECASE) | ||
| GO_IDENTIFIER = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") | ||
| GO_IDENTIFIER_PART = re.compile(r"[A-Z]?[a-z]+|[A-Z]+(?![a-z])|[0-9]+") | ||
|
|
||
|
|
||
| def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: | ||
|
|
@@ -36,6 +49,48 @@ def go_source_metadata(paths: list[Path]) -> list[dict[str, object]]: | |
| return json.loads(result.stdout) | ||
|
|
||
|
|
||
| def identifier_domain_token(identifier: str) -> str | None: | ||
| for component in identifier.split("_"): | ||
| for part in GO_IDENTIFIER_PART.findall(component): | ||
| normalized = part.lower() | ||
| for root, token in DOMAIN_NEUTRAL_ROOTS: | ||
| if normalized == root or normalized.startswith(root): | ||
| return token | ||
|
Comment on lines
+56
to
+58
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P3] Prefix matching rejects neutral branching tests Invariant: the domain-neutrality verifier must accept identifiers using Confidence: 0.96 |
||
| return None | ||
|
|
||
|
|
||
| def import_domain_token(import_path: str) -> str | None: | ||
| components = import_path.split("/") | ||
| if components and components[0] == "github.com": | ||
| components = components[1:] | ||
| for component in components: | ||
| for identifier in re.split(r"[.-]", component): | ||
| token = identifier_domain_token(identifier) | ||
| if token is not None: | ||
| return token | ||
| return None | ||
|
|
||
|
|
||
| def domain_vocabulary_hits(paths: list[Path]) -> list[tuple[Path, str]]: | ||
| hits: list[tuple[Path, str]] = [] | ||
| for path, metadata in zip(paths, go_source_metadata(paths), strict=True): | ||
| for import_path in metadata["imports"]: | ||
| token = import_domain_token(import_path) | ||
| if token is not None: | ||
| hits.append((path, token)) | ||
| for source in metadata["vocabulary"]: | ||
| phrase = DOMAIN_NEUTRAL_PHRASE.search(source) | ||
| if phrase is not None: | ||
| hits.append((path, phrase.group(0).lower())) | ||
| continue | ||
| for identifier in GO_IDENTIFIER.findall(source): | ||
| token = identifier_domain_token(identifier) | ||
| if token is not None: | ||
| hits.append((path, token)) | ||
| break | ||
| return hits | ||
|
|
||
|
|
||
| class RepositoryContract(unittest.TestCase): | ||
| @classmethod | ||
| def setUpClass(cls) -> None: | ||
|
|
@@ -415,15 +470,35 @@ def test_public_tree_excludes_private_context_and_v1_operating_guidance(self) -> | |
| def test_general_kernel_is_domain_neutral_and_owns_shared_control_laws(self) -> None: | ||
| kernel = REPO / "boatstack" / "kernel" | ||
| kernel_files = sorted(kernel.glob("*.go")) | ||
| production_files = [ | ||
| path for path in kernel_files if not path.name.endswith("_test.go") | ||
| ] | ||
| source = "\n".join(path.read_text() for path in production_files) | ||
| for token in ( | ||
| "git", "repository", "worktree", "branch", "pull request", | ||
| "coding agent", "publication", | ||
| ): | ||
| self.assertNotIn(token, source.lower(), token) | ||
| self.assertEqual([], domain_vocabulary_hits(kernel_files)) | ||
| with tempfile.TemporaryDirectory() as temporary: | ||
| fixture = Path(temporary) / "domain_leak_test.go" | ||
| for source, token in ( | ||
| ('package kernel\nconst fixtureDomain = "pull request"\n', "pull request"), | ||
| ("package kernel\ntype gitClient struct{}\n", "git"), | ||
| ("package kernel\nvar testRepository string\n", "repository"), | ||
| ("package kernel\ntype worktreeManager struct{}\n", "worktree"), | ||
| ('package kernel\nconst provider = "github"\n', "github"), | ||
| ("package kernel\ntype githubClient struct{}\n", "github"), | ||
| ("package kernel\ntype gitclient struct{}\n", "git"), | ||
| ("package kernel\ntype repositoryclient struct{}\n", "repository"), | ||
| ("package kernel\ntype worktreemanager struct{}\n", "worktree"), | ||
| ("package kernel\ntype branchmanager struct{}\n", "branch"), | ||
| ("package kernel\ntype publicationqueue struct{}\n", "publication"), | ||
| ("package kernel\ntype pullrequesthandler struct{}\n", "pull request"), | ||
| ("package kernel\ntype codingagentpolicy struct{}\n", "coding agent"), | ||
| ): | ||
| with self.subTest(token=token): | ||
| fixture.write_text(source) | ||
| self.assertEqual([(fixture, token)], domain_vocabulary_hits([fixture])) | ||
| fixture.write_text('package kernel\nimport "github.com/example/provider"\n') | ||
| self.assertEqual([], domain_vocabulary_hits([fixture])) | ||
| fixture.write_text( | ||
| 'package kernel\nimport vcs "github.com/go-git/go-git/v5"\nvar _ = vcs.PlainClone\n' | ||
| ) | ||
| self.assertEqual([(fixture, "git")], domain_vocabulary_hits([fixture])) | ||
| fixture.write_text("package kernel\nfunc TestDigitalSignature() {}\n") | ||
| self.assertEqual([], domain_vocabulary_hits([fixture])) | ||
|
|
||
| boatstack_packages = "github.com/operatorstack/boatstack/boatstack/" | ||
| kernel_package = boatstack_packages + "kernel" | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.