From 26b777b72e899eeee863d4db71ae5f036911e67d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D0=B8=D1=8F?= Date: Fri, 23 Oct 2026 20:13:38 +0300 Subject: [PATCH 1/7] feat(project): add receipt-backed Codex TOML native configuration codec --- .../adapters/nativeconfig/codex_document.go | 166 ++++++++++++++++++ .../adapters/nativeconfig/codex_toml_edit.go | 66 +++++++ .../adapters/nativeconfig/codex_toml_test.go | 139 +++++++++++++++ .../adapters/nativeconfig/document.go | 7 + .../adapters/nativeconfig/gemini_document.go | 3 + .../adapters/nativeconfig/project.go | 6 + .../adapters/nativeconfig/types.go | 16 +- install/integrationctl/agentplugins/go.mod | 1 + install/integrationctl/agentplugins/go.sum | 2 + 9 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 install/integrationctl/agentplugins/adapters/nativeconfig/codex_document.go create mode 100644 install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_edit.go create mode 100644 install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_test.go diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document.go b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document.go new file mode 100644 index 000000000..34db7411b --- /dev/null +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document.go @@ -0,0 +1,166 @@ +package nativeconfig + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "sort" + "strings" + + "github.com/creachadair/tomledit" + "github.com/pelletier/go-toml/v2" + "github.com/tailscale/hujson" +) + +// Only MCP entries enter the existing kernel's neutral mutation representation. +// The native TOML AST retains all other settings, types and comments. +type codexTOMLDocument struct { + body []byte + ast *tomledit.Document + original map[string]json.RawMessage + settings map[string]any +} + +func parseCodexTOML(body []byte, jsonc bool) (*document, error) { + if jsonc { + return nil, fmt.Errorf("%w: Codex requires one TOML config path", ErrMalformed) + } + var native map[string]any + if err := toml.Unmarshal(body, &native); err != nil { + return nil, fmt.Errorf("%w: invalid Codex TOML", ErrMalformed) + } + if native == nil { + native = map[string]any{} + } + servers, ok := native["mcp_servers"] + if !ok { + servers = map[string]any{} + } + if _, ok := servers.(map[string]any); !ok { + return nil, fmt.Errorf("%w: mcp_servers must be a table", ErrMalformed) + } + neutral, err := json.Marshal(map[string]any{"mcp_servers": servers}) + if err != nil { + return nil, fmt.Errorf("%w: invalid Codex MCP entry values", ErrMalformed) + } + doc, err := parseDocument(neutral, false) + if err != nil { + return nil, err + } + ast, err := tomledit.Parse(bytes.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("%w: invalid Codex TOML structure", ErrMalformed) + } + original, err := doc.codexEntries() + if err != nil { + return nil, err + } + delete(native, "mcp_servers") + doc.codexTOML = &codexTOMLDocument{body: bytes.Clone(body), ast: ast, original: original, settings: native} + return doc, nil +} + +func (doc *document) codexEntries() (map[string]json.RawMessage, error) { + entries, err := codecCollection(doc, CodecCodex, false) + if err != nil || entries == nil { + return nil, err + } + result := make(map[string]json.RawMessage, len(entries.Members)) + for i := range entries.Members { + member := &entries.Members[i] + body, err := entryCanonical(member) + if err != nil { + return nil, err + } + result[member.Name.Value.(hujson.Literal).String()] = body + } + return result, nil +} + +func (doc *document) renderCodexTOML() ([]byte, error) { + current, err := doc.codexEntries() + if err != nil { + return nil, err + } + names := codexChangedNames(doc.codexTOML.original, current) + if len(names) == 0 { + return bytes.Clone(doc.codexTOML.body), nil + } + for _, name := range names { + removeCodexTOMLEntry(doc.codexTOML.ast, name) + if body, exists := current[name]; exists { + if err := setCodexTOMLEntry(doc.codexTOML.ast, name, body); err != nil { + return nil, err + } + } + } + var result bytes.Buffer + if err := tomledit.Format(&result, doc.codexTOML.ast); err != nil { + return nil, fmt.Errorf("render Codex native config: %w", err) + } + next := result.Bytes() + parsed, err := parseCodexTOML(next, false) + if err != nil { + return nil, err + } + if !reflect.DeepEqual(doc.codexTOML.settings, parsed.codexTOML.settings) { + return nil, fmt.Errorf("rendered Codex config changed foreign settings") + } + observed, err := parsed.codexEntries() + if err != nil || !equalCodexEntries(current, observed) { + return nil, fmt.Errorf("rendered Codex MCP entries differ from requested entries") + } + return next, nil +} + +func codexChangedNames(previous, next map[string]json.RawMessage) []string { + changed := map[string]bool{} + for name, value := range previous { + changed[name] = !bytes.Equal(value, next[name]) + } + for name, value := range next { + changed[name] = !bytes.Equal(value, previous[name]) + } + result := make([]string, 0, len(changed)) + for name, differs := range changed { + if differs { + result = append(result, name) + } + } + sort.Strings(result) + return result +} + +func equalCodexEntries(a, b map[string]json.RawMessage) bool { + return len(a) == len(b) && len(codexChangedNames(a, b)) == 0 +} + +func projectCodexServer(server Server, placeholders Placeholders) (map[string]any, error) { + if server.OpenCodeV2 != nil { + return nil, fmt.Errorf("codex does not accept OpenCode V2 options") + } + if strings.EqualFold(strings.TrimSpace(server.Type), "remote") && server.RemoteTransport == "streamable-http" { + server.RemoteTransport = "" + } + projected, err := projectBaseServer(CodecMCPServers, server, placeholders) + if err != nil { + return nil, err + } + if headers, ok := projected["headers"]; ok { + delete(projected, "headers") + projected["http_headers"] = headers + } + if server.CodexAuth != nil { + if server.Type != "remote" { + return nil, fmt.Errorf("codex stdio servers do not accept remote authentication options") + } + if server.CodexAuth.BearerTokenEnvVar != "" { + projected["bearer_token_env_var"] = server.CodexAuth.BearerTokenEnvVar + } + if server.CodexAuth.OAuthResource != "" { + projected["oauth_resource"] = server.CodexAuth.OAuthResource + } + } + return projected, nil +} diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_edit.go b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_edit.go new file mode 100644 index 000000000..d03898d62 --- /dev/null +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_edit.go @@ -0,0 +1,66 @@ +package nativeconfig + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/creachadair/tomledit" + "github.com/creachadair/tomledit/parser" + "github.com/creachadair/tomledit/transform" + "github.com/pelletier/go-toml/v2" +) + +func removeCodexTOMLEntry(doc *tomledit.Document, name string) { + var remove []*tomledit.Entry + prefix := parser.Key{"mcp_servers", name} + doc.Scan(func(key parser.Key, entry *tomledit.Entry) bool { + if prefix.IsPrefixOf(key) { + remove = append(remove, entry) + } + return true + }) + for _, entry := range remove { + entry.Remove() + } +} + +func setCodexTOMLEntry(doc *tomledit.Document, name string, body []byte) error { + var value map[string]any + if err := json.Unmarshal(body, &value); err != nil || value == nil { + return fmt.Errorf("%w: Codex MCP entry must be a table", ErrMalformed) + } + var encoded bytes.Buffer + if err := toml.NewEncoder(&encoded).SetTablesInline(true).Encode(map[string]any{name: value}); err != nil { + return fmt.Errorf("encode Codex MCP entry: %w", err) + } + parsed, err := tomledit.Parse(&encoded) + if err != nil { + return fmt.Errorf("parse projected Codex MCP entry: %w", err) + } + entry := parsed.First(name) + if entry == nil || !entry.IsMapping() { + return fmt.Errorf("projected Codex MCP entry is not a mapping") + } + parent := doc.First("mcp_servers") + if parent != nil && parent.IsMapping() { + if inline, ok := parent.Value.X.(parser.Inline); ok { + inline = append(inline, entry.KeyValue) + parent.Value.X = inline + return nil + } + return fmt.Errorf("%w: Codex MCP collection is not a table", ErrMalformed) + } + if parent != nil && parent.IsSection() { + transform.InsertMapping(parent.Section, entry.KeyValue, false) + return nil + } + // An implicit parent may come from dotted keys or child tables. Adding a + // dotted global member handles both without redefining an existing table. + if doc.Global == nil { + doc.Global = &tomledit.Section{} + } + entry.Name = parser.Key{"mcp_servers", name} + transform.InsertMapping(doc.Global, entry.KeyValue, false) + return nil +} diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_test.go b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_test.go new file mode 100644 index 000000000..338920c5a --- /dev/null +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_test.go @@ -0,0 +1,139 @@ +package nativeconfig + +import ( + "errors" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/pelletier/go-toml/v2" +) + +func TestProjectCodexTOMLExactLifecyclePreservesForeignData(t *testing.T) { + for name, collection := range map[string]string{ + "inline": "mcp_servers = {foreign = {url = 'https://foreign.test/mcp'}} # inline policy\n", + "dotted": "mcp_servers.foreign.url = 'https://foreign.test/mcp' # dotted policy\n", + "parent": "[mcp_servers]\nforeign = {url = 'https://foreign.test/mcp'} # parent policy\n", + "child": "[mcp_servers.foreign]\nurl = 'https://foreign.test/mcp' # child policy\n", + } { + t.Run(name, func(t *testing.T) { + path := filepath.Join(t.TempDir(), "config.toml") + before := "# preserve native settings\ncreated = 2026-10-23\n" + collection + "\n[foreign]\nkeep = [1, 2, 3] # foreign comment\n" + mustWrite(t, path, before) + kernel := New() + request := Request{Paths: Paths{JSON: path}, Codec: CodecCodex, Action: ActionAdd, Name: "managed", Server: Server{Type: "remote", URL: "https://managed.test/mcp", RemoteTransport: "streamable-http", Headers: map[string]string{"X-Fixture": "inert"}}} + receipt, err := kernel.Apply(request) + if err != nil { + t.Fatal(err) + } + present, owned, err := kernel.Inspect(request.Paths, request.Codec, request.Name, &receipt) + if err != nil || !present || !owned { + t.Fatalf("exact Codex entry = %v/%v, %v", present, owned, err) + } + projectCodexAssertForeignTOML(t, path, before) + request.Action, request.Owned = ActionUpdate, &receipt + request.Server = Server{Type: "stdio", Command: "TEST-never-executed", Args: []string{"fixture"}, Env: map[string]string{"FIXTURE": "inert"}, CWD: filepath.Dir(path)} + receipt, err = kernel.Apply(request) + if err != nil { + t.Fatal(err) + } + request.Owned = &receipt + unchanged, _ := os.ReadFile(path) + if _, err := kernel.Apply(request); err != nil { + t.Fatal(err) + } + assertBytes(t, path, string(unchanged)) + request.Action = ActionRemove + if _, err := kernel.Apply(request); err != nil { + t.Fatal(err) + } + projectCodexAssertForeignTOML(t, path, before) + if present, _, err := kernel.Inspect(request.Paths, request.Codec, request.Name, nil); err != nil || present { + t.Fatalf("managed Codex entry survived: %v, %v", present, err) + } + }) + } +} + +func projectCodexAssertForeignTOML(t *testing.T, path, before string) { + t.Helper() + body, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + var original, actual map[string]any + if err := toml.Unmarshal([]byte(before), &original); err != nil { + t.Fatal(err) + } + if err := toml.Unmarshal(body, &actual); err != nil { + t.Fatal(err) + } + delete(actual["mcp_servers"].(map[string]any), "managed") + if !reflect.DeepEqual(original, actual) { + t.Fatalf("foreign TOML data changed: %+v", actual) + } + for _, comment := range []string{"preserve native settings", "policy", "foreign comment"} { + if !strings.Contains(string(body), comment) { + t.Fatalf("foreign TOML comment %q lost", comment) + } + } +} + +func TestProjectCodexTOMLRejectsMalformedCollisionAndForeignEdit(t *testing.T) { + for name, body := range map[string]string{ + "duplicate": "[mcp_servers]\nx={command='one'}\nx={command='two'}\n", + "malformed": "[mcp_servers\n", + "scalar": "mcp_servers = false\n", + } { + t.Run(name, func(t *testing.T) { + path := filepath.Join(t.TempDir(), "config.toml") + mustWrite(t, path, body) + request := Request{Paths: Paths{JSON: path}, Codec: CodecCodex, Action: ActionAdd, Name: "managed", Server: Server{Type: "remote", URL: "https://managed.test/mcp"}} + if _, err := New().Apply(request); !errors.Is(err, ErrMalformed) { + t.Fatalf("invalid TOML admitted: %v", err) + } + assertBytes(t, path, body) + }) + } + path := filepath.Join(t.TempDir(), "config.toml") + request := Request{Paths: Paths{JSON: path}, Codec: CodecCodex, Action: ActionAdd, Name: "managed", Server: Server{Type: "remote", URL: "https://managed.test/mcp"}} + receipt, err := New().Apply(request) + if err != nil { + t.Fatal(err) + } + if _, err := New().Apply(request); !errors.Is(err, ErrCollision) { + t.Fatalf("foreign entry was adopted: %v", err) + } + const edited = "[mcp_servers.managed]\nurl='https://managed.test/mcp'\nenabled=false # foreign policy\n" + mustWrite(t, path, edited) + for _, action := range []Action{ActionUpdate, ActionRemove} { + request.Action, request.Owned = action, &receipt + if _, err := New().Apply(request); !errors.Is(err, ErrNotOwned) { + t.Fatalf("edited native entry accepted by %s: %v", action, err) + } + assertBytes(t, path, edited) + } +} + +func TestProjectCodexTOMLRemovesCompleteNestedOwnedEntry(t *testing.T) { + path := filepath.Join(t.TempDir(), "config.toml") + const body = "[mcp_servers.managed]\ncommand='TEST-never-executed'\n[mcp_servers.managed.env]\nFIXTURE='inert'\n[mcp_servers.foreign]\nurl='https://foreign.test/keep' # keep\n" + mustWrite(t, path, body) + server := Server{Type: "stdio", Command: "TEST-never-executed", Env: map[string]string{"FIXTURE": "inert"}} + receipt, err := DesiredReceipt(path, CodecCodex, "managed", server, Placeholders{}) + if err != nil { + t.Fatal(err) + } + if _, err := New().Apply(Request{Paths: Paths{JSON: path}, Codec: CodecCodex, Action: ActionRemove, Name: "managed", Owned: &receipt}); err != nil { + t.Fatal(err) + } + actual, _ := os.ReadFile(path) + if strings.Contains(string(actual), "managed") || !strings.Contains(string(actual), "# keep") { + t.Fatalf("nested native removal changed foreign entry or left owned child: %s", actual) + } + if _, err := DesiredReceipt(path, CodecCodex, "sse", Server{Type: "remote", URL: "https://managed.test/mcp", RemoteTransport: "sse"}, Placeholders{}); err == nil { + t.Fatal("Codex silently accepted unsupported SSE transport") + } +} diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/document.go b/install/integrationctl/agentplugins/adapters/nativeconfig/document.go index 4f84a1f4f..598195707 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/document.go +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/document.go @@ -16,6 +16,7 @@ type document struct { root *hujson.Object jsonc bool commentsOnly bool + codexTOML *codexTOMLDocument } func parseDocument(body []byte, jsonc bool) (*document, error) { @@ -37,6 +38,9 @@ func parseDocument(body []byte, jsonc bool) (*document, error) { } func newDocument(jsonc bool, codec Codec) (*document, error) { + if codec == CodecCodex { + return parseCodecDocument(nil, jsonc, codec) + } return parseCodecDocument([]byte("{}\n"), jsonc, codec) } @@ -149,6 +153,9 @@ func removeEntry(obj *hujson.Object, name string) bool { } func (doc *document) render() ([]byte, error) { + if doc.codexTOML != nil { + return doc.renderCodexTOML() + } if doc.commentsOnly { geminiRemoveTrailingCommas(&doc.ast) } diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/gemini_document.go b/install/integrationctl/agentplugins/adapters/nativeconfig/gemini_document.go index 51c3da548..93b2dd4b7 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/gemini_document.go +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/gemini_document.go @@ -10,6 +10,9 @@ import ( // comment-aware, but trailing commas remain invalid. Keep the original AST so // managed entry mutations preserve comments and unrelated configuration. func parseCodecDocument(body []byte, jsonc bool, codec Codec) (*document, error) { + if codec == CodecCodex { + return parseCodexTOML(body, jsonc) + } if codec != CodecGemini || jsonc { return parseDocument(body, jsonc) } diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/project.go b/install/integrationctl/agentplugins/adapters/nativeconfig/project.go index b19769f80..5567bfda1 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/project.go +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/project.go @@ -37,12 +37,18 @@ func DesiredReceipt(resolvedPath string, codec Codec, name string, server Server } func projectServer(codec Codec, server Server, placeholders Placeholders) (map[string]any, error) { + if codec != CodecCodex && server.CodexAuth != nil { + return nil, fmt.Errorf("%s does not accept Codex authentication options", codec) + } if !supportedCodec(codec) { return nil, fmt.Errorf("unsupported native config codec %q", codec) } if codec == CodecOpenCodeV2 { return projectOpenCodeV2Server(server, placeholders) } + if codec == CodecCodex { + return projectCodexServer(server, placeholders) + } if server.OpenCodeV2 != nil { return nil, fmt.Errorf("%s does not accept OpenCode V2 options", codec) } diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/types.go b/install/integrationctl/agentplugins/adapters/nativeconfig/types.go index 52a07375a..fb0aa6c43 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/types.go +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/types.go @@ -18,6 +18,7 @@ const ( // Clients with different native transport keys require their own codec. CodecMCPServers Codec = "mcpServers" CodecGemini Codec = "gemini-mcpServers" + CodecCodex Codec = "codex-mcp-servers" CodecOpenCode Codec = "opencode-mcp" CodecOpenCodeV2 Codec = "opencode-v2-mcp" CodecWindsurf Codec = "windsurf-mcpServers" @@ -84,6 +85,13 @@ type Server struct { // OpenCodeV2 carries only the pinned V2 disabled/timeout contract. Other // codecs reject it rather than silently discard dialect-specific intent. OpenCodeV2 *OpenCodeV2Options `json:"opencode_v2,omitempty"` + // CodexAuth contains only nonsecret references from compatibility hints. + CodexAuth *CodexAuthOptions `json:"codex_auth,omitempty"` +} + +type CodexAuthOptions struct { + BearerTokenEnvVar string `json:"bearer_token_env_var,omitempty"` + OAuthResource string `json:"oauth_resource,omitempty"` } // OpenCodeV2Options is a closed subset of the pinned native MCP entry schema. @@ -178,6 +186,9 @@ func NewWithLockAcquirer(acquireLocks func(Paths, Codec) (func() error, error)) } func validateRequest(req Request) error { + if req.Codec == CodecCodex && req.Paths.JSONC != "" { + return fmt.Errorf("codex requires one TOML config path") + } if err := validateExactPath(req.Paths.JSON, "JSON native config path"); err != nil { return err } @@ -208,10 +219,13 @@ func validateRequest(req Request) error { } func supportedCodec(codec Codec) bool { - return codec == CodecMCPServers || codec == CodecGemini || codec == CodecOpenCode || codec == CodecOpenCodeV2 || codec == CodecWindsurf || codec == CodecCline + return codec == CodecMCPServers || codec == CodecGemini || codec == CodecCodex || codec == CodecOpenCode || codec == CodecOpenCodeV2 || codec == CodecWindsurf || codec == CodecCline } func codecCollectionKey(codec Codec) string { + if codec == CodecCodex { + return "mcp_servers" + } if codec == CodecOpenCode || codec == CodecOpenCodeV2 { return "mcp" } diff --git a/install/integrationctl/agentplugins/go.mod b/install/integrationctl/agentplugins/go.mod index c7b97bccd..b90e21266 100644 --- a/install/integrationctl/agentplugins/go.mod +++ b/install/integrationctl/agentplugins/go.mod @@ -8,6 +8,7 @@ replace github.com/777genius/plugin-kit-ai/install/integrationctl => ../ require ( github.com/777genius/plugin-kit-ai/install/integrationctl v0.0.0-20260919103549-148794ac5947 + github.com/creachadair/tomledit v0.0.29 github.com/dlclark/regexp2 v1.12.0 github.com/pelletier/go-toml/v2 v2.3.0 github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 diff --git a/install/integrationctl/agentplugins/go.sum b/install/integrationctl/agentplugins/go.sum index b37433a50..ebbe763e3 100644 --- a/install/integrationctl/agentplugins/go.sum +++ b/install/integrationctl/agentplugins/go.sum @@ -1,3 +1,5 @@ +github.com/creachadair/tomledit v0.0.29 h1:dB5CbdwJMpn/fmfAPTAAleXF/KJwY0Ggc1eL/zvZRgk= +github.com/creachadair/tomledit v0.0.29/go.mod h1:4SoTXxzHgvzHRMIJPw+o6zK/yXii4VjLrb6/3gCQnyA= github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8= github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= From 68df258589797bdbbccbff2a0d9540c107bb5bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D0=B8=D1=8F?= Date: Sat, 24 Oct 2026 11:56:34 +0300 Subject: [PATCH 2/7] docs(codex): preserve parser dependency boundary recovery plan --- .../codex-parser-recovery-plan.md | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 docs/plans/project-install-e2e/codex-parser-recovery-plan.md diff --git a/docs/plans/project-install-e2e/codex-parser-recovery-plan.md b/docs/plans/project-install-e2e/codex-parser-recovery-plan.md new file mode 100644 index 000000000..f3ecc9068 --- /dev/null +++ b/docs/plans/project-install-e2e/codex-parser-recovery-plan.md @@ -0,0 +1,76 @@ +# Codex concrete TOML parser recovery — fresh pre-code plan + +The reset erased unpublished finald8daad5a/source296acbc2 and their objects. +Published base0d1319f947c7f4403691b3a60b1193baa0074ee9 still contains concrete +tomledit/go-toml imports inside nativeconfig, which the unchanged contract +dependency-closure guard correctly rejects. Restore the previously reviewed +concrete backend extraction through two fresh independent plan GOs before code, +then fresh meaningful tests/lint and two fresh code GOs. Historical GOs/tests +are context, never evidence for reconstructed bytes. + +## Exact first slice: architecture without losing behavior + +Move retained TOML syntax parsing/editing into concrete adapters/codextoml, +outside the clients/domain/ports contract's nativeconfig closure. Preserve +tomledit/go-toml dependencies for that real consumer and useful legacy tests; +do not remove capability, widen guard snapshots/allowlists/platform sweeps or +change go.mod just to turn the guard green. Keep TOML-specific server projection +and pure desired-receipt/digest handling in the neutral kernel where appropriate. + +Expose only a small nativeconfig CodexDocument interface and parser function: +copy-returned MCP entry map as json.RawMessage plus render-from-current-entry +map. The concrete backend retains original bytes/TOML AST/foreign semantic +settings; nativeconfig owns hujson neutral edits, whole-entry receipt/digest, +FileIO, locks, original snapshots, CAS/rollback and committed cleanup. + +A per-Kernel WithCodexDocumentParser method returns a configured copy retaining +the exact injected FileIO/lock acquirer. No global registration, implicit default +parser or nativeconfig import of the concrete package. CodecCodex Inspect/Apply +refuse a missing parser before reads/locks/effects; ordinary codecs are unchanged. +Pure DesiredReceipt remains parser-free and effect-free. Reject literal/typed +nil documents before native methods, release acquired locks and perform no write. +Clone original input bytes before crossing the parser seam and clone raw entry/ +no-op output bytes so injected parsers cannot mutate retained snapshots. + +The concrete backend preserves existing malformed/duplicate TOML refusals, +inline/dotted/parent/child collection forms, exact original no-op bytes, foreign +settings/comments, complete owned nested-entry removal and all requested native +MCP values. Reparse rendered TOML and compare foreign semantics and desired MCP +entries before returning bytes. Neutral canonical JSON keeps UseNumber; conversion +back to TOML retains exact signed integer values, including above2^53, rather +than silently introducing float rounding. No serializer rewrite for other codecs. + +Supply the concrete parser explicitly on configured copies at all Codex project +mutation, registry/prepared inspection, object verification and native-settings +observation boundaries. Do not replace injected kernels with New() or alter user +Codex CLI/profile/trust/auth/model behavior. Update affected direct fixtures to +configure the concrete backend explicitly; retain every old TOML lifecycle and +refusal assertion at the concrete consumer plus new seam/codec regressions. + +## Fresh proof and incremental publication + +First verify the unchanged exact TestContractLayerDependencyClosure on its actual +internal/archtest package, including its unchanged platform sweep. Run complete +nativeconfig, concrete codextoml and preserved Codex client packages, affected +ProjectCodex provider/public lifecycle contracts, and pinned changed-code plus +full-file size/architecture lint. Meaningful seam tests cover missing parser +before FileIO/locks, injected I/O/lock preservation, adversarial byte mutation, +literal/typed nil with cleanup, raw-entry/no-op copy isolation and large integers. +No vendor/native/model/MCP/trust process executes during local checks. + +Freeze this first source slice for two fresh independent code reviews, commit +reports and send each clean checkpoint immediately to root for feature-branch +publication. Root may integrate this approved extraction to remove the inherited +architecture blocker and rerun the unchanged guard. This is not all-client or +current-native qualification. + +## Retained separate recovery obligations + +The lost final Codex correction also tightened exact client/scope/original-user/ +artifact/managed-layout/direct-delivery/binding/removal authority, physical +non-alias staging before projection, and exact current stdio data/package reuse. +Its public SDK A/B/pin-repair/restart/data and composed TLS Git/ZIP journeys must +be reconstructed through a separate reviewed authority recovery slice after this +architecture slice. Do not silently drop these known obligations or claim that +earlier native passes qualify changed source. Preserve the existing genuine +macOS/Windows evidence at its original source/run until a current-source replay. From 0d9f7042ec85ec1103cc2d305bce83ea55177c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D0=B8=D1=8F?= Date: Sat, 24 Oct 2026 12:10:41 +0300 Subject: [PATCH 3/7] refactor(codex): isolate concrete TOML parser from the portable contract dependency closure --- .../codex-parser-recovery-evidence.md | 33 ++++ .../codex-parser-recovery-review-r1.md | 12 ++ .../codex-parser-recovery-review-r2.md | 12 ++ .../adapters/codextoml/document.go | 115 +++++++++++ .../codex_toml_edit.go => codextoml/edit.go} | 10 +- .../adapters/codextoml/isolation_test.go | 55 ++++++ .../agentplugins/adapters/codextoml/json.go | 54 ++++++ .../lifecycle_test.go} | 56 ++++-- .../adapters/nativeconfig/codex_document.go | 178 ++++++------------ .../nativeconfig/codex_document_test.go | 116 ++++++++++++ .../adapters/nativeconfig/codex_server.go | 35 ++++ .../adapters/nativeconfig/document.go | 20 +- .../adapters/nativeconfig/gemini_document.go | 2 +- .../adapters/nativeconfig/kernel.go | 14 +- .../adapters/nativeconfig/types.go | 1 + .../clients/codex/project_identity.go | 5 +- .../clients/codex/project_lifecycle.go | 3 +- .../clients/codex/project_status.go | 2 + .../providers/project_codex_fixture_test.go | 3 +- 19 files changed, 567 insertions(+), 159 deletions(-) create mode 100644 docs/plans/project-install-e2e/codex-parser-recovery-evidence.md create mode 100644 docs/plans/project-install-e2e/codex-parser-recovery-review-r1.md create mode 100644 docs/plans/project-install-e2e/codex-parser-recovery-review-r2.md create mode 100644 install/integrationctl/agentplugins/adapters/codextoml/document.go rename install/integrationctl/agentplugins/adapters/{nativeconfig/codex_toml_edit.go => codextoml/edit.go} (83%) create mode 100644 install/integrationctl/agentplugins/adapters/codextoml/isolation_test.go create mode 100644 install/integrationctl/agentplugins/adapters/codextoml/json.go rename install/integrationctl/agentplugins/adapters/{nativeconfig/codex_toml_test.go => codextoml/lifecycle_test.go} (54%) create mode 100644 install/integrationctl/agentplugins/adapters/nativeconfig/codex_document_test.go create mode 100644 install/integrationctl/agentplugins/adapters/nativeconfig/codex_server.go diff --git a/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md b/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md new file mode 100644 index 000000000..7a6df0353 --- /dev/null +++ b/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md @@ -0,0 +1,33 @@ +# Codex concrete parser reconstruction checkpoint + +Both fresh plan GOs at5500e56f were committed atc50956de before source edits. +This first recovery slice reconstructs the neutral parser seam and concrete +retained TOML backend. Historicald8daad5a tests are not current proof. + +Nativeconfig now declares only CodexDocument/parser and configured-copy seam. +No concrete TOML production import remains. Missing parser refuses before +FileIO/locks, input bytes cross as clones, literal/typed-nil documents refuse +before methods, neutral entries and output bytes are copied. DesiredReceipt +remains pure/parser-free. Concrete codextoml retains TOML AST/foreign semantics, +original no-op bytes, complete nested-entry removal and checked reparse/render. +JSON UseNumber plus exact signed-integer conversion prevents >2^53 rounding. + +The previous TOML lifecycle/refusal tests were moved to the concrete consumer, +not discarded. Their four table forms, foreign semantic/comment preservation, +exact no-op, malformed/collision/edited ownership, nested removal and unsupported +SSE assertions remain. New seam and concrete tests exercise missing parser +before effects, injected I/O/locks, adversarial input mutation, nil/typed-nil +cleanup, raw/output isolation and9007199254740993 preservation. + +All four Codex boundaries explicitly configure the backend on the existing +kernel copy: component mutation, registry/prepared inspection, object verification, +and native-settings observation. User CLI/profile behavior is unchanged. The +kernel still owns validated paths/receipts/CAS/rollback/locks and other codecs. +Guard snapshots/platform sweep and dependency manifests are unchanged. + +At this initial source freeze only formatting/whitespace checks have passed. +Fresh architecture/full codec/preserved Codex/provider tests, lint and two +independent implementation reviews remain pending and must close before root +publishes/integrates this correction. No vendor/native, model, trust or MCP +process ran during source reconstruction. Exact Codex authority/public SDK/CLI +recovery is a separate required reviewed slice, not completed by this extraction. diff --git a/docs/plans/project-install-e2e/codex-parser-recovery-review-r1.md b/docs/plans/project-install-e2e/codex-parser-recovery-review-r1.md new file mode 100644 index 000000000..d86a0b80b --- /dev/null +++ b/docs/plans/project-install-e2e/codex-parser-recovery-review-r1.md @@ -0,0 +1,12 @@ +# Fresh Codex parser recovery plan R1 + +Reviewer finish_sources_vscode_cline, independent of author. **GO** at +5500e56f01f99fa28c9db619b4263b5b8e803dfa before reconstructed source. +Full plan read: concrete TOML AST/edit backend outside neutral contract closure, +no guard/dependency/platform weakening, per-Kernel copy injection preserving +FileIO/locks, missing parser before effects, typed-nil/byte/raw/no-op isolation, +UseNumber/exact large integers and whole-entry/foreign semantics retained. +Explicit four Codex composition boundaries and complete consumer/seam/unchanged +architecture tests are meaningful. Known exact authority recovery remains a +separate required slice; no earlier native pass is relabeled. No blocker, +source edit, Go or native process by this review. Fresh code reviews required. diff --git a/docs/plans/project-install-e2e/codex-parser-recovery-review-r2.md b/docs/plans/project-install-e2e/codex-parser-recovery-review-r2.md new file mode 100644 index 000000000..24c3d4994 --- /dev/null +++ b/docs/plans/project-install-e2e/codex-parser-recovery-review-r2.md @@ -0,0 +1,12 @@ +# Fresh Codex parser recovery plan R2 + +Reviewer finish_grok_integration, independent of author. **GO** at +5500e56f01f99fa28c9db619b4263b5b8e803dfa before reconstructed source. +Full plan preserves TOML behavior/dependencies outside guarded closure, unchanged +guard/platform snapshot, explicit configured-copy seam retaining injected I/O/ +locks, missing parser before effects, nil/typed-nil/byte/raw/no-op isolation, +parser-free desired receipts, AST/foreign/whole-entry semantics and exact large +signed integers. No implicit/global backend or lost capability. Authority/SDK/ +CLI restoration remains explicitly separate. Actual architecture test package, +meaningful seam/backend/Codex/provider/lint gates and two fresh code GOs remain +required. No blocking finding, source edit, Go or native execution by reviewer. diff --git a/install/integrationctl/agentplugins/adapters/codextoml/document.go b/install/integrationctl/agentplugins/adapters/codextoml/document.go new file mode 100644 index 000000000..34cad98e8 --- /dev/null +++ b/install/integrationctl/agentplugins/adapters/codextoml/document.go @@ -0,0 +1,115 @@ +package codextoml + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "sort" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" + "github.com/creachadair/tomledit" + "github.com/pelletier/go-toml/v2" +) + +type document struct { + body []byte + ast *tomledit.Document + original map[string]json.RawMessage + settings map[string]any +} + +// Parse supplies the retained concrete TOML backend at an explicit composition boundary. +func Parse(body []byte) (nativeconfig.CodexDocument, error) { + var values map[string]any + if err := toml.Unmarshal(body, &values); err != nil { + return nil, fmt.Errorf("%w: invalid Codex TOML", nativeconfig.ErrMalformed) + } + if values == nil { + values = map[string]any{} + } + servers, exists := values["mcp_servers"] + if !exists { + servers = map[string]any{} + } + collection, ok := servers.(map[string]any) + if !ok { + return nil, fmt.Errorf("%w: mcp_servers must be a table", nativeconfig.ErrMalformed) + } + entries := make(map[string]json.RawMessage, len(collection)) + for name, value := range collection { + entry, err := json.Marshal(value) + if err != nil { + return nil, fmt.Errorf("%w: invalid Codex MCP entry values", nativeconfig.ErrMalformed) + } + entries[name] = entry + } + ast, err := tomledit.Parse(bytes.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("%w: invalid Codex TOML structure", nativeconfig.ErrMalformed) + } + delete(values, "mcp_servers") + return &document{body: bytes.Clone(body), ast: ast, original: entries, settings: values}, nil +} + +func (doc *document) MCPEntries() (map[string]json.RawMessage, error) { + return cloneEntries(doc.original), nil +} + +func (doc *document) RenderMCPEntries(current map[string]json.RawMessage) ([]byte, error) { + current = cloneEntries(current) + names := changedNames(doc.original, current) + if len(names) == 0 { + return bytes.Clone(doc.body), nil + } + for _, name := range names { + removeCodexTOMLEntry(doc.ast, name) + if body, exists := current[name]; exists { + if err := setCodexTOMLEntry(doc.ast, name, body); err != nil { + return nil, err + } + } + } + var result bytes.Buffer + if err := tomledit.Format(&result, doc.ast); err != nil { + return nil, fmt.Errorf("render Codex native config: %w", err) + } + parsed, err := Parse(result.Bytes()) + if err != nil { + return nil, err + } + observed := parsed.(*document) + if !reflect.DeepEqual(doc.settings, observed.settings) { + return nil, fmt.Errorf("rendered Codex config changed foreign settings") + } + if len(changedNames(current, observed.original)) != 0 { + return nil, fmt.Errorf("rendered Codex MCP entries differ from requested entries") + } + return bytes.Clone(result.Bytes()), nil +} + +func cloneEntries(entries map[string]json.RawMessage) map[string]json.RawMessage { + result := make(map[string]json.RawMessage, len(entries)) + for name, body := range entries { + result[name] = bytes.Clone(body) + } + return result +} + +func changedNames(previous, next map[string]json.RawMessage) []string { + changed := map[string]bool{} + for name, body := range previous { + changed[name] = !bytes.Equal(body, next[name]) + } + for name, body := range next { + changed[name] = !bytes.Equal(body, previous[name]) + } + result := make([]string, 0, len(changed)) + for name, differs := range changed { + if differs { + result = append(result, name) + } + } + sort.Strings(result) + return result +} diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_edit.go b/install/integrationctl/agentplugins/adapters/codextoml/edit.go similarity index 83% rename from install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_edit.go rename to install/integrationctl/agentplugins/adapters/codextoml/edit.go index d03898d62..d943ecb1c 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_edit.go +++ b/install/integrationctl/agentplugins/adapters/codextoml/edit.go @@ -1,10 +1,10 @@ -package nativeconfig +package codextoml import ( "bytes" - "encoding/json" "fmt" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" "github.com/creachadair/tomledit" "github.com/creachadair/tomledit/parser" "github.com/creachadair/tomledit/transform" @@ -27,8 +27,8 @@ func removeCodexTOMLEntry(doc *tomledit.Document, name string) { func setCodexTOMLEntry(doc *tomledit.Document, name string, body []byte) error { var value map[string]any - if err := json.Unmarshal(body, &value); err != nil || value == nil { - return fmt.Errorf("%w: Codex MCP entry must be a table", ErrMalformed) + if err := decodeTOMLJSON(body, &value); err != nil || value == nil { + return fmt.Errorf("%w: Codex MCP entry must be a table", nativeconfig.ErrMalformed) } var encoded bytes.Buffer if err := toml.NewEncoder(&encoded).SetTablesInline(true).Encode(map[string]any{name: value}); err != nil { @@ -49,7 +49,7 @@ func setCodexTOMLEntry(doc *tomledit.Document, name string, body []byte) error { parent.Value.X = inline return nil } - return fmt.Errorf("%w: Codex MCP collection is not a table", ErrMalformed) + return fmt.Errorf("%w: Codex MCP collection is not a table", nativeconfig.ErrMalformed) } if parent != nil && parent.IsSection() { transform.InsertMapping(parent.Section, entry.KeyValue, false) diff --git a/install/integrationctl/agentplugins/adapters/codextoml/isolation_test.go b/install/integrationctl/agentplugins/adapters/codextoml/isolation_test.go new file mode 100644 index 000000000..5d671d737 --- /dev/null +++ b/install/integrationctl/agentplugins/adapters/codextoml/isolation_test.go @@ -0,0 +1,55 @@ +package codextoml + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/pelletier/go-toml/v2" +) + +func TestProjectCodexConcreteCopiesAndLargeIntegers(t *testing.T) { + const original = "# foreign policy\n[mcp_servers.foreign]\nurl='https://inert.test/mcp'\nlarge=9007199254740993\n" + input := []byte(original) + doc, err := Parse(input) + if err != nil { + t.Fatal(err) + } + input[0] = '!' + entries, err := doc.MCPEntries() + if err != nil { + t.Fatal(err) + } + entries["foreign"][0] = '!' + independent, err := doc.MCPEntries() + if err != nil || !json.Valid(independent["foreign"]) { + t.Fatal("concrete entries alias retained document", err) + } + noop, err := doc.RenderMCPEntries(independent) + if err != nil || string(noop) != original { + t.Fatalf("no-op bytes changed: %q %v", noop, err) + } + noop[0] = '!' + again, err := doc.RenderMCPEntries(independent) + if err != nil || string(again) != original { + t.Fatal("no-op output aliases retained document", err) + } + independent["managed"] = json.RawMessage(`{"command":"TEST-not-executed","large":9007199254740993}`) + body, err := doc.RenderMCPEntries(independent) + if err != nil { + t.Fatal(err) + } + var native map[string]any + if err := toml.Unmarshal(body, &native); err != nil { + t.Fatal(err) + } + servers := native["mcp_servers"].(map[string]any) + for _, name := range []string{"foreign", "managed"} { + if value := servers[name].(map[string]any)["large"]; value != int64(9007199254740993) { + t.Fatalf("integer changed for %s: %v", name, value) + } + } + if !bytes.Contains(body, []byte("# foreign policy")) { + t.Fatal("foreign comment lost") + } +} diff --git a/install/integrationctl/agentplugins/adapters/codextoml/json.go b/install/integrationctl/agentplugins/adapters/codextoml/json.go new file mode 100644 index 000000000..165cb940b --- /dev/null +++ b/install/integrationctl/agentplugins/adapters/codextoml/json.go @@ -0,0 +1,54 @@ +package codextoml + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "strconv" + "strings" +) + +func decodeTOMLJSON(body []byte, result *map[string]any) error { + decoder := json.NewDecoder(bytes.NewReader(body)) + decoder.UseNumber() + if err := decoder.Decode(result); err != nil { + return err + } + if _, err := decoder.Token(); err != io.EOF { + return fmt.Errorf("codex MCP entry has trailing JSON") + } + converted, err := tomlJSONValue(*result) + if err != nil { + return err + } + *result, _ = converted.(map[string]any) + return nil +} + +func tomlJSONValue(value any) (any, error) { + switch typed := value.(type) { + case json.Number: + if strings.ContainsAny(string(typed), ".eE") { + return strconv.ParseFloat(string(typed), 64) + } + return strconv.ParseInt(string(typed), 10, 64) + case map[string]any: + for key, child := range typed { + converted, err := tomlJSONValue(child) + if err != nil { + return nil, err + } + typed[key] = converted + } + case []any: + for i, child := range typed { + converted, err := tomlJSONValue(child) + if err != nil { + return nil, err + } + typed[i] = converted + } + } + return value, nil +} diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_test.go b/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go similarity index 54% rename from install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_test.go rename to install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go index 338920c5a..ec5a5e22e 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_toml_test.go +++ b/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go @@ -1,4 +1,4 @@ -package nativeconfig +package codextoml import ( "errors" @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" "github.com/pelletier/go-toml/v2" ) @@ -22,19 +23,19 @@ func TestProjectCodexTOMLExactLifecyclePreservesForeignData(t *testing.T) { path := filepath.Join(t.TempDir(), "config.toml") before := "# preserve native settings\ncreated = 2026-10-23\n" + collection + "\n[foreign]\nkeep = [1, 2, 3] # foreign comment\n" mustWrite(t, path, before) - kernel := New() - request := Request{Paths: Paths{JSON: path}, Codec: CodecCodex, Action: ActionAdd, Name: "managed", Server: Server{Type: "remote", URL: "https://managed.test/mcp", RemoteTransport: "streamable-http", Headers: map[string]string{"X-Fixture": "inert"}}} + kernel := newKernel() + request := nativeconfig.Request{nativeconfig.Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, nativeconfig.Action: nativeconfig.ActionAdd, Name: "managed", nativeconfig.Server: nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp", RemoteTransport: "streamable-http", Headers: map[string]string{"X-Fixture": "inert"}}} receipt, err := kernel.Apply(request) if err != nil { t.Fatal(err) } - present, owned, err := kernel.Inspect(request.Paths, request.Codec, request.Name, &receipt) + present, owned, err := kernel.Inspect(request.nativeconfig.Paths, request.Codec, request.Name, &receipt) if err != nil || !present || !owned { t.Fatalf("exact Codex entry = %v/%v, %v", present, owned, err) } projectCodexAssertForeignTOML(t, path, before) - request.Action, request.Owned = ActionUpdate, &receipt - request.Server = Server{Type: "stdio", Command: "TEST-never-executed", Args: []string{"fixture"}, Env: map[string]string{"FIXTURE": "inert"}, CWD: filepath.Dir(path)} + request.nativeconfig.Action, request.Owned = nativeconfig.ActionUpdate, &receipt + request.nativeconfig.Server = nativeconfig.Server{Type: "stdio", Command: "TEST-never-executed", Args: []string{"fixture"}, Env: map[string]string{"FIXTURE": "inert"}, CWD: filepath.Dir(path)} receipt, err = kernel.Apply(request) if err != nil { t.Fatal(err) @@ -45,12 +46,12 @@ func TestProjectCodexTOMLExactLifecyclePreservesForeignData(t *testing.T) { t.Fatal(err) } assertBytes(t, path, string(unchanged)) - request.Action = ActionRemove + request.nativeconfig.Action = nativeconfig.ActionRemove if _, err := kernel.Apply(request); err != nil { t.Fatal(err) } projectCodexAssertForeignTOML(t, path, before) - if present, _, err := kernel.Inspect(request.Paths, request.Codec, request.Name, nil); err != nil || present { + if present, _, err := kernel.Inspect(request.nativeconfig.Paths, request.Codec, request.Name, nil); err != nil || present { t.Fatalf("managed Codex entry survived: %v, %v", present, err) } }) @@ -90,27 +91,27 @@ func TestProjectCodexTOMLRejectsMalformedCollisionAndForeignEdit(t *testing.T) { t.Run(name, func(t *testing.T) { path := filepath.Join(t.TempDir(), "config.toml") mustWrite(t, path, body) - request := Request{Paths: Paths{JSON: path}, Codec: CodecCodex, Action: ActionAdd, Name: "managed", Server: Server{Type: "remote", URL: "https://managed.test/mcp"}} - if _, err := New().Apply(request); !errors.Is(err, ErrMalformed) { + request := nativeconfig.Request{nativeconfig.Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, nativeconfig.Action: nativeconfig.ActionAdd, Name: "managed", nativeconfig.Server: nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp"}} + if _, err := newKernel().Apply(request); !errors.Is(err, nativeconfig.ErrMalformed) { t.Fatalf("invalid TOML admitted: %v", err) } assertBytes(t, path, body) }) } path := filepath.Join(t.TempDir(), "config.toml") - request := Request{Paths: Paths{JSON: path}, Codec: CodecCodex, Action: ActionAdd, Name: "managed", Server: Server{Type: "remote", URL: "https://managed.test/mcp"}} - receipt, err := New().Apply(request) + request := nativeconfig.Request{nativeconfig.Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, nativeconfig.Action: nativeconfig.ActionAdd, Name: "managed", nativeconfig.Server: nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp"}} + receipt, err := newKernel().Apply(request) if err != nil { t.Fatal(err) } - if _, err := New().Apply(request); !errors.Is(err, ErrCollision) { + if _, err := newKernel().Apply(request); !errors.Is(err, nativeconfig.ErrCollision) { t.Fatalf("foreign entry was adopted: %v", err) } const edited = "[mcp_servers.managed]\nurl='https://managed.test/mcp'\nenabled=false # foreign policy\n" mustWrite(t, path, edited) - for _, action := range []Action{ActionUpdate, ActionRemove} { - request.Action, request.Owned = action, &receipt - if _, err := New().Apply(request); !errors.Is(err, ErrNotOwned) { + for _, action := range []nativeconfig.Action{nativeconfig.ActionUpdate, nativeconfig.ActionRemove} { + request.nativeconfig.Action, request.Owned = action, &receipt + if _, err := newKernel().Apply(request); !errors.Is(err, nativeconfig.ErrNotOwned) { t.Fatalf("edited native entry accepted by %s: %v", action, err) } assertBytes(t, path, edited) @@ -121,19 +122,34 @@ func TestProjectCodexTOMLRemovesCompleteNestedOwnedEntry(t *testing.T) { path := filepath.Join(t.TempDir(), "config.toml") const body = "[mcp_servers.managed]\ncommand='TEST-never-executed'\n[mcp_servers.managed.env]\nFIXTURE='inert'\n[mcp_servers.foreign]\nurl='https://foreign.test/keep' # keep\n" mustWrite(t, path, body) - server := Server{Type: "stdio", Command: "TEST-never-executed", Env: map[string]string{"FIXTURE": "inert"}} - receipt, err := DesiredReceipt(path, CodecCodex, "managed", server, Placeholders{}) + server := nativeconfig.Server{Type: "stdio", Command: "TEST-never-executed", Env: map[string]string{"FIXTURE": "inert"}} + receipt, err := nativeconfig.DesiredReceipt(path, nativeconfig.CodecCodex, "managed", server, nativeconfig.Placeholders{}) if err != nil { t.Fatal(err) } - if _, err := New().Apply(Request{Paths: Paths{JSON: path}, Codec: CodecCodex, Action: ActionRemove, Name: "managed", Owned: &receipt}); err != nil { + if _, err := newKernel().Apply(nativeconfig.Request{nativeconfig.Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, nativeconfig.Action: nativeconfig.ActionRemove, Name: "managed", Owned: &receipt}); err != nil { t.Fatal(err) } actual, _ := os.ReadFile(path) if strings.Contains(string(actual), "managed") || !strings.Contains(string(actual), "# keep") { t.Fatalf("nested native removal changed foreign entry or left owned child: %s", actual) } - if _, err := DesiredReceipt(path, CodecCodex, "sse", Server{Type: "remote", URL: "https://managed.test/mcp", RemoteTransport: "sse"}, Placeholders{}); err == nil { + if _, err := nativeconfig.DesiredReceipt(path, nativeconfig.CodecCodex, "sse", nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp", RemoteTransport: "sse"}, nativeconfig.Placeholders{}); err == nil { t.Fatal("Codex silently accepted unsupported SSE transport") } } + +func newKernel() nativeconfig.Kernel { return nativeconfig.New().WithCodexDocumentParser(Parse) } +func mustWrite(t *testing.T, path, body string) { + t.Helper() + if err := os.WriteFile(path, []byte(body), 0600); err != nil { + t.Fatal(err) + } +} +func assertBytes(t *testing.T, path, expected string) { + t.Helper() + body, err := os.ReadFile(path) + if err != nil || string(body) != expected { + t.Fatalf("bytes=%q, error=%v, expected=%q", body, err, expected) + } +} diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document.go b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document.go index 34db7411b..f9c003dcc 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document.go +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document.go @@ -5,62 +5,86 @@ import ( "encoding/json" "fmt" "reflect" - "sort" - "strings" - "github.com/creachadair/tomledit" - "github.com/pelletier/go-toml/v2" "github.com/tailscale/hujson" ) -// Only MCP entries enter the existing kernel's neutral mutation representation. -// The native TOML AST retains all other settings, types and comments. -type codexTOMLDocument struct { - body []byte - ast *tomledit.Document - original map[string]json.RawMessage - settings map[string]any +// CodexDocument retains concrete syntax outside the contract dependency closure. +// Implementations return independent entry/output byte slices. +type CodexDocument interface { + MCPEntries() (map[string]json.RawMessage, error) + RenderMCPEntries(map[string]json.RawMessage) ([]byte, error) } -func parseCodexTOML(body []byte, jsonc bool) (*document, error) { - if jsonc { - return nil, fmt.Errorf("%w: Codex requires one TOML config path", ErrMalformed) - } - var native map[string]any - if err := toml.Unmarshal(body, &native); err != nil { - return nil, fmt.Errorf("%w: invalid Codex TOML", ErrMalformed) +type CodexDocumentParser func([]byte) (CodexDocument, error) + +// WithCodexDocumentParser retains this Kernel's exact FileIO and lock authority. +func (kernel Kernel) WithCodexDocumentParser(parser CodexDocumentParser) Kernel { + kernel.codexParser = parser + return kernel +} + +func (kernel Kernel) requireDocumentParser(codec Codec) error { + if codec == CodecCodex && kernel.codexParser == nil { + return fmt.Errorf("codex TOML document parser is required") } - if native == nil { - native = map[string]any{} + return nil +} + +func (kernel Kernel) parseDocument(body []byte, jsonc bool, codec Codec) (*document, error) { + if codec != CodecCodex { + return parseCodecDocument(body, jsonc, codec) } - servers, ok := native["mcp_servers"] - if !ok { - servers = map[string]any{} + if err := kernel.requireDocumentParser(codec); err != nil { + return nil, err } - if _, ok := servers.(map[string]any); !ok { - return nil, fmt.Errorf("%w: mcp_servers must be a table", ErrMalformed) + if jsonc { + return nil, fmt.Errorf("%w: Codex requires one TOML config path", ErrMalformed) } - neutral, err := json.Marshal(map[string]any{"mcp_servers": servers}) + native, err := kernel.codexParser(bytes.Clone(body)) if err != nil { - return nil, fmt.Errorf("%w: invalid Codex MCP entry values", ErrMalformed) + return nil, err } - doc, err := parseDocument(neutral, false) + if nilCodexDocument(native) { + return nil, fmt.Errorf("codex TOML parser returned no document") + } + entries, err := native.MCPEntries() if err != nil { return nil, err } - ast, err := tomledit.Parse(bytes.NewReader(body)) + neutral, err := json.Marshal(map[string]any{"mcp_servers": cloneCodexEntries(entries)}) if err != nil { - return nil, fmt.Errorf("%w: invalid Codex TOML structure", ErrMalformed) + return nil, err } - original, err := doc.codexEntries() + doc, err := parseDocument(neutral, false) if err != nil { return nil, err } - delete(native, "mcp_servers") - doc.codexTOML = &codexTOMLDocument{body: bytes.Clone(body), ast: ast, original: original, settings: native} + doc.codexDocument = native return doc, nil } +func nilCodexDocument(value CodexDocument) bool { + if value == nil { + return true + } + v := reflect.ValueOf(value) + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: + return v.IsNil() + default: + return false + } +} + +func cloneCodexEntries(entries map[string]json.RawMessage) map[string]json.RawMessage { + result := make(map[string]json.RawMessage, len(entries)) + for name, body := range entries { + result[name] = bytes.Clone(body) + } + return result +} + func (doc *document) codexEntries() (map[string]json.RawMessage, error) { entries, err := codecCollection(doc, CodecCodex, false) if err != nil || entries == nil { @@ -73,94 +97,16 @@ func (doc *document) codexEntries() (map[string]json.RawMessage, error) { if err != nil { return nil, err } - result[member.Name.Value.(hujson.Literal).String()] = body + result[member.Name.Value.(hujson.Literal).String()] = bytes.Clone(body) } return result, nil } -func (doc *document) renderCodexTOML() ([]byte, error) { - current, err := doc.codexEntries() - if err != nil { - return nil, err - } - names := codexChangedNames(doc.codexTOML.original, current) - if len(names) == 0 { - return bytes.Clone(doc.codexTOML.body), nil - } - for _, name := range names { - removeCodexTOMLEntry(doc.codexTOML.ast, name) - if body, exists := current[name]; exists { - if err := setCodexTOMLEntry(doc.codexTOML.ast, name, body); err != nil { - return nil, err - } - } - } - var result bytes.Buffer - if err := tomledit.Format(&result, doc.codexTOML.ast); err != nil { - return nil, fmt.Errorf("render Codex native config: %w", err) - } - next := result.Bytes() - parsed, err := parseCodexTOML(next, false) - if err != nil { - return nil, err - } - if !reflect.DeepEqual(doc.codexTOML.settings, parsed.codexTOML.settings) { - return nil, fmt.Errorf("rendered Codex config changed foreign settings") - } - observed, err := parsed.codexEntries() - if err != nil || !equalCodexEntries(current, observed) { - return nil, fmt.Errorf("rendered Codex MCP entries differ from requested entries") - } - return next, nil -} - -func codexChangedNames(previous, next map[string]json.RawMessage) []string { - changed := map[string]bool{} - for name, value := range previous { - changed[name] = !bytes.Equal(value, next[name]) - } - for name, value := range next { - changed[name] = !bytes.Equal(value, previous[name]) - } - result := make([]string, 0, len(changed)) - for name, differs := range changed { - if differs { - result = append(result, name) - } - } - sort.Strings(result) - return result -} - -func equalCodexEntries(a, b map[string]json.RawMessage) bool { - return len(a) == len(b) && len(codexChangedNames(a, b)) == 0 -} - -func projectCodexServer(server Server, placeholders Placeholders) (map[string]any, error) { - if server.OpenCodeV2 != nil { - return nil, fmt.Errorf("codex does not accept OpenCode V2 options") - } - if strings.EqualFold(strings.TrimSpace(server.Type), "remote") && server.RemoteTransport == "streamable-http" { - server.RemoteTransport = "" - } - projected, err := projectBaseServer(CodecMCPServers, server, placeholders) +func (doc *document) renderCodexDocument() ([]byte, error) { + entries, err := doc.codexEntries() if err != nil { return nil, err } - if headers, ok := projected["headers"]; ok { - delete(projected, "headers") - projected["http_headers"] = headers - } - if server.CodexAuth != nil { - if server.Type != "remote" { - return nil, fmt.Errorf("codex stdio servers do not accept remote authentication options") - } - if server.CodexAuth.BearerTokenEnvVar != "" { - projected["bearer_token_env_var"] = server.CodexAuth.BearerTokenEnvVar - } - if server.CodexAuth.OAuthResource != "" { - projected["oauth_resource"] = server.CodexAuth.OAuthResource - } - } - return projected, nil + body, err := doc.codexDocument.RenderMCPEntries(cloneCodexEntries(entries)) + return bytes.Clone(body), err } diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document_test.go b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document_test.go new file mode 100644 index 000000000..101b9604b --- /dev/null +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_document_test.go @@ -0,0 +1,116 @@ +package nativeconfig + +import ( + "bytes" + "encoding/json" + "os" + "path/filepath" + "testing" +) + +type codexParserTestIO struct { + body []byte + reads, writes int +} + +func (files *codexParserTestIO) ReadNoFollow(string) ([]byte, os.FileMode, bool, error) { + files.reads++ + return files.body, 0600, true, nil +} +func (files *codexParserTestIO) WriteAtomic(_ string, body []byte, _ os.FileMode) error { + files.writes++ + files.body = bytes.Clone(body) + return nil +} +func (*codexParserTestIO) RemoveNoFollow(string) error { return nil } + +type codexParserTestDocument struct{ entries map[string]json.RawMessage } + +func (doc *codexParserTestDocument) MCPEntries() (map[string]json.RawMessage, error) { + return doc.entries, nil +} +func (*codexParserTestDocument) RenderMCPEntries(entries map[string]json.RawMessage) ([]byte, error) { + return json.Marshal(entries) +} + +func codexParserRequest(t *testing.T) Request { + t.Helper() + return Request{Paths: Paths{JSON: filepath.Join(t.TempDir(), "config.toml")}, Codec: CodecCodex, + Action: ActionAdd, Name: "managed", Server: Server{Type: "remote", URL: "https://inert.test/mcp"}} +} + +func TestProjectCodexMissingParserRefusesBeforeIOOrLocks(t *testing.T) { + files := &codexParserTestIO{body: []byte("original")} + locks := 0 + kernel := NewWithFileIO(files) + kernel.acquireLocks = func(Paths, Codec) (func() error, error) { locks++; return func() error { return nil }, nil } + request := codexParserRequest(t) + if _, err := kernel.Apply(request); err == nil { + t.Fatal("missing parser admitted mutation") + } + if _, _, err := kernel.Inspect(request.Paths, request.Codec, request.Name, nil); err == nil { + t.Fatal("missing parser admitted inspection") + } + if _, err := DesiredReceipt(request.Paths.JSON, request.Codec, request.Name, request.Server, Placeholders{}); err != nil { + t.Fatal("pure receipt gained parser requirement", err) + } + if files.reads != 0 || files.writes != 0 || locks != 0 { + t.Fatalf("missing parser effects reads=%d writes=%d locks=%d", files.reads, files.writes, locks) + } +} + +func TestProjectCodexParserCopyPreservesInjectedAuthority(t *testing.T) { + files := &codexParserTestIO{body: []byte("original")} + locks, releases := 0, 0 + original := NewWithFileIO(files) + original.acquireLocks = func(Paths, Codec) (func() error, error) { locks++; return func() error { releases++; return nil }, nil } + kernel := original.WithCodexDocumentParser(func([]byte) (CodexDocument, error) { + return &codexParserTestDocument{entries: map[string]json.RawMessage{}}, nil + }) + if _, err := kernel.Apply(codexParserRequest(t)); err != nil { + t.Fatal(err) + } + if original.codexParser != nil || kernel.files != files || files.reads == 0 || files.writes != 1 || locks != 1 || releases != 1 { + t.Fatalf("configured copy lost authority reads=%d writes=%d locks=%d releases=%d", files.reads, files.writes, locks, releases) + } +} + +func TestProjectCodexNilDocumentsCannotMutateSnapshotsOrLeakLocks(t *testing.T) { + for _, typed := range []bool{false, true} { + t.Run(map[bool]string{false: "literal", true: "typed"}[typed], func(t *testing.T) { + files := &codexParserTestIO{body: []byte("original snapshot")} + locks, releases := 0, 0 + kernel := NewWithFileIO(files) + kernel.acquireLocks = func(Paths, Codec) (func() error, error) { locks++; return func() error { releases++; return nil }, nil } + kernel = kernel.WithCodexDocumentParser(func(body []byte) (CodexDocument, error) { + body[0] = '!' + if typed { + var doc *codexParserTestDocument + return doc, nil + } + return nil, nil + }) + if _, err := kernel.Apply(codexParserRequest(t)); err == nil { + t.Fatal("nil document admitted") + } + if string(files.body) != "original snapshot" || files.writes != 0 || locks != 1 || releases != 1 { + t.Fatalf("parser aliased snapshot or leaked lock: body=%q writes=%d lock=%d release=%d", files.body, files.writes, locks, releases) + } + }) + } +} + +func TestProjectCodexParserRawEntriesDoNotAliasNeutralDocument(t *testing.T) { + raw := json.RawMessage(`{"url":"https://inert.test/mcp","large":9007199254740993}`) + parser := &codexParserTestDocument{entries: map[string]json.RawMessage{"foreign": raw}} + kernel := New().WithCodexDocumentParser(func([]byte) (CodexDocument, error) { return parser, nil }) + doc, err := kernel.parseDocument(nil, false, CodecCodex) + if err != nil { + t.Fatal(err) + } + raw[0] = '!' + entries, err := doc.codexEntries() + if err != nil || !bytes.Contains(entries["foreign"], []byte("9007199254740993")) || !json.Valid(entries["foreign"]) { + t.Fatalf("raw parser alias/number loss: %q %v", entries["foreign"], err) + } +} diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/codex_server.go b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_server.go new file mode 100644 index 000000000..2419ab390 --- /dev/null +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/codex_server.go @@ -0,0 +1,35 @@ +package nativeconfig + +import ( + "fmt" + "strings" +) + +func projectCodexServer(server Server, placeholders Placeholders) (map[string]any, error) { + if server.OpenCodeV2 != nil { + return nil, fmt.Errorf("codex does not accept OpenCode V2 options") + } + if strings.EqualFold(strings.TrimSpace(server.Type), "remote") && server.RemoteTransport == "streamable-http" { + server.RemoteTransport = "" + } + projected, err := projectBaseServer(CodecMCPServers, server, placeholders) + if err != nil { + return nil, err + } + if headers, ok := projected["headers"]; ok { + delete(projected, "headers") + projected["http_headers"] = headers + } + if server.CodexAuth != nil { + if server.Type != "remote" { + return nil, fmt.Errorf("codex stdio servers do not accept remote authentication options") + } + if server.CodexAuth.BearerTokenEnvVar != "" { + projected["bearer_token_env_var"] = server.CodexAuth.BearerTokenEnvVar + } + if server.CodexAuth.OAuthResource != "" { + projected["oauth_resource"] = server.CodexAuth.OAuthResource + } + } + return projected, nil +} diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/document.go b/install/integrationctl/agentplugins/adapters/nativeconfig/document.go index 598195707..895719597 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/document.go +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/document.go @@ -12,11 +12,11 @@ import ( ) type document struct { - ast hujson.Value - root *hujson.Object - jsonc bool - commentsOnly bool - codexTOML *codexTOMLDocument + ast hujson.Value + root *hujson.Object + jsonc bool + commentsOnly bool + codexDocument CodexDocument } func parseDocument(body []byte, jsonc bool) (*document, error) { @@ -37,11 +37,11 @@ func parseDocument(body []byte, jsonc bool) (*document, error) { return &document{ast: ast, root: root, jsonc: jsonc}, nil } -func newDocument(jsonc bool, codec Codec) (*document, error) { +func (kernel Kernel) newDocument(jsonc bool, codec Codec) (*document, error) { if codec == CodecCodex { - return parseCodecDocument(nil, jsonc, codec) + return kernel.parseDocument(nil, jsonc, codec) } - return parseCodecDocument([]byte("{}\n"), jsonc, codec) + return kernel.parseDocument([]byte("{}\n"), jsonc, codec) } func rejectDuplicateKeys(value *hujson.Value, path string) error { @@ -153,8 +153,8 @@ func removeEntry(obj *hujson.Object, name string) bool { } func (doc *document) render() ([]byte, error) { - if doc.codexTOML != nil { - return doc.renderCodexTOML() + if doc.codexDocument != nil { + return doc.renderCodexDocument() } if doc.commentsOnly { geminiRemoveTrailingCommas(&doc.ast) diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/gemini_document.go b/install/integrationctl/agentplugins/adapters/nativeconfig/gemini_document.go index 93b2dd4b7..36d8d4de6 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/gemini_document.go +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/gemini_document.go @@ -11,7 +11,7 @@ import ( // managed entry mutations preserve comments and unrelated configuration. func parseCodecDocument(body []byte, jsonc bool, codec Codec) (*document, error) { if codec == CodecCodex { - return parseCodexTOML(body, jsonc) + return nil, fmt.Errorf("codex TOML document parser is required") } if codec != CodecGemini || jsonc { return parseDocument(body, jsonc) diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/kernel.go b/install/integrationctl/agentplugins/adapters/nativeconfig/kernel.go index 849234f93..9c585f511 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/kernel.go +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/kernel.go @@ -60,6 +60,9 @@ func (kernel Kernel) ApplyBatch(requests []Request) (receipts []Receipt, err err return nil, fmt.Errorf("batched native config requests must share paths and codec") } } + if err := kernel.requireDocumentParser(requests[0].Codec); err != nil { + return nil, err + } acquireLocks := kernel.acquireLocks if acquireLocks == nil { acquireLocks = kernel.acquireCandidateLocks @@ -92,9 +95,11 @@ func (kernel Kernel) ApplyBatch(requests []Request) (receipts []Receipt, err err if err != nil { return nil, err } - doc, err := newDocument(file.jsonc, requests[0].Codec) + var doc *document if file.exists { - doc, err = parseCodecDocument(file.body, file.jsonc, requests[0].Codec) + doc, err = kernel.parseDocument(file.body, file.jsonc, requests[0].Codec) + } else { + doc, err = kernel.newDocument(file.jsonc, requests[0].Codec) } if err != nil { return nil, err @@ -247,11 +252,14 @@ func (kernel Kernel) Inspect(paths Paths, codec Codec, name string, owned *Recei if err := validateRequest(probe); err != nil { return false, false, err } + if err := kernel.requireDocumentParser(codec); err != nil { + return false, false, err + } file, err := kernel.resolve(paths) if err != nil || !file.exists { return false, false, err } - doc, err := parseCodecDocument(file.body, file.jsonc, codec) + doc, err := kernel.parseDocument(file.body, file.jsonc, codec) if err != nil { return false, false, err } diff --git a/install/integrationctl/agentplugins/adapters/nativeconfig/types.go b/install/integrationctl/agentplugins/adapters/nativeconfig/types.go index fb0aa6c43..3651fca87 100644 --- a/install/integrationctl/agentplugins/adapters/nativeconfig/types.go +++ b/install/integrationctl/agentplugins/adapters/nativeconfig/types.go @@ -175,6 +175,7 @@ type lockAcquirer func(Paths, Codec) (func() error, error) type Kernel struct { files FileIO acquireLocks lockAcquirer + codexParser CodexDocumentParser } func New() Kernel { return Kernel{files: conditionalOSFiles{}} } diff --git a/install/integrationctl/agentplugins/clients/codex/project_identity.go b/install/integrationctl/agentplugins/clients/codex/project_identity.go index c391ab4c1..a88b71e32 100644 --- a/install/integrationctl/agentplugins/clients/codex/project_identity.go +++ b/install/integrationctl/agentplugins/clients/codex/project_identity.go @@ -3,6 +3,7 @@ package codex import ( "fmt" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/codextoml" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/clients" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/domain" @@ -31,12 +32,12 @@ func inspectCodexProject(plan domain.DeliveryPlan, managed *domain.ClientBinding selected.NativeObjects = codexProjectOwned(managed.NativeObjects) managed = &selected } - return codexProjectLayout(plan.NativeRegistryRoot).Inspect(plan.Components, managed, kernel) + return codexProjectLayout(plan.NativeRegistryRoot).Inspect(plan.Components, managed, kernel.WithCodexDocumentParser(codextoml.Parse)) } func verifyCodexProjectObjects(root string, objects []domain.NativeObjectOwnership, allowMissing bool, kernel nativeconfig.Kernel) error { if err := validateCodexProjectObjects(root, objects); err != nil { return err } - return codexProjectLayout(root).Verify(codexProjectOwned(objects), allowMissing, kernel) + return codexProjectLayout(root).Verify(codexProjectOwned(objects), allowMissing, kernel.WithCodexDocumentParser(codextoml.Parse)) } diff --git a/install/integrationctl/agentplugins/clients/codex/project_lifecycle.go b/install/integrationctl/agentplugins/clients/codex/project_lifecycle.go index aa3715a58..6ce3cecd2 100644 --- a/install/integrationctl/agentplugins/clients/codex/project_lifecycle.go +++ b/install/integrationctl/agentplugins/clients/codex/project_lifecycle.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/codextoml" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/clients" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/clients/shared" @@ -64,7 +65,7 @@ func mutateCodexProject(ctx context.Context, env clients.Env, root, active strin servers[object.LogicalName] = server } return (shared.ProjectComponentMutation{Layout: codexProjectLayout(root), ActivePath: active, - Previous: codexProjectOwned(previous), Desired: codexProjectOwned(desired), Servers: servers, Kernel: env.NativeConfig}).Apply(ctx) + Previous: codexProjectOwned(previous), Desired: codexProjectOwned(desired), Servers: servers, Kernel: env.NativeConfig.WithCodexDocumentParser(codextoml.Parse)}).Apply(ctx) } func deactivateCodexProject(ctx context.Context, env clients.Env, request domain.DeactivationRequest) (domain.DeactivationOutcome, error) { diff --git a/install/integrationctl/agentplugins/clients/codex/project_status.go b/install/integrationctl/agentplugins/clients/codex/project_status.go index 7192bfccb..e156715aa 100644 --- a/install/integrationctl/agentplugins/clients/codex/project_status.go +++ b/install/integrationctl/agentplugins/clients/codex/project_status.go @@ -6,6 +6,7 @@ import ( "github.com/pelletier/go-toml/v2" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/codextoml" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/clients" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/clients/shared" @@ -63,6 +64,7 @@ type codexProjectSettings struct { } func readCodexProjectSettings(kernel nativeconfig.Kernel, path string) (codexProjectSettings, error) { + kernel = kernel.WithCodexDocumentParser(codextoml.Parse) var settings codexProjectSettings if _, _, err := kernel.Inspect(nativeconfig.Paths{JSON: path}, nativeconfig.CodecCodex, "agentplugins-policy-inspection", nil); err != nil { return settings, err diff --git a/install/integrationctl/agentplugins/providers/project_codex_fixture_test.go b/install/integrationctl/agentplugins/providers/project_codex_fixture_test.go index 562717985..bc4569032 100644 --- a/install/integrationctl/agentplugins/providers/project_codex_fixture_test.go +++ b/install/integrationctl/agentplugins/providers/project_codex_fixture_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/777genius/plugin-kit-ai/install/integrationctl/adapters/pathpolicy" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/codextoml" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/loader" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/specregistry" @@ -58,7 +59,7 @@ func newProjectCodexFixture(t *testing.T) projectCodexFixture { if err != nil { t.Fatal(err) } - f.kernel = nativeconfig.New() + f.kernel = nativeconfig.New().WithCodexDocumentParser(codextoml.Parse) f.activator = providers.Activator{Registry: f.registry, NativeConfig: &f.kernel} return f } From 5a7e0ab107d1d56fdd417f28a722b2037c45ba2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D0=B8=D1=8F?= Date: Sat, 24 Oct 2026 12:20:38 +0300 Subject: [PATCH 4/7] test(codex): preserve parser recovery fixture correction and fresh verification --- .../codex-parser-recovery-evidence.md | 21 +++++++++++++++++++ .../adapters/codextoml/lifecycle_test.go | 20 +++++++++--------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md b/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md index 7a6df0353..2824a7a6b 100644 --- a/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md +++ b/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md @@ -31,3 +31,24 @@ independent implementation reviews remain pending and must close before root publishes/integrates this correction. No vendor/native, model, trust or MCP process ran during source reconstruction. Exact Codex authority/public SDK/CLI recovery is a separate required reviewed slice, not completed by this extraction. + +## Fresh first execution and concrete fixture correction + +At initial sourcefff4b87f, unchanged TestContractLayerDependencyClosure actually +passed0.021s and complete nativeconfig passed4.013s. The moved backend test +initially failed compilation because mechanical type qualification also +qualified struct field labels/request selectors; both reviewers independently +reported that same concrete blocker. Only labels/selectors were corrected to +their original spelling, retaining qualified RHS types and all assertions. + +The initial full preserved Codex client attempt also failed on duplicate ambient +uppercase/lowercase HTTP_PROXY/HTTPS_PROXY/NO_PROXY variable names. The production +validator was not changed. A new run removed only the conflicting lowercase +ambient entries for that test process, preserving explicit fixture validation. + +After the test-only qualification correction, complete concrete codextoml tests +passed0.053s, full preserved Codex client passed0.181s, and all ProjectCodex +provider contracts passed0.733s. These are new workspace executions with the +verified Go1.26.10 toolchain/caches and one coordinated GOMAXPROCS2/-p2 process. +Failed compile/environment attempts are not passes. Lint and final independent +review closures remain pending; no native/client/model/MCP/trust execution. diff --git a/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go b/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go index ec5a5e22e..639889fd6 100644 --- a/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go +++ b/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go @@ -24,18 +24,18 @@ func TestProjectCodexTOMLExactLifecyclePreservesForeignData(t *testing.T) { before := "# preserve native settings\ncreated = 2026-10-23\n" + collection + "\n[foreign]\nkeep = [1, 2, 3] # foreign comment\n" mustWrite(t, path, before) kernel := newKernel() - request := nativeconfig.Request{nativeconfig.Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, nativeconfig.Action: nativeconfig.ActionAdd, Name: "managed", nativeconfig.Server: nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp", RemoteTransport: "streamable-http", Headers: map[string]string{"X-Fixture": "inert"}}} + request := nativeconfig.Request{Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, Action: nativeconfig.ActionAdd, Name: "managed", Server: nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp", RemoteTransport: "streamable-http", Headers: map[string]string{"X-Fixture": "inert"}}} receipt, err := kernel.Apply(request) if err != nil { t.Fatal(err) } - present, owned, err := kernel.Inspect(request.nativeconfig.Paths, request.Codec, request.Name, &receipt) + present, owned, err := kernel.Inspect(request.Paths, request.Codec, request.Name, &receipt) if err != nil || !present || !owned { t.Fatalf("exact Codex entry = %v/%v, %v", present, owned, err) } projectCodexAssertForeignTOML(t, path, before) - request.nativeconfig.Action, request.Owned = nativeconfig.ActionUpdate, &receipt - request.nativeconfig.Server = nativeconfig.Server{Type: "stdio", Command: "TEST-never-executed", Args: []string{"fixture"}, Env: map[string]string{"FIXTURE": "inert"}, CWD: filepath.Dir(path)} + request.Action, request.Owned = nativeconfig.ActionUpdate, &receipt + request.Server = nativeconfig.Server{Type: "stdio", Command: "TEST-never-executed", Args: []string{"fixture"}, Env: map[string]string{"FIXTURE": "inert"}, CWD: filepath.Dir(path)} receipt, err = kernel.Apply(request) if err != nil { t.Fatal(err) @@ -46,12 +46,12 @@ func TestProjectCodexTOMLExactLifecyclePreservesForeignData(t *testing.T) { t.Fatal(err) } assertBytes(t, path, string(unchanged)) - request.nativeconfig.Action = nativeconfig.ActionRemove + request.Action = nativeconfig.ActionRemove if _, err := kernel.Apply(request); err != nil { t.Fatal(err) } projectCodexAssertForeignTOML(t, path, before) - if present, _, err := kernel.Inspect(request.nativeconfig.Paths, request.Codec, request.Name, nil); err != nil || present { + if present, _, err := kernel.Inspect(request.Paths, request.Codec, request.Name, nil); err != nil || present { t.Fatalf("managed Codex entry survived: %v, %v", present, err) } }) @@ -91,7 +91,7 @@ func TestProjectCodexTOMLRejectsMalformedCollisionAndForeignEdit(t *testing.T) { t.Run(name, func(t *testing.T) { path := filepath.Join(t.TempDir(), "config.toml") mustWrite(t, path, body) - request := nativeconfig.Request{nativeconfig.Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, nativeconfig.Action: nativeconfig.ActionAdd, Name: "managed", nativeconfig.Server: nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp"}} + request := nativeconfig.Request{Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, Action: nativeconfig.ActionAdd, Name: "managed", Server: nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp"}} if _, err := newKernel().Apply(request); !errors.Is(err, nativeconfig.ErrMalformed) { t.Fatalf("invalid TOML admitted: %v", err) } @@ -99,7 +99,7 @@ func TestProjectCodexTOMLRejectsMalformedCollisionAndForeignEdit(t *testing.T) { }) } path := filepath.Join(t.TempDir(), "config.toml") - request := nativeconfig.Request{nativeconfig.Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, nativeconfig.Action: nativeconfig.ActionAdd, Name: "managed", nativeconfig.Server: nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp"}} + request := nativeconfig.Request{Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, Action: nativeconfig.ActionAdd, Name: "managed", Server: nativeconfig.Server{Type: "remote", URL: "https://managed.test/mcp"}} receipt, err := newKernel().Apply(request) if err != nil { t.Fatal(err) @@ -110,7 +110,7 @@ func TestProjectCodexTOMLRejectsMalformedCollisionAndForeignEdit(t *testing.T) { const edited = "[mcp_servers.managed]\nurl='https://managed.test/mcp'\nenabled=false # foreign policy\n" mustWrite(t, path, edited) for _, action := range []nativeconfig.Action{nativeconfig.ActionUpdate, nativeconfig.ActionRemove} { - request.nativeconfig.Action, request.Owned = action, &receipt + request.Action, request.Owned = action, &receipt if _, err := newKernel().Apply(request); !errors.Is(err, nativeconfig.ErrNotOwned) { t.Fatalf("edited native entry accepted by %s: %v", action, err) } @@ -127,7 +127,7 @@ func TestProjectCodexTOMLRemovesCompleteNestedOwnedEntry(t *testing.T) { if err != nil { t.Fatal(err) } - if _, err := newKernel().Apply(nativeconfig.Request{nativeconfig.Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, nativeconfig.Action: nativeconfig.ActionRemove, Name: "managed", Owned: &receipt}); err != nil { + if _, err := newKernel().Apply(nativeconfig.Request{Paths: nativeconfig.Paths{JSON: path}, Codec: nativeconfig.CodecCodex, Action: nativeconfig.ActionRemove, Name: "managed", Owned: &receipt}); err != nil { t.Fatal(err) } actual, _ := os.ReadFile(path) From 18837e79d6753d062fc4281f5b062245907d9320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D0=B8=D1=8F?= Date: Sat, 24 Oct 2026 12:22:30 +0300 Subject: [PATCH 5/7] fix(codex): close concrete parser lint findings and preserve fresh validation --- .../codex-parser-recovery-evidence.md | 19 +++++++++++++++++++ .../adapters/codextoml/document.go | 3 ++- .../agentplugins/adapters/codextoml/edit.go | 7 ++++--- .../agentplugins/adapters/codextoml/json.go | 17 +++++++++-------- .../adapters/codextoml/lifecycle_test.go | 3 ++- 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md b/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md index 2824a7a6b..9bcd02dd5 100644 --- a/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md +++ b/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md @@ -52,3 +52,22 @@ provider contracts passed0.733s. These are new workspace executions with the verified Go1.26.10 toolchain/caches and one coordinated GOMAXPROCS2/-p2 process. Failed compile/environment attempts are not passes. Lint and final independent review closures remain pending; no native/client/model/MCP/trust execution. + +## Fresh lint closure and narrow helper correction + +Pinned golangci-lint2.13.2 initially reported three new concrete-backend import +grouping issues and gocritic's pointer-to-map parameter warning. Those are +retained failures. The helper now returns its decoded map explicitly; its +UseNumber, trailing-input refusal and recursive exact conversion are unchanged. +Only the caller signature and import groups changed. Complete concrete-backend +tests passed again0.073s after this correction. + +The initial lint also emitted cache-write warnings because its default cache was +under read-only /root. Subsequent actual gates explicitly used the writable +workspace GOLANGCI_LINT_CACHE. The exact changed-code gate fromc50956de over +nativeconfig/codextoml/Codex/providers passed0issues. The separate full-file +revive/funlen/gocyclo/gocognit/dupl/depguard gate over those same packages also +passed0issues. No lint baseline, architecture snapshot or dependency policy was +changed. Fresh test results above remain attributed to their actual source +checkpoints; no vendor/native/model/trust/MCP process executed. Two final fresh +independent code-review closures are now requested on this corrected freeze. diff --git a/install/integrationctl/agentplugins/adapters/codextoml/document.go b/install/integrationctl/agentplugins/adapters/codextoml/document.go index 34cad98e8..186461b1d 100644 --- a/install/integrationctl/agentplugins/adapters/codextoml/document.go +++ b/install/integrationctl/agentplugins/adapters/codextoml/document.go @@ -7,9 +7,10 @@ import ( "reflect" "sort" - "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" "github.com/creachadair/tomledit" "github.com/pelletier/go-toml/v2" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" ) type document struct { diff --git a/install/integrationctl/agentplugins/adapters/codextoml/edit.go b/install/integrationctl/agentplugins/adapters/codextoml/edit.go index d943ecb1c..de34b55f8 100644 --- a/install/integrationctl/agentplugins/adapters/codextoml/edit.go +++ b/install/integrationctl/agentplugins/adapters/codextoml/edit.go @@ -4,11 +4,12 @@ import ( "bytes" "fmt" - "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" "github.com/creachadair/tomledit" "github.com/creachadair/tomledit/parser" "github.com/creachadair/tomledit/transform" "github.com/pelletier/go-toml/v2" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" ) func removeCodexTOMLEntry(doc *tomledit.Document, name string) { @@ -26,8 +27,8 @@ func removeCodexTOMLEntry(doc *tomledit.Document, name string) { } func setCodexTOMLEntry(doc *tomledit.Document, name string, body []byte) error { - var value map[string]any - if err := decodeTOMLJSON(body, &value); err != nil || value == nil { + value, err := decodeTOMLJSON(body) + if err != nil || value == nil { return fmt.Errorf("%w: Codex MCP entry must be a table", nativeconfig.ErrMalformed) } var encoded bytes.Buffer diff --git a/install/integrationctl/agentplugins/adapters/codextoml/json.go b/install/integrationctl/agentplugins/adapters/codextoml/json.go index 165cb940b..b8399ad65 100644 --- a/install/integrationctl/agentplugins/adapters/codextoml/json.go +++ b/install/integrationctl/agentplugins/adapters/codextoml/json.go @@ -9,21 +9,22 @@ import ( "strings" ) -func decodeTOMLJSON(body []byte, result *map[string]any) error { +func decodeTOMLJSON(body []byte) (map[string]any, error) { + var result map[string]any decoder := json.NewDecoder(bytes.NewReader(body)) decoder.UseNumber() - if err := decoder.Decode(result); err != nil { - return err + if err := decoder.Decode(&result); err != nil { + return nil, err } if _, err := decoder.Token(); err != io.EOF { - return fmt.Errorf("codex MCP entry has trailing JSON") + return nil, fmt.Errorf("codex MCP entry has trailing JSON") } - converted, err := tomlJSONValue(*result) + converted, err := tomlJSONValue(result) if err != nil { - return err + return nil, err } - *result, _ = converted.(map[string]any) - return nil + result, _ = converted.(map[string]any) + return result, nil } func tomlJSONValue(value any) (any, error) { diff --git a/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go b/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go index 639889fd6..f81e66054 100644 --- a/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go +++ b/install/integrationctl/agentplugins/adapters/codextoml/lifecycle_test.go @@ -8,8 +8,9 @@ import ( "strings" "testing" - "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" "github.com/pelletier/go-toml/v2" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" ) func TestProjectCodexTOMLExactLifecyclePreservesForeignData(t *testing.T) { From 40387fc85d227d81823b652d1e61aa38416aeb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D0=B8=D1=8F?= Date: Sat, 24 Oct 2026 12:23:57 +0300 Subject: [PATCH 6/7] docs(codex): preserve two final concrete parser implementation approvals --- .../codex-parser-recovery-evidence.md | 10 +++++++++ .../codex-parser-recovery-review-r1.md | 21 +++++++++++++++++++ .../codex-parser-recovery-review-r2.md | 21 +++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md b/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md index 9bcd02dd5..e3c675f1b 100644 --- a/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md +++ b/docs/plans/project-install-e2e/codex-parser-recovery-evidence.md @@ -71,3 +71,13 @@ passed0issues. No lint baseline, architecture snapshot or dependency policy was changed. Fresh test results above remain attributed to their actual source checkpoints; no vendor/native/model/trust/MCP process executed. Two final fresh independent code-review closures are now requested on this corrected freeze. + +## Two fresh final implementation reviews + +Both independent reviewers issued final GO at5708e8616bf6aea740e293c5d5929d845c884683 +after inspecting the exact narrow corrections and source-attributed actual gate +results. Their fresh reports are committed beside this evidence. This final +checkpoint adds documentation only; root may publish/integrate the approved +architecture extraction and rerun the unchanged assembled contract closure. +C2 authority, public lifecycle and genuine current-source native gates remain +required, and no prior native pass is relabeled. diff --git a/docs/plans/project-install-e2e/codex-parser-recovery-review-r1.md b/docs/plans/project-install-e2e/codex-parser-recovery-review-r1.md index d86a0b80b..638e64a8c 100644 --- a/docs/plans/project-install-e2e/codex-parser-recovery-review-r1.md +++ b/docs/plans/project-install-e2e/codex-parser-recovery-review-r1.md @@ -10,3 +10,24 @@ Explicit four Codex composition boundaries and complete consumer/seam/unchanged architecture tests are meaningful. Known exact authority recovery remains a separate required slice; no earlier native pass is relabeled. No blocker, source edit, Go or native process by this review. Fresh code reviews required. + +## Fresh final reconstructed implementation closure + +Reviewer: finish_sources_vscode_cline. Fresh independent final code GO at +5708e8616bf6aea740e293c5d5929d845c884683. + +The full production extraction review atfff4 stands. Exactfff..5708 closes the +moved-test field/selector qualification blocker without losing assertions. The +explicit decoded-map return retains UseNumber, trailing refusal and recursive +exact-integer conversion; remaining source edits are import grouping. Missing +parser/typed-nil/copy boundaries, preserved I/O/locks and four explicit concrete +composition boundaries remain sound. + +Actual unchanged closure PASS0.021s/full nativeconfig4.013s atfff4; corrected +full backend0.053s/Codex0.181s/ProjectCodex providers0.733s at478e; final helper +backend0.073s and both pinned lint gates0issues close current verification. +Initial compile/proxy/cache/lint failures are honestly retained. No guard, +snapshot, platform sweep or dependency policy was widened. No duplicate reviewer +Go/native execution. This GO qualifies architecture extraction only; exact C2 +authority, public SDK/CLI and current native/assembled qualification remain +separate required gates. diff --git a/docs/plans/project-install-e2e/codex-parser-recovery-review-r2.md b/docs/plans/project-install-e2e/codex-parser-recovery-review-r2.md index 24c3d4994..015d94130 100644 --- a/docs/plans/project-install-e2e/codex-parser-recovery-review-r2.md +++ b/docs/plans/project-install-e2e/codex-parser-recovery-review-r2.md @@ -10,3 +10,24 @@ signed integers. No implicit/global backend or lost capability. Authority/SDK/ CLI restoration remains explicitly separate. Actual architecture test package, meaningful seam/backend/Codex/provider/lint gates and two fresh code GOs remain required. No blocking finding, source edit, Go or native execution by reviewer. + +## Fresh final reconstructed implementation closure + +Reviewer: finish_grok_integration. Fresh independent final code GO at +5708e8616bf6aea740e293c5d5929d845c884683. + +The full production extraction review atfff4 stands. The concrete moved-test +namespace blocker is fixed with all old assertions retained. Explicit mapreturn +does not change exact decoding/refusal semantics. Configured kernel copies +preserve injected I/O/locks; missing parser and literal/typed-nil guards, input/ +raw/no-op copy isolation and pure DesiredReceipt remain coherent. All four +Codex composition boundaries supply the concrete backend explicitly. + +Actual unchanged closure PASS0.021s/full nativeconfig4.013s atfff4; corrected +full backend0.053s/Codex0.181s/ProjectCodex providers0.733s at478e; final helper +backend0.073s and both pinned lint gates0issues close current verification. +Initial compile/proxy/cache/lint failures are honestly retained. No guard, +snapshot, platform sweep or dependency policy was widened. No duplicate reviewer +Go/native execution. This GO qualifies architecture extraction only; exact C2 +authority, public SDK/CLI and current native/assembled qualification remain +separate required gates. From e630a5b0ac1cd94443cf710f93a79acb8274ac38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D0=B8=D1=8F?= Date: Sat, 24 Oct 2026 12:25:49 +0300 Subject: [PATCH 7/7] docs(codex): preserve exact project authority recovery plan --- .../codex-authority-recovery-plan.md | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 docs/plans/project-install-e2e/codex-authority-recovery-plan.md diff --git a/docs/plans/project-install-e2e/codex-authority-recovery-plan.md b/docs/plans/project-install-e2e/codex-authority-recovery-plan.md new file mode 100644 index 000000000..5522451f4 --- /dev/null +++ b/docs/plans/project-install-e2e/codex-authority-recovery-plan.md @@ -0,0 +1,91 @@ +# Codex exact project authority recovery — fresh amendment C2 + +The reset erased finald8daad5a including exact authority corrections and public +lifecycle fixtures. C1 is now independently reviewed with current tests at +5708e861 and reports8c4e1cec. C2 restores the known separate obligations against +that concrete parser seam. Historical GOs/native passes are context, never proof +for reconstructed bytes. Two fresh independent plan GOs precede C2 code. + +## Exact authority before effects + +Every project candidate includes either project scope or any nonempty project +root; inconsistent user/project facts must not fall into user CLI behavior. At +Codex project boundaries require literal ClientCodex for detected client, plan, +and populated delivery; matching explicit ScopeProject on plan and context; a +validated canonical target context retaining the exact original user ConfigRoot; +nonempty exact native project root; clean portable nonempty physical artifact; +and literal deterministic managed target/active paths under selected project's +.uap/managed/clients/codex/. Reject omitted, rebound, cleaned aliases, +foreign client/profile/project/artifact and malformed paths before reads/writes +or native runner effects. Root and native object ownership rules stay strict. + +Early activation preflight legitimately occurs before staging. Accept only an +entirely zero/unstaged delivery there, after validating exact plan/context; once +any delivery fact is present require full exact delivery client, active path and +owned base. Actual Activate always requires populated exact delivery. Validate +ActivationComplete refusal before any operational effect. Readiness remains +prepared with independently observed installed native configuration, never +automatically active/native discovery/trust or attested activation. + +Prepared inspection must construct complete client/scope/artifact/native-root/ +active-locator binding facts rather than relying on missing-field tolerance. +A real managed project binding requires literal client/scope/artifact/root and +target locator equality against the validated plan; validate native objects as +before. Confirmed project removal similarly requires exact recorded client, +artifact, selected original profile/project/native root and deterministic managed +artifact path before mutation, independent of current executable/version. +No current user-scope behavior or legacy marketplace evidence is broadened. + +## Physical stage and current stdio reuse + +Before ProjectOpenAIMCP writes anything, validate project staging as an existing +clean absolute physical nonalias directory strictly inside the selected project +and its managed target area, separate from active/native source/watch locations. +Validate all existing ancestors using no-follow containment; do not silently +canonicalize caller input. Refuse project root, original user profile, project B, +external source, active-path alias, symlink and nonclean spelling with whole-tree +inventories unchanged. This is adapter-local, not a generic pathpolicy rewrite. + +Read-only active reconstruction requires root exactly equal to plan.ActivePath +and strict plan authority. For each selected stdio server require the current +selected data locator to be nonempty clean absolute physical project-contained +input, then exact projected PLUGIN_ROOT==plan.ActivePath and +PLUGIN_DATA==current data locator. Omitted/relative/rebound data or either stale +package/data projection returns errors wrapping existing +domain.ErrProjectionRefreshRequired, with zero writes; installer can reproject +from the exact pinned package. Remote-only and skill-only reuse remains valid +without an artificial data requirement. Keep all authentication-reference and +transport validation. Never execute the inert MCP command. + +## Fresh composed proof and boundaries + +Direct adapter and real provider tests exercise all omitted/mismatched identity, +scope/context, artifact/layout, populated-delivery, prepared-binding and recorded +removal facts; assert no runner invocation and unchanged user/A/B/source/package +inventories on refusal. Positive exact project delivery must still install/ +verify/update/remove supported skill/command and stdio/remote MCP components. +Preserve malformed/collision/foreign-edited receipt and user contract tests. + +Reconstruct a real public Engine/SDK A/B journey with original user profile, +stdio persistent-data binding, unchanged sync, restart/cold pinned repair despite +moving source, explicit update and confirmed removal retaining owned data. +Reconstruct public production CLI journeys over actual disposable TLS Git and +HTTPS ZIP transports: saved manifest/lock/state, source pins and cold repair, +selected native config/skills, user/B foreign policy preservation, update/status/ +remove with retained data. Reuse existing project fixture infrastructure without +substituting fake parser/installer/provider results; host detector/security and +network endpoints may be inert controlled boundaries. Test fixture seed roots +are physically canonical before composition, retaining intentional alias +negative tests; no production normalization or platform skips. + +Run complete preserved Codex client/provider packages, affected public Engine/ +SDK and CLI journeys, unchanged architecture closure and both pinned changed +code/full-file size/arch lint. No vendor/model/auth/trust or MCP execution. +Freeze for two fresh independent code reviews, resolve findings, commit reports +and send root checkpoints immediately. C2 fixture proof does not relabel old +macOS/Windows native evidence or replace required current assembled/native gates. + +The existing project configuration readers have a known post-read size check; +this amendment does not authorize an unrelated kernel/reader redesign. Any new +concrete bounded-reader defect requiring source changes receives its own narrow +reviewed amendment rather than broadening this recovery silently.