Skip to content

feat!: round-4 architecture refactor (remove RawMapping/full, status & mcp packages, pagination generics, reproducible demo)#16

Merged
bim-ba merged 25 commits into
mainfrom
refactor/round-4-architecture
Jun 29, 2026
Merged

feat!: round-4 architecture refactor (remove RawMapping/full, status & mcp packages, pagination generics, reproducible demo)#16
bim-ba merged 25 commits into
mainfrom
refactor/round-4-architecture

Conversation

@bim-ba

@bim-ba bim-ba commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Round-4 architecture refactor (closes review points 3–7 + a demo-reproducibility smell). Built spec → plan → subagent-driven execution with a spec+quality review per task and an opus whole-branch review.

Workstreams

WS Change Type
W-A Remove the raw tracker issues full accessor: drops the CLI command, the issues_full MCP tool, IssuesClient.get_raw, and the RawMapping model — every resource is now a typed model. Updates ARCH-3 read-verb allow-list, ARCH-4, resources.md §4. breaking
W-B yandex/status/ package + read-only status_get MCP tool. auth status returns the bare native me (drops ServiceProbe + identity lambdas); StatusReporter shared by CLI and MCP. feat
W-C ycli/mcp/ package (server.py + cli.py + __main__.py); __init__.py stays fastmcp-free via a lazy __getattr__ so the base install loads the CLI sub-app without the mcp extra. refactor
W-D Pagination PEP 695 generics [P, T]; collect_single_page folded into SinglePageStrategy.collect_wrapped; is None cursor termination (API-contract correct). refactor
W-E Docstrings for four empty __init__.py. docs
W-G Demo GIF renders real CLI output from committed, leak-free fixtures via in-process responses (deterministic, offline) instead of hand-typed text; MCP list comes from the real ycli mcp methods. build

Surface change

  • MCP tools: -tracker_issues_full, +status_get (net 25 → 25).
  • CLI: -tracker issues full.
  • Snapshots regenerated on purpose (ARCH-6).

Verification

  • pytest 264 passed / 100% coverage; ruff format --check, ruff check, lint-imports all clean; ty check shows only the pre-existing smoke_test.py:21 diagnostic (present on main, not introduced here).
  • Whole-branch review (opus): all six cross-task seams verified; ready to merge.

Release note

Public-surface removal (full / issues_full) is breaking → on the pre-1.0 line, semantic-release cuts a minor bump (v0.9.0). Squash-merge with a feat!: title + a BREAKING CHANGE: footer. After release: run uv lock + a build: sync commit (PSR bumps pyproject, not the lock).

🤖 Generated with Claude Code

Sava Znatnov and others added 10 commits June 29, 2026 12:34
Covers seven workstreams: remove RawMapping/full raw accessor (W-A),
status package + native me + status_get MCP tool (W-B), ycli/mcp package
(W-C), pagination generics + Envelope protocol (W-D), smell sweep (W-E),
ARCHITECTURE/snapshots consolidation (W-F), and reproducible demo output
via committed fixtures rendered by the real CLI (W-G).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bite-sized TDD tasks for W-A..W-G: mcp package (fastmcp-free __init__),
RawMapping removal, status package + status_get, pagination generics,
__init__ docstrings, reproducible demo via committed fixtures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Server lives in ycli/mcp/server.py; the package __init__ stays fastmcp-free and
re-exports mcp/main lazily so the base install loads ycli.mcp.cli without the extra.
python -m ycli.mcp runs the server via __main__. Updates ARCH-3 prose.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BREAKING CHANGE: drops the 'tracker issues full' CLI command, the issues_full MCP
tool, IssuesClient.get_raw, and the RawMapping model. Every resource is a typed model.
Updates ARCH-3 read-verb allow-list, ARCH-4, resources.md, and the surface snapshots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Explodes yandex/status.py into a package; drops ServiceProbe and the per-service
identity lambdas — each ServiceAuthStatus now carries the bare native me model.
Adds the status_get MCP tool (namespace status, read-only). ARCH-1 carve-out note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Strategies are generic over page type P and item T; collect returns list[T].
Folds the free collect_single_page into SinglePageStrategy.collect_wrapped.
Cursor/url termination uses 'is None' to match the null-cursor API contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs/demo/render.py runs the real ycli in-process against committed JSON fixtures via
responses (deterministic, leak-free, offline); the demo's MCP tool list now comes from
the real 'ycli mcp methods' instead of a baked, drift-prone list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assert wiki via the correct wire structured_content (fastmcp's typed result.data
re-hydrates the undiscriminated me union unreliably); fix the ARCHITECTURE.md
Layout line (root mcp.py is now the mcp/ package); annotate the demo-render test helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
demo.yml stamped the regenerated-GIF commit with a marker that makes GitHub
cancel the run. Recursion is already prevented structurally (the job triggers on
docs/demo/** while the GIF output lives in docs/assets/), and GITHUB_TOKEN pushes
don't start new runs — so the marker was redundant, yet it rides a squash-merge
into main and silently cancels the python-semantic-release run. Remove it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bim-ba bim-ba force-pushed the refactor/round-4-architecture branch from ab50b3d to da35bc2 Compare June 29, 2026 11:06
actions-user and others added 15 commits June 29, 2026 11:08
Replace the _KeyRef/_IdRef/_DisplayRef wrapper models with shared KeyStr/IdStr/
DisplayStr annotations in ycli.yandex.models (one _extract(field) factory). Ref
fields are now plain strings; the per-model *_key/*_display flattening properties
are removed. JSON/YAML/MCP output flattens from {"key": "x"} to "x".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Recursive, model-agnostic _render: scalars as text, null/empty fields omitted from
the table (data unchanged — JSON/YAML still carry them), scalar lists joined with
', ', object lists as column tables with all-empty columns dropped. Replaces the
RichCell JSON-in-cell rendering; the model layer now flattens API ref wrappers, so
no presentation-side collapse is needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
render.py renders the tracker issue with --format pretty + FORCE_COLOR (the
interactive table a user actually sees, not JSON); fixtures are modern and
emoji-rich while still conforming to the API model schema (DEMO-42 in queue
DEMO; an onboarding wiki page). The mcp-extra is synced once off-camera in the
tape's Hide block and the shim runs uv with --no-sync, so 'mcp methods' shows
the real tool list with no install noise in the recording.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a negative-path test (a typo'd ROUTES key would otherwise silently emit an
error frame into the GIF), asserts column headers on the mixed-list render, and
rewrites the object-list row loop with an idiomatic strict zip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eld shape

The flatten removed the *_key/*_display properties; these two test files still
named/described them as property accessors though the bodies assert plain fields.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…trip

status_get returned an undiscriminated TrackerMe | WikiMe | FormsMe union;
fastmcp rebuilds result.data from the output schema and picked the first anyOf
branch, reshaping the wiki payload into the tracker shape and dropping fields
like username. Split ServiceAuthStatus into Literal-tagged subclasses combined
with Field(discriminator="service"); route probes through a TypeAdapter so the
discriminator is the single source of truth. The CLI/SDK path is unchanged and
still carries the bare native me model; a fastmcp round-trip test now asserts
wiki username survives on result.data.

Also applies the open drift-logs this work surfaced: the full-names naming rule
(CLAUDE.md), the make_cached_client provider pattern (resources.md S3 +
ARCHITECTURE.md), and logs+resolves the discriminated-union convention
(resources.md S5). All three drift entries transitioned OPEN->APPLIED with a
2026-06 monthly index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lors)

Drop style=flat-square (sharp grey #555) for shields.io default flat (rounded
rx=3, matching the DeepWiki badge). Add a per-badge logo (githubactions, pytest,
pypi, python, opensourceinitiative) with logoColor=white, and semantic colors:
CI auto-green on pass, coverage brightgreen, pypi/python blue, license grey.
All five URLs verified HTTP 200 with the logo embedded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_args.py mixed two concerns: CLI argument type aliases and request helpers.
Split by concern, matching the underscore-internal module convention
(_base.py/_deps.py/_mcp.py):
- tracker/_types.py  (KeyArg)  +  tracker/_utils.py (count_body, parse_fields)
- forms/_types.py    (SurveyIdArg) — no helpers, so no _utils

Updated all importers (tracker issues/transitions split across both modules;
the rest import only the type), renamed tests/.../test_args.py -> test_utils.py,
and refreshed the ARCHITECTURE.md layout + module legend. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors ycli.mcp: the CLI-only modules now live together under ycli/cli/:
- root.py    (Typer command tree + console entry point; was cli.py)
- context.py (AppContext)
- output.py  (the --format serializer)
- __init__.py lazily re-exports app/main via __getattr__ — domain cli.py modules
  import ycli.cli.context, so eagerly importing root here (it pulls every domain
  app) would be circular; same lazy pattern ycli.mcp already uses
- __main__.py for 'python -m ycli.cli'

Updated ~45 import sites, the ARCH-3 import-linter contract (ycli.cli.**), the
ARCHITECTURE.md layout and the ARCH-4 path note (src/ycli/cli/output.py). The
ARCH-4 test skips by basename 'output.py', which is unchanged, so the enforcing
check still holds without edit. Entry points ycli/yandex-cli still resolve to
ycli.cli:main; 'ycli --help' and 'python -m ycli.cli' both verified. No behavior
change.

INVARIANT FLAG (per CLAUDE.md): ARCH-4's documented path moved output.py ->
cli/output.py; the basename-based enforcement is unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… explicitly

The ycli.cli package __init__ no longer re-exports app/main via __getattr__. Domain
cli.py modules import ycli.cli.context, which runs this init; eagerly importing the
root app there (it pulls in every domain app) was the circular import the shim worked
around. With an empty __init__ the cycle can't form, and call sites reference the
module explicitly — more explicit, no magic:
- root.py renamed back to app.py (the app-submodule/attr collision is gone once
  __init__ no longer re-exports app)
- entry points (ycli.cli.app:main), __main__, render.py and ~20 test call sites now
  import ycli.cli.app directly
- guarded smoke_test's g.typer_instance (Typer | None) access — the lazy shim's Any
  return had been masking that pre-existing ty diagnostic

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per the project's explicit-naming preference, rename the domain-internal helper
modules (the leading underscore is dropped; _deps is spelled out in full):
  _deps.py       -> dependencies.py   (per domain)
  _base.py       -> base.py           (per domain)
  _types.py      -> typedefs.py       (per domain; avoids shadowing stdlib 'types')
  _utils.py      -> utils.py          (tracker)
  yandex/_mcp.py -> yandex/mcp.py     (shared MCP helpers)

Updated every importer, the conftest cache-reset fixture, the new_endpoint.py
scaffold templates, and the docs (ARCHITECTURE.md layout + legend,
docs/conventions/resources.md S3, CLAUDE.md). No behavior change; entry points,
the import-cycle fix and 100% coverage all hold.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ycli.cli.root -> ycli.cli.app rename shortened the patch target enough to fit
on one line; ruff format (run in CI as 'ruff format --check') wants it collapsed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bim-ba bim-ba merged commit f1d95c6 into main Jun 29, 2026
3 checks passed
Sign up for free to 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.

2 participants