Repository navigation
feat: add-component seeds C# and Python component repos once - #180
Merged
Merged
Conversation
`specfuse authoring add-component <csharp|python> <dir>`, run from the spec repo, creates ../<dir>, renders templates/components/<lang> into it, git-inits it, appends the component's groups to the project file, and runs generate.sh for them when the project has it. The seeded files are project-owned from then on: not in the scaffold manifest, never touched by upgrade, never emitted by the generator. C#: .slnx, Directory.Build.props (src/ + gen-src/), Domain / Infrastructure / Api projects with the package references the pinned generator's output compiles against (taken from the generator's own compile fixture), a Program.cs wiring the generated registrations, and the two seams generated code calls by name or needs to be usable: UserHelper and an ExceptionFilter mapping generated domain exceptions to HTTP. Requires a tenancy block (clabonte/generator#2883). Python: a uv workspace with contracts and core libraries whose package roots follow the generator's derivation; kebab-case project names are refused until clabonte/generator#2882 is fixed. Drift guard: example-regen seeds both from hello-orders, generates with the pinned jar, then builds and serves the C# API (200/404/400) and syncs, imports and tests the Python workspace. ci.yml covers the offline refusals and the seed shape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 28, 2026
…ifest (#185) Both seeded .gitignore files ignored *.generated-paths.json as "rewritten on every regen". The generator documents the opposite: the manifest is committed in the component, its capturedAt comes from the spec repo's <stateDir>/<group>.paths.json baseline rather than the clock, so a regen that moved nothing leaves it byte-identical (clabonte/generator#2055). Ignoring it discards the old-to-new path mapping a layout-changing release hands over. Co-authored-by: Claude Opus 5.5 <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.
Closes #177.
specfuse authoring add-component <csharp|python> <dir>runs from the spec repo. It creates../<dir>, renderstemplates/components/<lang>/into it and runsgit init. It also appends the component's groups to the project file. If the project hasscripts/specfuse/generate.sh(from #176), it runs that for the new groups; otherwise it prints the commands. Options:--dry-run,--group-prefix,--namespace,--project,--no-generate. Docs are indocs/add-component.md.Seeded once, then project-owned. The files are not in the scaffold manifest,
upgradenever touches them, and the generator never writes them, as decided in #177. The command refuses a directory that exists and isn't empty, and group names the project file already uses.What gets seeded
C#:
.slnx, andDirectory.Build.propscompilingsrc/andgen-src/.csprojfiles. Package references come from the generator's owncsharp-compile-fixture.Program.cs, wiring six generated registration methods across four namespacesUtils/UserHelper.cs: the generated controllers call it by name, and the generator never emits itFilters/ExceptionFilter.cs: without it, a generatedNotFoundExceptioncomes back as a 500.gitignorePython:
libs/contractsandlibs/coreHelloOrders→ho_contracts/ho_core)sqlalchemy[asyncio]The artifact set was found by building, not guessed
I prototyped against generator 0.19.0 until the seed compiled and served. Missing
snapshotandsnapshotContextgive CS0234; missingauthorizationRuntimegivesRoleRequirednot found; leaving outEntityFrameworkCore.InMemorybreaksConfigureServices; and so on.Generator bugs found along the way (filed)
initrequires, gives the invalid Python roothello-orders_contracts.add-component pythonrefuses such names and points to the workaround (a PascalCasename).servicealways emitsIEventCollector, and the event runtime requirestenancy, so single-tenant C# can't compile.add-component csharprequires atenancyblock and always registers the event artifacts."RestoManagerTestDb"as a default database name, andfrom rm_llm.models import ModelConfig. The seed'sProgram.cspasses an explicit database name.When #2882 and #2883 are fixed, the two refusals and their CI checks go away.
Drift guard
example-regen.yml(pinned jar):tenancyblock in the copy), generates, runsdotnet build, then starts the API and checks list 200, missing 404, bad body 400.uv sync --all-packages, imports every generated module, and runspytest.ci.yml(offline):git init, and no unrendered{{tokensRun locally: both
example-regensteps and theci.ymlstep pass against this branch, with the C# API answering 200/404/400 and Python showing every module importing and 3 tests passing. Also checked:add-componentdrivinggenerate.shfrom #176's branch. Not run locally: building the wheel, which was denied here; CI's smoke job builds it and runs the offline step through the installed CLI.Base:
main, not stacked. It runsgenerate.shonly when present, so it doesn't depend on #176 or #175.🤖 Generated with Claude Code