feat!: round-4 architecture refactor (remove RawMapping/full, status & mcp packages, pagination generics, reproducible demo)#16
Merged
Conversation
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>
ab50b3d to
da35bc2
Compare
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
tracker issues fullaccessor: drops the CLI command, theissues_fullMCP tool,IssuesClient.get_raw, and theRawMappingmodel — every resource is now a typed model. Updates ARCH-3 read-verb allow-list, ARCH-4,resources.md§4.yandex/status/package + read-onlystatus_getMCP tool.auth statusreturns the bare nativeme(dropsServiceProbe+ identity lambdas);StatusReportershared by CLI and MCP.ycli/mcp/package (server.py+cli.py+__main__.py);__init__.pystays fastmcp-free via a lazy__getattr__so the base install loads the CLI sub-app without themcpextra.[P, T];collect_single_pagefolded intoSinglePageStrategy.collect_wrapped;is Nonecursor termination (API-contract correct).__init__.py.responses(deterministic, offline) instead of hand-typed text; MCP list comes from the realycli mcp methods.Surface change
-tracker_issues_full,+status_get(net 25 → 25).-tracker issues full.Verification
pytest264 passed / 100% coverage;ruff format --check,ruff check,lint-importsall clean;ty checkshows only the pre-existingsmoke_test.py:21diagnostic (present onmain, not introduced here).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 afeat!:title + aBREAKING CHANGE:footer. After release: runuv lock+ abuild:sync commit (PSR bumps pyproject, not the lock).🤖 Generated with Claude Code