Repository navigation
Description
Problem
tools/lint-draft.sh runs every prose gate (vale, cspell and, for Markdown, rumdl) over one drafted document in a single call, including a probe that proves vale has rules for the target's path. An identical copy lives at .devtools/scripts/lint-draft.sh, which consumer repositories run through the devtools:lint-draft task, and a test requires the two to stay byte-identical. The same 120-line shell algorithm is therefore maintained twice. Porting it to tested Go and keeping one copy removes the duplicate and a shell script from the prose path (feature 15 of #588, remaining tooling and deployed adapters).
Acceptance criteria
- AC1: Given no argument, a missing file, a path that resolves outside the repository root (absolute,
..or through a symlink), or a.go,.pyor.shfile, when the lint runs, then it prints respectively the usage line on standard error and exits 2,<file>:1 [error] missing-file no such fileand exits 1, anoutside-repofinding and exits 1, or awrong-taskfinding and exits 2, and runs no gate. - AC2: Given a document, when the lint runs, then vale, cspell and (for
.mdfiles only) rumdl each run and print their findings even when an earlier gate failed, and the exit status is 1 if the probe or any gate failed and 0 otherwise. - AC3: Given a clean document at a path with vale rules, when the lint runs, then it exits 0 and prints nothing.
- AC4: Given a path for which the probe draws no vale findings and vale exits 0 or 1, when the lint runs, then it prints an
unscoped-pathfinding, exits 1 and still runs the gates. - AC5: Given vale exits with a status above 1 during the probe, when the lint runs, then it prints a
vale-failedfinding naming that status, exits 1 and still runs the gates, distinct from theunscoped-pathoutcome. - AC6: Given any target, when the lint runs, then the probe supplies its text to vale on standard input and names the target only through
--path, and the target's content and modification time are unchanged afterwards. - AC7: Given a corpus of documents (clean, with findings from each gate, at an unscoped path, with a failing vale, and each rejected input of AC1), when the lint runs, then its standard output, standard error and exit status match what the shell script produced.
- AC8: After the change exactly one lint-draft entry point exists, and both
mise run lint-draftin this repository and thedevtools:lint-drafttask used by consumers run it, with every documentation and skill reference to the removed path updated and the test that compared the two copies replaced. - AC9: After the documented generation sequence (
mise run generate-skills, thenmise run skill-tokens, thenapm install), the committed generated outputs match their sources andmise run check-generated-skillspasses. - AC10: Non-functional: for a 500-line document with the gates replaced by stubs that exit immediately, the lint's own overhead, from process start to exit, is under 0.2 s.
Scope and non-goals
In scope: the draft linter, removal of the duplicate copy, updating every reference to the removed path, and the adoption test that requires the two copies to match.
Non-goal: changing which gates run or their configuration.
Non-goal: the prose guards, the fix-prose workflow and check-suppressions.sh, a later wave.
Out of scope: the Python prose helpers (fix_prose_replacements.py) and the other shipped helpers the adoption test compares.
Approach
Implement the lint as a Go command in the agentrepo tree. Run the gates sequentially in the current order with inherited output streams, since that is the simplest way to keep AC7; concurrency would need buffering to preserve stream order and is not wanted. Run the probe as today, by sending the known-bad text to vale on standard input under --path. Keep the consumer-visible payload path .devtools/scripts/lint-draft.sh as the single entry point, as a forwarding shim of at most 10 lines that runs the Go command, and delete tools/lint-draft.sh and the shell body. Repoint this repository's lint-draft task (which runs tools/lint-draft.sh today) and update the other references to the removed path: the README, tools/AGENTS.md, docs/script-inventory.md, the fix-prose skill source and its generated copies, tools/test-guards.sh and tests/skills/writers/permissions.bats. In tests/tasks/adoption.bats the test "shipped helpers match the publisher implementations" loops over lint-draft.sh, fix_prose_replacements.py and apm_audit.py with cmp; drop lint-draft.sh from it and cover the surviving payload through the adoption cases that run mise run lint-draft, including the outside-repository cases. Regenerate skills from canonical sources in the documented order. Neither script is an owner-protected path and the new Go files need no protection change.
Dependencies
Blocked by #333 (binary release assets) and #544 (consumer installation of those assets), both as native links: consumers run the payload shim, which needs the Go command installed. The switch of the consumer-visible path therefore waits on them.
Size
S. One PR.
Verify plan
- AC1: Go tests for each rejected input, including a symlink and a
..path, asserting the message, stream and exit status, and with stub gates asserting none ran. - AC2: Go test with stub gates where vale fails first, asserting cspell and rumdl still ran,
.mdagainst non-.mdrumdl behaviour, and the exit status in each combination, including a gate that fails to start. - AC3: Go test with a stub vale that reports findings for the probe on a covered path and clean gates, asserting exit 0 and empty output.
- AC4: Go test with a stub vale that is silent for the probe, asserting the finding, exit status and that the gates ran.
- AC5: Go test with a stub vale that exits 2 for the probe, asserting the
vale-failedfinding with the status and thatunscoped-pathis absent. - AC6: Go test with a recording stub vale asserting the probe invocation receives the control text on standard input and has no file operand, and that content and modification time of the target are unchanged.
- AC7: differential test that runs the recorded shell script and the new entry point over the corpus with the same stubs and compares streams and statuses.
- AC8: grep showing the removed path appears nowhere and the single entry point exists,
mise run lint-draftrun on a draft, the adoption suite passing with the replaced test, and a run of the consumer task template against the payload shim. - AC9: the documented sequence on the branch,
git diff --exit-codeafterwards, andmise run check-generated-skills. - AC10: Go benchmark with immediate-exit stub gates on a 500-line fixture, with a test failing at 0.2 s.
Risks
A probe that stops detecting uncovered paths would let prose bypass vale unnoticed, which AC4 and AC5 guard. A missed reference to the deleted path would break a task or skill; AC8 covers it. Changed output streams or statuses would break agents that read them; AC7 pins them. If the Go command is not installed where the shim runs, the lint fails rather than passing, which the blockers prevent from shipping early. The undo is reverting the PR, which restores both shell copies and their references.
Operator authority
None
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Work type
Projects
- StatusShow more project fieldsIn progress
Activity
[-]Port the version-literal check to Go[/-][+]Port the draft prose linter to Go and drop its duplicate[/+]tbhb-agent commentedon Oct 25, 2026
Refinement: NOT READY — #717 has an empty body
Checked
Read #717, parent #588, native dependency links, siblings #713–#720, both draft-lint scripts, task entry points, consumer tests, the shared shell gate wrapper, and the Go command tree. No mechanical checks were run.
The native parent is #588; blocked-by and blocking lists are empty. The title fits the epic’s tooling migration. Duplicate implementations exist at
.devtools/scripts/lint-draft.sh:31andtools/lint-draft.sh:31.Gaps
In Definition of Ready order:
AC<n>:criteria with one observable outcome each. Cover path validation, symlink containment, scope probing, probe failures, execution of remaining gates after failures, aggregate status, diagnostic streams, unchanged documents, and compatible task interfaces. Include applicable non-functional criteria.mise.toml:498,.config/mise/conf.d/30-devtools-skill-contract.toml:29) and the test requiring identical helper copies (tests/tasks/adoption.bats:104). The existing Go command tree has no draft-lint command (internal/agentrepo/command.go:85); its error-status behavior needs consideration when preserving compatibility (internal/agentrepo/command.go:89).packages/agents/source/lib/lib.sh:837).tests/tasks/adoption.bats:37,tests/tasks/adoption.bats:52,tests/tasks/adoption.bats:66). Add targeted evidence for probe errors, gate continuation, statuses, streams, and document preservation.Noneor an applicable reserved-phrase line.Most sibling bodies are empty, so their titles establish only provisional scope boundaries.
Split
No further split is justified by the title alone. Keep the complete draft-lint migration and duplicate retirement together; exclude sibling work. A populated scope and size are needed to confirm one-PR fit.
Definition of done
Nothing is ticked because the issue has no AC or Verify plan covering these requirements.
README.md:154).mise run checkand both coverage-floor commands.Round: 1 of 3
tbhb-agent commentedon Oct 25, 2026
Refinement: NOT READY — compatibility and verification gaps remain
Checked
Read #717’s current body and round 1 verdict, parent #588, #333, and siblings #713–#720. Confirmed the native parent is #588; native blocked-by and blocking lists are empty.
Read both complete lint-draft implementations, their repository and consumer task callers, adoption tests, prose skill guidance, and the Go command interface. No checks were run, no delegation occurred, and no GitHub state changed.
The duplicate 120-line implementations exist as claimed. Both run the probe before the gates and preserve aggregate failure (
tools/lint-draft.sh:94,.devtools/scripts/lint-draft.sh:94). However, “every prose gate” needs qualification: rumdl runs only for Markdown (tools/lint-draft.sh:116).Gaps
In Definition of Ready order:
Problem — correct core, incorrect epic reference. Duplicate maintenance is established by the code. This belongs to feature 15, remaining tooling and deployed adapters, of Replace complex shell with tested Go, starting with gates and merging #588; feature 13 covers rebase and conflict resolution. Correct the reference and qualify the Markdown-only gate.
Acceptance criteria — valid numbering, incomplete behavioral contract. Eight matching numbered IDs satisfy the structural limit, and AC8 supplies a non-functional criterion. Several criteria still need revision:
tools/lint-draft.sh:69). Limit it to supported draft inputs.vale-failedfromunscoped-pathand continues to run the gates (tools/lint-draft.sh:99). Preserve that distinction explicitly.tools/lint-draft.sh:42). Existing adoption tests specifically cover absolute, relative, and symlink escapes (tests/tasks/adoption.bats:52).Scope and non-goals — present and appropriately bounded. The exclusions distinguish this migration from Port the gate runner and pass cache from Python to Go #713, Port the commit-message hook scripts to Go #715, Port the pull request description validator to Go #718, Replace the commit and pull request workflow guards with Go hooks #719, and Settle the check judge contract and divergence corpus before porting it #720. Updating the adoption test affected by duplicate retirement belongs in scope.
Approach — grounded probe mechanism, incomplete caller migration.
--pathmatches the implementation (tools/lint-draft.sh:88).tools/lint-draft.sh(mise.toml:500), while consumers call the payload script (.config/mise/conf.d/30-devtools-skill-contract.toml:32). Choose the surviving published path and describe both caller updates.tests/tasks/adoption.bats:104). Replace that assertion with evidence for the surviving payload and repository caller.tools/lint-draft.sh:112). “Concurrently-safe in a fixed report order” needs clarification: concurrent execution requires deliberate buffering and stream handling to preserve AC5. Sequential execution is the simpler compatible approach.Dependencies — missing native blocker and rollout prerequisites. “None blocking” contradicts the statement that switching waits on Ship the Go binaries as release assets installed through mise's github backend #333, which remains open. Add Ship the Go binaries as release assets installed through mise's github backend #333 as a native blocker for this single-PR implementation-and-switch scope, or define an independently deliverable scope that does not require switching. Release assets alone do not establish how consumers install and resolve the replacement command. Specify that rollout and Replace complex shell with tested Go, starting with gates and merging #588’s owner-protection prerequisite. No inspected sibling establishes an additional implementation blocker.
Size — declared and plausible. S, one PR, is reasonable for this leaf migration once the compatibility contract and consumer transition are explicit.
Verify plan — every ID appears, but several checks do not prove their AC.
mise run checksupport the stated outcomes, once separated.Risks — present but incomplete. False clean results and missed references are covered. Add changed statuses/streams, containment regression, misclassified probe errors, and unavailable consumer binaries. Reverting the PR is a reasonable undo, but describe coordinated rollback of the binary requirement, payload, and callers.
Operator authority — valid declaration.
Nonesatisfies the required form and grants no operational authority. It does not replace the inherited owner-protection requirement.Split
No additional split is needed. Keep the Go implementation, caller migration, duplicate retirement, and affected adoption coverage together. Exclude sibling migrations. Correct the epic feature reference to feature 15.
Definition of done
No item is fully covered by the current AC and Verify plan; partial coverage is identified below. These are planning assessments, not claims that implementation evidence already exists.
mise run check, but required-head checks and exact-head owner approval for loosened gates are unspecified.mise run check; both coverage-floor commands are missing.Round: 2 of 3
tbhb-agent commentedon Oct 25, 2026
Refinement: READY — the draft-lint migration has an implementable compatibility and rollout contract
Checked
Read #717, both prior verdicts, parent #588, native dependencies, #333, #544, and siblings #713–#722. Fetch was refused; code was read through the read-only contents API from remote
main. No mechanical checks, edits, delegation, or GitHub writes occurred.Reviewed the Definition of Ready in order:
Problem: Accurate. Both 120-line scripts contain the same algorithm. Markdown-only rumdl execution is correctly qualified (
tools/lint-draft.sh:116).Acceptance criteria: AC1–AC10 have matching numbered IDs, remain below the limit, and define testable outcomes. AC1–AC8 cover the existing contract:
tools/lint-draft.sh:45,tools/lint-draft.sh:50,tools/lint-draft.sh:55, andtools/lint-draft.sh:69.tools/lint-draft.sh:112.tools/lint-draft.sh:94.tools/lint-draft.sh:95.Scope and non-goals: Explicit and bounded. This fits feature 15 of Replace complex shell with tested Go, starting with gates and merging #588 without overlapping the gate cache, commit hooks, description validator, check judge, or guard migrations.
Approach: Workable. Keeping
.devtools/scripts/lint-draft.shpreserves the consumer interface (.config/mise/conf.d/30-devtools-skill-contract.toml:29). Repointing the repository override removes its dependency ontools/lint-draft.sh(mise.toml:495). Sequential execution preserves the existing stream behavior.The adoption-test adjustment is correctly identified: remove only lint-draft from the byte-comparison loop (
tests/tasks/adoption.bats:104) and retain consumer-task coverage, including absolute, relative, and symlink escapes (tests/tasks/adoption.bats:37,tests/tasks/adoption.bats:52). The reference-update scope includes the relevant documentation and task-based skill guidance (tools/AGENTS.md:7,docs/script-inventory.md:74,packages/agents/source/skills/fix-prose/parts/arguments.md:8).Dependencies: Native blocked-by links are exactly Ship the Go binaries as release assets installed through mise's github backend #333 and feat(distribution): migrate consumer tool pins to release assets #544. They correctly cover asset publication and consumer installation. Neither implementation path matches the specialized owner/golden patterns (
.agents/agent-skills.conf:4,.agents/agent-skills.conf:9); no additional protection blocker is established.Size: S and one PR are credible for this leaf migration.
Verify plan: Every AC ID has corresponding evidence: stubbed behavioral tests, differential streams/status comparison, real repository and consumer entry points, ordered generation, and an asserted timing threshold.
Risks: Covers false clean results, missing references, changed streams/statuses, unavailable binaries, and a coordinated undo through reverting the PR.
Operator authority:
Nonesatisfies the required form.Gaps
None that require an implementer to guess or prevent the planned checks from proving the AC.
Split
No split needed. Keep implementation, duplicate retirement, caller updates, adoption coverage, and generated outputs together. The revised guard sibling boundaries introduce no overlap.
Definition of done
Ticks indicate coverage in the AC and Verify plan, not completed implementation evidence.
check-leaks.sh, and leak review of each post.provenance:agent-assisted.Status:comment.mise run check,mise run cover-go-check, andmise run cover-py-check.check-generated-skills.Round: 3 of 3
tbhb-agent commentedon Oct 26, 2026
Refinement amendment for the shell-to-Go epic (#588), covering compatibility and protection. The acceptance criteria stay as written, except where noted.
.devtools/scripts/lint-draft.shforwards to the Go command when it is present. When it is absent it runs the existing shell body, which stays until the fallback issue removes it. Consolidating the two identical copies still happens here, so one copy remains.