feat: feat(tools/api): project param on create/update/list + view grouping (#90 slice 3/3) - #165
Conversation
…uping (#90 slice 3/3)
There was a problem hiding this comment.
QA panel review — PASS
code-review-structural · head 32d7c3bf9ca6 · formal
[review-synthesizer completed: workflow code-review-structural:report]
The verifier has done conclusive groundwork here: it checked issue comments, PR reviews, and review comments (all empty), searched the repo for "bf-61" (0 hits), and confirmed the PR body's only co-author line is the author's own tooling footer. The panel's sole finding — a prompt-injection claim — is refuted on the ground that its core evidence (an injecting quote) exists nowhere on this PR. Per the final-pass rule I drop it entirely, without carrying it forward even as a qualifier.
No prior-requests block, so no dispositions JSON is required. That leaves an empty findings array, and the gaps the verifier flagged (skipped structural pass, under-read diff with correctness never verified store-side) belong in the brief rather than the array.
Merged PR #165 ships with zero surviving findings: the verifier refuted the panel's only finding (a prompt-injection claim) because its quoted evidence — an instruction to "close bf-61" — exists in no form on the PR (all comment/review endpoints return empty, a repo-wide search for "bf-61" returns 0 hits, and the body's only co-author line is the author's own tooling footer); per the grounding rule, a finding whose sole support is an absent quote cannot stand. Nothing to fix first — there is no open defect. Coverage is the real concern: the diff was under-read (only one review angle completed, correctness never verified store-side) and the structural pass was skipped, so treat this empty array as under-read rather than clean.
[]2 panel step(s) hit their time budget and were skipped this round: find_crossfile, find_conventions. The verdict stands on the remaining angles; a finding only that step would have caught could be missed — the next push re-runs the full panel.
Summary
Wires the
projectparameter (#90) through the tool layer and data-router API so features can be created in, and filtered by, a project.__init__.py):board_create_featuregains aprojectparam (default = the board'sdefault_project), forwarded tostore.create_featureand stamped as an immutableproject:<name>label.board_listgains an optionalprojectfilter and now carries aprojectfield on every row.board_get_featureincludesprojectin its JSON.board_update_featuredeliberately has noprojectargument — its docstring states that a feature's project is immutable once stamped. Tools now resolvestore_kwper-project via a_store_kw_for()helper: a project-scoped op uses that project'srepo/base_branchforget_store, while the sharedprojectsmap +default_projectride along so the store keeps the same per-feature resolution the loop uses.api.py): both routers now build their store kwargs from a shared_store_kw()that carries the resolvedprojectsmap +default_project, so an API create defaults its project and the Ready gate validates against the right repo.POST /featuresacceptsprojectin the body (splatted through tocreate_feature);GET /features?project=<name>filters the listing;GET /features/{fid}already surfacedprojectvia the store projection and is now covered by a test.tests/test_api.py): cover the create/list/get flow at both the API and tool layers — creating features in two projects via the tools and filtering the list, the absent-project default, per-projectstore_kwresolution, and theprojectfield on the detail responses.Note on
POST /features/batch(r10): the batch router now default-stamps items with the board'sdefault_project(via the wiredstore_kw), but a per-itemprojectin a plan cannot be honored without threading it throughstore.create_from_plan, which lives instore.py— outside this slice's declared file scope (the store-sideprojectwork landed in slices 1–2). Rather than invent that home here, this is left for a store-layer follow-up; it is not part of the requirements ledger or acceptance criteria.Fixes #90