Skip to content

feat(api): projects route shell (7 routes, REST-corrected) — #20#24

Closed
neversettle17-101 wants to merge 2 commits into
mainfrom
feat/20
Closed

feat(api): projects route shell (7 routes, REST-corrected) — #20#24
neversettle17-101 wants to merge 2 commits into
mainfrom
feat/20

Conversation

@neversettle17-101
Copy link
Copy Markdown
Collaborator

@neversettle17-101 neversettle17-101 commented May 28, 2026

Implements #20 (#18a) — Projects route shell + request/response transport on top of the Phase 1a skeleton (#14). Rebased on main.

Design reference: ao-routes-analysis — the legacy route audit + per-route verdicts this PR implements.

Scope

Maps the /api/v1 projects layer: routes, request decoding, response encoding, the OpenAPI contract, and the package boundaries. No project.Manager implementation — that lands on top of this. With the (nil) Manager the route shell returns 501; with a Manager injected the same handlers run end-to-end and a correct request returns 2xx (verified below).

This PR also lands the code-first OpenAPI + TypeScript codegen pipeline (Go is the source of truth; openapi.yaml and the frontend types are generated, with a CI drift guard) — see the OpenAPI section below.

Route surface (5 canonical)

Method Path Success Errors
GET /api/v1/projects 200 {projects: Summary[]} 500
POST /api/v1/projects 201 {project} 400 INVALID_JSON|PATH_REQUIRED|NOT_A_GIT_REPO, 409 PATH_ALREADY_REGISTERED|ID_ALREADY_REGISTERED (details.suggestion), 500
GET /api/v1/projects/{id} 200 {status:"ok"|"degraded", project} 404 PROJECT_NOT_FOUND, 500
PATCH /api/v1/projects/{id} 200 {project} 400 INVALID_JSON|IDENTITY_FROZEN|INVALID_LOCAL_CONFIG, 404, 409 PROJECT_DEGRADED|PROJECT_MISSING_PATH, 500
DELETE /api/v1/projects/{id} 200 {projectId, removedStorageDir} 400 INVALID_PROJECT_ID, 404, 500

Per the route-analysis verdicts, POST /projects/reload is dropped (a Next.js in-process cache hack) and POST /projects/{id}/repair is deferred. Legacy PUT /projects/{id} and POST /projects/{id} are unregistered → 405.

REST corrections vs legacy TS surface

# TS violation Go fix
R3 PUT /projects/{id} aliased to PATCH PATCH only; PUT → 405
R5 degraded GET returns 200 with an error field discriminator {status, project}
R6 inconsistent ok/success flags dropped on 2xx; return the affected resource
R9 bare {error: msg} locked envelope {error, code, message, requestId, details?}

Request ↔ response layer (where the shapes live)

Split by product command vs HTTP wire shape (backend-code-structure.md):

Piece Lives in Why
Request commands — AddInput, UpdateConfigInput internal/project/dto.go Product commands shared by HTTP + CLI; handlers decode the body straight into them
Response envelopes — {projects}, {project}, {status, project} internal/httpd/controllers/dto.go HTTP-only wire shapes; getProjectResponse maps project.GetResult onto the OpenAPI oneOf
Transport primitives — JSON writer, error envelope, body decode internal/httpd/httpx shared leaf package so both httpd and controllers use it without an import cycle

Each handler runs nil-guard → decode → call Manager → encode. Only domain.ProjectID stays in domain/; the controller depends solely on project.Manager.

OpenAPI — code-first, Go is the source of truth

openapi.yaml is generated from Go (do not hand-edit) and stays OpenAPI 3.1.0:

  • apispec.Build() (internal/httpd/apispec/build.go) reflects the contract types + an operation registry into the document via swaggest/openapi-go; cmd/genspec writes it on go generate; apispec embeds/serves it at GET /api/v1/openapi.yaml and slices it into the 501 bodies as before.
  • Annotations live in two places only: schema facets (description/enum/default) as struct tags on the existing project.* / httpx.Error types; operation metadata (paths, status codes, summaries, the ok|degraded oneOf) in Build(). required is derived from the omitempty convention by an InterceptProp hook — structs stay clean. Clean schema/TS names (Project, Summary, APIError, …) via InterceptDefName.
  • Frontend types fall out of the same spec: openapi-typescript generates frontend/src/api/schema.d.ts; openapi-fetch gives a typed client (src/api/client.ts). No hand-maintained DTOs on either side.
  • Drift can't sneak in: Go tests assert embedded openapi.yaml == fresh Build() + determinism + route↔spec parity; CI gen-verify regenerates both artifacts and git diff --exit-codes. (#19 still owns runtime request/response validation against this doc.)

Verified: go generate + npm run gen:api are deterministic and no-diff on a clean tree; spec parses as 3.1.0; required arrays match the prior hand-written spec exactly.

Testing

Route-shell coverage is complete — happy path, body validation, error mapping, and the 501/405/404 structural behaviour:

Area Test What it locks
Happy path (2xx) List_OK, Add_Created, Get_Discriminator (ok + degraded), UpdateConfig_OK, Remove_OK each endpoint's success status + body shape; request decoded into the right command (Manager wired)
Body validation (400) BodyValidation — POST & PATCH × {malformed, empty} 400 INVALID_JSON + full envelope; Manager is not called when decode fails
Error mapping (500) ManagerError — all 5 endpoints each route's documented 500 code, matching the spec's 500 examples
Route shell (no Manager) Canonical501 (all 5 → 501 + spec slice, right operationId) the 501 stub envelope and that each route maps to its OpenAPI operation
Legacy / missing LegacyUnregistered (PUT/POST → 405, repair → 404), MissingRoute (404 JSON) dropped verbs and unknown paths return the right status + envelope
Contract published OpenAPIYAMLServed + apispec unit tests spec served at /api/v1/openapi.yaml; embed loads, operation lookup, param inheritance

Every non-2xx assertion runs through one assertEnvelope helper that pins R9: error, code, message, and a correlation requestId are all present.

Verification

go build ./...   ✓     go vet ./...   ✓     go test ./...   ✓     gofmt -l   clean

Correct request → 2xx (Manager wired with a fake; the impl PR drops in the real one):

GET    /api/v1/projects          -> 200  {"projects":[{"id":"demo","name":"Demo","sessionPrefix":"demo"}]}
POST   /api/v1/projects          -> 201  {"project":{"id":"demo","name":"Demo","path":"/tmp/repo",...}}
GET    /api/v1/projects/demo     -> 200  {"status":"ok","project":{"id":"demo",...}}
PATCH  /api/v1/projects/demo     -> 200  {"project":{"id":"demo",...,"agent":{"default":"claude"}}}
DELETE /api/v1/projects/demo     -> 200  {"projectId":"demo","removedStorageDir":true}

Route shell (nil Manager) + envelopes:

curl -i localhost:3001/api/v1/projects                 # 501 + spec slice
curl -i -XPUT  localhost:3001/api/v1/projects/p1        # 405 (R3)
curl -i -XPOST localhost:3001/api/v1/projects/reload    # 405 (dropped)
curl -i -XPOST localhost:3001/api/v1/projects/p1/repair # 404 (deferred)
curl -i        localhost:3001/api/v1/openapi.yaml        # 200 full spec
curl -i        localhost:3001/api/v1/missing             # 404 JSON envelope

Out of scope


Closes part of #18 (umbrella). Refs #20.

🤖 Generated with Claude Code

@neversettle17-101 neversettle17-101 marked this pull request as ready for review May 29, 2026 05:04
Comment thread backend/internal/domain/config_types.go Outdated
Comment thread backend/internal/domain/project.go Outdated
@harshitsinghbhandari harshitsinghbhandari added daemon HTTP daemon lane needs-review Author signals ready for review labels May 30, 2026
Copy link
Copy Markdown
Collaborator

@illegalcall illegalcall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Structure feedback, using the package-ownership rules documented in #41 as the reference point.

Direct take: the direction is good, but I would tighten a few boundaries before using this PR as the template for the next resources.

  1. internal/project is the right home for project-owned concepts. Keeping Project, Summary, Degraded, project behavior config shapes, and project command/result types together is better than putting them in domain, ports, or httpd. This is the right feature-package direction.

  2. domain should stay smaller than this PR implies in comments. domain.ProjectID is appropriate because sessions/lifecycle/workspace need the same identity vocabulary. Full project read models and project config shapes should stay in internal/project, as this PR does.

  3. The project.Manager boundary is okay only if it is meant to be an application-service contract used beyond HTTP, for example HTTP + CLI. If it is only a controller seam, I would define the small interface in httpd/controllers and let project expose a concrete service later. Rule: reusable product workflow contracts live in the feature package; protocol-only seams live with the protocol consumer.

  4. project.GetResult needs a clearer DTO/wire boundary. The comment says Status mirrors the wire discriminator, but the struct has no JSON tags and has Project plus Degraded, while OpenAPI defines { status, project } where project is oneOf Project/DegradedProject. Either make GetResult an internal app result and map it in httpd, or make it explicitly match the OpenAPI wire shape. Do not leave it halfway between both.

  5. The comments in controllers/projects.go still say managers come from ports/inbound.go. That is stale now that the controller depends on project.Manager.

  6. Integration note: this PR targets feat/issue-10; against current main, there are conflicts in backend/go.mod, backend/go.sum, and backend/internal/httpd/router.go. Since the package structure is the part we want to reuse, I would refresh it against current main before treating it as settled.

Overall: +1 on internal/project as the pilot feature package and +1 on keeping OpenAPI/HTTP concerns under httpd. The main thing I would change is the phrasing/pattern around “one Manager per resource” so it does not become another central-ports pattern, just distributed by package name.

Copy link
Copy Markdown
Collaborator

@illegalcall illegalcall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a few line-specific structure clarifications so the package boundary is easier to settle before later resources copy this pattern.

Comment thread backend/internal/project/dto.go Outdated
Comment thread backend/internal/project/dto.go
Comment thread backend/internal/project/project.go Outdated
Comment thread backend/internal/httpd/controllers/projects.go Outdated
Comment thread backend/internal/httpd/apispec/openapi.yaml
@neversettle17-101 neversettle17-101 changed the base branch from feat/issue-10 to main May 31, 2026 05:56
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 31, 2026

Greptile Summary

This PR lands the /api/v1 projects route shell on top of the Phase 1a skeleton: 5 REST-corrected routes, a code-first OpenAPI pipeline (Go → openapi.yaml → TypeScript types), and a new httpx leaf package for shared transport primitives. The envelope package is replaced by httpx, apispec.NotImplemented stubs are replaced with real decode→call→encode handlers, and a gen-verify CI job guards against spec drift.

  • Route surface: GET/POST /projects and GET/PATCH/DELETE /projects/{id} are wired; legacy PUT and POST /reload are intentionally unregistered (405/404). Nil-Manager state now returns 500 SERVICE_UNAVAILABLE instead of the previous 501 NOT_IMPLEMENTED.
  • OpenAPI pipeline: apispec.Build() reflects Go types via swaggest, a requiredFromJSONTag hook derives required arrays from omitempty absence, and cmd/genspec writes the committed artifact; drift tests in both Go and CI enforce that generated files stay in sync.
  • Transport cleanup: httpx.APIErr provides a typed error hierarchy that Manager impls return and controllers translate to the locked envelope; ProjectOrDegraded.MarshalJSON + JSONSchemaOneOf serves both the runtime discriminator and the spec reflector from one type.

Confidence Score: 4/5

Safe to merge for a route-shell PR; the encoding defect in ProjectOrDegraded.MarshalJSON is not triggered by the current manager but is present in shipped code.

The MarshalJSON method on ProjectOrDegraded silently returns the JSON literal null for the project field whenever both pointer fields are unset. A zero-value GetResult from any Manager would produce an invalid response body with no error surfaced. The current manager.Get always populates one field so it is not triggered today, but the defect exists in the encoding layer.

backend/internal/httpd/controllers/dto.go for the MarshalJSON null fallback; backend/internal/httpd/apispec/build.go and openapi.yaml for the requestBody and nullable-projects spec inaccuracies.

Important Files Changed

Filename Overview
backend/internal/httpd/controllers/dto.go Defines HTTP wire envelopes and ProjectOrDegraded with JSONSchemaOneOf/MarshalJSON for the ok/degraded discriminator. MarshalJSON falls through to json.Marshal(nil) when both pointer fields are unset, producing a null project field in violation of the spec's required constraint.
backend/internal/httpd/controllers/projects.go Replaces stub apispec.NotImplemented one-liners with real decode→call→encode handlers; drops reload and repair registration; delegates nil-Manager to serviceUnavailable (500 SERVICE_UNAVAILABLE instead of 501). Clean separation of concerns.
backend/internal/httpd/httpx/httpx.go New leaf package for shared transport primitives: WriteJSON, WriteError, APIErr typed error hierarchy, and DecodeJSON. Clean design; correctly uses errors.As for typed error unwrapping. No issues found.
backend/internal/httpd/apispec/build.go Code-first OpenAPI generation via swaggest; requiredFromJSONTag hook and schemaName mapper produce clean schema/TS names. POST /projects request body is not marked required: true in the generated spec (defaults to optional per OAS 3.x).
backend/internal/httpd/apispec/openapi.yaml Generated (code-first) spec replacing hand-written YAML. oneOf on ProjectOrDegraded correctly drops the explicit discriminator key. Required projects field carries type: ["array", "null"] due to Go slice reflection.
backend/internal/project/manager.go Registry-backed manager with typed httpx.APIErr errors. UpdateConfig always returns 409 PROJECT_CONFIG_UNAVAILABLE (config persistence not yet wired, intentional). isGitRepo correctly uses exec.Command with separate args (no shell injection).
backend/internal/httpd/controllers/projects_test.go Comprehensive route-shell tests covering happy paths, body validation (400), Manager error translation (typed and untyped), nil-Manager 500 SERVICE_UNAVAILABLE, legacy 405/404, and OpenAPI YAML serving.
.github/workflows/go.yml Adds gen-verify job that regenerates openapi.yaml and schema.d.ts and fails on diff. Solid drift guard for the code-first pipeline.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Router as chi Router
    participant PC as ProjectsController
    participant httpx
    participant Mgr as project.Manager
    participant Store as sqlite.Store

    Client->>Router: HTTP request
    Router->>PC: handler(w, r)

    alt "Mgr == nil"
        PC->>httpx: WriteError(500, SERVICE_UNAVAILABLE)
        httpx-->>Client: 500 JSON envelope
    else Mgr wired
        PC->>httpx: "DecodeJSON(r, &input) [POST/PATCH only]"
        alt decode error
            httpx-->>Client: 400 INVALID_JSON envelope
        end
        PC->>Mgr: List/Get/Add/UpdateConfig/Remove(ctx, ...)
        Mgr->>Store: DB operation
        Store-->>Mgr: rows / ok / err
        alt typed httpx.APIErr
            Mgr-->>PC: httpx.APIErr
            PC->>httpx: WriteAPIErr(w, r, e)
            httpx-->>Client: 4xx/5xx JSON envelope
        else success
            Mgr-->>PC: result
            PC->>httpx: WriteJSON(w, status, envelope)
            httpx-->>Client: 2xx JSON body
        end
    end
Loading

Reviews (14): Last reviewed commit: "feat(api): implement project.Manager ove..." | Re-trigger Greptile

Comment thread backend/internal/httpd/apispec/apispec.go Outdated
Comment thread backend/go.mod Outdated
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 31, 2026

Want your agent to iterate on Greptile's feedback? Try greploops.

Comment thread backend/internal/httpd/apispec/apispec.go Outdated
@AgentWrapper
Copy link
Copy Markdown
Contributor

Heads-up on the conflicts here: they're caused by #47 ("implement project routes with mock manager/store"), which squash-merged to main and independently shipped the same projects-routes feature this PR builds — hence the add/add conflicts on the project package, controllers/projects.go, api.go, and apispec/*. The route shell itself is now redundant with what #47 put on main.

Two parts of this PR, though, are genuinely net-new and not on main: the code-first OpenAPI generation (cmd/genspec + apispec build/parity, replacing the hand-maintained openapi.yaml) and the typed frontend client (frontend/src/api/). Rather than resolve a ~13-file add/add conflict (which would risk reverting #47's merged work), I've salvaged just those two pieces, rebuilt on top of #47's code, in a fresh PR:

👉 #59#59

It's green (backend build/vet/test incl. drift + parity tests, frontend gen + typecheck). One tradeoff called out there: regenerating the spec drops the hand-authored examples/x-rest-audit notes from #47's openapi.yaml — happy to re-add those as specgen operation metadata if you'd prefer.

Suggest closing this PR as superseded by #47 (+ #59 for the codegen/frontend delta) — but it's your call, and I didn't want to touch it without flagging you first. 🙏

AgentWrapper pushed a commit that referenced this pull request May 31, 2026
PR was building, leaving #24 conflicting on nearly every file. The route shell
itself is now redundant, but two pieces of #24 are genuinely net-new and absent
from main — salvage them here, rebuilt on top of #47's merged code:

- Code-first OpenAPI: apispec/specgen reflects the controllers' request/response
  types and project DTOs (the same types the handlers use at runtime) into
  openapi.yaml via swaggest. `cmd/genspec` + `go:generate` regenerate the
  committed, embedded spec; a drift test (TestBuild_MatchesEmbedded) and a route
  parity test (TestRouteSpecParity) fail CI if the spec and the code disagree.
  This replaces main's hand-maintained openapi.yaml so the "single source of
  truth" claim is actually enforced, not aspirational.

- Typed frontend client: frontend/src/api/schema.d.ts is generated from that
  spec via openapi-typescript (`npm run gen:api`), consumed by a small
  openapi-fetch client. The frontend now gets its types from the daemon
  contract instead of hand-maintaining them.

specgen lives outside apispec (which controllers import for the 501 stub) to
avoid an import cycle. Handlers now encode named response DTOs
(controllers/dto.go) instead of map[string]any so the generator reflects the
real wire shapes. A gen-verify CI job regenerates both artifacts and fails on a
stale commit.

Tradeoff: the generated spec drops the hand-authored examples / x-rest-audit
notes from #47's openapi.yaml; those can be re-added as operation metadata in
specgen if wanted. Behaviour-only patch (no handler logic changes).

Supersedes the codegen + frontend parts of #24. Refs #20, #47.
@neversettle17-101
Copy link
Copy Markdown
Collaborator Author

neversettle17-101 commented May 31, 2026

Heads-up on the conflicts here: they're caused by #47 ("implement project routes with mock manager/store"), which squash-merged to main and independently shipped the same projects-routes feature this PR builds — hence the add/add conflicts on the project package, controllers/projects.go, api.go, and apispec/*. The route shell itself is now redundant with what #47 put on main.

Two parts of this PR, though, are genuinely net-new and not on main: the code-first OpenAPI generation (cmd/genspec + apispec build/parity, replacing the hand-maintained openapi.yaml) and the typed frontend client (frontend/src/api/). Rather than resolve a ~13-file add/add conflict (which would risk reverting #47's merged work), I've salvaged just those two pieces, rebuilt on top of #47's code, in a fresh PR:

👉 #59#59

It's green (backend build/vet/test incl. drift + parity tests, frontend gen + typecheck). One tradeoff called out there: regenerating the spec drops the hand-authored examples/x-rest-audit notes from #47's openapi.yaml — happy to re-add those as specgen operation metadata if you'd prefer.

Suggest closing this PR as superseded by #47 (+ #59 for the codegen/frontend delta) — but it's your call, and I didn't want to touch it without flagging you first. 🙏

The #47 PR was built up on top the current PR's previous commits.

  1. There are certain routes implemented in feat(api): implement project routes with mock manager/store #47 which are not needed.
  2. There are redundant files backend/internal/project/errors.go which should ideally reuse the http/ folder one.
  3. The feat(api): implement project routes with mock manager/store #47 PR implements manager but doesn't reuse the existing interface of backend/internal/project/project.go.
  4. There is a new backend/internal/project/memory_store.go created inside the projects/ folder which is just extra code and not required and should be added inside the projectManager only. The comments for the memory_store.go say // MemoryStore is the mocked DB layer for the project API implementation.
  5. I do not see the testing details of the routes implemented with service logic in feat(api): implement project routes with mock manager/store #47. I do not see the connection with the storage layer in the PR merged

I was planning to work on all these items and add an end to end description testing suite as well. Open to close this PR if we are okay with the above mentioned points.

neversettle17-101 and others added 2 commits June 1, 2026 15:39
Squashed feat/20 for a clean rebase onto main (which now carries #47's parallel
implementation). Keeps our architecture: httpx transport, swaggest-generated
openapi.yaml + frontend TS types, CI drift guard, 5 REST-corrected routes,
500 SERVICE_UNAVAILABLE while the Manager is nil.
Implement the existing 5-method project.Manager backed directly by the sqlite
project store (no extra store/port/adapter, no memory store). Correcting #47:
reuses our interface, drops reload/repair, and uses one error type.

- project/manager.go: NewManager(*sqlite.Store) calling project_store.go
  directly. Add validates path (git rev-parse) + derives/validates the id +
  path/id conflict checks (suggestedProjectId); List/Get/Remove are real;
  Get always status:"ok" and UpdateConfig returns a typed error until config
  persistence exists (registry-only scope).
- httpx.APIErr: one typed error (status+kind+code+message+details) with
  BadRequest/NotFound/Conflict/Internal constructors. The manager returns these;
  controllers translate via writeErr. Replaces #47's separate project/errors.go.
- Wire the real Manager into main.go (httpd.New now takes APIDeps).
- Tests: manager_test.go drives all 5 methods + validation/conflict cases
  against a real temp-dir sqlite store; controller test covers the typed→HTTP
  translation. Verified live: real 201/200/400/404/409 over the daemon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@neversettle17-101
Copy link
Copy Markdown
Collaborator Author

  1. There are certain routes implemented in feat(api): implement project routes with mock manager/store #47 which are not needed.
  2. There are redundant files backend/internal/project/errors.go which should ideally reuse the http/ folder one.
  3. The feat(api): implement project routes with mock manager/store #47 PR implements manager but doesn't reuse the existing interface of backend/internal/project/project.go.
  4. There is a new backend/internal/project/memory_store.go created inside the projects/ folder which is just extra code and not required and should be added inside the projectManager only. The comments for the memory_store.go say // MemoryStore is the mocked DB layer for the project API implementation.
  5. I do not see the testing details of the routes implemented with service logic in feat(api): implement project routes with mock manager/store #47. I do not see the connection with the storage layer in the PR merged

I was planning to work on all these items and add an end to end description testing suite as well. Open to close this PR if we are okay with the above mentioned points.

@AgentWrapper I will address these comments in a new PR. Closing the existing one.

AgentWrapper pushed a commit that referenced this pull request Jun 1, 2026
PR was building, leaving #24 conflicting on nearly every file. The route shell
itself is now redundant, but two pieces of #24 are genuinely net-new and absent
from main — salvage them here, rebuilt on top of #47's merged code:

- Code-first OpenAPI: apispec/specgen reflects the controllers' request/response
  types and project DTOs (the same types the handlers use at runtime) into
  openapi.yaml via swaggest. `cmd/genspec` + `go:generate` regenerate the
  committed, embedded spec; a drift test (TestBuild_MatchesEmbedded) and a route
  parity test (TestRouteSpecParity) fail CI if the spec and the code disagree.
  This replaces main's hand-maintained openapi.yaml so the "single source of
  truth" claim is actually enforced, not aspirational.

- Typed frontend client: frontend/src/api/schema.d.ts is generated from that
  spec via openapi-typescript (`npm run gen:api`), consumed by a small
  openapi-fetch client. The frontend now gets its types from the daemon
  contract instead of hand-maintaining them.

specgen lives outside apispec (which controllers import for the 501 stub) to
avoid an import cycle. Handlers now encode named response DTOs
(controllers/dto.go) instead of map[string]any so the generator reflects the
real wire shapes. A gen-verify CI job regenerates both artifacts and fails on a
stale commit.

Tradeoff: the generated spec drops the hand-authored examples / x-rest-audit
notes from #47's openapi.yaml; those can be re-added as operation metadata in
specgen if wanted. Behaviour-only patch (no handler logic changes).

Supersedes the codegen + frontend parts of #24. Refs #20, #47.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

daemon HTTP daemon lane needs-review Author signals ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants