Skip to content

Agents write granular claim-logs because the CLI's in-loop signals reward per-function anchoring #142

Description

@cmcdonald99

For new contributors: this issue is self-contained — it explains the problem, where the code lives, how to build/test/dogfood, and concrete acceptance criteria. Start by reading the "Orientation" section, then pick a task from "Scope". Tasks are independent; #3 (docs) is a good first PR, #1#2 (Rust) are the core fix.

TL;DR

Agents using the surface CLI write granular "claim-logs" — one claim per function, near-1:1 symbol→claim mapping, almost no prose. A hub is supposed to be the opposite: an onboarding doc that documents a system, prose-first, with claims that are coarse and span multiple anchor points (at: lists).

The fix is not "write better docs." It's that the CLI's in-loop signals reward the wrong behavior, and those signals are what actually teach agents (they read CLI output every iteration; they read prose docs once, if ever).

Why this happens (root cause)

Signal Where What it teaches the agent
surf suggest prints a "starter hub" listing every unanchored public symbol, one at: per symbol surf-cli/src/suggest.rsprint_human (~L166–189) "Write one claim per function."
lint_under_coverage nags for every uncovered public function surf-cli/src/lint.rslint_under_coverage (~L376) "Drive the list to zero → one claim per function."
The multi-site at: list (the actual tool for coarse, system-level claims) is documented only as a staleness footnote docs/guides/authoring-hubs.md (anchor grammar section) Never framed as the default; agents don't reach for it.
AGENTS.md step 4: "Added public behavior? Add a hub claim for it — the under-coverage warning flags public functions with no claim." AGENTS.md Encodes the function-granular instinct in writing.

There is currently no signal that rewards prose or claim consolidation. The only counter-pressure ("too many anchors in one hub") fires at the hub level, not the claim level.

What good vs. bad looks like

Bad (a "claim-log"): many claims, each anchoring a single symbol, restating what one function does, with a thin one-line # heading and no real prose. In this repo the maintainer points at hubs/cli-check.md as too thin — three single-site claims, ~5 lines of prose.

Good (an onboarding doc): a summary: that frames the whole system; a handful of coarse claims, several of which use multi-site at: lists to anchor one invariant across the 2–3 places it actually lives; and prose with structure (tables, ## sections, the "single most important distinction", a "Boundary" note on what the gate does not cover) that a human or agent reads to onboard. Anatomy of a good claim:

- claim: "trading commission is the only MULTI-LEVEL stream: it reads referral_graph for up to
    three ancestors, is lifetime, skips self-edges, and pays REFERRAL_COMMISSION_RATES[tier][level]"
  at:
    - backend/.../referral-commission.service.ts > ReferralCommissionService > buildCommissionRecords
    - packages/.../ReferralCommission.ts > REFERRAL_COMMISSION_RATES   # one invariant, two sites
  hash: 78e302b45972

One claim describes a behavior of the system and seals every span that behavior depends on — not one claim per function.

Scope (prioritized — lead with the CLI)

1. Reframe surf suggest output — highest leverage

surf-cli/src/suggest.rsprint_human. Today it prints a copy-pasteable starter hub with one at: per symbol, which agents treat as "claims to write."

  • Add a preamble making clear these are undocumented symbols, not a list of claims — group related symbols into one system-level claim with a multi-site at: list, prose first.
  • Consider grouping suggestions by file/module, and/or emitting a single multi-anchor at: skeleton instead of N 1:1 stubs, so the default output shape models a good hub.
  • Keep --format json shape unchanged (tooling depends on it; see json_shape_has_no_hash test).

2. Add symmetric, claim-level lint nudges (advisory, never blocking)

surf-cli/src/lint.rs. Mirror lint_coarse_span / lint_under_coverage with warnings that push toward consolidation:

  • Thin-prose warning — a hub whose markdown body is mostly frontmatter / has a very low prose-to-claim ratio. A hub is an onboarding doc; flag when it isn't one.
  • Granularity / "claim-log" smell — a hub with many single-site claims and near-1:1 symbol mapping → suggest consolidating into fewer multi-anchor claims.
  • Add a new Severity::Warn Finding for each; follow the existing lint_coarse_span pattern and add tests in surf-cli/tests/lint.rs.

3. Reframe the docs to match — good first PR

  • docs/guides/authoring-hubs.md: promote the multi-site at: list from staleness-footnote to the recommended default for system claims; add a short "a hub is an onboarding doc — prose first" section using the good/bad contrast above.
  • AGENTS.md: rewrite step 4 so the instinct is "extend an existing system claim's prose / add an anchor site" before "add a new granular claim."
  • This repo's docs/ is canonical (the docs site is generated from it) — see CONTRIBUTING "Docs source of truth".

4. (Optional) Prose-first surf new scaffold

surf-cli/src/new.rs — ship a template with ## How it works headings and one multi-anchor example claim, so a fresh surf new is shaped like a good hub.

Orientation for contributors

Layout (from CONTRIBUTING.md):

  • surf-core/ — pure parse/resolve/hash logic, no I/O.
  • surf-cli/ — the surf binary: commands + all I/O. All the code in this issue lives here, under surf-cli/src/{suggest,lint,new}.rs, with per-command tests in surf-cli/tests/.

Build & test:

cargo build
cargo test --all
cargo fmt --all
cargo clippy --all-targets -- -D warnings   # CI fails on any warning

Dogfood your change (Surface governs its own hubs — this is the best way to see suggest/lint output for real):

cargo run -q -p surf-cli -- suggest "surf-cli/src/**/*.rs"   # see #1's output
cargo run -q -p surf-cli -- lint                              # see #2's warnings
cargo run -q -p surf-cli -- check

If you change a symbol a hub anchors, check/lint will flag it — re-read the prose and surf verify "<at>" if it still holds (don't rubber-stamp). Add a [Unreleased] line to CHANGELOG.md for user-facing changes, and an entry to docs/dogfood-log.md if you hit a notable moment.

Acceptance criteria

  • surf suggest human output no longer reads as "a list of claims to write" — it explicitly steers toward coarse, multi-anchor, prose-first hubs. JSON output unchanged.
  • surf lint emits at least one new advisory warning that flags thin-prose and/or claim-log-shaped hubs, with tests in surf-cli/tests/lint.rs. Warnings stay exit-0 (non-blocking).
  • docs/guides/authoring-hubs.md and AGENTS.md frame hubs as onboarding docs and multi-site at: lists as the default for system claims.
  • cargo test --all, cargo fmt --all, cargo clippy --all-targets -- -D warnings all clean.
  • cargo run -p surf-cli -- check / lint stay green on this repo (re-verify intentionally changed anchors).

Key insight

surf suggest + lint_under_coverage are the agent's de-facto teachers, and both currently teach "one claim per function." Fixing those two surfaces changes behavior more than any doc edit — which is why the CLI tasks (#1, #2) are prioritized over the docs task (#3).

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationdxDeveloper experience / CLI ergonomicsenhancementNew feature or requestgood first issueGood for newcomers

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions