Skip to content

[SPEC-FIX] Move annotation examples from Section 11 into Section 3 (Annotation Rules) #95

@michael-conrad

Description

@michael-conrad

Problem

Section 11 of doc/swagger/swagger.md contains three before/after annotation examples (GET+path params, GET+query params, POST+body param). These examples belong next to the rules they illustrate in Section 3 (Annotation Rules) — not orphaned at the end of the document in a rarely-reached dead zone. A developer reading Section 3.1 to learn the inline annotation pattern has to scroll past Sections 4-10 (anti-patterns, TDD, validation, smoke tests, verification, bugs) to find the examples.

Additionally, the existing codebase contains one val-bound route (listStatusesRoute in ApiRepositoryStatusControllerBase.scala:45) that uses a legacy alias pattern. Section 3.1 and Section 4 (anti-patterns) currently describe val-bound as a hypothetical pattern with a generic example (val getApiRoot = apiOperation[...]; get("...", operation(getApiRoot))). With a real val-bound example being added in §3.7.4, these sections must be updated to reference the actual pattern and cross-link to the concrete example.

Fix

Move the three before/after examples from Section 11 into a new subsection Section 3.7 (Annotation Examples) with subsections 3.7.1-3.7.3, add a fourth subsection 3.7.4 for the val-bound route with legacy alias, update Sections 3.1 and 4 to reference the real val-bound pattern from the codebase, and delete Section 11.

ID Example Moves From Moves To
11.1 GET with path params — getIssue old §11 §3.7.1
11.2 GET with query params — listIssues old §11 §3.7.2
11.3 POST with body param — createIssue old §11 §3.7.3
(new) Val-bound route with legacy alias — listStatusesRoute / listStatuses (new) §3.7.4

3.1 and §4 wording updates

Section 3.1 currently says:

Where a pre-existing val-bound operation already exists in the codebase (e.g., val getApiRoot = apiOperation[...]; get("...", operation(getApiRoot))), that val-bound instance may be retained — do not rewrite it solely for style consistency. All new annotations must use the inline pattern.

Section 4 (anti-patterns) currently says:

Do NOT use val-bound pattern for new annotations (val name = apiOperation[...]; get("...", operation(name))) — use inline pattern instead. Pre-existing val-bound instances may be retained where they already exist in the codebase.

Both reference a hypothetical val name = apiOperation[...]; get("...", operation(name)) pattern. With a real val-bound example now documented in §3.7.4, these sections must be updated to:

  1. §3.1: Replace the hypothetical example with a cross-reference to §3.7.4 as the concrete val-bound pattern. The wording should explicitly note that the codebase's val-bound pattern is val route = get("...", apiOperation[...]) {...} (route bound to val with apiOperation inline in the transformer list) rather than val op = apiOperation[...]; get("...", operation(op)) (operation extracted to a separate val). The actual pattern in the codebase binds the entire route expression to a val, with apiOperation appearing inline within that route's transformer list — not the Scalatra operation(op) extraction pattern.

  2. §4: Update the anti-pattern to distinguish between the two val-bound patterns. The operation(op) extraction pattern (val op = apiOperation[...]; get("...", operation(op))) remains prohibited for new annotations. The route-level val binding (val route = get("...", apiOperation[...]...) {...}) is the pattern that already exists in the codebase and may be retained — with a cross-reference to §3.7.4.

3.7.4 content

This example comes from the existing codebase — ApiRepositoryStatusControllerBase.scala:45:

Before (un-annotated val-bound route):

val listStatusesRoute = get("/api/v3/repos/:owner/:repository/commits/:ref/statuses")(referrersOnly { repository =>
  // ... response logic ...
})

get("/api/v3/repos/:owner/:repository/statuses/:ref") {
  listStatusesRoute.action()
}

After (annotated val-bound route):

val listStatusesRoute = get("/api/v3/repos/:owner/:repository/commits/:ref/statuses",
  apiOperation[Seq[ApiCommitStatus]]("listStatuses")
    .summary("List commit statuses for a ref")
    .description("Returns commit statuses for a specific ref in the specified repository")
    .parameters(
      pathParam[String]("owner").description("Repository owner"),
      pathParam[String]("repository").description("Repository name"),
      pathParam[String]("ref").description("Ref to list statuses for (SHA, branch name, or tag name)")
    )
    .responseMessages(ResponseMessage(404, "Ref not found")))(referrersOnly { repository =>
  // ... response logic ...
})

get("/api/v3/repos/:owner/:repository/statuses/:ref") {
  listStatusesRoute.action()
}

Only the primary val route binding receives the apiOperation annotation. The legacy alias route uses .action() to dispatch without a separate apiOperation — Scalatra-Swagger introspects the val binding to discover the operation, so the alias inherits the documentation automatically.

Verification against source code

The implementing agent MUST verify the §3.7.4 example against the actual source file src/main/scala/gitbucket/core/controller/api/ApiRepositoryStatusControllerBase.scala to confirm:

  1. The route path strings in the before/after example match the actual route definitions exactly
  2. The referrersOnly authenticator in the before example matches the actual code
  3. The parameter names (owner, repository, ref) match the actual route path parameters
  4. The .action() delegation pattern in the legacy alias matches the actual code
  5. The description text (summary, description, parameter descriptions) is accurate for what the endpoint actually returns

If the verified source code differs from the proposed example, the example MUST be updated to match the actual source code — code is the authoritative source per project guidelines.

Success Criteria

SC Criterion Evidence Type
SC-1 §11 deleted from doc/swagger/swagger.md string
SC-2 §3.7 exists with four subsections (3.7.1, 3.7.2, 3.7.3, 3.7.4) string
SC-3 All three original before/after examples preserved verbatim semantic
SC-4 §3.7.4 contains a val-bound route with legacy alias example showing listStatusesRoute and listStatusesRoute.action() string
SC-5 §3.7.4 example references the source file ApiRepositoryStatusControllerBase.scala string
SC-6 §3.7.4 after example includes apiOperation annotation on the val declaration string
SC-7 §3.7.4 explains that the legacy alias inherits documentation via .action() without its own apiOperation semantic
SC-8 Route paths, authenticators, and parameter names in §3.7.4 verified against actual source code in ApiRepositoryStatusControllerBase.scala semantic
SC-9 §3.1 updated to reference §3.7.4 as the concrete val-bound example and accurately describes the route-level val binding pattern (val route = get("...", apiOperation[...]...)) rather than the operation(op) extraction pattern semantic
SC-10 §4 anti-pattern entry for val-bound updated to distinguish the prohibited operation(op) extraction pattern from the permitted route-level val binding pattern, with cross-reference to §3.7.4 semantic

Dependencies

Constraints

  • No .opencode-specific references (per [SPEC-FIX] Add before/after annotation examples and remove .opencode references in swagger docs #93 scope) — verify none introduced
  • bathPath upstream typo preserved as-is
  • Inline annotation pattern (not val-bound) preserved in examples 3.7.1-3.7.3
  • Val-bound example (3.7.4) documents a pre-existing pattern — new annotations must still use inline pattern per §3.1
  • §3.1 and §4 wording must accurately distinguish between the prohibited operation(op) extraction pattern and the existing route-level val binding pattern, cross-referencing §3.7.4

🤖 Co-authored with AI: OpenCode (ollama-cloud/glm-5.1)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions