Skip to content

Nothing checks Starlark, and dead API calls survive for months #721

Open

Description

@David-Noble-at-work

Feature: #837 — lint provider. (Moved from QualityCoverage on 2026-09-05: lint.starlark is an embedded linter, and every lint files under the lint provider.)

Charter: docs/plans/lint-starlark.md

The observed failure

devlore-registry/packages/docker/ shipped 28 .star files. Read against the provider tree on
2026-08-26, the deploy path could not execute on any platform:

Written in the package Reality
plan.package.install("docker-ce", "docker-ce-cli") No package namespace — it is pkg, and it takes a list, not varargs
plan.verify(name, check=…, optional=True) No Verify method on any provider. All four verify.star files are entirely these calls
plan.file.write(path=…, content=…) It is plan.file.write_text(destination_path=…, content=…, mode=…)
plan.user.add_to_group(user, "docker") No user provider
plan.download(url, dest) appnet.download(url) returns bytes; there is no dest form
plan.notify(…) Does not exist
Upgrade/install.star UpgradePhaseOrder is {prepare, upgrade, migrate, verify} — install is not an upgrade phase

The registry's CI validates knowledge schemas and package structure. None of it reads a .star
file's contents, so all of the above passed continuously.

A syntax checker would not have helped

Every line above is syntactically valid Starlark. buildifier parses all of them without
complaint and always would have. The defects are resolution errors — wrong namespace, absent
method, wrong arity, wrong parameter name — and a general-purpose Starlark formatter knows nothing
about devlore's provider surface, so it structurally cannot see them.

This is the load-bearing point: the cheap check is available today and would have caught nothing.

The cost is low, because the machinery exists

  1. go.starlark.net is already a direct dependency; go.starlark.net/syntax is imported by ten files.
  2. cmd/star/provider/starindex/provider.go already parses Starlark — syntax.FileOptions{},
    walking DefStmt, LoadStmt, AssignStmt. Its doc: "AST-based indexing of Starlark source files."
  3. Resolution ground truth is generated: action_names.gen.go enumerates every valid
    <namespace>.<method>; the ParameterNames tables in each gen/provider.gen.go enumerate every
    valid keyword argument. Reading these means the checker cannot drift from codegen — the same
    discipline Knowledge/extract.star already applies to Go.
  4. There is a home and a hole: LintCopyright, LintGo, LintGoStyle, LintMarkdown, LintShell,
    LintTools — and no LintStarlark, across 162 .star files.

Rules worth considering beyond dead references

  • Lifecycle verbs. cmd/lore/lore/builder.go:31 denies plan.assemble, clear, load, run,
    save to phase scripts at runtime. A checker could reject them at author time.
  • Phase entry point. Every phase script must define def <phase>(package, phase) matching its
    filename and its action directory's phase order.
  • Lambdas — conditional on devlore-test writes the receipt after deleting the workspace it reads from #720. Whether this belongs in a linter depends on whether that fix lands.

What has to be discovered

  • How much of plan.<namespace>.<method>(…) resolution is tractable statically? Starlark is dynamic;
    handling the literal call shape likely covers every real package script, but coverage should be
    measured, not assumed.
  • Star extension (LintStarlark, joining make check via LintAll), Go analyzer, or star
    subcommand? The extension family is the obvious home, but star currently fails to load its own
    extensions.
  • Does it run against devlore-registry too? The motivating failure is in that repo, whose CI would
    need the checker as a built artifact.
  • Adopt buildifier alongside as a parse gate? Note -module-docstring and -unused-variable are
    both wrong for devlore — the latter fires on the mandated (package, phase) signature of every
    phase script ever written. Its formatter also rewrites kwarg=value to kwarg = value, which no
    file in the corpus uses and which is not configurable.

Exit criteria

  • Running the checker over devlore-registry/packages/docker/ as it stood on 2026-08-26 reports
    every row in the table above. That corpus is the regression fixture — worth preserving before the
    stale files are deleted.
  • The checker reads the generated tables rather than a hand-maintained surface list, proven by adding
    a provider method and observing the checker accept it with no edit.
  • Zero false positives across the 162 .star files in devlore-cli.

Epic: #452 · Feature: #340 — this is #340's Phase 2 row, star lint starlark, which its coverage table marks Not done.


Epic: #815 · Feature: #837

Plan and design documents

Kind Document
Plan docs/plans/feature/721-lint-starlark.md

The link is to develop and is dead until the branch merges, which is the form the process
requires: a link to the feature branch is correct only while the work is open and dead from the moment
it closes, which is when the issue is most likely to be read.

Activity

  1. David-Noble-at-work commented on Sep 5, 2026

    @David-Noble-at-work
    ContributorAuthor

    Ruled 2026-09-05: the Starlark linter is a built-in, in Go, beside the code model — not a LintStarlark extension. That closes the charter's open question ("Star extension, Go analyzer, or star subcommand?") in favour of the built-in, which also removes the circularity the charter noted: a .star extension checking .star files would depend on the loader it is meant to protect.

    The extension family stays the home for the lints that shell out — shellcheck, buildifier — and star lint starlark can still be the command name; what changes is that it is implemented in Go against the generated action tables, the way the goast analysis is.

    Interim, also ruled: buildifier now, rather than waiting for this. It is available, catches formatting and a handful of real defects (confusing names, unused variables), and is already gating noblefactor-ops#140's .star files in the PR script. It is not a substitute for resolution checking — every one of the registry's 28 broken files would pass it — and this issue remains the real fix.

  2. added
    Epic:ProvidersThe provider surface: one feature per provider
    and removed
    Epic:QualityCoverageCode quality, coverage, and the lint gates
    on Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Epic:ProvidersThe provider surface: one feature per providertaskImplementation work item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions