Skip to content

bootstrap rules for go-error-wrapping-guide.md (5 rules) - #2

Merged
bborbe merged 3 commits into
masterfrom
feat/bootstrap-error-wrapping
May 31, 2026
Merged

bootstrap rules for go-error-wrapping-guide.md (5 rules)#2
bborbe merged 3 commits into
masterfrom
feat/bootstrap-error-wrapping

Conversation

@bborbe

Copy link
Copy Markdown
Owner

Summary

  • Migrates `docs/go-error-wrapping-guide.md` from prose-only to rule-blocks-inline (Model A: hand-author the contract once)
  • Appends 5 `### RULE` blocks: 3 MUST mechanical + 2 SHOULD judgment
  • Adds 3 `rules/go/*.yml` ast-grep detectors for the mechanical rules
  • `rules/index.json` grows 4 → 9 entries
  • Mirrors the proven `bootstrap-rules-go-time-injection.md` template (PR clarify CQRS handler-error semantics in go-cqrs.md #1 of master's rule-base foundation work)

Rules added

IDLevelEnforcement
`go-errors/no-fmt-errorf`MUST`rules/go/no-fmt-errorf.yml`
`go-errors/no-bare-return-err`MUST`rules/go/no-bare-return-err.yml`
`go-errors/no-context-background-in-business-logic`MUST`rules/go/no-context-background-in-business-logic.yml`
`go-errors/inner-closure-no-double-wrap`SHOULD`judgment` (whole-function reasoning)
`go-errors/sentinel-err-prefix-naming`SHOULD`judgment` (type semantics)

Scope note: doc's `## Key Rules` lists 7 items; this PR extracts 5. Skipped #4 (multi-return wrapping — snippet variation of rules 1+2) and #6 (remove unused imports — goimports' job).

Test plan

  • `make precommit` clean
  • `make build-index` deterministic (`git diff --exit-code rules/index.json` after second run)
  • `rules/index.json` has 9 entries, sorted by id, each entry's keys alphabetically sorted
  • All 5 new entries: `owner: go-error-assistant`, `doc_path: docs/go-error-wrapping-guide.md`, `anchor == id`
  • The two judgment rules: `enforcement: judgment` (literal string)
  • All 3 ast-grep YAMLs: `severity: error`, ID matches the rule block, ignores include `main.go` + `/main.go` + `/mocks/**` (per pitfalls in `docs/ast-grep-rule-writing-guide.md`)
  • Operator-side post-merge: `scripts/scan.sh` against `bborbe/run` or `bborbe/dark-factory` — confirm detectors fire on real code (especially `pkg/factory/factory.go:1556` time.Now() finding now joined by error-wrapping findings)

@ben-s-pull-request-reviewerben-s-pull-request-reviewerBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

PR Review Summary

Branch:feat/bootstrap-error-wrappingmaster
Files changed: 6 (3 YAML rules, 1 doc with 5 RULE blocks, 1 index, 1 prompt artifact)


Automated Checks

CheckResult
LICENSE presentLICENSE file exists
rules/index.json JSON valid✅ Valid JSON (verified via Python)
.claude-plugin/plugin.json JSON valid✅ Valid JSON
.claude-plugin/marketplace.json JSON valid✅ Valid JSON
make precommit⛔ Blocked by sandbox (could not run Makefile)

Review Findings

Must Fix (Critical)

None identified. The mechanical YAML rules are correctly structured, the inside/has ast-grep composition in no-bare-return-err.yml is valid, and the $$$ARGS variadic capture in no-fmt-errorf.yml follows ast-grep conventions.

Should Fix (Important)

  1. Version mismatch between manifest and changelogCHANGELOG.md shows latest version as v0.11.0, but both plugin.json and marketplace.json have "version": "0.9.12". These MUST agree before release per the version alignment rule in CLAUDE.md. This PR does not itself change version strings, but this existing drift should be resolved separately.

  2. No smoke tests for the three mechanical ast-grep YAMLsrules/go/no-bare-return-err.yml, rules/go/no-context-background-in-business-logic.yml, and rules/go/no-fmt-errorf.yml have no test coverage. The plan's concern correctly notes this: the judgment rules (4–5) are intentionally YAML-free, but the three mechanical detectors should at minimum be smoke-tested against known Bad/Good patterns (e.g., a minimal Go file exercising each pattern) to confirm they fire as expected. Consider adding a scripts/smoke-test-rules.sh that compiles a temporary Go file and runs ast-grep scan against it.

Nice to Have (Optional)

  1. prompts/completed/ artifact committed to repoprompts/completed/007-bootstrap-rules-go-error-wrapping.md is a dark-factory execution artifact. Whether to commit these is project-specific; if the prompts/ directory is meant to be a clean inbox/outbox, consider whether completed prompts should live elsewhere or be excluded from the repo entirely.

  2. $$$ARGS in no-fmt-errorf.yml could be documented — The $$$ARGS variadic capture is correct for ast-grep but may be unfamiliar to future maintainers. A brief comment in the YAML or a note in docs/ast-grep-rule-writing-guide.md would reduce future confusion.


Verdict

{
"verdict": "request-changes",
"summary": "The five new RULE blocks and three ast-grep YAMLs are structurally correct and follow all project conventions. However, version drift between CHANGELOG.md (v0.11.0) and the plugin manifests (0.9.12) is a release-blocking inconsistency that must be resolved, and the three mechanical rule YAMLs lack smoke tests to confirm they fire on real code patterns.",
"comments": [
{
"file": "CHANGELOG.md",
"line": 11,
"severity": "major",
"message": "Version mismatch: CHANGELOG.md top entry is v0.11.0 but .claude-plugin/plugin.json and .claude-plugin/marketplace.json both show version 0.9.12. These four version strings must agree before release per CLAUDE.md version alignment rule."
},
{
"file": ".claude-plugin/plugin.json",
"line": 4,
"severity": "major",
"message": "Version mismatch: plugin.json version is 0.9.12 but CHANGELOG.md top entry is v0.11.0. Must match CHANGELOG.md."
},
{
"file": ".claude-plugin/marketplace.json",
"line": 8,
"severity": "major",
"message": "Version mismatch: marketplace.json metadata.version is 0.9.12 but CHANGELOG.md top entry is v0.11.0. Must match CHANGELOG.md."
},
{
"file": ".claude-plugin/marketplace.json",
"line": 15,
"severity": "major",
"message": "Version mismatch: marketplace.json plugins[0].version is 0.9.12 but CHANGELOG.md top entry is v0.11.0. Must match CHANGELOG.md."
},
{
"file": "rules/go/no-bare-return-err.yml",
"line": 1,
"severity": "nit",
"message": "No smoke test for this mechanical ast-grep rule. Add a smoke test (e.g., scripts/smoke-test-rules.sh) to confirm the detector fires on a synthetic `if err != nil { return err }` and does NOT fire on `if err != nil { return errors.Wrapf(ctx, err, \"...\") }`."
},
{
"file": "rules/go/no-context-background-in-business-logic.yml",
"line": 1,
"severity": "nit",
"message": "No smoke test for this mechanical ast-grep rule. Add a smoke test to confirm the detector fires on `context.Background()` in a non-exempt path and does NOT fire in main.go or cmd/**."
},
{
"file": "rules/go/no-fmt-errorf.yml",
"line": 1,
"severity": "nit",
"message": "No smoke test for this mechanical ast-grep rule. Add a smoke test to confirm the $$$ARGS variadic capture correctly matches fmt.Errorf calls and does NOT fire on errors.Wrapf."
}
],
"concerns_addressed": [
"correctness: ast-grep `inside: { kind: if_statement, has: { pattern: err != nil } }` — reviewed the YAML, the composition is valid ast-grep; multi-statement if bodies where `return err` appears inside a closure nested within the if block would be a judgment-tier false positive handled by the go-error-assistant, not a mechanical rule bug.",
"correctness: enforcement field backtick-wrapped paths — confirmed this is the output format of build-index.py and matches the pilot entry; not a bug.",
"correctness: fmt.Errorf $$$ARGS variadic capture — valid ast-grep syntax; $$$ARGS captures variadic args correctly.",
"tests: no smoke coverage for mechanical YAMLs — raised as Should Fix; confirmed by review."
]
}

bborbe added 2 commits June 1, 2026 00:20
bborbe convention is jobs.test (verified across dark-factory, vault-cli,
scripts, semantic-search). coding diverged with jobs.precommit (name:
Precommit), which left the master-protection ruleset's required 'test'
check permanently MISSING — PRs stayed BLOCKED even after bot APPROVE.
Renaming aligns coding with the org standard and unblocks PR merge
through the gate.
CHANGELOG.md top entry is v0.11.0 but plugin.json and marketplace.json
were both stuck at 0.9.12. maintainer-agent-releaser bumped the CHANGELOG
for v0.10.0 and v0.11.0 but did not touch the manifests. make
release-check fails on this drift; flagged by the pr-reviewer-agent on
PR #2.
Aligning all four version strings to 0.11.0 (matches CHANGELOG). This
is NOT a new release commit — purely a drift fix. The next genuine
release will bump from 0.11.0 to whatever's next.
@ben-s-pull-request-reviewer
ben-s-pull-request-reviewerBot dismissed their stale reviewMay 31, 2026 22:28

superseded by new automated review

@ben-s-pull-request-reviewerben-s-pull-request-reviewerBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

{
"verdict": "approve",
"summary": "Bootstrap of 5 RULE blocks (3 mechanical ast-grep + 2 judgment) for go-error-wrapping-guide.md with matching index entries. All YAML files are structurally valid, index entries are correctly sorted with matching anchors/IDs, CI job rename is consistent with recent commits, and rule-block formatting matches the pilot. One known limitation: the fmt.Errorf ast-grep pattern catches both %w-wrapped and standalone forms — the doc permits standalone fmt.Errorf as 'new error without cause' per its examples, so false positives at the mechanical layer are expected and will be adjudicated at the judgment tier (as noted in the PR concerns).",
"comments": [
{
"file": "rules/go/no-fmt-errorf.yml",
"line": 9,
"severity": "major",
"message": "Pattern `fmt.Errorf($$$ARGS)` fires on both fmt.Errorf with %w-wrapped err AND standalone fmt.Errorf(\"message only\") without err. The doc's 'Fix: New Error Without Cause' section explicitly allows standalone fmt.Errorf as an acceptable alternative to errors.Errorf. The rule cannot distinguish between these without whole-function analysis, so false positives are expected at the mechanical layer — judgment tier must filter them. This is acknowledged in the PR concerns and is acceptable behavior given the ast-grep limitation."
},
{
"file": "rules/index.json",
"line": 24,
"severity": "nit",
"message": "enforcement field for mechanical rules uses backtick-wrapped paths (`rules/go/no-bare-return-err.yml`) consistent with existing go-time entries, but the pilot entry go-context/cancel-check-in-loop uses a longer natural-language enforcement string. This is a cosmetic formatting inconsistency across entries, not a functional defect — the field is valid YAML in both forms."
}
],
"concerns_addressed": [
"correctness: no-fmt-errorf ast-grep pattern confirmed to fire on both wrapped and standalone forms — judgment tier required to filter, acceptable per PR concerns",
"correctness: no-bare-return-err pattern correctly targets bare return inside if err!=nil, does not fire on multi-return idioms like return result, err",
"correctness: no-context-background-in-business-logic rule correctly distinguishes business logic from cmd/bootstrap code via path ignores",
"correctness: index.json verified: 9 entries, sorted, anchors match IDs, owner go-error-assistant on all new entries, doc_path consistent",
"tests: ast-grep rules lack unit test YAMLs — acknowledged limitation per PR concerns, no test suite exists in this plugin-only repo per CLAUDE.md",
"tests: index.json changes not validated against a schema — build-index determinism relies on human review and make precommit (JSON validity only)"
]
}

@bborbe
bborbe merged commit f97da49 into masterMay 31, 2026
1 check passed
@bborbe
bborbe deleted the feat/bootstrap-error-wrapping branch May 31, 2026 22:29
bborbe added a commit that referenced this pull request Jun 2, 2026
Restructures the 6 MUST/SHOULD sections in docs/go-prometheus-metrics-guide.md
into canonical `### RULE` blocks consumable by the rule-base walker
(scripts/build-index.py). Mirrors the bootstrap template proven across
PRs #2-5 (errors, security, factory, http-handler).
Rules added to rules/index.json (21 → 27 entries):
- go-prometheus/counter-pre-initialization (MUST, judgment)
Pre-init counters with .Add(0) for known label combos so rate()
doesn't silently skip absent series.
- go-prometheus/composed-metrics-interface (SHOULD, judgment)
Split fat Metrics interfaces into focused sub-interfaces (ISP).
- go-prometheus/no-gauge-for-monotonic (MUST, judgment)
Never use GaugeVec for values that only increase — breaks rate()
and increase() queries.
- go-prometheus/counter-total-suffix (MUST, judgment for now)
Counter Name must end with _total. Mechanical ast-grep YAML was
drafted but CounterOpts struct-literal traversal in ast-grep
0.43.0 needs more debugging; deferring to a focused follow-up
PR rather than blocking this bootstrap.
- go-prometheus/help-string-quality (MUST, judgment)
Unique, accurate Help strings per metric; reject copy-paste residue.
- go-prometheus/label-naming-consistency (MUST, judgment)
Same label name for same concept across all metrics in the project.
All rule blocks carry: Owner (go-metrics-assistant), Applies when,
Enforcement, Why, Bad/Good code snippets. Bad example for
no-gauge-for-monotonic also de-trades (candleHandleTotalCounter +
broker label → orderHandleTotalCounter + tenant label) — leftover
trading-domain leak from PR #6's first trim pass.
No personal vault paths, no trading-domain terms, no remaining MUST
section without a rule id (verified by grep).
bborbe added a commit that referenced this pull request Jun 2, 2026
Restructures the 8 MUST sections in docs/go-testing-guide.md into
canonical `### RULE` blocks. Same template as PRs #2-5, #8.
Rules added (all judgment for this PR; mechanical ast-grep follow-ups
tracked separately):
- go-testing/no-stdlib-table-tests (MUST)
Ginkgo-suite packages must use DescribeTable/Entry, not for/t.Run.
- go-testing/no-testing-t-direct (MUST)
No bare testing.T in Ginkgo-suite packages — use Describe/It.
- go-testing/no-bare-error-call (MUST)
Wrap error-returning calls in Gomega matchers (Succeed/HaveOccurred/
MatchError) — errcheck enforces this in precommit.
- go-testing/suite-test-file-required (MUST)
Every test-containing package needs *_suite_test.go or Ginkgo silently
discovers no specs.
- go-testing/main-test-with-compiles (MUST)
Every binary needs main_test.go with a Compiles It-block backed by
gexec.Build.
- go-testing/suite-timeout-required (MUST)
suiteConfig.Timeout must be set so hung tests fail fast.
- go-testing/counterfeiter-mocks-required (MUST)
No hand-written mocks — they drift silently.
- go-testing/libtime-injection-required (MUST)
Cross-references go-time/no-time-now-direct; test-quality scope.
rules/index.json: 27 → 42 entries
This also picks up 7 agent-cmd/* entries from PR #9 (#9 merged the
doc but didn't include the walker output regen — caught locally via
make build-index). Net new in this PR: 8 go-testing rules.
Pre-emptive checks (lessons from PRs #6, #8): no personal vault paths,
no trading-domain terms, no internal contradictions (rule examples
don't violate sibling rules).
bborbe added a commit that referenced this pull request Jun 2, 2026
Restructures the 4 MUST sections in docs/go-licensing-guide.md into
canonical `### RULE` blocks. Same template as PRs #2-5, #8, #10.
Rules added:
- go-licensing/license-file-required (MUST, judgment)
Public Go repos MUST have a root LICENSE file. Private/internal repos
exempt. Detection requires file-existence check — not ast-grep.
- go-licensing/readme-license-section-required (MUST, judgment)
Public README must have a ## License H2 section pointing to LICENSE.
Markdown structure check — not ast-grep.
- go-licensing/source-file-header-required (MUST, addlicense)
All non-vendor *.go files need the 3-line BSD-2 header. Enforcement
delegates to 'addlicense -check' via make precommit (canonical tool;
already wired into the toolchain).
- go-licensing/copyright-year-discipline (MUST, judgment)
No bulk year-updates for trivial changes; no future/non-numeric
years. PR-scope diff inspection — partial ast-grep detection
possible for the future-year case but bulk-update trigger needs
diff-scope reasoning.
rules/index.json: 42 -> 46
Walker drift detected by PR #13's check-index target during first
precommit run — regenerated rules/index.json and re-ran precommit
clean. New guard working as designed.
Pre-emptive checks (lessons from #6, #8): no personal vault paths,
no trading-domain terms, no internal contradictions, all cross-refs
resolve. License-assistant agent exists at agents/license-assistant.md.
bborbe added a commit that referenced this pull request Jun 2, 2026
Restructures the godoc style guide with canonical `### RULE` blocks
for its enforceable conventions. Mirrors PRs #2-5, #8, #10, #14.
Rules added (rules/index.json: 46 -> 50):
- go-doc/exported-item-must-have-comment (MUST, judgment)
Every exported function/type/const/var needs a doc comment. Mechanical
follow-up: revive's 'exported' rule.
- go-doc/comment-starts-with-name (MUST, judgment)
Doc comment first word must match the identifier name. Mechanical
follow-up: revive (or ast-grep via the PR #11 recipe).
- go-doc/package-comment-in-doc-go (SHOULD, judgment)
Package comment belongs in doc.go to survive refactors of feature files.
- go-doc/third-person-no-signature-repeat (SHOULD, judgment)
No first-person prose; don't restate the signature.
All examples generic (Order, Customer, Discount, Add) — no trading domain.
All rule IDs unique against the 46 existing entries. godoc-assistant
agent exists at agents/godoc-assistant.md.
Pre-emptive checks (lessons from PRs #6, #8, #14):
- No personal vault paths
- No trading-domain terms
- All cross-refs resolve
- make build-index regenerated; check-index target passes
bborbe added a commit that referenced this pull request Jun 2, 2026
Restructures the 6 enforceable conventions in docs/go-architecture-patterns.md
into canonical `### RULE` blocks. Mirrors PRs #2-5, #8, #10, #14, #15.
Rules added (rules/index.json: 50 -> 56, new go-architecture/* family):
- go-architecture/counterfeiter-directive-on-interface (MUST)
Every substitutable interface needs a //counterfeiter:generate directive
so go generate regenerates the fake when the interface drifts.
- go-architecture/new-prefix-constructor-naming (MUST)
Constructors start with New (not Create/Make/etc). Partial overlap with
go-factory/no-impl-in-factory-pkg for Create*.
- go-architecture/constructor-returns-interface (MUST)
New* returns the interface, not *concrete struct. Hides implementation,
keeps dependency direction one-way.
- go-architecture/private-struct-matches-interface (SHOULD)
UserService -> userService (first letter lowered). godoc pairs them;
IDE outlines pair them; refactors find them with one rename.
- go-architecture/no-globals-or-singletons (MUST)
Service deps via constructor injection, never package-level vars.
Globals break parallel tests, hide the dep graph, make refactors fragile.
- go-architecture/business-logic-not-in-main (MUST)
main.go is wiring; domain operations live in pkg/. Mixed main.go is
untestable and unreachable from other binaries.
Cross-references to existing rules (already in index, not duplicated):
- go-errors/no-context-background-in-business-logic — covers context discipline
- go-context/cancel-check-in-loop — covers infinite-loop ctx.Done() checks
- go-time/* family — covers libtime injection
All examples generic (User, UserService, Worker, log/db deps).
No trading-domain terms (KafkaBrokers grep hit is server infrastructure,
not trading-broker terminology — pre-existing in main.go example).
No personal vault paths.
Pre-emptive checks (lessons from PRs #6, #8, #14, #15): clean grep,
unique rule IDs (6 new vs 50 existing), go-architecture-assistant
agent exists at agents/go-architecture-assistant.md, make build-index
regenerated, check-index passes.
bborbe added a commit that referenced this pull request Jun 2, 2026
…Error
PR #33 bot review caught two real bugs in batch 5's
use-error-code-constants YAML + doc:
1. libhttp.NewJSONError doesn't exist. The doc text mis-named
NewJSONErrorHandler (a handler factory, different surface
entirely). Dropped from YAML regex and doc text.
2. libhttp.WrapWithCode signature is (err, code, statusCode),
NOT (err, statusCode, code). Verified against
~/Documents/workspaces/http/http_error-handler.go:65,88. Both
the YAML pattern and doc's Bad/Good examples had the wrong
arg order — would have caused real violations to be missed
silently AND the doc examples would not have compiled.
Updated:
- rules/go/use-error-code-constants.yml: $CODE binds to arg #2
(not #3); dropped NewJSONError from regex; updated comment
block to cite the verified signatures.
- docs/go-json-error-handler-guide.md: Applies-when + Enforcement
text use correct function names + arg positions; Bad/Good
examples show actual libhttp call order.
Verified via re-smoked fixture: bad calls with raw "VALIDATION_ERROR"
in position #2 fire (both 3-arg WrapWithCode and 4-arg
WrapWithDetails); good calls with libhttp.ErrorCodeValidation
constant in position #2 are clean.
Re bot's index.json drift concern: 'make build-index' regenerates
index from the doc's Enforcement field — manual doc edits ARE
the source of truth and propagate to the index automatically.
Not a process gap.
Sign up for freeto 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.

1 participant

@bborbe