You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Delete the NyxID-org org_idfield from the goal/session models (GoalDoc, the cross-crate SessionDoc), the HTTP DTOs (CreateGoalRequest, GoalView, SessionView) — which drops the org_id property from the runtime-generated OpenAPI schemas — and the goal-issue hidden marker (GoalMarker), with parse-and-ignore backward-compat for already-filed goal issues. Also remove every internal carrier that threads the field.
This is the third PR. It depends on #270 (which already dropped Ownership.org_id, so the Ownership builders in goals.rs/sessions.rs compile without the field) and must be co-committed with the session-dispatch consumers (the service.rsSessionDoc{org_id} construction etc.) so the workspace compiles after SessionDoc.org_id is deleted. The vault/codex_provider pure pass-through params are #273 (they don't read org_id, so they can lag).
It still appears in the public request/response surface (CreateGoalRequest, GoalView, SessionView) and in the durable goal-issue marker; the owner-only mandate requires removing it.
Backward-compat (the subtle part) — parse-and-ignore, NO version bump
A goal is durably mirrored as a GitHub Issue whose body carries a v:1 JSON marker historically including "org_id":.... CRITICAL FINDING:parse_marker has no production read-back caller (only unit tests call it; the in-memory GoalIssueStore is authoritative and does not rehydrate goals from issues on boot). GoalMarker has no #[serde(deny_unknown_fields)], so serde silently drops a leftover org_id key in any pre-removal body. Therefore: keep marker v:1, just remove the field from GoalMarker + render_marker. A version bump to v:2 or adding deny_unknown_fields would make parse_marker reject every already-filed goal issue (UnsupportedVersion/unknown-field) — explicitly forbidden.SessionDoc likewise has no deny_unknown_fields, so legacy session docs carrying org_id still deserialize.
Proposed Solution — Implementation Spec
backend/fkst-control-plane/src/goals/model.rs
L66-68 delete pub org_id: Option<String> + its doc-comment from GoalDoc. Keep owner_user_id and repo: Option<RepoRef> (the GitHub repo — KEEP).
Tests: sample_goal_doc() drop org_id: None (L197); explicit_null_fields_serialize_as_null drop the raw.get("org_id")...Bson::Null assertion (L233-234); set_fields_round_trip drop doc.org_id = Some("org-1") (L248).
L48-59render_marker: delete org_id: doc.org_id.clone() (L53). Keep v: 1 — do NOT bump.
L63-86parse_marker: NO logic change (no deny_unknown_fields, so an old body with "org_id" still parses and the key is ignored).
L1-15 module doc + L13 example literal: remove the org_id key from the documented marker shape and the owner/org/packages/repo prose.
Tests: goal() fixture drop org_id: Some("org-9") (L105); marker_round_trips drop the org_id assertion (L120). The marker_v2_unsupported (L148-154) / marker_hand_edited_invalid_package_rejected (L156-166) literals carry "org_id":null — leave them (they still parse). ADDmarker_old_body_with_org_id_parses_and_ignores_it: feed {"v":1,...,"org_id":"org-9",...}, assert Ok and that the parsed struct exposes no org_id. ADD a render test asserting render_marker(doc) output does NOT contain the substring org_id.
L82-85 delete SessionDoc.org_id (with its #[serde(default, skip_serializing_if = "Option::is_none")]) + doc-comment. No cfg_attr/schema-feature change needed (removing a field is schema-safe for both control-plane and the worker).
Tests: drop org_id: None from the sample fixture (L184); update ownership_fields_are_omitted_when_absent (drop the org_id-omitted assertion, L305-308), ownership_fields_round_trip_when_set (drop doc.org_id = Some("org-1") + assert, L315/L321), legacy_docs_without_ownership_fields_still_deserialize (drop raw.remove("org_id") + back.org_id == None, L330/L333). Keep/ADD a regression test that a serialized SessionDoc with a stray org_id key still deserializes (back-compat with already-persisted org-tagged docs).
backend/fkst-control-plane/src/routes/goals.rs
Delete CreateGoalRequest.org_id (L64-66) and GoalView.org_id (L104) — both #[derive(ToSchema)], so org_id leaves those component schemas in /openapi.json.
TryFrom<GoalDoc> for GoalView (L231-252): drop org_id: doc.org_id (L246).
trigger handler: drop org_id: goal.org_id.clone() from the GoalTriggerInfo literal (L1140).
Tests: owned_goal(owner, org) → owned_goal(owner) (drop the org field, L1298-1312); goal_ownership_is_sub_keyed_and_never_ownerless drop the org_id assertion; goal_view_emits_explicit_nulls drop body["org_id"].is_null() (L1355) and the org_id: None seed; goal_view_status_is_snake_case drop the seed (L1372). KEEP — DO NOT TOUCH:CreateRepoSpecBody.org_login (L216-218), CreateRepoSpec.org_login, is_org_repo, the org_login idempotency match, install_hint_message wording (GitHub org).
backend/fkst-control-plane/src/routes/sessions.rs
Delete SessionView.org_id (L58-59) + its doc-comment (ToSchema → leaves the schema). TryFrom<&SessionDoc> for SessionView: drop org_id: doc.org_id.clone() (L88).
Test session_view_emits_explicit_nulls_and_z_suffixed_timestamps: drop org_id: None from the SessionDoc literal (L282) and "org_id" from the explicit-null list (L305). Add an assertion that the serialized SessionView has no org_id key.
struct SessionOwner (L63-67): drop org_id (L66). If it has no non-test constructor after the classic-POST-/sessions removal, prefer shrinking to a one-field wrapper (do not delete the type unless confirmed dead and the re-export at sessions/mod.rs:22 is updated).
struct GoalTriggerInfo (L71-83): drop org_id (L76).
create_for_goal SessionDoc construction: drop org_id: trigger.org_id.clone() (L596).
Also co-commit the remaining SessionDoc { org_id: None } literals the compiler surfaces in sessions/dispatch_tests.rs (L136/L177), sessions/repo_tests.rs (L22), and any integration test fixtures (tests/sessions_api.rs, tests/admin_metrics.rs, tests/goal_session_token.rs, tests/github_app_installations.rs, tests/activation_dispatch.rs, tests/claim_placement.rs, tests/reassign_redispatch.rs) — pure struct-seed updates (these may instead be swept in #274 if that keeps this PR focused; if so, this PR will not compile until #274 lands, so prefer folding the seed updates here).
Scope Check
✅ In scope: every NyxID-org_id field/mapping/literal on goal/session models, DTOs, the marker, and the immediate carriers.
HTTP request (breaking-ish):CreateGoalRequest uses #[serde(deny_unknown_fields)], so a client still sending org_id now gets 400 (previously accepted). Document this as a deliberate owner-only contract change; coordinate with the frontend dropping the field.
HTTP response:GoalView/SessionView no longer emit org_id (was explicit null).
Persistence: new goal-issue markers omit org_id; old ones remain forward-readable (parse-and-ignore). No migration, no GitHub-side body rewrite.
Acceptance Criteria / Definition of Done
GoalDoc, SessionDoc, CreateGoalRequest, GoalView, SessionView, GoalMarker carry no org_id.
Marker stays v:1; marker_old_body_with_org_id_parses_and_ignores_it and the no-org_id-in-render test pass; a SessionDoc with a stray org_id key still deserializes.
cargo test -p fkst-control-plane -p fkst-shared green; cargo build -p fkst-worker green; the generated /openapi.json no longer advertises org_id on the four DTOs (org_login preserved on repo-create).
Workspace compiles at the commit boundary (co-commit the cross-crate SessionDoc consumers).
Summary
Delete the NyxID-org
org_idfield from the goal/session models (GoalDoc, the cross-crateSessionDoc), the HTTP DTOs (CreateGoalRequest,GoalView,SessionView) — which drops theorg_idproperty from the runtime-generated OpenAPI schemas — and the goal-issue hidden marker (GoalMarker), with parse-and-ignore backward-compat for already-filed goal issues. Also remove every internal carrier that threads the field.This is the third PR. It depends on #270 (which already dropped
Ownership.org_id, so theOwnershipbuilders ingoals.rs/sessions.rscompile without the field) and must be co-committed with the session-dispatch consumers (theservice.rsSessionDoc{org_id}construction etc.) so the workspace compiles afterSessionDoc.org_idis deleted. Thevault/codex_providerpure pass-through params are #273 (they don't readorg_id, so they can lag).Problem / Motivation
owner_user_idonly.org_idis now write-only dead data after Strip NyxID-org authz: remove visible_org_ids/require_org_writer/OrgRole branches (owner-only) #270 removed the last authz reader.CreateGoalRequest,GoalView,SessionView) and in the durable goal-issue marker; the owner-only mandate requires removing it.Backward-compat (the subtle part) — parse-and-ignore, NO version bump
A goal is durably mirrored as a GitHub Issue whose body carries a
v:1JSON marker historically including"org_id":.... CRITICAL FINDING:parse_markerhas no production read-back caller (only unit tests call it; the in-memoryGoalIssueStoreis authoritative and does not rehydrate goals from issues on boot).GoalMarkerhas no#[serde(deny_unknown_fields)], so serde silently drops a leftoverorg_idkey in any pre-removal body. Therefore: keep markerv:1, just remove the field fromGoalMarker+render_marker. A version bump tov:2or addingdeny_unknown_fieldswould makeparse_markerreject every already-filed goal issue (UnsupportedVersion/unknown-field) — explicitly forbidden.SessionDoclikewise has nodeny_unknown_fields, so legacy session docs carryingorg_idstill deserialize.Proposed Solution — Implementation Spec
backend/fkst-control-plane/src/goals/model.rspub org_id: Option<String>+ its doc-comment fromGoalDoc. Keepowner_user_idandrepo: Option<RepoRef>(the GitHub repo — KEEP).sample_goal_doc()droporg_id: None(L197);explicit_null_fields_serialize_as_nulldrop theraw.get("org_id")...Bson::Nullassertion (L233-234);set_fields_round_tripdropdoc.org_id = Some("org-1")(L248).backend/fkst-control-plane/src/goals/marker.rsGoalMarker.org_id(L31). Keepv,goal_id,owner_user_id,package_names,repo.render_marker: deleteorg_id: doc.org_id.clone()(L53). Keepv: 1— do NOT bump.parse_marker: NO logic change (nodeny_unknown_fields, so an old body with"org_id"still parses and the key is ignored).org_idkey from the documented marker shape and theowner/org/packages/repoprose.goal()fixture droporg_id: Some("org-9")(L105);marker_round_tripsdrop theorg_idassertion (L120). Themarker_v2_unsupported(L148-154) /marker_hand_edited_invalid_package_rejected(L156-166) literals carry"org_id":null— leave them (they still parse). ADDmarker_old_body_with_org_id_parses_and_ignores_it: feed{"v":1,...,"org_id":"org-9",...}, assertOkand that the parsed struct exposes noorg_id. ADD a render test assertingrender_marker(doc)output does NOT contain the substringorg_id.backend/fkst-shared/src/models/mod.rs(cross-crate)SessionDoc.org_id(with its#[serde(default, skip_serializing_if = "Option::is_none")]) + doc-comment. Nocfg_attr/schema-feature change needed (removing a field is schema-safe for both control-plane and the worker).org_id: Nonefrom the sample fixture (L184); updateownership_fields_are_omitted_when_absent(drop the org_id-omitted assertion, L305-308),ownership_fields_round_trip_when_set(dropdoc.org_id = Some("org-1")+ assert, L315/L321),legacy_docs_without_ownership_fields_still_deserialize(dropraw.remove("org_id")+back.org_id == None, L330/L333). Keep/ADD a regression test that a serializedSessionDocwith a strayorg_idkey still deserializes (back-compat with already-persisted org-tagged docs).backend/fkst-control-plane/src/routes/goals.rsCreateGoalRequest.org_id(L64-66) andGoalView.org_id(L104) — both#[derive(ToSchema)], soorg_idleaves those component schemas in/openapi.json.TryFrom<GoalDoc> for GoalView(L231-252): droporg_id: doc.org_id(L246).createhandler: droporg_id: request.org_idfrom the constructedGoalDocliteral (L346). (Therequire_org_writerblock at L311-314 was already removed in Strip NyxID-org authz: remove visible_org_ids/require_org_writer/OrgRole branches (owner-only) #270.)triggerhandler: droporg_id: goal.org_id.clone()from theGoalTriggerInfoliteral (L1140).owned_goal(owner, org)→owned_goal(owner)(drop the org field, L1298-1312);goal_ownership_is_sub_keyed_and_never_ownerlessdrop theorg_idassertion;goal_view_emits_explicit_nullsdropbody["org_id"].is_null()(L1355) and theorg_id: Noneseed;goal_view_status_is_snake_casedrop the seed (L1372). KEEP — DO NOT TOUCH:CreateRepoSpecBody.org_login(L216-218),CreateRepoSpec.org_login,is_org_repo, the org_login idempotency match,install_hint_messagewording (GitHub org).backend/fkst-control-plane/src/routes/sessions.rsSessionView.org_id(L58-59) + its doc-comment (ToSchema → leaves the schema).TryFrom<&SessionDoc> for SessionView: droporg_id: doc.org_id.clone()(L88).authorize_session_read/authorize_session_write: droporg_id: session.org_id.as_deref()from theOwnershipliterals (L218, L240) — required because Strip NyxID-org authz: remove visible_org_ids/require_org_writer/OrgRole branches (owner-only) #270 removedOwnership.org_id. Update the doc-comments (L113-118, L158-160, L204-208, L227-230) from "Org members can read/stop" to owner+admin only.session_view_emits_explicit_nulls_and_z_suffixed_timestamps: droporg_id: Nonefrom theSessionDocliteral (L282) and"org_id"from the explicit-null list (L305). Add an assertion that the serializedSessionViewhas noorg_idkey.backend/fkst-control-plane/src/routes/goals_submit.rsbuild_goal(L312-337): droporg_id: None(L332) + its stale comment (L329-331). Droporg_id: goal.org_id.clone()from the otherGoalTriggerInfoliteral (L182). (Theauthorize_objectorg-writer arm was already removed in Strip NyxID-org authz: remove visible_org_ids/require_org_writer/OrgRole branches (owner-only) #270.)backend/fkst-control-plane/src/sessions/service.rs(co-committed dispatch consumers)struct SessionOwner(L63-67): droporg_id(L66). If it has no non-test constructor after the classic-POST-/sessions removal, prefer shrinking to a one-field wrapper (do not delete the type unless confirmed dead and the re-export atsessions/mod.rs:22is updated).struct GoalTriggerInfo(L71-83): droporg_id(L76).create_for_goalSessionDoc construction: droporg_id: trigger.org_id.clone()(L596).org_id: None(L3166) and the doc-comment mention (L3152). (Thelist_for_scope/resolve_provider_choicearg removals at L2300/L2334 are Drop the no-op org_id pass-through params from vault/codex_provider/session dispatch #273 — they still compile here passingsession.org_id.as_deref()only untilSessionDoc.org_idis gone; therefore in THIS PR change those two calls to drop the now-deleted-field arg as part of the co-commit, OR keep Drop the no-op org_id pass-through params from vault/codex_provider/session dispatch #273 folded into this PR. Pick one and state it; default: change L2300/L2334 here to pass no org arg, and let Drop the no-op org_id pass-through params from vault/codex_provider/session dispatch #273 drop the underlying params.)Also co-commit the remaining
SessionDoc { org_id: None }literals the compiler surfaces insessions/dispatch_tests.rs(L136/L177),sessions/repo_tests.rs(L22), and any integration test fixtures (tests/sessions_api.rs,tests/admin_metrics.rs,tests/goal_session_token.rs,tests/github_app_installations.rs,tests/activation_dispatch.rs,tests/claim_placement.rs,tests/reassign_redispatch.rs) — pure struct-seed updates (these may instead be swept in #274 if that keeps this PR focused; if so, this PR will not compile until #274 lands, so prefer folding the seed updates here).Scope Check
org_idfield/mapping/literal on goal/session models, DTOs, the marker, and the immediate carriers.org_login/CreateRepoSpecBody/is_org_repo/install_hint_message;SetupRepoRef/SetupResponse.repo;RepoRef/RepoRefView(GitHub repo owner). Thevault/codex_providerparam signatures are Drop the no-op org_id pass-through params from vault/codex_provider/session dispatch #273.API / Persistence Impact
org_iddisappears fromCreateGoalRequest,GoalView,SessionView(and fromSetupRequestalready in Strip NyxID-org authz: remove visible_org_ids/require_org_writer/OrgRole branches (owner-only) #270).tests/openapi.rspins noorg_idassertion, so it stays green by construction (verified in Final org_id test + OpenAPI sweep and owner-only regression guards #274).CreateGoalRequestuses#[serde(deny_unknown_fields)], so a client still sendingorg_idnow gets 400 (previously accepted). Document this as a deliberate owner-only contract change; coordinate with the frontend dropping the field.GoalView/SessionViewno longer emitorg_id(was explicit null).org_id; old ones remain forward-readable (parse-and-ignore). No migration, no GitHub-side body rewrite.Acceptance Criteria / Definition of Done
GoalDoc,SessionDoc,CreateGoalRequest,GoalView,SessionView,GoalMarkercarry noorg_id.v:1;marker_old_body_with_org_id_parses_and_ignores_itand the no-org_id-in-render test pass; aSessionDocwith a strayorg_idkey still deserializes.cargo test -p fkst-control-plane -p fkst-sharedgreen;cargo build -p fkst-workergreen; the generated/openapi.jsonno longer advertisesorg_idon the four DTOs (org_login preserved on repo-create).SessionDocconsumers).