yowainwright / yowainwright/src-lint
Build a fast, polyglot import-boundary linter
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 1
- Forks
- 0
- Avg merge
- 9h 42m
- Merged PRs (30d)
- 7
Description
Build a fast, polyglot import-boundary linter
Build a standalone C CLI that rejects dependency edges which cross an intended service, component, package, or Proto boundary.
Contents
- 1. Enforcement contract
- 2. Configuration and inheritance
- 3. Analyzer and cache
- 4. Commands and graph output
- 5. Delivery and acceptance
1. Enforcement contract
Imports are the first enforcement seam. Call-chain analysis can build on the same graph after import checks are trustworthy.
| Finding | Default mode | Strict mode |
|---|---|---|
| Direct traversal into a sibling service's internals | Error | Error |
| Explicit configured boundary violation | Error | Error |
| Inferred boundary with incomplete evidence | Advisory | Error |
| Unresolved import | Advisory | Error |
services/orders/create.ts
-> ../billing/internal/ledger.ts TL1001 boundary violation
-> ../billing/api/index.ts allowed public entry
Zero-configuration analysis must fail on obvious sibling-service traversal. Discovery may suggest additional boundaries, but inference cannot weaken an explicit rule.
Initial files:
src/imports.csrc/boundaries.ctests/imports_test.c
2. Configuration and inheritance
One root rc file defines repository policy. A nested rc file inherits its parent configuration. Nested maps merge; child scalar and array values replace parent values.
version = 1
strict = false
[cache]
max_mib = 8
[boundaries.billing]
root = "services/billing"
public = ["api/**", "proto/**"]
allow = ["shared/**"]
The same model supports .tree-legibilityrc.toml, .tree-legibilityrc.json, and .tree-legibilityrc.yaml. Multiple rc files in one directory are an error.
repo/.tree-legibilityrc.toml
services/billing/.tree-legibilityrc.toml
root and allow are inherited
public is replaced by the child value
Initial files:
src/config.cinclude/tree_legibility/config.htests/config_test.c
3. Analyzer and cache
The executable is C11 with no runtime dependency. Language adapters produce the same file, symbol, and import-edge model for TypeScript, JavaScript, Python, Go, and Proto sources.
flowchart LR
Files["Changed source files"] --> Parsers["Language adapters"]
Parsers --> Graph["Dependency graph"]
Config["Merged boundary policy"] --> Rules["Boundary evaluator"]
Graph --> Rules
Rules --> Findings["Diagnostics and graph output"]
Cache["8 MiB repository cache"] <--> Parsers
Tree-sitter adapters are the target parser layer. The first vertical slice may use lexical adapters behind that interface so the graph and enforcement contracts can be tested before grammars are vendored.
The disposable cache lives at .tree-legibility/cache/ and defaults to an 8 MiB hard limit. A key covers tool version, parser version, effective configuration, path, and source content. Eviction is least-recently-used by stored bytes.
Performance budgets:
--helpstarts in at most 10 ms in a release build.- A warm one-file check takes at most 50 ms in the 10,000-file fixture.
- The cache stays within its configured limit after every successful command.
Initial files:
src/scanner.csrc/graph.csrc/cache.c
4. Commands and graph output
The standalone CLI is the source of truth. ESLint, Ruff, golangci-lint, editors, and CI consume stable output instead of owning separate rule implementations.
tree-legibility check [path] [--strict] [--format text|json]
tree-legibility discover [path] [--format text|json]
tree-legibility graph [path] [--format json|html]
JSON graph output uses a node and edge document suitable for JSONCrack-like rendering. A violation edge includes its rule, source location, owner, and suggested public entry.
{
"nodes": [
{
"id": "orders/create.ts",
"boundary": "orders"
}
],
"edges": [
{
"from": "orders/create.ts",
"to": "billing/internal/ledger.ts",
"status": "violation"
}
]
}
Exit codes are stable.
0means the graph is clean.1means policy findings exist.2means the input or configuration is invalid.
Relevant references
5. Delivery and acceptance
The first vertical slice proves the core path before adding every parser and rc syntax.
| Phase | Result |
|---|---|
| Foundation | CMake build, bounded ignore policy, diagnostics, and tests |
| Import slice | TypeScript and JavaScript imports with service-boundary failures |
| Polyglot | Python, Go, and Proto adapters with shared fixtures |
| Policy | TOML, JSON, and YAML parity with parent-child merging |
| Insight | Discovery, incremental cache, JSON graph, and interactive HTML |
The import slice is complete when two sibling services produce one deterministic TL1001 diagnostic, permit a public entry, emit equivalent JSON, and pass under AddressSanitizer.
Initial files
CMakeLists.txtsrc/main.ctests/fixtures/services/.github/workflows/ci.yml
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read CMakeLists.txt and src/main.c first, then inspect src/imports.c, src/boundaries.c, and tests/imports_test.c for the initial import slice. Start with the TypeScript and JavaScript service fixtures under tests/fixtures/services/ and run the CMake test workflow. Done means deterministic TL1001 output, public-entry allowance, equivalent JSON, and AddressSanitizer passing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, go, javascript, python, typescript
- Domain
- build-system, cli, devtools, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100