create_issue / update_issue now accept a milestone argument: a numeric milestone id or a milestone title (resolved case-insensitively against open and closed milestones; unknown titles return a clear error). On update_issue, milestone: 0 clears the milestone.
New GiteaClient._resolve_milestone_id mirrors the existing label name->id resolver.
A BeforeValidator rejects booleans so they are not silently coerced to an id.
MCP schemas, docs/api-reference.md and docs/write-mode.md updated (the write-mode tool list was stale — 6 listed vs 16 implemented — now complete).
8 new tests; make lint (ruff/black/mypy) and full suite green at 83% coverage.
Gitea 1.26.2 (this instance) exposes no project endpoints in its REST API (verified via swagger.v1.json). Project/board support is therefore not implementable and is documented as intentionally unsupported. Closes the actionable scope of #22.
Guardrails
All new behaviour stays behind the existing write-mode + policy gating (write_operation=True); no new default-on surface.
Implements the milestone-assignment part of #22.
## Changes
- `create_issue` / `update_issue` now accept a `milestone` argument: a numeric milestone **id** or a milestone **title** (resolved case-insensitively against open and closed milestones; unknown titles return a clear error). On `update_issue`, `milestone: 0` clears the milestone.
- New `GiteaClient._resolve_milestone_id` mirrors the existing label name->id resolver.
- A `BeforeValidator` rejects booleans so they are not silently coerced to an id.
- MCP schemas, `docs/api-reference.md` and `docs/write-mode.md` updated (the write-mode tool list was stale — 6 listed vs 16 implemented — now complete).
- 8 new tests; `make lint` (ruff/black/mypy) and full suite green at 83% coverage.
## Projects investigation (also part of #22)
Gitea 1.26.2 (this instance) exposes **no project endpoints** in its REST API (verified via `swagger.v1.json`). Project/board support is therefore not implementable and is documented as intentionally unsupported. Closes the actionable scope of #22.
## Guardrails
All new behaviour stays behind the existing write-mode + policy gating (`write_operation=True`); no new default-on surface.
Refs #22
Gitea may return JSON null for an issue's `labels`, `assignees`, or
`user` fields. `dict.get(key, [])` returns None when the key is present
with a null value (the default is only used for missing keys), so the
list comprehensions raised `'NoneType' object is not iterable` for
otherwise-valid issues. Coalesce with `or []` / `or {}` so empty/null
collections normalize to empty results.
Adds a regression test covering all three null fields.
Adds reusable, secret-safe logging helpers to `logging_utils`:
- `log_event(logger, level, event, **context)` emits a named event with a
sanitized `context` mapping (sensitive keys masked as `***`).
- `log_nullable_field(...)` records whether a parsed field is None plus its
runtime type, without dumping its contents.
- `sanitize_context(...)` is the shared masking primitive.
The JSON formatter now serializes a record's `context` into the payload.
`get_issue_tool` is instrumented at DEBUG (`get_issue.start`,
`get_issue.payload_shape`, `get_issue.field_check` for labels/assignees/user)
so the nullable-field parsing that caused #13 is diagnosable going forward.
Adds tests for the helpers, the formatter, and the get_issue instrumentation,
and documents the pattern in docs/observability.md.
Every write tool's `except (Auth...)` re-raise and `except GiteaError ->
RuntimeError` wrapping was previously untested, leaving write_tools at 60%
coverage and the repo below the 80% gate. Adds parametrized error-path tests
for all 15 write tools (backend error wrapping + auth propagation), raising
write_tools coverage to 99% and total coverage above the gate.
Add a `milestone` argument to `create_issue` and `update_issue` accepting
either a numeric milestone id or a title (resolved case-insensitively against
open and closed milestones, with a clear error for unknown titles). On
`update_issue`, `milestone: 0` clears the milestone. A BeforeValidator rejects
booleans so they are not silently coerced to an id.
Gitea Projects (Kanban boards) were investigated for #22 and are intentionally
left unsupported: Gitea 1.26.2 exposes no project endpoints in its REST API.
Documented this in api-reference.md and refreshed the (stale) write-mode tool
list to cover all 16 write tools.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Latte
merged commit 026f3a654f into main2026-06-22 16:00:30 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements the milestone-assignment part of #22.
Changes
create_issue/update_issuenow accept amilestoneargument: a numeric milestone id or a milestone title (resolved case-insensitively against open and closed milestones; unknown titles return a clear error). Onupdate_issue,milestone: 0clears the milestone.GiteaClient._resolve_milestone_idmirrors the existing label name->id resolver.BeforeValidatorrejects booleans so they are not silently coerced to an id.docs/api-reference.mdanddocs/write-mode.mdupdated (the write-mode tool list was stale — 6 listed vs 16 implemented — now complete).make lint(ruff/black/mypy) and full suite green at 83% coverage.Projects investigation (also part of #22)
Gitea 1.26.2 (this instance) exposes no project endpoints in its REST API (verified via
swagger.v1.json). Project/board support is therefore not implementable and is documented as intentionally unsupported. Closes the actionable scope of #22.Guardrails
All new behaviour stays behind the existing write-mode + policy gating (
write_operation=True); no new default-on surface.Refs #22
Gitea may return JSON null for an issue's `labels`, `assignees`, or `user` fields. `dict.get(key, [])` returns None when the key is present with a null value (the default is only used for missing keys), so the list comprehensions raised `'NoneType' object is not iterable` for otherwise-valid issues. Coalesce with `or []` / `or {}` so empty/null collections normalize to empty results. Adds a regression test covering all three null fields.