Skip to content

Replace Makefile with Taskfile.yml and move all generation-related logic there#5050

Open
denik wants to merge 45 commits intomainfrom
denik/task-file
Open

Replace Makefile with Taskfile.yml and move all generation-related logic there#5050
denik wants to merge 45 commits intomainfrom
denik/task-file

Conversation

@denik
Copy link
Copy Markdown
Contributor

@denik denik commented Apr 21, 2026

Each task describes inputs (sources) and outputs as precise as possible.

The tasks mostly match what we had in Makefile but slightly more consistent approach:

  • All go related tasks (tidy, lint, test) now have 3 subtasks for each submodule (root, tools, codegen) which are aggregated into main one. Previously we did not run tests & linters for tools and codegen modules.
  • The targets that use lintdiff.py to do incremental run have -q suffix (old fmtfull -> new fmt, old fmt -> new fmt-q).
  • generate:genkit is task that runs genkit + follow ups. “generate” is aggregate over all generation work.

This also consolidates all knowledge about generation in one place:

  • tools/post-process.sh is removed, the followup commands are now part of generate:genkit task
  • testmask no longer contains of dependencies of CI targets, it reads those from Taskfile.yml

The runner (go-task) is packaged as a go tool, no installation need. Shortcut ./task is available to run it. Makefile is temporarily a wrapper that calls ./task.

This enables caching - go-task will only re-run if sources changed. This helps when checking after agent work - if they did run linters / tests already, then running ./task is very quick.

~/work/cli-trees/task-file % time ./task

./task 612.63s user 310.22s system 726% cpu 2:06.95 total

~/work/cli-trees/task-file % time ./task

./task 2.43s user 10.58s system 133% cpu 9.727 total

Additionally, all golangci-lint tasks are fixed to use per-worktree & per-submodule tmp directory, which enables parallel runs within worktree and across worktrees.

denik added 30 commits April 21, 2026 14:08
Replace the hardcoded prefix table with parsing of Taskfile.yml so the
list of paths that triggers each CI test target is derived from the
test:exp-aitools, test:exp-ssh, and test:pipelines tasks' `sources:`.
Also makes the tool CWD-independent via `git rev-parse --show-toplevel`.

Co-authored-by: Isaac
Introduce Taskfile.yml as the single source of truth for build, test,
lint, format, and codegen commands. Provide a thin Makefile wrapper that
forwards all targets to task so existing `make <target>` invocations keep
working for single-word targets. Move python/Makefile into the same
Taskfile under python:* tasks.

Task is vendored via a separate tools/task module to avoid its
charmbracelet dependencies conflicting with tools/go.mod (golangci-lint
pins older charmbracelet versions).

Also update acceptance test helper to build yamlfmt directly — the
Makefile wrapper can no longer forward the old `tools/yamlfmt` target
since it's now a Task-only dependency.

Co-authored-by: Isaac
Replace `make <target>` with `go tool -modfile=tools/task/go.mod task <target>`
in CI workflows and the setup-build-environment action. This skips the
Makefile wrapper and surfaces task's real target names (e.g. task:exp-aitools,
fmt:full) in CI logs.

release-build.yml inlines the python wheel build steps (which used to live
in python/Makefile) so the release job does not depend on task.

Co-authored-by: Isaac
Replace make commands with their task equivalents in the build/test and
code-quality sections so AI agents and human readers see the current
invocation style.

Co-authored-by: Isaac
Each submodule gets its own minimal .golangci.yaml so lint:tools and
lint:codegen can run from those directories without inheriting the root
config. The root config enables the ruleguard gocritic check against
`libs/gorules/rule_*.go`, which golangci-lint resolves relative to the
working directory — from inside tools/ or bundle/internal/tf/codegen/
that path does not exist and lint aborts before running.

Co-authored-by: Isaac
Shortens `go tool -modfile=tools/task/go.mod task <target>` to
`./task <target>` and resolves the modfile via `git rev-parse
--show-toplevel` so the script works from any subdirectory.

Co-authored-by: Isaac
Replace every `go tool -modfile=tools/task/go.mod task` invocation in CI
workflows, the Makefile wrapper, and the dbr:test deco command with the
shorter `./task`. The dbr:test command previously pointed at tools/go.mod,
which does not include task as a tool — the wrapper fixes that too.

Co-authored-by: Isaac
- build: correct generates from `databricks` to `cli` (Go module name)
- drop build:vm (unused)
- snapshot, snapshot:release, schema:for-docs, docs: add sources/generates
- lint:tools, lint:codegen: include .golangci.yaml, go.mod, go.sum in sources
- python:docs, python:codegen: include docs and databricks inputs
- split fmt into fmt:python, fmt:go, fmt:yaml; fmt and fmt:full dispatch
  the three in parallel via deps

Co-authored-by: Isaac
Renames lint:check/lint:tools/lint:codegen to lint:go:root/lint:go:tools/
lint:go:codegen to make it explicit they're Go-specific. Adds lint:go that
runs all three in parallel so a single invocation covers the whole repo.

Co-authored-by: Isaac
The `fmt` aggregate uses the incremental `fmt:go` (via lintdiff.py) for a
fast interactive loop, while `fmt:full` uses `fmt:go:full` to sweep the
whole tree. Python and YAML tasks are shared between the two aggregates.

Co-authored-by: Isaac
The root CLI binary only imports from bundle/, cmd/, experimental/,
internal/, libs/, so test-only trees (acceptance/, integration/), the
tools/ and bundle/internal/tf/codegen/ submodules, and _test.go files
are excluded from build/snapshot/snapshot:release source globs. This
lets Task's checksum cache skip rebuilds when only tests change.

Task v3 uses `- exclude: <pattern>` for glob exclusion (the `!` prefix
from doublestar isn't honored by Task's source matching).

Also remove the _build-yamlfmt task — nothing referenced it, and
acceptance tests build yamlfmt directly via BuildYamlfmt with exeSuffix
for Windows.

Co-authored-by: Isaac
Introduce `generate` as an aggregator that runs each generator in
sequence:
- `generate:commands` (universe + genkit; was top-level `generate`)
- `generate:schema` (was `schema`; now has tight sources, no longer
  gated by `git diff` against merge-base)
- `generate:schema-docs` (was `schema:for-docs`)
- `generate:docs` (was `docs`)
- `generate:validation` (now has tight sources)
- `generate:direct` (unchanged)
- `generate:openapi-json` (was `codegen:openapi-json`)

Each reflection-based generator (schema, schema-docs, validation, docs)
lists its Go sources explicitly and includes `go.mod`/`go.sum`, so the
aggregator picks up SDK version bumps from `generate:commands`
automatically. Test files are excluded from the source globs.

Trim `tools/post-generate.sh` to only post-process genkit output
(consistency check, tagging.py relocation, whitespace fix). The former
`make schema`/`make schema-for-docs`/`make generate-validation`/
`make -C python codegen` calls are gone — those run as standalone tasks
in the aggregator now.

Update `.github/workflows/push.yml`, `AGENTS.md`,
`.agent/rules/auto-generated-files.md`,
`.agent/skills/pr-checklist/SKILL.md`, `bundle/docsgen/README.md`, and
schema test comments to reference the new task names.

Also add `.databricks/` (snapshot binary output) to `.gitignore`.

Co-authored-by: Isaac
…efault

- Move refschema regeneration into a standalone generate:refschema task
  with a tight sources list so the cache tracks what actually affects
  `bundle debug refschema` (cmd, dresources adapters, libs/structs, go.mod).
- generate:commands now only runs genkit + post-process.
- Aggregator runs generate:refschema between commands and the other
  generators so fresh out.fields.txt feeds generate:direct.
- default now invokes the full generate aggregator instead of just
  generate:schema + generate:validation.
- Fix acceptance/install_terraform.py to skip doc-comment lines when
  reading ProviderVersion (regressed by the recent doc comment added to
  bundle/internal/tf/codegen/schema/version.go).

Co-authored-by: Isaac
Convention change: the plain task name is the full variant. Quick /
incremental variants carry a -q suffix on the top-level namespace.

- fmt:full -> fmt, fmt:go:full -> fmt:go (full is the default)
- fmt (old incremental) -> fmt-q; fmt:go (old incremental) -> fmt-q:go
- lint:full removed; lint is now the aggregator across all Go modules
- lint (old incremental root+fix) -> lint-q:go:root; lint-q alias added
- lint:go aggregator runs modules sequentially (concurrent golangci-lint
  invocations have shown unreliable behavior)
- default task now runs the -q variants for a quick dev loop
- new `all` task runs the full suite (checks, fmt, lint, test, generate,
  python:lint, python:test) — replaces old default semantics

Close the gap where CI only linted the root module: CI now runs
`./task lint` which covers root + tools + bundle/internal/tf/codegen.

lintdiff.py gains --root-module to skip paths under nested go.mod files;
needed for `golangci-lint run` (typechecks) but not for `fmt` (filesystem
walk), so only lint-q:go:root opts in.

Co-authored-by: Isaac
Drop the --root-module flag; infer from the wrapped subcommand instead.
`golangci-lint run` typechecks and needs the filter; `golangci-lint fmt`
walks the filesystem and wants to see changed files across modules.
Callers no longer need to know which mode applies.

Co-authored-by: Isaac
generate:commands is expensive (bazel/genkit build) and requires a
universe checkout. Its outputs are committed, so a fresh worktree at any
commit already has them. Run `./task generate` explicitly when codegen
inputs change.

Co-authored-by: Isaac
- Use {{.GO_TOOL}} everywhere (redefine with {{.ROOT_DIR}} so tasks with
  `dir:` can reference it).
- Drop dead `{{.X | default ...}}` fallbacks in task-local vars; the
  top-level vars are always set and pass through via `deps:` + `vars:`.
- Serialize `checks` (tidy/ws/links) and `test:update-all` — they were
  racing on the same files via parallel `deps:`.
- Add `deps: [tidy]` to `snapshot` and `snapshot:release` (matches
  `build`).
- Add `desc:` to `generate:direct-apitypes` and
  `generate:direct-resources`.
- Expand `lint-q:go:root` desc to note it skips tools/ and codegen/.
- Drop `sources:` from `ws` (script is fast; no point caching).
- Fix code-generation section comment to list all reflection-based
  generators (refschema, schema-docs missing).

Co-authored-by: Isaac
- `lint:go:root` and `lint-q:go:root` now exclude tools/** and
  bundle/internal/tf/codegen/** from sources. golangci-lint `./...` stops
  at module boundaries, so changes there never affect root-module lint
  output — no reason to invalidate the cache.

- Split `tidy` by module, matching the `lint:go` layout:
  - `tidy` — aggregator across all three modules
  - `tidy:root`  — root (previously `tidy`)
  - `tidy:tools` — tools/
  - `tidy:codegen` — bundle/internal/tf/codegen

  `build`, `snapshot`, `snapshot:release` switch to `deps: [tidy:root]`
  (they only build the root module); `checks` still calls the
  aggregator.

Co-authored-by: Isaac
Mirrors main's e24ae4e (Remove Slow tests #5032): drop test:slow,
test:slow-unit, test:slow-acc, and the `-short` flag from test:unit
and test:acc. Slow tests support in the acceptance runner was removed
because the split between short and fast tests adds complexity and
makes it possible to miss failures.

Co-authored-by: Isaac
Switch lint:go and tidy aggregators from sequential cmds: to parallel
deps:. Each lint:go module sets its own TMPDIR under .tmp/ so concurrent
golangci-lint invocations — both siblings here and sibling worktrees
running lint at the same time — don't serialize on the shared /tmp lock.
The TMPDIR path lives under the repo (but not at ROOT_DIR itself, which
would trip Go's "go.mod in os.TempDir" check). Add /.tmp/ to .gitignore.

Co-authored-by: Isaac
Rename generate:direct-{apitypes,resources,clean} to
generate:direct:{apitypes,resources,clean} so the direct engine
subtasks follow the same colon-namespaced convention as lint:go:root,
tidy:root, etc.

Also broaden generate:refschema sources to all of bundle/ (excluding
_test.go) plus go.mod / go.sum — the command reflects over types
reachable from bundle/, so narrowing to a handful of packages risks
missing changes that affect the schema.

Co-authored-by: Isaac
…nerate.sh

Renamed because the task does more than generate CLI commands: it also emits
the tagging workflow + tagging.py, bumps the SDK version, and post-processes
the output. Moved the steps from tools/post-generate.sh (previously invoked
via .codegen.json post_generate hook) inline into the task so ./task generate
is self-contained and the tree is left clean afterwards. Also dropped
generate:direct:clean which was out of place.

Co-authored-by: Isaac
The tasks target the python/databricks-bundles package (pydabs), not
Python tooling in general (fmt:python stays, since that one does apply
repo-wide). Renaming avoids confusion and matches how the project
refers to the package elsewhere.

Co-authored-by: Isaac
The old `test:unit` only ran `go test ./...` in the root module; tests in
tools/ and bundle/internal/tf/codegen weren't run. Split into per-module
tasks plus an aggregator that concatenates their JSON outputs.

Also makes tools/testmask tolerate non-string `sources:` entries
(e.g. `- exclude: tools/**`), which previously made yaml.Unmarshal fail
once those excludes were introduced.

Co-authored-by: Isaac
Previously `cat … > test-output-unit.json` (and the top-level `test`
merge) ran every invocation even when all sub-tasks were cache-hit.
Declaring the inputs as `sources:` and the merged file as `generates:`
lets Task skip the cat when nothing upstream changed.

Co-authored-by: Isaac
Consistent with other colon-namespaced tasks (test:unit:*, test:acc).
testmask now derives the Taskfile task name from the CI output name by
replacing "-" with ":", so the list of targets no longer needs both
forms — a single slice suffices.

CI target identifiers (job names, cache keys) remain dash-separated.

Co-authored-by: Isaac
Rename to integration-short, dbr-integration, dbr-test.
These are single-task variants without a parent aggregator, so dash
suffixes fit better than colon namespacing.

Co-authored-by: Isaac
Redundant — the `deps:` structure already makes parallel execution
obvious to anyone reading the task definition, and users running
`./task --list` don't need that implementation detail in the summary.

Co-authored-by: Isaac
Previous comments claimed generate:genkit bumps the SDK version in
go.mod/go.sum; it doesn't. Genkit regenerates CLI command stubs from
.codegen/_openapi_sha and refreshes .gitattributes, but SDK bumps are
a manual `go get` step. TestConsistentDatabricksSdkVersion is what
keeps the two in sync.

Co-authored-by: Isaac
denik added 7 commits April 21, 2026 14:08
Replace every `:` with `-` in task names, so every task is a valid
target name for tools that don't allow `:` — primarily `make`, where
`make test:unit` parses as target `test` with prerequisite `unit`
rather than as a single target `test:unit`.

With dashes everywhere:
  * `make X` and `./task X` invoke the same task for every X, making
    the Makefile wrapper a true drop-in for former `make` users.
  * testmask no longer needs the CI-name ↔ Taskfile-name translation:
    the CI job output names are the task names.

CI job identifiers (job names, cache-key strings) are unchanged; they
were already dash-separated.

Co-authored-by: Isaac
The previous `%: @./task "$@"` pattern rule didn't trigger for phony
targets in GNU Make 3.81 (Apple's default); `make test-unit` printed
"Nothing to be done". `.DEFAULT` is the built-in hook for unmatched
targets and fires reliably.

Co-authored-by: Isaac
Mirrors `default` but swaps in the non-incremental fmt/lint variants.
Also tightens the `all:` description to mention regeneration scope
(skips genkit) and test updates.

Co-authored-by: Isaac
Drop the leading \`-\` on test-update and test-update-templates cmds.
The dash tells Taskfile to ignore a non-zero exit, which masked broken
acceptance tests — we want these to fail the dev loop.

Co-authored-by: Isaac
Removing as unused. out.test.toml is regenerated by the normal
test-update flow.

Co-authored-by: Isaac
Replace the broad \`**/*.go + acceptance/**\` source globs with the
same curated list the \`build\` task uses (acceptance_test.go builds
the CLI in-process via BuildCLI), plus acceptance/** for the test
harness and fixtures, plus go.mod/go.sum.

Two separate source lists because out.* files play different roles:
- test-acc: out.* are golden inputs. A change to a committed out.*
  must re-run the test to confirm it still matches.
- test-update*: out.* are outputs. The task rewrites them, so
  keeping them in sources would change the checksum each run and
  force re-execution on every invocation.

The latter uses a shared &ACC_SOURCES_UPDATE YAML anchor across
test-update, test-update-templates, and test-update-aws.

Co-authored-by: Isaac
@@ -1,3 +1,3 @@
---
description: Rules for how to deal with auto-generated files
globs:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a task that parses Taskfile.yml to auto-gen this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be a follow up

Comment thread Taskfile.yml
generates:
- test-output.json
cmds:
- cat test-output-unit.json test-output-acc.json > test-output.json
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cating two JSON doesn't create a JSON. Better to use jq here (ditto for others)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are json-lines files

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use .jsonl then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could rename, but out of scope there (and might need updating other places like deco repo)

Comment thread Taskfile.yml Outdated
sed -i 's|tagging.py|internal/genkit/tagging.py|g' .github/workflows/tagging.yml
fi
- "{{.GO_TOOL}} yamlfmt .github/workflows/tagging.yml"
- ./tools/validate_whitespace.py --fix
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we reference ws-fix task?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, 4d19c6b

denik added 2 commits April 21, 2026 15:39
The test failed on shallow CI clones (default fetch-depth: 1) because
HEAD~2 doesn't resolve. The well-known empty tree SHA
(4b825dc) is always available to git
regardless of clone depth, and diffing HEAD against it produces the
full file set — still exercising the non-empty result path.

Co-authored-by: Isaac
Addresses review feedback from @janniklasrose — dedupe the direct
validate_whitespace.py --fix call.

Co-authored-by: Isaac
Comment thread Taskfile.yml
generates:
- test-output.json
cmds:
- cat test-output-unit.json test-output-acc.json > test-output.json
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use .jsonl then?

Drops the \`git rev-parse --show-toplevel\` subshell in favor of
\`dirname "$0"\`, which is simpler and works even outside a git repo
(e.g. in archive extractions or release artifacts).

Co-authored-by: Isaac
@denik denik requested review from pietern and simonfaltum April 21, 2026 14:44
denik added 4 commits April 21, 2026 16:46
The system \`task\` binary in some PATHs is broken / missing; the
repo's wrapper at ./task always works. Update AGENTS.md / CLAUDE.md,
.agent/rules/auto-generated-files.md, bundle/docsgen/README.md, and
the comment block in bundle/internal/schema/main_test.go.

Co-authored-by: Isaac
Why: the minimal configs in tools/ and bundle/internal/tf/codegen/
existed only to opt nested modules out of root's ruleguard check
(whose rules path is cwd-relative and unresolvable from those dirs).
Disabling gocritic via --disable is equivalent and removes the
duplicated enable lists.

Co-authored-by: Isaac
- Introduce &BUILD_SOURCES, &ROOT_LINT_SOURCES, &FMT_GO_SOURCES,
  &SCHEMA_SOURCES to dedupe identical source lists across paired
  full/quick variants and build/snapshot.
- Add go.mod and go.sum to test-unit-root sources so SDK bumps
  invalidate the checksum cache (test-unit-tools and test-unit-codegen
  already list them).

Co-authored-by: Isaac
Copy link
Copy Markdown
Member

@simonfaltum simonfaltum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deep review (Isaac + Cursor, 2-round swarm)

Verdict: Not ready yet

Summary: 2 Critical | 9 Major | 2 Gap (Major) | 3 Nit | 4 Suggestion

Two independent reviewers (Isaac and Cursor) produced findings in parallel, then cross-reviewed each other's work, verifying claims against the code before agreeing / disagreeing. Inline comments follow below on specific lines. A few points that don't map cleanly to a single line:

Themes

  1. Makefile wrapper is lossy. Plain make now errors (.DEFAULT is a catch-all, not a default goal), and make wsfix routes to a non-existent task (renamed to ws-fix). Either commit to a proper compat shim or drop the wrapper.
  2. Windows CI path is broken. ./task is a POSIX shell script; the Windows matrix legs in push.yml don't set shell: bash and default to pwsh. On main today these call make (an .exe).
  3. CI trigger graph is self-blind. tools/testmask only treats go.mod / go.sum / setup-build-environment/ as "common trigger" patterns. Changes to Taskfile.yml, the task launcher, or tools/testmask/** itself fall through to test only, skipping specialized jobs (test-exp-aitools, test-exp-ssh, test-pipelines) precisely when their trigger or definition changed. Ironically this very PR would skip its own specialized tests if landed as-is.
  4. Similarly for python CI: python_push.yml still filters only on paths: python/** but the pydabs-* task bodies now live in root Taskfile.yml.
  5. generate-genkit shell rewrite subtly changes semantics. The old Makefile A && B || C && D always ran D (the git checkout). The new grouped A && B || (C && D) skips D when the SHA is already local — so genkit can build from whatever revision the user has checked out. Minimal shell repro confirmed.
  6. default / full dev loops mutate goldens. They invoke test-update, not test. Bare ./task can silently bless acceptance-output regressions.
  7. Cache source-list patterns are often wrong: ROOT_LINT_SOURCES omits root go.mod/go.sum; pydabs-codegen has overlapping sources: and generates:; generate-genkit sources miss go.mod/go.sum that its embedded consistency assertion reads.

Findings dropped on cross-review (for transparency)

  • ./task lint dropping --timeout=15m — verified: golangci-lint v2.5.0's --timeout is now "Disabled by default," so no regression.
  • generate-refschema losing the old - ignore-errors prefix — verified: libs/testdiff's -update mode overwrites goldens on mismatch rather than failing, so the - was masking real errors, not providing needed resilience.
  • ./task --force generate-schema in CI — correct pattern for generator && git diff --exit-code verification jobs; cache hits would make the check a no-op.
  • make VAR=VAL dropping variables — GNU make does forward command-line variables to the delegated child; the issue is only the "no default goal" + "ws-fix rename" points.

Full synthesis: see detailed inline comments below.

Comment thread task
@@ -0,0 +1,2 @@
#!/bin/sh
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] Windows CI jobs call a POSIX-only launcher

./task is #!/bin/sh, but .github/workflows/push.yml Windows matrix jobs (test, test-exp-ssh, test-pipelines) invoke ./task … directly. On GitHub Actions, Windows run: steps default to pwsh (no workflow-level defaults.run.shell: bash, and only one per-step shell: bash exists in push.yml — line 153, the git diff step). pwsh can't invoke an extensionless shebang script.

On main today these steps call make (a native .exe resolvable under pwsh), so this is a genuine regression. Note: the composite setup-build-environment/action.yml:41-42 does set shell: bash for ./task install-pythons — the author knew the issue at that site but missed the job-level call sites.

Suggested fix: add a task.cmd / PowerShell wrapper next to task, OR add shell: bash to every Windows run: ./task … step, OR set defaults.run.shell: bash at the job level for Windows matrix legs.

Found by: Cursor, Confirmed by: Isaac

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows CI tests did pass on the PR though.

Comment thread Taskfile.yml
Comment on lines +618 to +621
cd {{.UNIVERSE_DIR}} && \
git cat-file -e $(cat {{.ROOT_DIR}}/.codegen/_openapi_sha) 2>/dev/null || \
(git fetch --filter=blob:none origin master && \
git checkout $(cat {{.ROOT_DIR}}/.codegen/_openapi_sha))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] generate-genkit can build from the wrong universe revision

The old Makefile pattern A && B || C && D always ran D (the git checkout) regardless of whether the SHA was already local. The new grouped form is A && B || (C && D), which skips D when git cat-file -e succeeds. Consequence: if the requested OpenAPI SHA already exists in the local universe clone but isn't currently checked out, the task skips the checkout and builds genkit against whatever revision the user had checked out previously.

Minimal sh repro (verified):

  • Old true && true || true && echo X → prints X.
  • New true && true || (true && echo X) → does NOT print X.

This is a silent generator correctness bug — output diverges from .codegen/_openapi_sha.

Suggested fix: use explicit control flow so the checkout is unconditional after the existence check:

if ! git cat-file -e $(cat .../_openapi_sha) 2>/dev/null; then
    git fetch --filter=blob:none origin master
fi
git checkout $(cat .../_openapi_sha)

Found by: Cursor, Confirmed by: Isaac (with repro)

Comment thread Makefile
.PHONY: bench10_summary
bench10_summary: bench10.log
./tools/bench_parse.py $<
.DEFAULT:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Major] Makefile compatibility wrapper is not actually backwards-compatible

Two separate breaks, both verified on the PR branch:

  1. make with no target now fails: make: *** No targets. Stop. GNU .DEFAULT is a catch-all for named unmatched targets; it is not a default goal. The old Makefile had default: checks fmt lint.
  2. make wsfix forwards to ./task wsfix, but the task was renamed to ws-fix. So make wsfix now errors: task: Task "wsfix" does not exist. Did you mean "ws-fix"?make: *** [wsfix] Error 200. wsfix appears in the workdir CLAUDE.md and was called directly by the now-deleted tools/post-generate.sh. No migration alias exists.

(Cross-review note: Isaac's original R1 also claimed make VAR=VAL target drops variables; Cursor verified this part was wrong — GNU make does forward command-line variables to delegated children. The issue is just .DEFAULT vs default goal and the wsfix rename.)

Suggested fix: either (a) add .DEFAULT_GOAL := default + a default: ; @./task rule and keep aliases for renamed legacy targets (wsfix: ; @./task ws-fix), or (b) delete the Makefile and let make's own error point users to ./task. The current half-wrapper is the worst of both.

Found by: Isaac + Cursor (both)

run: |
if ! git diff --exit-code; then
echo "ERROR: detected changed files in the repository; Most likely you have out.test.toml files that are out of date. Run 'make generate-out-test-toml' to update."
echo "ERROR: detected changed files in the repository; Most likely you have out.test.toml files that are out of date. Run './task generate-out-test-toml' to update."
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Major] CI error message tells developers to run a task that does not exist

This error message says Run './task generate-out-test-toml' to update., but no such task exists in Taskfile.yml. The old main:Makefile had it (go test ./acceptance -run '^TestAccept$$' -only-out-test-toml); it was dropped in the migration.

Verified by running ./task generate-out-test-toml on the PR branch: it exits 200 with Task does not exist. Developers who hit this CI failure will follow the message into a second error.

(Also: .agent/rules/auto-generated-files.md on main mentions make generate-out-test-toml; that would route through .DEFAULT and hit the same dead end.)

Suggested fix: port the generate-out-test-toml target from the old Makefile into Taskfile.yml (it's a one-line go test invocation), and update agent docs if they still reference the old command.

Found by: Isaac (cross-review, R2)

Comment thread Taskfile.yml
Comment on lines +10 to +24
default:
desc: Quick dev loop (checks, formatters, incremental lint, tests). Use `all` for the full suite.
cmds:
- task: checks
- task: fmt-q
- task: lint-q
- task: test-update

full:
desc: More complete dev loop
cmds:
- task: checks
- task: fmt
- task: lint
- task: test-update
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Major] default / full dev loops rewrite acceptance goldens instead of verifying them

Both default ("Quick dev loop") and full invoke test-update rather than test. In -update mode, libs/testdiff/golden.go:45-50 overwrites expected files on mismatch instead of failing.

So a developer running bare ./task (the default target) can silently bless acceptance-output regressions. default is the implicit entrypoint for agent workflows — it needs to be verification-only.

Suggested fix: have default / full run test (verification), not test-update (mutation). If a "regen then verify" loop is useful, make it an explicitly named mutating task like update.

Found by: Cursor (cross-review, R2)

Comment thread .gitignore
# Local development notes, tmp
/pr-*
/tmp/
/.tmp/
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Nit] /tmp/ (pre-existing) and /.tmp/ (new) have different semantics

/tmp/ (line 63, pre-existing) is developer scratch; /.tmp/ (this line, new) is the per-module golangci-lint TMPDIR root configured in Taskfile.yml. Readers can't tell them apart from the current comment (line 61: # Local development notes, tmp).

Suggested fix: add an explanatory comment next to /.tmp/, e.g. # Taskfile cache for per-module golangci-lint TMPDIR (see Taskfile.yml).

Found by: Isaac (R1)

Comment thread Taskfile.yml
Comment on lines +162 to +166
fmt-q-go:
desc: Format changed Go files (diff vs main)
sources: *FMT_GO_SOURCES
cmds:
- "./tools/lintdiff.py {{.GO_TOOL}} golangci-lint fmt"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Nit] fmt-q-go shares *FMT_GO_SOURCES with fmt-go; identical checksums mean fmt-q-go gets cached as "done" after fmt-go

Both tasks declare identical sources: via the same anchor. Once fmt-go has run, go-task's checksum cache will treat fmt-q-go as already up-to-date with the same checksum, even though the two have different semantics (full fmt vs diff-only fmt).

Low impact, but a user who runs fmt-go and then explicitly asks for fmt-q-go gets a no-op.

Suggested fix: either (a) give fmt-q-go a distinguishing source (e.g. .git/HEAD so branch changes invalidate it), or (b) set method: none on fmt-q-go so it always runs (it's cheap when nothing changed).

Found by: Isaac (R1), Confirmed by: Cursor (R2)

Comment thread tools/lintdiff.py
import argparse
import subprocess

NESTED_MODULES = ("bundle/internal/tf/codegen", "tools")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Nit] NESTED_MODULES is prefix-based but looks explicit

NESTED_MODULES = ("bundle/internal/tf/codegen", "tools") — the membership check at line 65 matches path == m or path.startswith(m + "/"), so it correctly catches tools/task/go.mod via the tools/ prefix. That's four real nested modules covered by two entries.

A future contributor adding a module outside these two prefixes will silently miss it; one adding a sibling to tools/task/ might not realize the prefix semantics and add a redundant entry.

Suggested fix: add a comment documenting the prefix-based semantics, or replace with a dynamic walker that discovers go.mod files (excluding the root).

Found by: Isaac (cross-review, R2)

Comment thread Taskfile.yml
# .codegen/_openapi_sha. SDK version bumps (go.mod/go.sum) are a manual
# step outside this task; TestConsistentDatabricksSdkVersion (run inside
# generate-genkit) asserts the two stay in sync.
- task: generate-genkit
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] generate-schema-docs silently loses sinceVersion annotations on tag-less checkouts

The old tools/post-generate.sh (deleted in this PR) ran git fetch origin 'refs/tags/v*:refs/tags/v*' before invoking schema-for-docs. bundle/internal/schema/since_version.go:78-99 shells out to git tag --list 'v*' and git show <tag>:… to compute sinceVersion fields.

With no tags present (e.g., a fresh shallow clone), getVersionTags returns empty and the generated jsonschema_for_docs.json silently drops sinceVersion fields — no error.

CI is unaffected because the validate-generated check runs ./task --force generate-schema without --docs, but local ./task generate now produces subtly different jsonschema_for_docs.json output on tag-less checkouts.

Suggested fix: either add git fetch --tags as a pre-step in generate-schema-docs, or have computeSinceVersions log a warning when it sees zero tags but nonzero commit history.

Found by: Isaac (cross-review, R2)

Comment thread Taskfile.yml
Comment on lines +315 to +323
deps: ['test-unit-root', 'test-unit-tools', 'test-unit-codegen']
sources:
- test-output-unit-root.json
- test-output-unit-tools.json
- test-output-unit-codegen.json
generates:
- test-output-unit.json
cmds:
- cat test-output-unit-root.json test-output-unit-tools.json test-output-unit-codegen.json > test-output-unit.json
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Concurrent ./task test* runs share worktree-level output files

test-unit cats three per-module JSON files into test-output-unit.json (line 323); test then cats test-output-unit.json and test-output-acc.json into test-output.json (line 311). The three specialized test tasks (test-exp-aitools, test-exp-ssh, test-pipelines) all also end with cat ... > test-output.json.

Two agents running ./task test* (or mixes of them) in the same worktree will interleave writes — gotestsum --jsonfile opens-and-truncates. Cursor correctly noted origin/main already had this race via make test, so it's pre-existing, not a new Taskfile regression.

Still worth improving given the PR's stated motivation ("faster verification after agent work") specifically incentivizes concurrent invocations.

Suggested fix: write per-run JSON into .task/run-<id>/test-output-*.json and have the aggregator cat from there. Or at minimum, document that concurrent test* invocations in the same worktree are unsupported.

Found by: Isaac (R1), scope-corrected by Cursor (R2)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants