diff --git a/cli/internal/agentpluginscli/project_grok_cycle_contract_test.go b/cli/internal/agentpluginscli/project_grok_cycle_contract_test.go new file mode 100644 index 000000000..67fbd5224 --- /dev/null +++ b/cli/internal/agentpluginscli/project_grok_cycle_contract_test.go @@ -0,0 +1,187 @@ +//go:build linux + +package agentpluginscli + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/adapters/dirswap" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/processlock" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/statev2" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/domain" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/installer" + clientplanner "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/planner" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/plannertest" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/providers" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/providerstest" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/transaction" + legacyports "github.com/777genius/plugin-kit-ai/install/integrationctl/ports" +) + +type cycleGrokCLIRunner struct{ project, executable string } + +func (r cycleGrokCLIRunner) Run(_ context.Context, command legacyports.Command) (legacyports.CommandResult, error) { + if command.Dir != r.project || !reflect.DeepEqual(command.Argv, []string{r.executable, "inspect", "--json"}) { + return legacyports.CommandResult{}, fmt.Errorf("unregistered inert Grok CLI command") + } + plugins := []any{} + root := filepath.Join(r.project, ".grok", "plugins") + entries, err := os.ReadDir(root) + if err != nil && !os.IsNotExist(err) { + return legacyports.CommandResult{}, err + } + for _, entry := range entries { + path := filepath.Join(root, entry.Name()) + body, err := os.ReadFile(filepath.Join(path, ".grok-plugin", "plugin.json")) + if err != nil { + return legacyports.CommandResult{}, err + } + var manifest map[string]any + if err := json.Unmarshal(body, &manifest); err != nil { + return legacyports.CommandResult{}, err + } + plugins = append(plugins, map[string]any{"name": manifest["name"], "scope": "project", "path": path, "enabled": true}) + } + body, err := json.Marshal(map[string]any{"grokVersion": "1.0.70", "cwd": r.project, "projectRoot": r.project + "/", "projectTrusted": true, "plugins": plugins, "skills": []any{}}) + return legacyports.CommandResult{Stdout: body}, err +} + +// Exercise public Cobra routing and the real grouped lifecycle with an inert +// in-memory inspector. No vendor executable is run or qualified. +func TestProjectGrokPublicGroupedRepairRestagesIntactAndMissingPackage(t *testing.T) { + client := fixtureClient(t, domain.ClientGrok) + f := newCLIFixture(t, []domain.DetectedClient{client}) + project := filepath.Join(f.root, "project") + for _, path := range []string{project, client.ConfigRoot} { + if err := os.MkdirAll(path, 0700); err != nil { + t.Fatal(err) + } + } + client.ExecutablePath, client.Version = filepath.Join(f.root, "inert-grok"), "1.0.70" + if err := os.WriteFile(client.ExecutablePath, []byte("inert bytes; never executed\n"), 0700); err != nil { + t.Fatal(err) + } + f.app.Detector = staticDetector{clients: []domain.DetectedClient{client}} + stateRoot := filepath.Join(project, ".uap") + f.store = statev2.Store{Path: filepath.Join(stateRoot, "state-v2.json"), ProjectRoot: project} + f.app.Scope, f.app.ProjectRoot, f.app.ManagedRoot, f.app.StateStore = domain.ScopeProject, project, filepath.Join(stateRoot, "managed"), f.store + f.app.EnvironmentLockPath = filepath.Join(stateRoot, "environment.lock") + planner := plannertest.NewPlanner(clientplanner.Planner{ProjectRoot: project, ManagedRoot: f.app.ManagedRoot}) + f.app.Planner, f.app.Targets = planner, planner + runner := cycleGrokCLIRunner{project, client.ExecutablePath} + f.app.Lifecycle.ProjectRoot, f.app.Lifecycle.StateStore = project, f.store + f.app.Lifecycle.Planner, f.app.Lifecycle.Targets = planner, planner + f.app.Lifecycle.Activator = providerstest.NewActivator(providers.Activator{Runner: runner}) + f.app.Lifecycle.NativeObserver = providerstest.NewObserver(providers.NativeIdentityObserver{Runner: runner, Stager: f.app.Lifecycle.Stager}) + f.app.Lifecycle.Lock = processlock.Lock{Path: filepath.Join(stateRoot, "mutation.lock")} + f.app.Lifecycle.Kernel = transaction.Kernel{StateStore: f.store, Directory: dirswap.Manager{JournalDir: filepath.Join(stateRoot, "operations")}} + f.app.Lifecycle.PluginData = providers.PluginDataManager{Base: filepath.Join(stateRoot, "plugin-data")} + var err error + f.app.Installer, err = installer.New(installer.Config{ProjectRoot: project, StateRoot: stateRoot, Registry: f.app.ClientRegistry, Runner: runner, HelperExecutable: client.ExecutablePath, EnableNativeObserver: true, TrustedLocalPackages: true}) + if err != nil { + t.Fatal(err) + } + f.app.Lifecycle.CheckNamespace = f.app.Installer.CheckProjectNamespace + source := writeCLIPlugin(t) + skill := []byte("---\nname: guide\ndescription: Inert public repair\n---\nRecorded source.\n") + if err := os.MkdirAll(filepath.Join(source, "skills", "guide"), 0700); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(source, "skills", "guide", "SKILL.md"), skill, 0600); err != nil { + t.Fatal(err) + } + run := func(verb, selector string, mutated bool) { + t.Helper() + args := []string{verb, selector, "--scope", "project", "--project-root", project, "--target", "grok", "--format", "json"} + if verb == "add" { + args = append(args, "--no-save") + } + stdout, stderr, err := f.execute(false, args...) + if err != nil { + t.Fatalf("actual public %s: %v\n%s\n%s", verb, err, stdout, stderr) + } + var result map[string]any + if err := json.Unmarshal([]byte(stdout), &result); err != nil { + t.Fatal(err) + } + data := result["data"].(map[string]any) + output := data + if verb == "add" && !mutated { + if _, exists := data["targets"]; exists || data["operation_id"] != nil { + t.Fatal("actual repeat add invented a batch/group", stdout) + } + } else { + if data["batch"] != true { + t.Fatal("actual grouped lifecycle lost batch output", stdout) + } + output = data["targets"].([]any)[0].(map[string]any)["output"].(map[string]any) + } + expectedSource := "direct local source" + if verb == "repair" || verb == "remove" { + expectedSource = "local" + } + if output["source"] != expectedSource { + t.Fatal("actual public source differs", verb, stdout) + } + out := output["result"].(map[string]any) + if out["mutated"] != mutated { + t.Fatalf("actual public %s mutated=%v want%t", verb, out["mutated"], mutated) + } + } + run("add", source, true) + state, err := f.store.Load() + if err != nil || len(state.Installations) != 1 { + t.Fatal(err) + } + i := state.Installations[0] + beforeNoop, err := os.ReadFile(f.store.Path) + if err != nil { + t.Fatal(err) + } + run("add", source, false) + run("update", i.DeclaredName, false) + afterNoop, err := os.ReadFile(f.store.Path) + if err != nil || string(beforeNoop) != string(afterNoop) { + t.Fatal("actual public no-op changed installed state", err) + } + var binding domain.ClientBinding + for _, b := range i.Clients { + binding = b + } + run("repair", i.InstallationID, true) + path := filepath.Join(binding.TargetLocator, "skills", "guide", "SKILL.md") + if err := os.Remove(path); err != nil { + t.Fatal(err) + } + before, err := os.ReadFile(f.store.Path) + if err != nil { + t.Fatal(err) + } + files := preparedClaudeCLIFiles(t, binding.TargetLocator) + _, _, err = f.execute(false, "repair", i.InstallationID, "--scope", "project", "--project-root", project, "--target", "grok", "--format", "json") + after, readErr := os.ReadFile(f.store.Path) + if err == nil || !strings.Contains(err.Error(), "native identity ownership is indeterminate") || readErr != nil || string(before) != string(after) || !reflect.DeepEqual(files, preparedClaudeCLIFiles(t, binding.TargetLocator)) { + t.Fatal("public grouped repair adopted or changed partially missing package", err, readErr) + } + if len(files) != 2 || files[filepath.Join(binding.TargetLocator, "plugin.json")] == "" || files[filepath.Join(binding.TargetLocator, ".grok-plugin", "plugin.json")] == "" { + t.Fatal("inert owned package membership changed before exact absence fault") + } + for _, relative := range []string{"plugin.json", ".grok-plugin/plugin.json", "skills/guide", "skills", ".grok-plugin", "."} { + if err := os.Remove(filepath.Join(binding.TargetLocator, filepath.FromSlash(relative))); err != nil { + t.Fatal("exact nonrecursive owned package absence fault", err) + } + } + run("repair", i.InstallationID, true) + restored, err := os.ReadFile(path) + if err != nil || string(restored) != string(skill) { + t.Fatal("public grouped repair did not restore exact missing leaf", err) + } + run("remove", i.DeclaredName, true) +} diff --git a/docs/GROK_PROJECT_BYOK_OBSERVATION_PLAN.md b/docs/GROK_PROJECT_BYOK_OBSERVATION_PLAN.md new file mode 100644 index 000000000..e7f99fa0c --- /dev/null +++ b/docs/GROK_PROJECT_BYOK_OBSERVATION_PLAN.md @@ -0,0 +1,181 @@ +# Grok installed startup through its documented custom provider + +## Current observed boundary and authority + +PR522 current source b4a2d5dea1cc2cd9a0165660924732ec6e1df811 has tree +d09f2103389f697b3c6c5afb3f01056aae247d93. Local baseline +648d79623b71efd1518d60eb02f90ffe7f34b4f3 has that exact tree, preserving +the published runtime concurrency overlay. Genuine runtime40408742120 already +passes public project add and the exact disabled/untrusted installed-candidate +inspection, then the ordinary native startup asks for vendor sign-in. That +failure proves neither loaded skills nor an unavailable custom-provider route. +All five qualification flags and vendor/model flags remain false. + +The publisher's official settings and enterprise documentation, checked on +2026-10-27, documents user-scope model. with model, base_url, env_key and +api_backend=chat_completions, plus per-model key resolution for headless use: +https://docs.x.ai/build/settings/reference +https://docs.x.ai/build/settings +https://docs.x.ai/build/enterprise +These current documents support a bounded custom-provider experiment, not an +assertion that the pinned1.0.70 binary has already accepted it. Its reported +build f50904eaee6c does not resolve in the publisher's public grok-build Git +history, so no immutable source claim is made for that build. + +The actual pinned help permits --model, --single, streaming-json and the +existing deny-all command. Preserve the exact native pin and original HOME. +This slice may configure an explicitly labelled scripted loopback provider; +it may not invent a vendor account, use a real API key, alter enterprise +requirements, grant trust, enable tools or weaken any installed ownership guard. + +## Bounded implementation after two independent plan GOs + +Add one Grok-only helper and inert contracts, with small explicit hooks in the +existing four-client attempt. No new generic provider/client framework. +Before the first native command in this fresh fixture, start a context-owned +HTTP listener on 127.0.0.1:0 and exclusively create original +HOME/.grok/config.toml. Select one fixed model uap-grok-fixture in that user +config, with model=uap-grok-fixture, the held loopback /v1 base URL, +env_key=UAP_GROK_TEST_PROVIDER_KEY, api_backend=chat_completions, +supports_backend_search=false, supports_reasoning_effort=false and max_retries=0. +Use the exact --model uap-grok-fixture on the existing native startup argv. +Keep --deny "", default permission mode, no-subagents, disable-web-search and +max-turns1 unchanged. No trust configuration is authored; the existing native +inspection must still prove the untrusted disabled Prepared candidate. + +Generate a fixture-only private random credential for that loopback endpoint. +Pass only its explicit environment variable in this Grok fixture's closed child +environment; inherit no host credentials. Do not put its value in config, +argv, command receipts or evidence. Add the newly authored config's digest to +the existing protected set before any native command. Never reset that baseline +after startup. Preserve all runtime/archive/source/installer/receipt/data guards. + +The listener accepts only POST /v1/chat/completions with the exact held bearer, +loopback peer, bounded headers and a maximum1MiB body within the original total +deadline. Permit at most one admitted request and one fixed response. Record +wrong-route/auth/method/duplicate/overlimit/invalid-JSON failures as finite +categories only, without echoing headers, credentials or exception text. +Strictly decode JSON with the existing duplicate/depth/number limits. Require +an object, exact selected model, stream=true and a nonempty bounded messages +array. Unknown request fields remain opaque captured research bytes; they are +not approved runtime grammar or accepted qualification. Do not interpret or +execute tool requests. Reply with the minimal fixed OpenAI-compatible SSE text +completion followed by [DONE], containing only a fixture sentinel independent +of installed skill contents. No tool-call response or provider-directed action. + +Capture the admitted request body and fixed response bytes under two exact +Grok-specific filenames plus one bounded provider receipt. Redact the private +fixture credential before any bytes enter f.files, including native stdout and +stderr. The receipt contains only fixed transport/model labels, request and +response counts, whether the exact test credential matched, finite error and +ordinary listener shutdown status. Never retain HTTP headers, config bodies, +real auth state or private native profile inventories. Reject any remaining +fixture credential canary before artifact serialization and in the shared +Python envelope validator. + +Tie HTTP deadlines to the existing fixture context and use bounded server +read/header/write/idle deadlines. Close the listener and active handler on every +exit, including constructor/setup/native failure; await its serve goroutine +and record actual shutdown outcome before the attempt summary is finalized. +Cleanup failure remains a test failure. Do not extend the original150/180-second +attempt budgets, native60-second command limit, command inventory, process +supervision, hosted admission or output bounds. Other three clients receive no +listener, profile, environment, argv, provider artifact or cleanup change. + +This is a first measured custom-provider capture. Preserve the existing +deliberate runtime fatal gate even if the native command exits0. No exact +native session/stream/skill-load grammar is inferred from a scripted text +response. All qualification, vendor-authentication and real-model flags remain +false. A fresh genuine hosted capture must establish whether the pinned native +client accepts this supported route and what it actually discovers before a +separate reviewed full user/A/B lifecycle and native restart/removal slice. +Grok's production project MCP remains explicitly unsupported. + +## Closed scope and verification + +Files: repotests/agentplugins_project_native_cli_attempt_test.go; one new +repotests/agentplugins_grok_project_native_provider_test.go containing the +bounded helper; one new matching provider-contract test file; existing shared +CLI argv contracts only for their exact Grok argv expectation; and +scripts/project_native_runtime.py plus scripts/test_project_native_runtime.py +for the exact Grok-only provider member/receipt/secret closure, and this plan. +Preserve all existing shared-client and failure admission cases. + +Inert actual-HTTP contracts must prove the single exact route/credential/model +exchange and fixed SSE, refusal of wrong route/auth/method/model/stream, +duplicates, malformed or duplicate-key JSON, oversized body and late/cancelled +requests, bounded cleanup after partial input/failure, and secret-canary +redaction/refusal. Verify unchanged deny-all flags, protected config bytes and +original HOME; no loopback authority is added to other clients. Python contracts +must reject incomplete/extra/misrouted provider members, inconsistent counts, +malformed finite receipt fields and unredacted canaries; all existing source, +member, failed-attempt and false-qualification checks remain required. + +After independent code review, use the exclusive Go/lint slot for focused +Grok/new-provider/shared-CLI contracts, affected all-enabled lint and unfiltered +whole-file size/architecture lint. Run the shared Python admission tests, +gofmt/whitespace checks and retained actual failed Grok envelope replay. No +vendor process executes locally. Root alone publishes to existing PR522 with +fresh actual ref and expected_sha, force=false. No new PR or release; VS Code +is deferred by the latest explicit owner instruction. + + +## Review checkpoint + +CLI R1 and desktop R2 independently approved plan SHA256 +dd85798b4871626281b79ca3e4692584a5158633b46ef403be544fe3d804ff56 +before plan commit b9a40b860061b3671fff62cb9f17be2da69ae195 and source edits. +The listener is created only after deferred finish owns cleanup, before the +first native command. The implementation additionally enforces a literal +origin-form RequestURI, held Host, single authorization/content-type values +and actual loopback peer; no unknown request field supplies qualification. + +Both critics independently approved the complete six-file implementation +manifest SHA256 7a33859c0ac4df509f0e18021b5c4a169ee41d58cd30a99ff114da585ddcfd20, +conditional on actual focused Go and both lint gates. All23 existing/new shared +Python admission contracts pass in0.023 seconds. The Go gates are pending the +exclusive shared validation slot; this checkpoint does not claim they passed. +No vendor executable has run locally. The genuine new-source first capture and +all subsequent full native lifecycle proof remain outstanding. + +## Final correction and actual local verification + +An independent critic subsequently found that whole-JSON decoding alone misses +an escaped private-key prefix in a multi-record native JSONL stream. The final +correction retains raw and whole-JSON canaries, then checks each bounded JSON +record before capture and at final admission. Go uses a Go1.22-compatible +`bytes.Cut` loop without an all-lines allocation; Python checks every Grok +member, including envelopes without a provider receipt, through bounded +`BytesIO` iteration. The existing one-MiB native member bound applies first. +Actual first/last-record escaped-prefix controls, benign JSONL and over-bound +controls cover the demonstrated gap. Both independent critics re-reviewed the +correction and approved the final bytes. + +The actual affected lint initially reported three concrete issues: the inert +route request needed an explicit context and `http.NoBody`, and connection-state +accounting needed a tagged switch. These were fixed without suppression or a +newer-than-Go1.22 API; both critics approved the exact mechanical delta. Final +provider source SHA256 is +`1cdd22201b8b18966515b92f1026fd2ee8a732bc393e4cbf900c353f11dc9d8a`, +and its contract file is +`6aeaf8af3953324b28075e042835b4875e3e66e2c780b831c4fb30bc93bba801`. +The shared Python admission source/test hashes are +`21f93c824812be17d06a4e7ce70757e3caf957d50686e4353eb0c26d2adaa568` +and `1d22830536b24de7241ecd40942ab7a5c06696288153fdc1882ff8b1bf406f98`. + +Final focused Grok/provider/shared-CLI inert Go tests passed in `1.026s`. +All23 Python admission contracts passed in `0.031s`. Actual all-enabled affected +lint and unfiltered whole-file size/complexity/architecture lint each reported +`0 issues`; gofmt and diff whitespace checks pass. Checks used the exclusive +slot, canonical Go1.26.10, bounded existing caches and direct source indexing +after the duplicate toolchain cleanup. No vendor executable ran locally. + +The actual prior Grok native envelope from source +`b4a2d5dea1cc2cd9a0165660924732ec6e1df811`, tree +`d09f2103389f697b3c6c5afb3f01056aae247d93`, also re-admitted all19 closed members +under the corrected Python code. Its inner SHA256 is +`dfa0d82412f2654e51bcfcde0c856f00763774dd77c5617b99e284f85704b354`. +The failed-attempt result, all five false qualification flags, false vendor-auth +and false real-model flags remain unchanged. These local checks qualify the +fixture correction only; genuine acceptance of the documented provider settings +and the complete native project lifecycle remain outstanding. diff --git a/docs/GROK_PROJECT_CONFIG_MUTATION_DIAGNOSTIC_PLAN.md b/docs/GROK_PROJECT_CONFIG_MUTATION_DIAGNOSTIC_PLAN.md new file mode 100644 index 000000000..0e5d4f8a0 --- /dev/null +++ b/docs/GROK_PROJECT_CONFIG_MUTATION_DIAGNOSTIC_PLAN.md @@ -0,0 +1,113 @@ +# Grok: bounded original TOML mutation diagnostic + +## Observed boundary and source authority + +Published PR522 source6f001863659da7fc9fae4302ec43c6e6a8c2f840, tree +b272f10383728fff0da86f79566e43aa2a400dc2, has the already audited genuine +run40460245073/native122046695647. The original HOME/.grok/config.toml +digest changes after a rejected provider route and canceled native startup. +The changed bytes and fields are not observed. This does not identify a +credential failure, a harmless default, or a permissible configuration rewrite. +The route observation, nested privacy and complete-JSON numeric corrections +are separately reviewed local work and must remain preserved on this lane. + +Current f.check has only the generic protected-digest fatal for this file. +configureGrokProvider exclusively authors the supported original-user recipe, +records its digest before the first native child, and keeps one held listener. +No equivalent Grok config diagnostic exists. No immutable pin1.0.70 config- +writer implementation has been established; the unresolved build identifier +and current documentation cannot supply the unknown changed setting. + +The workspace already pins pelletier/go-toml/v2 v2.3.0. Use that existing +parser and the existing held ordinary-file read mechanics; add no dependency, +product parser, account, native request or executable acquisition/execution. +This plan is diagnostic only. Root owns Grok code/publication; CLI drafted it. + +## Eligibility, original cancellation and read boundary + +Add one small Grok-only hook at the existing changed protected-file branch, +before the same unchanged fatal. Require the exact original-HOME config path, +Grok client, configured provider, recorded native-attempt use and all three +native-attempt capture members. Capture at most once, including refusals. +Do not inspect other paths/clients or alter either expected/observed digest. + +The route guard cancels the existing fixture context before this post-native +check. Context cancellation alone must not manufacture fresh execution time: +the original f.started+150s work boundary remains authoritative. If that +absolute boundary has expired, record only a fixed work-deadline refusal and +read nothing. If early provider cancellation occurs while that original work +budget remains, permit this one bounded read as part of the already-required +immutability diagnostic. Do not start a child, renew a context/deadline, retry, +change cancellation, or use the180s cleanup reserve for diagnostic work. +The original total180s check, process/cgroup cleanup and fatal remain intact. + +Hold/read at most64KiB with existing no-follow, ordinary original-user-owned, +single-link, stable-inode/metadata/parent checks and checked close behavior. +Require its SHA256 to equal the digest already observed by the fatal guard +before interpreting bytes. No Readlink, redirected HOME/profile or second read. +Keep the actual byte count and hash facts finite; never retain the TOML body. + +## Parse, independent recipe and finite projection + +Hold the exact authored configuration bytes when they are first written; +derive expectations from those bytes and held listener/model identity, never +from the changed file. Parse the held recipe and observed bounded file as +complete TOML with the existing library's duplicate/syntax checks. Export only +fixed refusal categories for parse/read/recipe/value failures, never errors. +Bound decoded structure to depth32,16384 nodes and4096 UTF8 bytes per key. + +This diagnostic supports only tables, arrays, strings, booleans, integers and +finite floats. Unsupported date/time/other values or nonfinite numbers cause a +fixed type/value refusal without hashes or partial metadata. Preserve integer +versus float categories. No TOML-to-product configuration or acceptance parser +is added. Any encoded private fixture credential in decoded keys/values must +refuse before projection or hashing, using the reviewed bounded privacy check. + +Report fixed presence/type/equality facts for exactly eight authored scalar +paths: models.default and the model.uap-grok-fixture fields model, base_url, +env_key, api_backend, supports_backend_search, supports_reasoning_effort and +max_retries. Their labels are static. Do not export any actual string/value; +in particular no API key, endpoint/port or env_key value enters evidence. +Also report presence-aware whole semantic equality and top-level field counts. + +For changed top-level members only, report total changed count and at most +three deterministic summaries sorted by member-name SHA256. Each summary has +only the name SHA, finite before/after type (including absent), and SHA256 of +standard Go encoding/json Marshal bytes for each present validated TOML value. +This declared semantic encoding is not a claim about raw TOML spelling/order. +Do not walk arbitrary leaf paths or serialize unknown keys as dynamic keys. +Before emitting any hash, refuse private canary content; hashes are never data +ownership, native success, trust or baseline authority. Bound every canonical +value to64KiB; overlimit/encoding failure emits no partial value metadata. + +Bound the complete serialized observation to2KiB, including original digest, +status, known facts and differences. Omit additional summaries deterministically +with an exact omitted count, or use a fixed no-detail bound refusal. Preserve +all original protected maps, configuration bytes, expected recipe and fatal +outcome even when semantic equality or every known-field comparison is true. + +## Actual inert controls and publication + +Exercise the real eligible held-file hook with independent TOML fixtures: +format-only rewrite, added/removed/changed table/scalar, each supported type, +integer/float distinctions, exact known recipe and changed safety flags. +Compare selected hashes to independently stated semantic byte strings, not a +copy of the implementation. Include private unknown names/values/paths and +encoded provider canaries; none may enter the complete serialized observation. +Prove strict duplicate/malformed/deep/large/unsupported-time/nonfinite refusal, +zero/many changes and stable selection under reordered keys. Verify full file +identity/bytes and original protected digests remain unchanged before/after. + +Test actual early-canceled context plus still-live original work budget, and +expired original work boundary before any read callback; no new deadline is +created. Cover wrong client/path/missing native triple, once-only behavior, +symlink/hardlink/held-digest mismatch and private read/parse errors. Preserve +the original child-fatal/profile contracts and failed all-false artifact replay. + +Two independent plan GOs precede root-owned source edits. Run focused inert +Grok/shared contracts, affected all-enabled and unchanged whole-file lint in +the exclusive slot; obtain two independent code reviews and exact tree checks. +Root alone publishes on existing PR522 using a fresh non-forced ref lease. +Review fresh genuine route and TOML observations independently before any +later fixture/policy/lifecycle amendment. No native admission or permission +change, local vendor/installer, new PR, release, VS work or qualification claim. diff --git a/docs/GROK_PROJECT_CONFIG_MUTATION_DIAGNOSTIC_VALIDATION.md b/docs/GROK_PROJECT_CONFIG_MUTATION_DIAGNOSTIC_VALIDATION.md new file mode 100644 index 000000000..1cb874dd4 --- /dev/null +++ b/docs/GROK_PROJECT_CONFIG_MUTATION_DIAGNOSTIC_VALIDATION.md @@ -0,0 +1,55 @@ +# Grok original TOML diagnostic: exact reviewed validation + +## Source and review + +The source base is published PR522 head +`84d7ec345e9e7d766aa0cc8fad72b4cbcf0aeadb`, tree +`13dccf898c2efce4b831c46aa3b6eb2a06aa4c71`. The accepted plan remains +`GROK_PROJECT_CONFIG_MUTATION_DIAGNOSTIC_PLAN.md`, SHA256 +`b539cb9ee53b7184ad5e8fcecc9348f97584b46e6787a00a3033204d6989377e`. +Its independent root R1 and shell R2 plan approvals preceded implementation. + +The exact five-file source manifest is +`5447c3a733067d95856c2cb3ff7735d135534c9d3029e1d2c058da1c882b836c`. +The retained shell review gave final CODE GO on that manifest. A fresh +independent CLI reviewer read the complete five-file delta, held-file reader, +privacy/provider integration and frontend admission, independently verified +all five hashes, and gave CODE GO without findings on 2026-10-27. + +The local code commit is `35ce606713a523a69d4a1036eacb827b9434fff9`; +its exact reviewed tree is `76127307c3399c3a9d53f8cb7dffc1f5d864beea`. +This validation record changes no reviewed source bytes. + +## Actual local gates + +The focused inert Go contracts selected by +`^(TestGrok|TestProjectNativeCLI)` passed in 1.158 seconds with Go 1.26.10. +The actual all-enabled affected-line lint gate against the source base +reported zero issues. The unchanged unfiltered whole-repotests +revive/funlen/gocyclo/gocognit/dupl/depguard gate also reported zero issues. +The exact five Go files are gofmt clean and the staged whitespace check passed. + +The first sandboxed compile was unable to write because the shared filesystem +was full; it was an environment failure, not a test result. After removing +only stale compiler outputs and disposable task caches, the same authorized +inert tests and both lint gates completed with approved execution outside the +disk-restricted sandbox. No source, evidence, module cache or toolchain was +removed. No vendor client or positive hosted-environment mock ran locally. + +## Genuine evidence and unchanged boundary + +The current genuine runtime is run `40495826839`, native job `122173276421`. +Its native artifact `12733470239` has verified outer SHA256 +`7801ec66e7813cef0f7856964a3933d44a4f4742feb9b682bcdd3c9a40ace349`. +The actual 20-member native archive replays through current Python admission +with its original failed/incomplete state and all five qualification flags +false. The installed candidate remains disabled and untrusted. The provider +record retains its rejected GET route, zero accepted requests/responses and +ordinary shutdown; no unseen request path or configuration value is inferred. + +The new diagnostic performs only a bounded, once-only observation before the +same fatal protected-file guard. It does not adopt changed bytes, renew the +original work deadline, permit an unknown setting, or qualify a native result. +Fresh source-bound hosted route/TOML evidence and its independent review remain +required before any subsequent fixture or lifecycle amendment. Root alone +owns publication to existing PR522; no release or VS Code work is included. diff --git a/docs/GROK_PROJECT_NATIVE_SHARED_CLIENT_PLAN.md b/docs/GROK_PROJECT_NATIVE_SHARED_CLIENT_PLAN.md new file mode 100644 index 000000000..7f5e46b71 --- /dev/null +++ b/docs/GROK_PROJECT_NATIVE_SHARED_CLIENT_PLAN.md @@ -0,0 +1,124 @@ +# Grok: source-defined shared HTTP client opt-out for the existing capture + +PRE-CODE proposal. Obtain two independent GPT-6.1 high/xhigh reviews before any +source edit. Read AGENTS.md, PROJECT_INSTALL_OWNER_SCOPE.md, +AUTHORING_CAPABILITY_PRESERVATION.md and the completion plan. No release or +VS Code work. Existing production behavior and strict provider admission stay +unchanged. This is one fixture configuration experiment, not native qualification. + +## New exact hosted evidence + +Current published source a1af0da362f020111a3af6a16e50cd265a708224, +tree87fb8a405c0e362fd5dad867e105523029ff7d12. Genuine runtime40579668485, +job122458819911, fails after3.99s. Public add succeeds Prepared. Native inspection +exits0 in79ms and joins the exact binding target/physical ID and project scope; +plugin enabled=false,projectTrusted=false,one provided skill,no loaded skill or +MCP server. The native --single child fails after322ms, with empty raw streams. +The strict provider observes GET and exact literal RequestURI '/', loopback peer, +matching Host, relative URL, empty URL host/raw path and absent Content-Type. +It rejects before credential/body admission, cancels the context, admits0requests/ +0responses and closes normally. The context flag is not a timeout-expiration +measurement. No Authorization value/absence, body content or prewarm identity +was captured; all5/runtime/auth/model flags remain false. + +Native12769367534:4674B,20closed members,outer +2f7a5bfca938ffe30cb60fc4f216b7825b2d41e9c5ccca29b38af009969cae12, +inner1b3ac8f968f012832421bffa9a2255b4a82f34aaa81415e29eb618ee2f3d141d. +Frontend12769462428:3386B,25members,outer +c31367f0eedf32083bed51bd4dba9aaf8ba018c886e2a1f2836fb8589e1ad72a. +Exact clean source/tree/build,24frontend stream joins and unchanged exact-source +failed admission pass root audit /tmp/uap-additional-grok-a1af-current-w3kcpcdl/report-final.json, +SHA2568e9b49eab61de2a98dcfb4ab714e5bbf604c4e765696301df731f1e109ac830d. Independently review these actual raw facts before approving this plan. + +## Primary source guidance and its limit + +Verified official xai-org/grok-build snapshot +2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8 contains the candidate startup chain: +session_setup -> sampler_prewarm -> prewarm_transport. The last function obtains +the origin from configured base_url and issues GET to that origin via a shared +uncredentialed HTTP client. Its official wire test observes GET '/'. This source +mechanism is consistent with the new actual URI, but does not prove the shipped +1.0.70/f50904eaee6c binary uses that exact implementation. That build hash does +not resolve in the public snapshot; keep the uncertainty explicit. + +The exact shared_http.rs blob e659c7fcc4eb8bba52a3c882fa57f670269c4bf4, +SHA25675e919946fb52b887f9bb0a905d8244596b0228235b1eed8313b76d50f8b5943, +defines GROK_SAMPLER_SHARED_CLIENT='0' (or case-insensitive 'false') as a +process-latched rollback to a fresh reqwest client per SamplingClient. Auth, +extra headers,baseURL andUser-Agent remain applied per request; this switch is +not an authentication or permission override. pooled_client returns SharingDisabled +before creating a pooled client, and prewarm_transport returns before its GET. +Exact prewarm.rs blob4ee8ad76886f1d28c8a7e1269623fa27e9f5fb3d, +SHA256626aa25d3515423372a812b785f7ccdc090161a3a83dd7a0f4355401602e34e5. +Official shared_http_kill_switch test blob4db871673b5c07023bc3955840397cc478bf90d2 +pins independent client construction at this snapshot; it is not a test of our +shipping binary. Source research /tmp/uap-additional-grok-first-route-research.json, +SHA256f793a0bafe287387d446ac081459fac350653269819c7a2aecc57df1260f6dd0, +retains seven independently Git-blob-verified primary source receipts. Refer to +those exact publisher URLs, not an assumed current main or a third-party account. + +## Proposed narrow fixture change + +Only in projectNativeCLIAttempt.env's existing Grok + nonnil held fixture-provider +branch, append exactly GROK_SAMPLER_SHARED_CLIENT=0 alongside the existing fixture +key. Keep the base closed environment, original HOME and updater setting intact. +The value is authored by the fixture before each child starts; never import an +ambient value or mutate process-global environment. Applying it to the existing +Grok child sequence is consistent before each separate native process's possible +once-only latch; no new command, process, request or capture stage is added. +A Grok fixture with no held provider, every other client and ordinary production +commands receive no new variable. Do not add a generic configuration API or +client-specific branch elsewhere in the production installer. + +The provider file, literal authenticated POST /v1/chat/completions route, Host, +loopback, MIME, credential, body/model, count, response, cancellation and cleanup +gates must remain byte-identical to a1af. GET '/' remains rejected. Keep original +config bytes, model selection, permissions/default deny-all, untrusted project, +no-subagents/no-websearch/max1turn flags,150second work/180second total origins, +60second native command and all acquisition/scanner/source pins unchanged. Do not +modify a provider to serve the prewarm, admit empty credentials or normalize URIs. +The failure artifact schema, shared CLI matrix and all qualification values stay +unchanged. Existing finite route diagnostics continue to describe actual failures. + +This is a source-guided control applied to the actual pinned client: a new genuine +run must determine whether it honors the opt-out and what request follows. If it +still sends GET, or rejects the setting, preserve that result and do not assume +the binary matches the publisher source. If it reaches authenticated POST and +native output, independently review those actual bytes before planning a grammar +or full lifecycle. A native response alone cannot load an untrusted disabled skill. + +## Meaningful inert verification + +Use the existing environment/protected-source fixture helpers, no native binary. +Check the concrete constructed child environment, with ambient conflicting/private +values: exact single0variable only for Grok with held provider; no ambient escape; +all original HOME/profile/fixturekey/updater values retained. Check Grok without +provider and Qoder/Kiro/Windsurf fixtures omit it, even if an irrelevant provider +pointer is supplied. Preserve existing environment/parser/provider contracts. +Do not test the Rust publisher code locally or manufacture native eligibility. + +Run focused Grok/shared Go contracts, current Python runtime admission contracts, +unchanged old a1af raw failed replay, actual affected all-enabled and unfiltered +six-linter gates, gofmt andwhitespace. Provider digest must remain +865265e28731ceee3816ab1d5c4fc137d29d859e6851675d90fbcc867f1daa88. +Verify new Go assertions do not simply compare an echoed constant: they must +cover routing by client/provider, ambient isolation, absence of duplicate variables +and retention of existing original-profile/credential values. A real inert child +is unnecessary unless existing composition fails to expose the concrete env. + +## Review and publication + +After two exact plan GOs, commit this accepted plan before the small code change +in an isolated branch from fresh agreed a1af. Two independent exact code reviews +and executed serial gates follow; root coordinates the shared validation slot. +Preserve other continuation checkouts and all existing history. Root publishes +normal expected-head fast-forward to existing PR522, then reviews the actual new +hosted result. No force, replaced PR, unchanged retry, runtime/lifecycle/auth/model +success inference, main merge, release or VS Code authorization is included. + +## Accepted before implementation + +Both independent GPT-6.1 Sol xhigh reviewers accepted the exact draft SHA256 `bd14b445e2c257e231ac9d8e609fd0326b4b6f5b877298c5a91b6e19a8e78d47`. + +- `/tmp/uap-additional-grok-shared-client-plan-R1.md`: SHA256 `98c374d5d2aff23e7af63b95feb934218e474ec4f49cf852cee13644378cf36c`. +- `/tmp/uap-grok-shared-client-plan-r2-review.json`: SHA256 `ff22fd6d0d97768937f877f611d09726c620f7f0ecd0d2d8adb039b47eb11940`. diff --git a/docs/GROK_PROJECT_NATIVE_SHARED_CLIENT_VALIDATION.md b/docs/GROK_PROJECT_NATIVE_SHARED_CLIENT_VALIDATION.md new file mode 100644 index 000000000..5057be68c --- /dev/null +++ b/docs/GROK_PROJECT_NATIVE_SHARED_CLIENT_VALIDATION.md @@ -0,0 +1,232 @@ +# Grok shared HTTP client fixture: reviewed validation + +Reviewed code: `74f72491fdbeb722e845de71217e9d1751330020`. +Tree: `53b0e7f46404658bd385b92edddea12522c247dc`. +Base: `a1af0da362f020111a3af6a16e50cd265a708224`. +Approved plan commit: `7e5e5629d3f730df7bb2ee17bf2765882cd74112`. + +Two independent GPT-6.1 Sol xhigh reviewers approved the exact plan before code +and the final implementation after inspecting executor receipts. No blocking +source findings remain. + +The existing Grok child environment receives `GROK_SAMPLER_SHARED_CLIENT=0` +only when its fixture holds the private provider. Eight concrete client/provider +cases verify routing, ambient isolation, no duplicate entries, retained original +HOME/profile/state/Git/key/updater values and no process-global mutation. + +The provider source stays byte-identical, SHA256 +`865265e28731ceee3816ab1d5c4fc137d29d859e6851675d90fbcc867f1daa88`. +Its exact authenticated POST route, request grammar, response, failure latch, +cancellation and shutdown remain unchanged. GET `/` remains rejected. No client +pin, config, trust/permission, command, timeout or qualification flag changed. + +## Actual local inert gates + +- Focused Grok/shared CLI Go contracts: PASS, package1.138 seconds. +- Python runtime admission: 38 tests PASS,5.503 seconds. +- Affected all-enabled lint: zero issues. +- Unfiltered revive/funlen/gocyclo/gocognit/dupl/depguard: zero issues. +- Original a1af native archive: all20 member bodies and the summary admitted + unchanged; failed status and allfive/runtime/auth/model false flags preserved. +- gofmt, whitespace, source-manifest and unchanged-provider checks passed. + +Only inert repository contracts and artifact admission ran locally; no vendor +binary or scanner ran. The public source supports this configuration experiment, +while equivalence to the shipping1.0.70/f50904eaee6c build remains unproved. +A new genuine disposable hosted capture must show whether the pinned binary +honors the setting and what request follows. The previous failure is not promoted +to success. This validation makes no lifecycle, authentication, model, main-merge, +release or deferred VS Code claim. + +## Exact review and execution records + +- `/tmp/uap-additional-grok-shared-client-code-R1.md`: SHA256 `15f043736ab0deadf9b985eafbb8fd7bf148e3853cb81d5631b249a06a5157cc`. +- `/tmp/uap-grok-shared-client-code-r2-review.json`: SHA256 `8e842b6b91bc559f53f47893d14ee0b7427d42b2955ef868876d46b6897ef842`. +- `/tmp/uap-additional-final-serial-gates-1d4m_qyl/report.json`: SHA256 `1ca286a35b6c73e4dc32b6ef5525741aa8b323bb726ceda86968c06c017e00de`. +- `/tmp/uap-additional-final-serial-gates-1d4m_qyl/grok-unchanged-old-replay.json`: SHA256 `82ccf6beb6b90bd35bfac3a0ef2eb983f62286a82bc99ce86c0909503b823479`. + +Local receipt paths identify this execution. The committed source, approved plan +and this validation document preserve the reviewable repository history. + +## Refused second main request: bounded diagnostic candidate + +The separate accepted amendment is committed before code at +`bb94f00bba12d59b80bdd2bfd600aa3af6e66d72`, following two independent plan GOs. +It addresses the actual `348e2ce8` capture: one first main exchange and one title +exchange completed, then an additional main HTTP attempt hit the original +duplicate refusal before its body was read. That capture remains failed and +all five qualification flags, runtime, authentication and real-model claims +remain false. Its second body and purpose are unknown. + +The candidate adds one main-only observation slot. Original context, literal +route, loopback and authentication checks run first. Under the existing mutex, +the completed first main request/response and distinct held title fixture permit +one atomic duplicate/failure reservation. Body reading and privacy validation +run outside the lock, at most 1 MiB + 1, under the unchanged five-second HTTP +read limit and original 60/150/180-second origins. No deadline is renewed. +The unconditional deferred path settles the observation and executes the +original HTTP 400 refusal and duplicate cancellation. A third or concurrent +request remains immediately refused and can cancel a blocked observer; it +cannot claim another slot, replace the first failure or increment counters. + +Only a complete bounded UTF8 strict JSON object that passes both held-key and +recursive privacy checks is retained, opaquely. No field becomes a new model, +tool, prompt or response grammar. Every refusal retains zero raw bytes. After +the original stop settles handler/connection bookkeeping, export snapshots +state under the lock, then validates and persists outside it through the +unchanged evidence/privacy guards. + +The optional fixed `grok-provider.duplicate.json` receipt is at most 2048 bytes. +Its closed fields are schema version 1, role `main-duplicate-diagnostic`, attempt +index 2, a finite status, original failure `duplicate`, false response-issued, +captured byte count and SHA256. `grok-provider.duplicate-request.json` exists only +for a captured status, within the existing 1 MiB member bound and with its exact +length/hash. Refused statuses have no raw member, zero length and empty digest. +Go and Python readers join the pair to the failed all-false tuple, original +first exchange and separate title evidence, and independently recheck structure, +privacy, member isolation and byte/hash consistency. Historical captures without +this optional pair retain their original admission. + +New inert contracts are authored for the actual loopback first/second/third HTTP +sequence, original five-second partial read, concurrent cancellation and stop, +ineligible/title/incomplete-first requests, finite JSON and both held-key privacy +refusals, plus coherent hash-updated artifact mutations. These contracts are +synthetic and have not yet been executed for this candidate. The existing +main/title schemas and response bytes, command/config/profile/process guards, +native pins, selection and workflow are unchanged. Before publication this +candidate still requires exact-source executed Go/Python/lint gates, original +capture replay and two independent CODE critics. A fresh genuine hosted capture +and independent raw audit are required to observe any new refused body; even a +safe captured body remains failed, unqualified diagnostic evidence. + +The first code review found a Unicode-scalar mismatch: Go's existing JSON +decoder replaces escaped lone UTF16 surrogates, while Python refuses their UTF8 +encoding. The new diagnostic now refuses unpaired high/low escapes and invalid +pairs, including complete nested JSON values/lines in strings or keys, under +the existing 4x text, 16384 node and 64 depth privacy envelope. Old decoders and +privacy scanners remain unchanged. Valid pairs, literal U+FFFD, raw UTF8, +escaped slash/quotes and ordinary literal backslash-u text remain data. Retained +raw bytes are never normalized. Coherent refusal and raw-preserving positive +controls are authored in both languages; exact-source execution and fresh +independent CODE review remain required. + + +## Final refused-duplicate source checks, 2026-10-28 + +Exact code `221f4a60b8d179f5e8b2ac007fa57ce010957b3b`, tree +`0a1839161a76dd50acf03301d081d0d3bd793a36`, received independent root +and Gemini CODE GOs after the Unicode correction and a one-file equivalent +contract-style correction. All diagnostic helpers, Python and original guards +are unchanged by that final style-only commit. + +The actual focused Go selection completed 56 top-level entries: 233 passed +cases and the unchanged child-only fatal-helper skip outside its child +environment. Its actual parent fatal-guard controls passed. All seven new +duplicate contract groups, including actual partial-body and concurrent HTTP +cleanup, passed. The 46 runtime and 12 Grok Python tests passed on the identical +Python scripts tree. Affected all-enabled lint and unfiltered whole six-linter +gates each reported zero issues. The first style-lint failure is retained. + +Complete original and current Python modules replayed the original 348 +25-member and 611 21-member archives under their exact original source trees; +all bytes and failed/all-false results remain unchanged. The inherited 348 +Cline capture remains independently audited. No vendor client, public installer +or scanner ran locally. This publication only adds validation documentation to +the exact reviewed code. A fresh hosted capture and two independent raw reviews +remain required to observe the refused second body. No second response or +runtime, lifecycle, MCP, authentication or model qualification is introduced. + +## Supported turn-summary suppression before original config freeze + +The fresh diagnostic run `40610932429` on source +`b40da640c388d807cc3dc1fbb36c38e0d1b411ef`, tree +`71b98e819bdd555154096c9c75850b9b40d3f54c`, retained the refused second +main-route request: 1,424 bytes, SHA256 +`303ad4817a23b68aa02717ecedb70404362cf1397324da5a4d25d12f60a86bb5`. +Two independent raw audits admitted only failed, all-false diagnostic evidence. +The system message and wrapper around the actual prompt and fixed reply exactly +match the pinned public source's per-turn dashboard-summary helper. This is a +specific measured side call; it supplies no plugin activation or runtime proof. + +The accepted plan SHA256 is +`3bc56715f4b326634f99210fc7bde69a711c2edbe7c2880fece74ec950c4593e`, +committed before implementation as +`15a2ca3b8b0396a917121d94b4e991737f4a0aec`, tree +`7962f86b94c8749ce57b5bb4776bad6596b2a207`. Root and an independent reviewer +accepted its narrow supported-setting candidate before code. + +The complete two-model TOML recipe now appends the native boolean +`[features] turn_summary = false` before the existing original HOME config's +sole write, authored-byte clone and protected digest freeze. The original HOME, +both model routes and credentials, source/config/permission/process guards and +all deadlines remain unchanged. No environment override, request allowance, +additional response, retry, profile adoption or qualification flag is added. +The independent complete-map contract adds only the expected boolean subtree; +new inert controls exercise the complete frozen recipe, changed/invalid feature +data without authority adoption, and ambient feature/model isolation. +The existing config diagnostic compares its original single-model recipe; +the two-model title recipe therefore retains its finite `recipe-mismatch` +refusal after a config mutation. No semantic diagnostic allowance is added. + +Public source tree `2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8` defines the +boolean feature, config precedence, actor spawn-time snapshot and early return +before dashboard-summary work. Unset later title refresh defaults to this same +feature, so the recipe also disables that later display-only refresh. The +separate initial-title model, request validator, fixed response and counter are +preserved. The public tree is not proven equivalent to the shipping +1.0.70/f50904eaee6c binary; the fresh genuine run must establish effectiveness. + +All main/title handlers, refused-duplicate capture and privacy/export controls, +readers, collectors, workflow, pins, production and other client source remain +preserved. Any unexpected additional request still receives the same refusal +and original cancellation. Even a clean native exit reaches the unchanged +intentional failed raw-review barrier with runtime and every qualification flag +false. Exact code reviews and measured gates are pending for this amendment; +publication and a fresh hosted observation require those checks, followed by +two independent raw audits. This record conveys no new native qualification. + +The author's bounded development selection ran the new configuration controls +and existing complete-map test: four top-level tests and ten cases passed, +with no skips (package JSON elapsed0.019seconds). The raw JSON log SHA256 is +`a3267a561a123b587f25dc8ae3015f2a883edd779c4293ab675f06e3046da66a`. +An initial new-test expectation incorrectly required semantic projection after +the unchanged single-model recipe guard; that failure is retained. Corrected +controls require its exact finite refusal, separately verify TOML/map changes, +and prove that observed bytes and protected authority are not adopted. These +are owned inert loopback/configuration checks, without a native client, public +installer or scanner. Final committed-source gates remain pending. + +## Exact suppression candidate final gates + +Implementation `a922e32f2004e81cfba2f55bf21219a2fbbe3ffe`, tree +`4cb264cd986280dd70eb3701ab4914ddafc04ca3`, received root CODE R1 GO +(receipt SHA256 `c8a911ba3838b4da5146d903ec4c13bbc0eea2ee15219982c20a7628b52577d0`) +and independent Native CODE R2 GO +(`91a82de3d74bf584f9f73b93a2def5e50fc2a817975a75243406998f04aaa8a5`). +Both reviewed all four frozen files, eighteen unchanged whole-file anchors +and the exact two literal exceptions before publication. + +The actual final focused Go selection ran59 top-level tests and243 case events: +242 passed, with only the unchanged child-only protected-fatal helper skipped +outside its child environment. Its actual format-only and migration-flag parent +fatal/guard controls passed; no new feature check was skipped. The package +completed in7.278s. Affected all-enabled lint and whole unfiltered six-linter +checks both exited0 with zero issues. Initial absent-Go-PATH setup failure and +its controlled pinned-PATH retry remain recorded; no source changed. + +Whole original/current b40 and348 raw replays preserved every closed member, +full returned result, original source identity and failed/all-false tuple. +The previous58 passing Python tests transfer through byte identity of the +entire scripts tree `91ae9d8eb2b8f0790fed890da556b6e66597a0cc`; they are not +represented as new executions. Final gate/review receipt SHA256: +`6982f66f2774fb0ec9f0217980c5b2df4453aad84e84a081e379cd7b7f966cdd`. +Whole-reader replay and Python transfer receipt SHA256: +`eea7a4d3a5e9f75e007d72699853e93f74abff604d831dea8a0e8aec8520f54d`. + +This publication adds only this validation record after the reviewed code. +A fresh genuine hosted capture must determine whether the pinned shipping +binary honors the supported setting. Two independent raw audits remain +required. Original initial-title/main/duplicate handling, source/config/pin +and ordinary cleanup guards, time budgets and the intentional all-false +review barrier are unchanged. No runtime, lifecycle, MCP, vendor-authentication +or real-model qualification follows from these inert gates. diff --git a/docs/GROK_PROJECT_OBSERVATION_RECOVERY_PLAN.md b/docs/GROK_PROJECT_OBSERVATION_RECOVERY_PLAN.md new file mode 100644 index 000000000..18f457bf6 --- /dev/null +++ b/docs/GROK_PROJECT_OBSERVATION_RECOVERY_PLAN.md @@ -0,0 +1,111 @@ +# Grok: recover reviewed observation and privacy work after scratch replacement + +## Source and retained authority + +PR522 remains published at `6f001863659da7fc9fae4302ec43c6e6a8c2f840`, +tree `b272f10383728fff0da86f79566e43aa2a400dc2`. The isolated recovery checkout +reproduces that complete tree. Preserve all published behavior and history. +The genuine run40460245073/native122046695647 failed the original provider +route and original-HOME config digest guards. No request body was admitted; +the route attributes and changed TOML members remain unknown. + +The execution environment replacement lost root's unpublished route, nested-JSON +privacy and complete-numeric-JSON correction files. Their old reviews are design +background, not exact-source approval for reconstructed bytes. Shell recovered +the final numeric-only four-file patch; desktop can recover part of the earlier +Python implementation. No lost whole-file hash or old test result is claimed +for a reconstruction. This plan requires two fresh independent plan GOs before +source reconstruction and two fresh full code reviews afterward. + +## Finite route observation + +Keep `grokProjectProviderRoute` byte-identical. When the first winning failure +is `route`, record one optional `route_observation` under the existing provider +mutex, in the same receipt. A preceding failure prevents later replacement. +Other failure paths and cancellation, request/body handling, counts and deadlines +remain unchanged. No raw request, credential, header, hostname or arbitrary URI +is added to evidence. + +The closed fields are method (`POST`, `GET`, `other`), literal request URI +(`/v1/chat/completions`, `/chat/completions`, `/v1/models`, `/models`, +`/v1/responses`, `/responses`, or `other`), and booleans for loopback peer, +matching expected host, relative URL, empty URL host, empty raw path and the +existing exact content-type predicate. Content-type count is `none`, `one`, or +`multiple`; its classification is `missing`, `json-bare`, `json-utf8`, or `other`. +Only one value of at most128 bytes is MIME-parsed for classification. JSON with +no parameters is `json-bare`; exactly a case-insensitive UTF-8 charset is +`json-utf8`. This classification never changes route admission. Multiple values +are `other`, no values are `missing`. Literal exactness still requires one +value equal to `application/json`. + +Python admits the optional exact field/type/enum set only on a started provider +whose first failure is `route`. It rejects inconsistent count/classification/ +exactness and an observation in which every original route predicate passes. +Old receipts without an observation remain admitted unchanged and unqualified. +Use one small new Go route helper/contracts file and narrow receipt/failure +callsites, keeping the original existing provider source below its size gate. + +## Bounded nested privacy and complete-number correction + +Replace the published one-layer JSON scan with the already reviewed finite +design: valid UTF-8, existing1MiB member limit, literal private prefix check, +and one shared budget of four times whole-member bytes,16384 visited nodes and +depth64 across nested strings, keys, arrays, objects, siblings and JSONL lines. +Use the existing strict JSON decoder, retaining its depth32/key4096 and +duplicate/nonfinite/resource refusal. Every extra parse consumes that same +byte budget. Exact-key redaction still happens before final refusal and before +evidence storage/base64 encoding. Do not decode URLs, base64 or arbitrary +substrings and do not change another client's privacy implementation. + +Only a static nonfinite-number error can invoke the extra complete-syntax +decision. Go performs two `json.RawMessage` decodes and requires exact EOF; +Python uses numeric placeholders only for this syntax decision and excludes +nonstandard NaN/Infinity constants. Complete overflowing JSON still refuses. +An ordinary numeric-looking prefix with trailing text continues literal and +subsequent JSONL checks. Integer conversion/resource failures remain conservative. +No node/depth reset, hash exception or new work budget is introduced. + +Implement the Grok-only Go walker in one new bounded helper/contracts file, +remove only the superseded two private helper bodies, and replace the matching +Python function. The current reviewed Cline implementation and retained Grok +numeric patch are references; all reconstructed Grok bytes receive fresh reviews. + +## Separate already approved TOML observation + +Preserve the exact recovered `GROK_PROJECT_CONFIG_MUTATION_DIAGNOSTIC_PLAN.md` +body (original SHA256b539cb9ee53b7184ad5e8fcecc9348f97584b46e6787a00a3033204d6989377e). +Root and shell supplied its two independent plan GOs. It remains a separate +incremental diagnostic slice after these prerequisites, using the original +absolute150-second work deadline even after an early provider cancellation. +Its bounded held read, eight authored scalar comparisons, private-value refusal, +semantic hashes and unchanged fatal are not permission to adopt any rewrite. + +## Required controls and publication + +Route controls exercise the real inert loopback provider, each fixed category, +raw private canaries and unknown values, first-failure retention, absent optional +field on all other reasons, no request/body/count promotion and unchanged stop. +Python hostile enum/type/extra-field/contradiction controls match those fields. +Privacy controls cover nested encoded strings and keys, multiple layers, +JSONL with non-JSON and numeric-looking first lines, `1e999 1e999`, complete +nonfinite values, duplicate keys, invalid UTF-8, oversized keys, aggregate node/ +byte/depth exhaustion and nonmutation. Test the real capture refusal boundary; +do not merely compare one helper to another copy of itself. + +After independent code review, run focused Grok/shared inert contracts, all +runtime Python contracts, all-enabled affected-line lint and the unchanged +unfiltered whole-file six-linter gate in the exclusive slot. Reacquire the +genuine6f archives with API hashes, closed canonical ZIP/CRC/source/stream +checks and replay failed all-false admission. No vendor executable or positive +hosted eligibility may run locally. Source pins, original HOME, authority, +process cleanup, all five qualifications and the deliberate capture failure +remain unchanged. Root alone publishes small reviewed commits to the existing +PR with a fresh non-forced head lease. No release, replacement PR or VS Code work. + +## Reconstructed source validation, 2026-10-27 + +CLI R1 and shell R2 independently approved plan SHA256602585766fd034764fc12a226d50b72570908904f83f34faa23ef2c0d84c5798 before source edits. Both then reviewed the complete reconstructed five-file slice and gave code GOs on the final exact hashes. The existing route predicate is byte-identical. A concrete noctx lint finding was fixed with the Go1.22-compatible checked `http.NewRequestWithContext` test helper; both critics reviewed that final helper and its callsites. + +Focused Grok/shared inert Go contracts passed in1.067s, including an actual test-binary child that verifies nested credential refusal before any capture persistence. The runtime Python suite passed28 tests in0.088s. All-enabled affected-line lint and the unfiltered six-linter whole-file gate each reported0 issues. No vendor client or positive hosted guard ran locally. + +The genuine6f native/frontend archives were reacquired with their original API sizes/SHA256 and inner hashes. Canonical unique ordinary members, bounded sizes and CRCs were checked. Exact source6f/treeb272, empty source/build-clean streams, all24 frontend stream length/hash joins and all20 native bodies passed reconstruction replay. The original route failure, zero requests/responses, unmatched credential and normal provider shutdown remain unchanged. Admission is still failed/incomplete, with all five qualification, vendor-authentication and real-model flags false. This validates the recovered scanner and envelope; it does not qualify native execution or identify the unseen route/TOML changes. diff --git a/docs/plans/project-install-e2e/grok-origin-route-diagnostic-plan.md b/docs/plans/project-install-e2e/grok-origin-route-diagnostic-plan.md new file mode 100644 index 000000000..06bc6ccf5 --- /dev/null +++ b/docs/plans/project-install-e2e/grok-origin-route-diagnostic-plan.md @@ -0,0 +1,61 @@ +# DRAFT: Grok first rejected GET — finite origin-route observation + +Status: research/pre-code only. No implementation is authorized by this document until the owner/root obtains two independent plan-review/fix rounds. This research worker made no repository changes and ran no Go, lint, or Grok executable. + +## Scope and inspected source + +Read AGENTS.md, docs/PROJECT_INSTALL_OWNER_SCOPE.md, docs/AUTHORING_CAPABILITY_PRESERVATION.md, and docs/plans/project-install-e2e/completion-plan.md. Observed /tmp/uap-grok-publish at c6c58579674e0bdb230a556f0bef1de38ce40612 with a clean short status. Root owns all checkout, checkpoint, manifest and remote mutations. Preserve parallel work in both existing checkouts. VS Code is deferred, no release is authorized, and legacy capabilities remain preserved. + +Relevant current files: repotests/agentplugins_grok_project_native_route_test.go, repotests/agentplugins_grok_project_native_provider_test.go, scripts/project_native_runtime.py and its focused runtime-admission contracts. The current finite Go URI observer and the Python grok_provider_route_observation validator recognize exactly /v1/chat/completions, /chat/completions, /v1/models, /models, /v1/responses and /responses; all other RequestURI strings become other in the observer. The separate unchanged provider admission guard allows only the literal POST /v1/chat/completions plus its existing loopback/Host/URL/MIME predicates and exact fixture bearer check. + +## Genuine new blocker and limits + +Exact source c6c58579674e0bdb230a556f0bef1de38ce40612, tree a9c7f08283d631d8e0af2e448601b89f201dbc43; run 40569312299, job 122421982285. Native artifact 12766872743 outer ZIP SHA256 9696d09f9d1f94a868d1830da142a373e4a38ea671c5baca89935a0fec6b075d (4673 bytes); frontend artifact 12766048484 outer ZIP SHA256 b856a564db2322a913318d5955a54d086871851fa2ff0faaf74e5222a0c3205e (3391 bytes). The root independently audited both outer/closed ZIP archives at /tmp/uap-additional-grok-c6-audit-u8yod99g/archive-audit.json. + +The migration sentinel prevents the previous full-config fatal. Genuine native inspect succeeds with projectTrusted=false, the owned project plugin visible as enabled=false with one skill, prepared only. The first native --single attempt aborts at 325 ms after provider failure=route: observed method GET, URI other, loopback peer and matching Host, relative URL, empty URL host and raw path, missing Content-Type. Admitted requests/responses remain 0/0 and ordinary provider shutdown completes without forced close. Native streams are empty; all runtime qualifications remain false/incomplete. + +The provider checks route before bearer and cancels the attempt context on first failure. Therefore credential_matched=false is not evidence of credential absence, and the deadline=true receipt is not a measured 150-second timeout. other excludes only the six exact observer literals; it does not identify any other path, query, endpoint purpose, or caller. + +## Primary-source research, exact identities + +Official xai-org/grok-build public snapshot 2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8 has a complete 5102-entry tree. All seven cited source blobs were fetched read-only via the GitHub API, verified by recomputing the Git blob SHA1 from the exact UTF-8 bytes, and independently SHA256 hashed. Exact hashes, source URLs and line excerpts are in /tmp/uap-additional-grok-first-route-research.json. + +- crates/codegen/xai-grok-shell/src/agent/mvp_agent/session_setup.rs, blob 061460db05fa95afe7d0fa8aba726c34a7bbec5d, SHA256 266eaf035f690ec57bf62b7d9f06bbf72922b17e07f6cc6229820a015cf05ea7: line 610 spawns sampler transport prewarm using session_sampling.base_url; line 1071 does so on restored sampling base URL. +- crates/codegen/xai-grok-shell/src/agent/mvp_agent/sampler_prewarm.rs, blob ae011abbbfb595ccf79bdac2acf68c7af08a5773, SHA256 2b825cbe4165140211510ae0e77b438acc6349f5ab355683b18e1222b599a254: lines 30–31 spawn prewarm_and_record, whose line 64 calls xai_grok_sampler::prewarm_transport. +- crates/codegen/xai-grok-sampler/src/prewarm.rs, blob 4ee8ad76886f1d28c8a7e1269623fa27e9f5fb3d, SHA256 626aa25d3515423372a812b785f7ccdc090161a3a83dd7a0f4355401602e34e5: lines 70–83 resolve endpoint origin and obtain pooled client; line 200 issues client.get(origin).send().await; lines 218–224 derive url.origin().ascii_serialization(), stripping the inference path/query. +- crates/codegen/xai-grok-sampler/tests/prewarm.rs, blob a806fd1b01327e86e31a1d46177a4eb0c135539e, SHA256 c8bbcd17803f5607650907ce97c499ddb0ccdc29633cb5b259004aaaec50c822: lines 40–44 assert the wire request begins GET / HTTP/1.1. Lines 47–63 assert no content-length, authorization or x-api-key for this public-source test. +- crates/codegen/xai-grok-sampler/src/prewarm_tests.rs, blob e0a65970db6506d125e89f0dffe3c3ad5520ebe0, SHA256 a6e64926e03fe22d2cdcfa8bde780692bdf55a5d7d125e7f4dc25beced8eff5c: lines 12–23 expressly test path/query stripping and loopback port preservation. +- crates/codegen/xai-grok-sampler/src/shared_http.rs, blob e659c7fcc4eb8bba52a3c882fa57f670269c4bf4, SHA256 75e919946fb52b887f9bb0a905d8244596b0228235b1eed8313b76d50f8b5943: source documents GROK_SAMPLER_SHARED_CLIENT=0 (or false) and pooled_client returns SharingDisabled before obtaining a client when this source-defined switch is set. Ordinary sampling still constructs a fresh client. +- crates/codegen/xai-grok-sampler/tests/shared_http_kill_switch.rs, blob 4db871673b5c07023bc3955840397cc478bf90d2, SHA256 43a0dc29fe72e561f125c382a0b58cf9e4a1a2f5d78248ba1b137c9eedcd93d5: source test sets that exact switch and checks two independent sampling clients/connections. + +Public source supports a concrete hypothesis: the first GET may be the transport prewarm to /. Shipping build f50904eaee6c does not resolve in this public repository, so source guidance is not proof of binary equivalence. No current genuine observation identifies / or measures absence of credentials. Do not label the c6 route as prewarm. + +## Proposed next slice: diagnostic only + +1. In grokProjectObserveRoute, add only the exact literal / to the existing closed RequestURI switch. Store that same public fixed literal in the existing URI field. No new receipt field, raw path, URL, request URI, query, header, bearer, body, error text or arbitrary digest is exported. +2. Keep grokProjectProviderRoute, admit, serve, failWithRequest, first-failure locking/cancellation, authored configuration, environment, credential, response, request counters, all deadlines, shutdown and qualification logic unchanged. +3. Preserve other for /?private=..., //, /v1/, escaped/absolute forms and every unknown URI; compare RequestURI exactly, never infer the route from URL.Path. A root-path request with a query must remain other. +4. Update deterministic contract coverage to prove / receives the finite label, GET / still fails existing admission, and first actual refused GET / records only those facts with 0/0 admitted counters and unchanged cancellation. Existing first-error/nonroute-error retention controls remain required. +5. Extend private-value controls with /?credential=private-query (and an unknown root-like value) so no query/body/header text can escape. Keep serialized observation bounded under the existing 512-byte test. +6. Add only the same exact / literal to the Python grok_provider_route_observation diagnostic URI enum. Preserve every closed-field, exact-type, failure-state, contradiction, source, archive, completeness and false-qualification rule. This is failed-evidence admission, not provider request admission. Do not infer the label from URL.Path or admit arbitrary root-like strings. +7. Focused Python controls must admit an authored failed GET / observation under the existing closed provider envelope and reject query, absolute, escaped and other root-like arbitrary URI values. Replay the actual source-bound c6 other capture unchanged, and retain the legacy optional-no-observation envelope contract. No raw old evidence is edited. + +A fixed / label is enough to discriminate the strongest source-derived call path. Hashing arbitrary request data is unnecessary for this slice. No accepted GET, synthetic models listing, guessed health reply, MIME relaxation, bearer bypass, deadline increase or second provider transaction is proposed. + +## Review and verification gates + +Obtain two independent plan-review/fix rounds against this document and the exact inspected c6 source. Reviewers must assess finite-data privacy, exact RequestURI semantics, matching Go/Python diagnostic enums, unchanged admission/cancellation and honest qualification. Root alone implements after those gates, followed by independent implementation review and ordinary exact-head repository verification, including the actual focused inert Go/Python controls and retained failed-evidence replay. No local vendor execution or fake hosted-guard environment is permitted. + +A new genuine hosted native run at the reviewed implementation commit/tree must preserve the existing source/workflow/client/archive provenance and ordinary unique artifact-name/closed-ZIP checks. Record exact run/job/artifact IDs plus ZIP SHA256 and size; audit the original native-inspect and native-attempt streams and provider receipt. A successful diagnostic run may still end in the same intentional route refusal. Preserve all false/incomplete runtime/lifecycle claims. + +Interpretation is bounded: +- GET plus exact URI / proves only that the actual rejected exchange was GET / under the existing envelope predicates. It is consistent with public-source prewarm; it does not prove caller identity or absence of credentials/body. +- URI other remains unresolved. Do not widen this label set speculatively or serve a response. +- Any different first failure is recorded honestly under existing first-error semantics. +- No diagnostic result promotes enabled=false/prepared discovery to native runtime, skill loading, session trust, MCP, model or lifecycle success. + +## Conditional follow-up, excluded from this slice + +If new genuine evidence records exact GET /, the source-defined GROK_SAMPLER_SHARED_CLIENT=0 becomes a concrete narrow fixture-environment candidate. Draft and independently review that separate plan using the new evidence before editing the environment. It would preserve the sole accepted authenticated POST route and suppress an optional source-defined pooling optimization rather than inventing a GET response; new genuine proof would still be mandatory. This document does not authorize the switch or claim it exists/works in shipping build f50904eaee6c. + +If genuine evidence remains other, return to source-driven finite diagnostics without guessing. The prepared Grok leaf and its existing source/tests remain preserved. diff --git a/docs/plans/project-install-e2e/grok-origin-route-diagnostic-review.md b/docs/plans/project-install-e2e/grok-origin-route-diagnostic-review.md new file mode 100644 index 000000000..8379ac0a0 --- /dev/null +++ b/docs/plans/project-install-e2e/grok-origin-route-diagnostic-review.md @@ -0,0 +1,15 @@ +# Grok origin-route diagnostic: preimplementation review + +Exact plan SHA256: `a30c9ab852be76cb1dc4a85c8befb924d095871a89beedaa663d94516575d3f9`. + +Before code changed, independent GPT-6.1 Sol xhigh reviewers approved this exact +revision: `kimi_current_review` R1 and `native_current_review` R2. R1 first +identified a missing matching Python diagnostic enum; that omission was corrected +in the plan and both reviewers then returned GO. + +The scope is only the fixed `/` RequestURI diagnostic label and matching Python +failed-evidence intake. Provider admission, authorization, MIME, configuration, +environment, replies, counters, first-failure cancellation, process budgets and +qualification remain unchanged. Public-source prewarm remains guidance, not proof +that the shipping binary sent `/` or omitted a credential. The environment switch +is explicitly excluded. diff --git a/docs/plans/project-install-e2e/grok-origin-route-diagnostic-validation.md b/docs/plans/project-install-e2e/grok-origin-route-diagnostic-validation.md new file mode 100644 index 000000000..a09c5e8a4 --- /dev/null +++ b/docs/plans/project-install-e2e/grok-origin-route-diagnostic-validation.md @@ -0,0 +1,54 @@ +# Grok finite origin-route diagnostic: implementation validation + +Reviewed implementation: `be852aed7f8c9c87497c0a57e531ccc7a20eae7c`, tree `c26668d31a5e62c3e5852aa15c135f5588f2b31b`. +Baseline: `c6c58579674e0bdb230a556f0bef1de38ce40612`. The approved pre-code plan and its two independent +reviews are retained alongside this record. No vendor client ran locally. + +## Independent code review + +Two independent GPT-6.1 high/xhigh critics returned CODE GO for these exact source +bytes. R1 reviewed the full diff, actual provider admission and inert transport +contracts; record SHA256 `d7ee2e31c04da37c24e0db3907c067fe16accf73cdc3aa8e491636be5b554bbf`. +R2 independently checked the manifest, unchanged provider, finite privacy controls +and failed-evidence intake; record SHA256 `7aa7b86888bca3efed07c271f610fe22f9241a82cabaa691d2792c4e1dc8df53`. +Neither critic executed tests or granted native qualification. + +The functional delta is only the literal `/` in the finite Go diagnostic observer +and Python failed-evidence URI enum. Existing exact POST route/authentication/body +admission, environment, configuration, pins, budgets, cancellation, first failure, +provider replies and all qualification gates remain unchanged. The provider file +SHA256 remains `865265e28731ceee3816ab1d5c4fc137d29d859e6851675d90fbcc867f1daa88`. + +## Executed inert verification + +The root executor ran Go1.26.10 and golangci-lint2.13.2 serially: + +- `go test -p=1 -count=1 -timeout=120s ./repotests -run '^(TestGrok|TestProjectNativeCLI)'`: PASS, package1.119s. +- `python3 -B -m unittest test_project_native_runtime` from scripts: PASS,38tests. + Existing absent-observation and contradiction controls remain included. +- All-enabled affected lint against the baseline: PASS,0issues. +- Unfiltered whole-package revive,funlen,gocyclo,gocognit,dupl,depguard: PASS,0issues. +- gofmt and whitespace checks: PASS. + +The first lint invocation failed before analysis because the selected Go binary +was absent from that validation process PATH. Adding the already-selected toolchain +to PATH resolved the environment error; no source changed. The failed log remains +in the validation record. Final validation report SHA256 `e31b5d1caaf61f228d671926e3f00140d98f70ab52b9e63b47de2dd8ed7c40d1`. + +The unchanged original c6 native ZIP from run40569312299/job122421982285, +artifact12766872743, inner SHA256 +`1c85c32f6f895df7fe5fa6024eb260159c7c6db0a3d0862b67f4a0f948fc4125`, was +re-admitted through the new code with its original source identity. All20member +bodies and the failed summary remain identical. The old GET URI remains `other`; +all five qualifications and runtime completion remain false. Replay record SHA256 +`40820d71ca45d0bda4c05ddd2007ae4525c1cdf74b02852339b0cc1a7fe63c3c`. + +## Remaining native boundary + +A fresh genuine hosted run is required to observe whether the previously refused +GET is exactly `/`. This diagnostic change does not admit that request or establish +its prewarm identity, missing credentials/body, shipping-source equivalence, loaded +skill, MCP operation or lifecycle completion. No pool-switch, route, authentication, +trust, timeout or profile exception is introduced. PR522 remains unqualified until +its applicable native and integration gates pass; this record authorizes no merge, +release or VS Code work. diff --git a/docs/plans/project-install-e2e/remaining-native-qualification.md b/docs/plans/project-install-e2e/remaining-native-qualification.md index b63c17d55..8340189a6 100644 --- a/docs/plans/project-install-e2e/remaining-native-qualification.md +++ b/docs/plans/project-install-e2e/remaining-native-qualification.md @@ -837,6 +837,46 @@ State stays Prepared. Label `provider_kind=scripted_loopback`, vendor authentication false and real-model use false. Raw bounded evidence stays in closed CI artifacts, not Git or recursively uploaded native profiles. +## Grok opaque catalog research amendment + +Genuine help run 40240232499/artifact 12616383397 confirms the frozen 1.0.70 +archive/executable and `inspect --json`, `plugin list --json`, and `mcp list +--json` help. After two independent plan rounds, admit explicit `observe` +research only. Keep the nine fixed version/help probes unchanged. Require six +empty baseline catalog operations in A/outside, then author distinct text-only +user/A/B `.grok/plugins//.grok-plugin/plugin.json` and +`skills//SKILL.md`, and require nine populated catalog operations in +A/B/outside. No executable payload, hooks, commands, MCP definition, native +trust, provider, SDK or account mutation is introduced. Outside remains a +separate cwd, never HOME. + +Each operation uses existing hosted admission, immutable runtime checks and +`Context.probe`, with max 30 seconds capped by the original shared 360-second +remaining deadline. Every required operation must return exact integer zero, +error None, empty stderr and strict UTF-8 whole duplicate-free finite JSON +object/list, bounded to one MiB, 32 levels and 8,192 nodes. Preserve raw streams. +The schema remains opaque; root kind, digest and marker-token occurrences are +research observations, never selected source/loading evidence. Owned ordinary +single-link UID files retain exact mode, bytes and original identity across each +probe, using descriptor-relative no-follow parent/file opens and held reads. + +Only completed fixed observations may pass `grok.scope-observation` research; +all product lifecycle, native restart/removal, MCP initialize/list/call, +authentication and model qualification flags remain false. Failures, malformed +JSON, alias/replacement/mutation, timeout or missing operations fail the research +slice. Freeze genuine observed source schemas and native modes with a new +separate reviewed amendment before real UAP lifecycle implementation or a +native-source qualification claim. Existing skills-only project MCP refusal +remains intact. + +Every native child, including version/help and all catalogs, receives literal +`GROK_DISABLE_AUTOUPDATER=1` through the existing environment allowlist. This is +the documented suppression control in the official +[settings reference](https://docs.x.ai/build/settings/reference); it does not +claim network isolation or supply vendor authentication. Immutable runtime +identities remain checked before and after every child. Inert contracts assert +the exact supplied environment on every help/catalog call. + ## Kimi installed local lifecycle amendment Genuine Kimi API run 40238495966 at `ac3a7e5da565590fd85c4ff9795cae6b312ed6b1`, @@ -976,6 +1016,36 @@ remain false. A bounded retained failure cannot become a passing E2E gate. Further source/provider/permission/lifecycle changes require their own observed contract and independent review; legacy user and baseline gates remain required. +## Observed Grok 1.0.70 project inspection admission + +Genuine runtime [40353442588](https://github.com/777genius/universal-agent-plugins/actions/runs/40353442588) +stopped before public `add-a` mutation because the existing project inspector +accepted only the previously measured 1.0.67 version. The follow-up genuine +read-only [observe run 40353442581](https://github.com/777genius/universal-agent-plugins/actions/runs/40353442581) +succeeded at source `3234be86f0a3217ebd2371dc5e8fa0b26d3dcd26`, tree +`74490cebf339e8538b1f237b478aceeb58667e3d`. Native artifact `12670385326` +(SHA256 `a0d9c2e0858b5bbb2297f4d6f6f99aa71a57cfbc3d9ba5ec908434dfe14db957`) +retains the actual bounded 1.0.70 inspection responses. + +The measured empty A and populated A/B responses contain the existing identity +subset: exact `grokVersion`, canonical `cwd`, same `projectRoot` with an optional +trailing separator, boolean `projectTrusted`, and `skills`/`plugins` arrays. +Populated A/B each identify their own project plugin by exact name/scope/path +with `enabled=false`, plus the independent original-user plugin. Outside cwd +lists only the user plugin and has no project root. The separate plugin-list +command returned empty arrays even for these candidates; it is not a substitute +for project inspection. Native trust and MCP execution remain unqualified. + +Admit exactly the measured 1.0.70 version alongside 1.0.67 in the existing +project-inspection parser. Retain strict duplicate/case-fold handling, exact +cwd/root, typed trust and collections, unique plugin names, canonical absolute +paths, scope checks, ownership and namespace collision rules. All other versions +remain closed. Update only the corresponding version-specific user guidance; +project activation remains Prepared and MCP qualification remains unchanged. +Add focused tests for both admitted versions and unchanged refusals, then rerun +the existing genuine Grok startup capture. No new command, alternate catalog, +trust grant, provider, permission mode, deadline or cleanup policy is introduced. +Two independent plan reviews precede this product correction. | Client | Pinned native version | Fixed restricted startup | | --- | --- | --- | | Qoder | 1.1.78 | `--print --output-format stream-json --no-session-persistence --permission-mode default --tools "" --plugin-dir ` | @@ -1636,6 +1706,115 @@ not qualify the observed macOS race; the original hosted macOS contract remains required. Two independent plan rounds precede implementation, followed by independent code review and exact-source validation. +## Draft Grok installed prepared-candidate visibility capture + +This proposed narrow follow-through awaits scope assessment and two independent +plan approvals before code. It adds one visibility stage to the existing Grok +installed-startup attempt, not another collector, lifecycle case or capability. + +The concrete gap is the join between existing evidence sets. Genuine observe +run `40353442581`, source `3234be86f0a3217ebd2371dc5e8fa0b26d3dcd26`, +tree `74490cebf339e8538b1f237b478aceeb58667e3d`, artifact `12670385326` +(ZIP SHA256 `a0d9c2e0858b5bbb2297f4d6f6f99aa71a57cfbc3d9ba5ec908434dfe14db957`) +measured project candidate name/scope/path and disabled status, but those native +directories were authored directly by research. The composed saved lifecycle +test exercises the public installer with an inert native runner. The real +installed-startup fixture validates public add and installed skill/receipts, +but retains no independent raw `inspect --json` output after that add. +Production `inspectProject` deliberately permits file-valid Prepared output +when its native list lacks the candidate, so successful public add alone does +not establish candidate visibility. This is a missing research observation, +not evidence of a production bug or a reason to tighten preparation behavior. + +After the existing `f.setup`, run exactly one Grok-only command, label +`native-inspect`, argv ` inspect --json`, cwd selected project A, +timeout 30 seconds. Reuse the same command supervisor, original HOME, literal +updater suppression, immutable/protected checks, bounded streams and original +150-second work / 180-second total deadline. Other clients' command tables and +all existing native-startup argv remain unchanged. There is no additional +provider, login, trust grant, plugin enable, permissions change or native tool. + +Parse only the already measured identity subset using the existing strict JSON +and key-folding helpers: exact `grokVersion=1.0.70`, cwd A, projectRoot A with +the measured optional trailing separator, boolean projectTrusted and typed +skills/plugins arrays. Require projectTrusted=false and exactly one selected +name in the complete plugins array, scope=project, path equal to the already +validated selected binding locator and enabled=false. Refuse ambiguous names, +wrong types, aliased/noncanonical paths, duplicate or case-folded identity +fields, missing candidate or changed version/scope/path/status. Preserve and +recheck authored and delivered skill bytes around this read-only command. +Require the selected locator to be a canonical direct child of A's exact +`.grok/plugins`, with the validated physical-artifact basename. Require its +ordinary owned directory ancestry and held native manifest/skill paths without +aliases or extra links before and after inspection; string equality alone +cannot establish this path authority. Reuse existing bounded no-follow readers. +Capture the full raw command triple; summarize only visible disabled Prepared +candidate identity. Do not treat counts, labels or retained files as skill +loading, active discovery or source revision/runtime proof. + +Admit the new complete triple only for Grok in the existing closed frontend; +partial/unknown members and every other client's admission remain unchanged. +Root owns this minimal shared frontend change. Add focused inert native-JSON, +fixed command/cwd/deadline, wrong-client and artifact-triple contracts, using +the held genuine inspection bytes as the positive schema reference. Then run +the existing restricted startup attempt unchanged. The whole initial attempt +still deliberately fails; all five qualification, vendor-authentication and +real-model flags remain false. This stage does not establish B/outside +isolation, full lifecycle, restart/removal or a new supported native surface. +Further lifecycle/qualification work requires its own reviewed evidence and +definition. Independent code review and a fresh genuine hosted capture remain +mandatory; no vendor process may run locally. + +## Grok empty-profile migration sentinel amendment + +The genuine PR522 source `753936db2e50613c2a4310848a262ec4f25dea87`, +run `40553581491` / native artifact `12759346679` (outer SHA256 +`f90a4502e5e0bac54dfbb37372a29916431a2c0b4fc7aedb0afed6fa5832531a`), +fails the original config digest guard. All eight model facts remain equal; +the sole added table has key SHA256 +`dfd76b3ecbefe4606d9cbfa7e7c41884b22d214a0c13560bb7eb10e65c11851f`. +Independently verified public snapshot +`xai-org/grok-build@2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8` identifies +the exact source-defined table as +`marketplace = { default_skills_installs_purged = true }`. +Its canonical JSON value SHA256 is +`292bc27c01bd4c8e2df64aacd7e75d92db6d9e3f446970ea2d7d97f011250093`. + +The primary shell `extensions/marketplace.rs` blob +`ea896d05e4471573974e273049d6deb868e92cd6` lines963–1082 reads a typed +boolean, returns before migration when true and sets it even for an empty +legacy registry. Shell `plugin/mod.rs` blob +`60d0d7fba53875e2456b6ebef0fc0744f4fe35ac` lines1346–1390 writes the +literal boolean into that table. These exact Git blob identities were +independently reconstructed. This does not resolve shipping build +`f50904eaee6c` to the public snapshot or export the changed raw TOML. + +Root R1 and independent CLI R2 approved the exact plan SHA256 +`30a05da404dc1de8103c030510aff5c34daf2d128f5ad6ea462c8a488e986e77` +before implementation. The disposable original HOME has no legacy installs. +Author only `[marketplace] default_skills_installs_purged = true` in the +initial provider recipe before its first write/vendor child. Freeze all bytes +and the original digest as before; any later format/removal/false-toggle +continues to fail the original protected guard. Preserve the eight model +fields, environment, commands, provider route guard, diagnostic limits, +process/deadline/admission and all false qualification flags. This grants no +marketplace source, trust, permission, account or plugin activation. + +Inert contracts assert the complete three-table recipe, independently fixed +semantic hash and initial held bytes/digest. Actual inert test children prove +the original fatal for formatting, removing or toggling the flag. Existing +diagnostic count assertions retain one changed member against the new third +authored table. Focused `^(TestGrok|TestProjectNativeCLI)` contracts passed +in 1.161s with Go 1.26.10 and approved inert loopback access; the first restricted +invocation could not bind sockets. Actual all-enabled root lint versus the +PR520 base and unfiltered whole-root six-linter gate both reported zero issues. +Gofmt/diff checks are clean; the actual twenty-member native archive still +replays as failed/incomplete with all five qualifications false. No vendor or +public installer ran locally. Independent exact-code review precedes root +publication. A fresh genuine run must establish preservation; +the existing rejected GET remains `uri=other`, and this amendment accepts no +new HTTP route or final capture/qualification boundary. + ## Observed Gemini wait-for-previous description correction Genuine [runtime run 40367376528](https://github.com/777genius/universal-agent-plugins/actions/runs/40367376528), @@ -1794,6 +1973,648 @@ then review and validate the combined source before merging the base PR. Update current-source support guidance only to the actually observed level. Do not publish a release or claim real-model/OAuth qualification from a fixture. + +# Grok: isolate the measured initial-title side call in a fixed fixture + +PRE-CODE proposal. Two independent exact-plan GOs before implementation. Read +AGENTS.md, PROJECT_INSTALL_OWNER_SCOPE.md and capability preservation. No release, +VS Code, trust widening, real account/model, local native execution or generic +provider/model acceptance. Root alone publishes to existing PR522. + +## New source-bound actual evidence + +Current611f7dd79a923a3b8e445222cde8395bd1af1062, +treee2b94d93297bbbf9ef316af922a87c426ac083d7. Runtime40586113413, +job122486692802 fails after3.68seconds. Native12775012222 outer5333B SHA256 +d1aa56111ae7e3270069b47abcc9a022dfdd7e3fa0ee444ccd43e1f70701c7d3, +inner e4869bdb8bd69c35a21090d7bd0fb441d4930f2b6358992e6a7707dd6fcfc3a9, +21ordinary closed members. Frontend12774612789 outer3395B SHA256 +0a2b462b88809e6dcc861e8123a00d929e48709d9130c5b6aa70b6a0f645e32d, +25members. Exact source/tree/cleanbuild,24stream joins, official archive/executable +pins, public add/source/package/physical/binding/receipt joins and exact-source +failed-envelope reader replay pass independent audit +/tmp/uap-grok-611-current-40586113413/independent-raw-audit.json SHA256 +f36b98f1cb8585efce2cdfd616557cabe4f3b0132aafb26e4b2ee1da7ed2f45c. + +Public add exits0 Prepared. Actual inspect exits0 in68ms and selects exactly the +receipt-owned project plugin,enabled=false,projectTrusted=false,one provided +skill,zero loaded skills and no MCP. Native startup is canceled after364ms by +provider request-shape refusal: one captured authenticated literal-loopback POST, +zero responses,ordinary provider shutdown,forced_close=false. The body is an +initial-title call with model=grok-4.6,two messages,stream=true,temperature1, +max_tokens100 and forced session_title function schema. It is not the main +uap-grok-fixture request. The main provider correctly refuses it; no completed +native output or loaded skill is proved. Every5/runtime/auth/model flag is false. + +## Exact publisher source and limits + +Official xai-org/grok-build snapshot2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8 +contains independently Git-blob/SHA-verified source retained in +/tmp/uap-grok-title-source, research /tmp/uap-grok-title-source-research.json. +Shipping buildf50904eaee6c is unresolved in that public Git snapshot; neither the +request's agreement with source nor the earlier optout proves binary equivalence. + +- config/mod.rs blob568b51d0631e3fb2f1331b107501a5f9b1b72ea3, + SHA256a84e2c680ec3ef795d2847c7b1832247e797392d7e9f7f2d17b410585c936f63, + lines500-625: `[models] session_summary` / GROK_SESSION_SUMMARY_MODEL resolves + the auxiliary title model; empty values fall back to compiled grok-4.6. +- session/summary.rs blobf0c3ae859ffb6ffe3f891fca4f2b458cca601209, + SHA2568d9dafb70d64704c48d1b27330af4218b8ba5a51cb8b20814557f974d06d7806: + SummaryGenerator starts an initial title task on first nonempty content. +- session/helpers/session_summary.rs blob0f2af81e78701043b8446a8bffb2272921f3802c, + SHA256fde704829e87366b93280fe031e01aaaca2d5bd169a67d0bc777f3013f7f8837, + lines188-244 constructs the measured two-message session_title request and + reads a title from returned function-call arguments. +- agent/mvp_agent/agent_ops.rs blobfa77f1ec809cf3c944ba91895a73ba422a24054a, + SHA256399735fd57b748735efaaef3f956c6b428af60bbe436754031e91a9342e776bd, + lines85-135 resolves the configured auxiliary model's sampler; missing model + resolution falls back to the primary sampler with the auxiliary model slug. +- agent/config.rs bloba430462ad3b8e0611368ad2051f093827d7babee, + SHA25651eadad05c42e19954fe0822dd187dc59694252bec34d513cc329a05394b47a2, + lines4489-4515 returns a catalogued custom auxiliary model's own endpoint/key; + lines4597-4612 preserves them while stamping local context/max_retries. + +The examined CLI/summary/persistence source has no verified initial-title disable +setting. GROK_TITLE_REFRESH controls later turn-checkpoint refresh and does not +suppress the initial SummaryGenerator. Do not add it as an assumed solution. + +## Proposed narrow fixture boundary + +Preserve the original main provider's literal authenticated POST route, loopback, +Host,MIME,credential,body/model,one-request/response,count,failure,cancellation and +cleanup predicates. Main provider admission/capture/serve/response functions and +the shared native command fatal/review-barrier path remain byte-identical. Do not +let the main provider accept grok-4.6,a title request,unknown models or a second +request. Original Prepared/untrusted/disabled inspection remains unchanged. + +Only for the existing Grok attempt with its held main provider, start one separate +fixed loopback title fixture before configuring children. Generate its own fresh +private128bit fixture key,distinct from the main key. Author the original HOME's +already frozen config before startup with `[models] session_summary = +"uap-grok-title-fixture"` and a separate `[model.uap-grok-title-fixture]` entry: +that exact model, its held127.0.0.1:random/v1 endpoint, its dedicated env_key, +chat_completions backend,unsupported search/reasoning and max_retries0, matching +the established custom-model recipe. Keep default/main model,original HOME, +marketplace sentinel,existing shared-client optout and all other config unchanged. +Add exactly the held title key to the existing closed child environment; do not +import ambient GROK/session-summary/title keys or alter global environment. + +The title fixture is a bounded one-exchange sibling with its own count, raw +request/response and finite receipt. Reuse only existing strict route/ordinary +transport cleanup helpers where this does not alter the main model boundary. +Require exact POST /v1/chat/completions,loopback peer,matching Host,relative URL, +empty URL host/raw path,one application/json and one exact Bearer dedicated key. +Reject redirects,GET/prewarm,missing/wrong/multiple credential or MIME and second +attempts immediately. Original context origins150/180seconds and native60seconds +are unchanged; listener limits/deadlines are no larger than existing provider. + +Require whole strict duplicate-free bounded JSON with the exact source-derived +current schema: model uap-grok-title-fixture; stream true; stream_options exactly +include_usage true; temperature1;max_tokens100; exactly the measured system text +and current fixed harmless prompt wrapped in user_query; exactly one session_title +function with measured description,strict single-string field schema and exact +forced tool_choice. Reject extra/missing fields,wrong text/model/schema,tool calls, +NaN/trailing data and body>1MiB. Do not interpret or forward returned native paths. +After exact route/authentication and the1MiB read bound, require a whole strict +JSON object before retention. Check both original held fixture keys against raw +bytes and recursively decoded JSON strings/keys (including JSON-escaped spellings) +before any body capture. Any privacy match fails/cancels without retaining body. +Malformed/nonJSON/trailing/duplicate/nonfinite/oversized bodies and all credential +failures likewise retain no raw body. Only a safe bounded strict JSON object is +captured, with requests=1, before the full fixed title-schema check; a schema +refusal retains that safe request,records0responses,cancels and stays allfalse. +No response or raw capture follows malformed/oversize/privacy refusal. + +Return exactly one deterministic OpenAI chat-completions SSE tool-call result for +session_title with fixed harmless title `Disposable plugin startup observation`, +finish_reason=tool_calls and one[DONE]. No generic text fallback or model request +forwarding. This synthetic title function is internal title metadata,never native +MCP,installation,skill loading,authentication or real model evidence. Main provider +continues its existing one fixed response only for its original fixture model. +Add a finite privacy refusal only to the new title receipt/validator; original +main provider failure grammar stays byte-identical. Both fixture listeners must +shut down ordinarily with descendants/connection +closure; either failure or forced close remains failure. Keep the original main admission/handler functions byte-identical. Extend only +the existing retention redaction hook to redact both held private keys from every +retained child/provider stream; no raw headers/key values. Its decoded/raw privacy +checks must refuse an unsafe title body before retention rather than rewriting +that raw request. Original main body/model/count/route predicates are untouched. + +Export only three new finite names grok-title-provider.json, +grok-title-provider.request.json and grok-title-provider.response.sse with receipt/ +request/response presence matching actual0/1counts. Receipt<=2048bytes,ordinary +closed output membership and existing stream/aggregate bounds remain unchanged. +Extend Python admission in a separate explicit title-member validator; do not +relax the original main receipt validator. Require exact fixed title role/model/ +transport,finite failures,booleans/counts,coherent response and closure and private +key refusal. Old611/a1af/c6 failed archives must re-admit unchanged byte-for-byte. +The existing unconditional final review-barrier failure stays intact even if both +responses and native exit0 are observed. Every qualification flag stays false. + +## Meaningful verification, review and publication + +New inert controls independently build literal measured-title requests and assert +one exact response/count/receipt/membership. Reject main model/title mixups, +unknown/extra/missing fields,changed system/prompt/schema,wrong endpoint/credential/ +MIME,oversized/duplicate/trailing bodies,second call,context failure,write failure +and nonordinary closure. Verify exact SSE tool-call name/id/argument/title/DONE +with independently stated literals/digests. Test both key canaries in retained raw +and encoded JSON and closed child env with conflicting ambient values. Exercise +configuration parsing for exactly two held distinct model endpoints/keys and +unchanged default/main/sentinel; no vendor process. Preserve all existing main +provider negative cases and exact old-source failed-reader replays. Verify the +main handler functions and original unconditional native fatal/review barrier +are unchanged by hashes/diff,not renamed or bypassed. + +Freeze the reviewed plan in existing consolidated remaining-native-qualification +before code; create isolated checkout from fresh agreed current611 and preserve +concurrent work. Two independent exact code critics follow implementation. Run +focused Grok/shared inert Go,affected runtime Python,actual affected full-root +all-enabled and unfiltered six-linter gates,gofmt,whitespace under serialized slots. +No native/vendor,scanner or public installer binary runs locally. Root publishes +normal expected-head fast-forward only after gates and critics; then audit the +new genuine hosted capture. If shipping ignores the auxiliary model setting or +emits a different request,retain the refusal and plan from that evidence. No +broad fallback,changed trust,parser assumption or completed-cycle claim follows. + +## Accepted dedicated-title amendment before implementation + +Exact plan SHA256 `72cc3bcbe925d310806833836d6fa97d65dd1693ad0bcaaf9069e7939f8da07b` +received independent PLAN GOs from root and GPT-6.1 Sol xhigh +`gemini_qoder_finish` before any code edit. Receipt SHA256s are +`13a6e861fa554a9e106bdf9cde5711e4136a45b140086eab64c310068dbdc3bd` and +`134bb3ba23b7cd1ccbc553faa2f18a2d68a50df859ed6c82c469a157168ab614`. +Both rehashed all18 pinned primary files and reviewed separate auxiliary model +resolution and exact measured title schema. The original title config recipe +is authored before the existing initial baseline freeze; no later baseline +adoption is allowed. Safe bounded route/auth/strict-JSON/raw-and-decoded-key +privacy precede request retention; schema refusal sends no response. Main +provider handlers and existing fatal/review barrier remain unchanged. +Root freshly checked PR522 remains611 before this isolated checkout. + + +## Grok dedicated title fixture: final reviewed validation (2026-10-27) + +The accepted separate-title plan `72cc3bcbe925d310806833836d6fa97d65dd1693ad0bcaaf9069e7939f8da07b` is implemented by code source `3c38d0d5143b4490ff67822954fe9794e55498a8`, tree `28bfe97d594d45a0a7d59767dd427c218d8889fe`. Two exact independent code critics approved the final source: root receipt `ddd9a66d794faf55fd350248f13a1e212a1c7092421670d455fa12401ede980d` and GPT-6.1 Sol xhigh Gemini reviewer `3e7beb62d23ea66ba1f262f87c367448be475e431bb03d03e0a7a6fada93204b`. + +The held original HOME is authored once with separate main and initial-title loopback endpoints and private keys. The title handler admits one exact source-defined request and fixed internal session-title SSE response. Both literal and recursively encoded fixture keys are refused before raw request retention. The original main provider route/model/count/authentication functions, shared native process guard and unconditional final failed-only review barrier are unchanged. Three closed title evidence names have a separate bounded validator; all five qualification flags, native completion, vendor authentication and real-model qualification stay false. + +Actual final focused inert Go contracts passed (package 2.150s); affected all-enabled lint against program base `a412ba5e137fdcb7e004393229b323211ed746e1` and unfiltered revive/funlen/gocyclo/gocognit/dupl/depguard each reported zero issues. The 41 Python contracts passed in 8.678s on preceding source `3edd581c5b9e3c39b7089dd4d9a5c78233d86b81`; the entire scripts tree is identical through final source (`2d4ac1a96e1f5ccf3a0d6ca7af546d4a39753406`). Original611 and a1af failed archives retain all21/20 member bytes and summaries with all flags false under original and current readers. Gate and transfer receipt SHA256 `954aba61be3972a92fb6a551e7e144b7b655e7a7e0ac58d11999e249b3fd296e`. + +Two measured inert-test lint issues were corrected without changing the module version or any guard: context propagation and Go1.22-compatible HTTP request construction. Earlier failed logs remain in scratch audit evidence. No vendor client, public installer or scanner executed locally. Publication only schedules a fresh genuine hosted diagnostic; public-source/shipping equivalence, completed startup, loaded skills, user/project lifecycle and authentication remain unproved. + + +--- + +# Grok: bounded capture of the already-refused second main request + +PRE-CODE diagnostic amendment. Two independent exact-plan GOs and root's accepted plan commit before source changes. Root alone performs Git/GitHub mutations/publication. No local native/acquired client/public installer/scanner execution. No production adapter, provider acceptance, trust/profile authority, native pin, workflow/selection, model, tool, permission or qualification expansion. VS Code and releases remain deferred. + +## 1. Measured source-bound need + +Current PR522 source `348e2ce8b3bca4f117673a3b6bf80bedf8a64975`, tree `b12330110d2dea50e613bf36ac3b3485c4cd2f1c`, checkout `/tmp/uap-grok-dedicated-title-current`. Hosted run40599296059 native122524071409 failed23:25:43 UTC. Inherited Cline122524071582 completed success23:28:09; all current captures are completed, preserved and independently audited. Do not rerun unchanged source. + +Actual evidence is preserved under `/tmp/uap-grok-348-current-40599296059`: + +| Artifact | ID | Outer SHA256 | Inner SHA256 | Members | +| --- | --- | --- | --- | --- | +| Native | 12778988075 | 6629d43cc1a3d835d95949afb06b279334f158cc166e847bfe9d949da30b3e86 | 96fd685041010dbf1d9bcfc7f11eb7680f145cf3160608a1ed5898bfad33e153 | 25 | +| Frontend | 12778368337 | 066b0b7fc95d8819779838b46df0b24add56bf6a8bceac148180be270ef67d60 | 9ee9b68f7c5317b1c2677584eec9077ca4a62b66504a1124cb7371fa9a64c915 | 25 | + +Independent raw/source receipt `/tmp/uap-grok-348-current-40599296059/independent-raw-audit.json`, SHA256 `95a89455a84656cac9b6122258a54579ea8d1a45e3e780888cf2ca8934313fb1`, verifies API/ZIP/CRC,24 frontend streams, exact current source/build/pins/scanner and public installation/source/physical/binding/committed receipt joins. Root's second detailed raw review is pending when this draft is written; do not count the peer receipt as two completed raw reviews. + +Public add exits0 Prepared. Actual native inspection exits0 and retains projectTrusted=false, plugin enabled=false, zero loaded skills and no MCP. No native skill execution or lifecycle proof follows. + +The separately configured title fixture now succeeds exactly once:1220byte strict request SHA256 `6310901ba3d8138dbf06eb01e447139f815a42424513ed048263f39b1ced658a`;570byte exact SSE SHA256 `eea6c4c389c7e27cf0e6d1cc9e705bc84a4d77ae8c5afddaa846ec8255e6ed68`; correct credential, failure none, ordinary shutdown, no forced close. The original main fixture retains one42233byte request SHA256 `001a585d3fc552ec983c8b996b0b4fd0acc719cb1759710a6534cd4d481affff` and one394byte original response SHA256 `f2c19ea9dbc0ddbf7cc2facaa5d2d01877897afa0f6fea33cc5651c0ecc9f7b9`. + +Native stdout1476bytes SHA256 `2cefc7bfe383c0a900df87811d4e0dacc14dccc34ae7c97e30960d70ef0ef562` has two available_commands frames, the main sentinel text, usage and a successful end_turn. This is followed by an actual second main HTTP attempt that hits the unchanged provider's duplicate guard. Main receipt remains requests1/responses1/credential_matched true/failure duplicate/shutdown_complete true/forced_close false. The native command is cancelled after369ms, exit-1/errortrue/deadline=true; total fixture3549ms. The deadline field is ctx.Err()!=nil, not evidence of an elapsed60second timeout. Its original child/capture/cleanup guard remains fatal. No probe or tool exchange exists; allfive/runtime/auth/model flags remain false. + +### What source establishes, and what it does not + +`grokProjectProvider.admit` checks the original context, literal loopback POST route, matching Host/relative URL/no raw path, exact single application/json and one matching Bearer key before assigning duplicate when attempted is already true. It calls failWithRequest and returns before serve reaches body read. failWithRequest cancels the original context. Therefore the second attempt passed these route/header/auth predicates, but its body/model/purpose was neither read nor retained. The requests counter counts retained requests, not HTTP attempts. + +No current raw evidence establishes a retry, second auxiliary role, equivalent body, additional prompt or new valid model grammar. Public publisher source may inform future analysis but cannot reconstruct an uncaptured actual request. No new response/admission is justified. A bounded failure-only observation of the already-refused request is the smallest concrete next step. + +Pins remain1.0.70 archive11d27a30adfb75c4b9b180072ab27412a1916f054ff5497ab70fb9a77d304bcc / executable22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9; shipping buildf50904eaee6c. Public source snapshot2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8 is not claimed equivalent to that unresolved shipping build. This amendment changes no native configuration or source-guided protocol. + +## 2. Exact diagnostic eligibility and refusal + +Instrument only the original main provider's duplicate refusal in the existing canonical main+title Grok fixture. The title transport remains a distinct one-call fixture and is never eligible. A duplicate body may be observed only when all original route/header/auth/context checks already passed, and under the existing provider lock: + +* This is the held main provider, with its distinct held title fixture/keys configured. +* The first original main exchange has already retained exactly1 request and written exactly1 response. +* No earlier duplicate diagnostic has been claimed. + +Claim this diagnostic once, atomically. Do not increment original Requests/Responses, reset attempted/failure, start another model/HTTP request, change model admission, or grant a response. Concurrent duplicates, third/further attempts and all other original refusal reasons retain their original immediate refusal/cancellation with no additional body capture. Do not record a second refused-body slot or retry a refused/partial capture. + +### Explicit ordering and unchanged origins + +The diagnostic must read a body before invoking the original duplicate cancellation, because that cancellation tears down the handler's BaseContext and native child. This is an explicit narrow timing change for the one eligible already-refused request. It is not described as byte-identical immediate cancellation. + +After the original duplicate decision/one-shot claim, read at most1MiB+1 through the existing request body under the original unrenewed5second HTTP ReadTimeout and the unchanged original work/total contexts. Do not set a new read deadline, extend a server deadline, sleep, rebase an origin or add a new context budget. Check the original context before and after observation; a cancelled/expired origin refuses raw retention. The pre-existing native60second/work150second/total180second limits remain fatal. If the original work context expires during a partial body, existing final cleanup may abort the read; retain a finite refusal, not partial raw bytes. + +Regardless of capture result, the existing closed-provider HTTP400 refusal and failure=duplicate/cancellation path execute. Use an unconditional deferred failure/cancellation safeguard for the diagnostic branch so read/parse/privacy errors cannot return as accepted. No SSE/content response is sent for the second request, and no provider acceptance or counter increase follows. Any cleanup failure remains failure evidence. All other rejection paths remain immediate. + +## 3. Raw privacy/structure gate before retention + +A complete body must be nonempty, at most1MiB, UTF8 and one whole strict JSON object through the existing duplicate-rejecting finite Go decoder: depth32,16384nodes,4096byte keys, finite numbers, no trailing document. Reject array/scalar/null/malformed/duplicate/nonfinite/deep/oversized/truncated/invalidUTF8 without keeping raw bytes. + +Before retaining the object, check both original held main/title keys against raw bytes and the fully decoded/re-encoded object. Reuse the existing recursive Grok credential/privacy scanner on raw and decoded representations to catch JSON-escaped/nested strings and keys under its existing shared resource budgets. Any credential/private-namespace or privacy resource refusal retains no body. No file/profile/header/URL/private key or untrusted field is copied into diagnostic metadata. Do not redact a failing body into apparent safe evidence; raw retention occurs only after strict structure and privacy pass. + +A safe bounded strict object is retained opaquely even if its model/messages/other semantics are unfamiliar. That retention is diagnostic evidence of refused input; it is not a new provider schema. Do not classify the body as a retry/auxiliary/tool/result or act on its content. Keep the first original request/response and title request/response unmodified. + +## 4. Finite additional artifacts + +Add at most these two fixed Grok-only members, exported from the actual held main duplicate observation during existing finish: + +| Member | Bound | Purpose | +| --- | --- | --- | +| grok-provider.duplicate.json | 2048bytes | Closed diagnostic receipt, never successful provider evidence. | +| grok-provider.duplicate-request.json | 1MiB | Optional complete raw refused JSON object after privacy gate. | + +The receipt has exactly schema_version(integer1), role(`main-duplicate-diagnostic`), attempt_index(integer2), status, original_failure(`duplicate`), response_issued(booleanfalse), captured_bytes(integer) and captured_sha256(string). Status is exactly one of captured/body-refused/json-refused/privacy-refused/context-refused. Captured requires the raw member, exact positive byte length<=1MiB and recomputed lowercase64hex SHA256. Every refused status requires no raw member, captured_bytes=0 and captured_sha256 empty. No inferred cause/model, raw headers, profile content or arbitrary error string is serialized. No receipt is present when no eligible diagnostic was claimed. + +The original main receipt remains the original schema and failure duplicate with Requests/Responses1. The title receipt remains unchanged. New artifacts cannot coexist with another client, missing main/title fixture evidence, nonduplicate main failure, incomplete first exchange, unknown receipt fields/statuses or a successful runtime tuple. No raw member may appear without its receipt; no captured receipt may omit/mismatch raw. Existing historical failures without these members remain accepted unchanged. + +At the measured25member full capture, this adds at most2 members (27total); original per-member/archive aggregate/privacy bounds remain unchanged. Do not raise generic archive limits, admit arbitrary new member names or alter frontend evidence. The unchanged shared final privacy/secret-free checks must recheck exported bytes before persistence. + +## 5. Implementation boundaries + +Expected source edits are only: + +* Existing `agentplugins_grok_project_native_provider_test.go`: small private one-shot observation state, exact duplicate-only hook and finish export. The original context/route/auth guards, first request capture/model predicate, fixed response bytes, original refusal message/failure value, counters, transport configuration and stop/cleanup body remain unchanged except the explicit bounded-before-cancel diagnostic ordering above. +* New small dedicated Grok refused-duplicate helper and inert contract files for bounded capture/privacy/schema and actual HTTP refusal behavior. +* Existing `scripts/project_native_runtime.py`: one exact Grok duplicate-member validator and narrow allowed-member union. Existing main/title receipt/response validators stay byte-identical. +* A focused Python contract addition covering only the closed new diagnostic; preserve all old tests/outcomes and exact selectors. Add no workflow/new native lane. +* Accepted plan and concise factual validation documentation. + +Keep main/title model configuration, environment, title handler/schema/SSE, original user HOME/profile freeze, Prepared/untrusted/disabled inspection, public installation, command fatal barrier, all process/supervisor/environment/authority guards and other clients byte-identical. No change to the original number of accepted main/title exchanges or any qualification field. If implementation needs another path/behavior or privacy/budget exception, stop for exact amended review before editing it. + +## 6. Meaningful checks and next actual evidence + +Under root's serialized test slot, inert HTTP contracts perform the actual first accepted main request/response and one eligible second request to the held loopback fixture, with an independently authored harmless JSON object and separate held title key. Assert first response bytes/counters unchanged; second is HTTP400, never SSE/200, original context cancels, Failure duplicate remains, accepted counts1/1 remain, only one diagnostic retained, and ordinary existing stop behavior or truthful forced failure. Synthetic capture is not native evidence. + +Exercise malformed/duplicate/trailing/nonfinite/null/array/deep/node/key/UTF8/oversize/partial bodies and raw/escaped/nested main/title credential strings and keys. Check zero raw retention on every refusal. Wrong/multiple auth, route/query/method/host/MIME/loopback/context, incomplete first exchange, title-provider duplicate and third/concurrent attempt cannot acquire another capture. Partial-body cancellation stays within existing server/original origins, fails truthfully, renews no context and sends no accepted response. A new synthetic raw object must remain ordinary data; no real tool/model is invoked. + +Both Go artifact helper and independent Python reader contracts enforce exact member/receipt relation, status typing, byte/hash/privacy revalidation, fixed role/index/failure/false response flag, first-exchange receipt joins and client isolation. Coherent hash-updated malicious objects must fail privacy/structure, not only mismatch hashes. Unknown/extra receipt fields, altered counters, forged success flags, missing title/main evidence, raw without receipt, duplicate raw slot, unknown files and refused status with a body refuse. Existing failed-only runtime boundary/allfivefalse remains mandatory. + +Execute focused inert Grok provider/title/project/native-runtime contracts plus affected Python runtime contracts, gofmt/whitespace, affected all-enabled lint againsta412ba5e137fdcb7e004393229b323211ed746e1 and whole unfiltered revive/funlen/gocyclo/gocognit/dupl/depguard. Record actual command/source/exits/log hashes; no skipped predicate or revised production assertion. No native/vendor/client/public installer/scanner execution locally. Broaden only for concrete remaining risk. + +Scratch pure readers replay unchanged34825member capture and prior61121member/representative earlier captures with their original exact source identities; original bytes/hashes/outcomes remain unchanged. Preserve inherited current Cline success independently. No old capture is relabeled or promoted. Obtain two full exact-source CODE critics and measured gates before root normal-FF publication. + +Only a fresh genuine current-source hosted capture may provide the actual second raw object or precise refusal metadata. Audit its exact source/tree/pins/public/inspection/first/title/duplicate evidence and full fatal/cleanup boundary independently before any new proposal. Even an observed safe second JSON body grants no response or completion; this lane remains failed/allfivefalse/runtimefalse/authfalse/modelfalse. No speculative second-auxiliary response, retry allowance, trust change or generic provider admission follows. + +## Baseline preservation anchors + +| Path | SHA256 | +| --- | --- | +| `repotests/agentplugins_grok_project_native_provider_test.go` | 6cb8f0f06e3e8a5ffec6dfb654e1566000ac9ca8dec1d450c4aa0d27a2421c65 | +| `repotests/agentplugins_grok_project_native_route_test.go` | e41da580ebea3277d155e6a443149bb4463b65cc40fcf3fa3fa6d6f7ba78ccf7 | +| `repotests/agentplugins_grok_project_native_title_test.go` | 76d595042a2fc2d3639cb4dad0568b8691485f342101689a76a97d844cf27658 | +| `repotests/agentplugins_grok_project_native_privacy_test.go` | 4df4765a0caec14e4aa55963cb3000e1204ed0a9c1d1afe122d46cf6210c5164 | +| `repotests/agentplugins_project_native_cli_attempt_test.go` | 07f40ed4e0def62f58698cd6d8ce839d407e6bc751e952680460affe39670791 | +| `repotests/agentplugins_grok_project_native_attempt_test.go` | ee4f42b4574190e5350a1dde9f0bc4f90517bd5d1da70d27d78916e317f1aea4 | +| `repotests/agentplugins_grok_project_native_environment_test.go` | dbbae16a28cfe9e893c30475ba8bfac343b41b06c3c264ad7dc0d2965b69cdb4 | +| `scripts/project_native_runtime.py` | 24f39c61856d689535ac2fea81e01890caa780f4b74630b78c12c0df39cd6aee | + +This draft author read source/raw and wrote this scratch proposal only. No implementation, tests/gates/native execution or Git/GitHub mutations occurred for this plan. + + +## Accepted refused-duplicate diagnostic plan before implementation + +Root R1 GO receipt SHA256 `089275e9328eff1c195bbc1915d56391868c0de6c7c4c7ba48c95aaab2f62969` and independent Kimi R2 GO receipt SHA256 `60d45fb3f2056ac8bfa2d9f0a8809efd9b372dbe7c720fc8c4097c9558aad4df` approve the exact draft above, SHA256 `670b875b7f2283058fcf3220a5cc3b222831e368c72b95bc10392e8bb233c0db`, before any implementation. + +The draft-time pending root RAW review is now complete: root receipt SHA256 `32168fb35ff64fedba411599187159a3090a4556ccc4ce101a937549019ee423`, alongside independent peer receipt `95a89455a84656cac9b6122258a54579ea8d1a45e3e780888cf2ca8934313fb1`. Both preserve actual348 failed-only source-bound evidence; no qualification follows. Current native and inherited Cline jobs are complete and retained. + +Both critics require the one eligible duplicate/first-failure decision to be atomic with no earlier failure overwrite, with no provider mutex held during bounded body/privacy work. Every path must execute original HTTP400 and duplicate cancellation, keep accepted counters1/1 and original read/work/total origins, and export only a settled finite receipt with no partial or private body. Independent Python finite byte/number validation and concurrent/partial-read controls are required by the accepted plan. Root alone owns commits/publication after two CODE GOs and measured gates. + + +# Grok: supported suppression of the measured post-turn dashboard summary + +PRE-CODE PLAN DRAFT. Root and Native must independently approve this exact plan SHA256, and root must commit the accepted plan before implementation. This author performed source/raw reads and wrote scratch research/this proposal only. No implementation, test, native/acquired executable, public installer or scanner runs locally for this plan. Root alone owns Git/GitHub mutations and publication. + +## 1. Exact source and measured reason + +Plan base is published Grok source `b40da640c388d807cc3dc1fbb36c38e0d1b411ef`, tree `71b98e819bdd555154096c9c75850b9b40d3f54c`. Run `40610932429`, native job `122562097571`, has completed. Its actual evidence received two independent RAW GOs for failed diagnostic evidence only: + +- Root `/tmp/uap-grok-b40-current-40610932429/root-raw-audit.json`, SHA256 `6fb432088fa50f3093c7240911be9695186fb3c077f3bdb96c1b28fcf464785c`. +- Peer `/tmp/uap-grok-b40-independent-40610932429/independent-raw-audit.json`, SHA256 `88c920ace4af34d476cdf133ea769751c136503e9149673718dfa48e3075fd9e`. + +| Artifact | ID | Outer SHA256 | Inner SHA256 | Members | +| --- | --- | --- | --- | --- | +| Grok native | 12782438155 | 9cf3260936143bed9bf9bc53fe29fe67db8fe4ffa01ae720e45ecbfe691141a2 | 86ed0632dd8345b0a224d77563ca5f44708e69a35829524076d668ffd7946f16 | 27 | +| Grok frontend | 12782527675 | 8d331a482694e89814f8e6e7ff875d3f836ce86ad95ac98a5a9eac7c41e5deb0 | ee229372d55c8e33c1aec9379734ec695e26d5d106f62acde0a8566ce59a1ce8 | 25 | + +The diagnostic retained the complete second main-route body: 1,424 bytes, SHA256 `303ad4817a23b68aa02717ecedb70404362cf1397324da5a4d25d12f60a86bb5`. Its receipt is index2, role main-duplicate-diagnostic, captured, original_failure duplicate, response_issued false. Original first main and separate title counters/responses remain untouched. This is a measured dashboard summary request over the completed reply, rather than an assumed retry. + +Its system message is exactly the pinned `TURN_SUMMARY_SYSTEM`: 1,022 UTF8 bytes, SHA256 `9411e91b009d5f6672caa80f5aa9fa2eadbf4d1aa12d0b1488238afa64f58103`. Its user message exactly equals the source wrapper around the actual first main request's final user content and the fixed `UAP_GROK_PROVIDER_CAPTURE` reply. The model is the original main fixture, with streaming and include_usage true. The provider correctly refused this additional request and kept original cancellation. + +Public add and Prepared ownership checks do not establish native activation. The measured project remains untrusted; the selected plugin is disabled and skills/MCP inventories are empty. Runtime and all five qualification flags, authentication and real-model qualification remain false. No old capture is upgraded by this proposal. + +## 2. Pinned primary source and lineage limits + +All cited files belong to public `xai-org/grok-build` tree `2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8`. Its complete 5,102-entry tree is non-truncated. This author independently recomputed Git blob SHA1, SHA256 and lengths for all eighteen previously retained title/source files and thirteen additional turn-summary files, and verified their paths/blob identities against that exact tree. Both actual ZIPs above were independently rehashed and every closed member's size/SHA/CRC checked. + +Primary research receipt: `/tmp/uap-grok-turn-summary-kimi-primary-research.json`, SHA256 `3de4e360348bd3188ea35f584fb48538f3dba89245f78801d0ed3e0d8daae930`. Native independently researched the same source: `/tmp/uap-grok-turn-summary-native-research/independent-source-research.json`, SHA256 `5e72513507b8d734cd87aade73afc448dec03c2b2f88cc99f5fb0feea9d6ca74`. + +| Source path under `crates/codegen/` | Exact Git blob | Source fact | +| --- | --- | --- | +| xai-grok-config-types/src/registry.rs | 645a34036de2a83981f8f90ba0e2cf0d7a334a40 | Lines229-236 define TurnSummary, key turn_summary, path features.turn_summary, env GROK_TURN_SUMMARY, default true. Feature::resolve uses requirements/environment/config/remote/default. | +| xai-grok-config/src/resolved.rs | 7ee2b55aa5d44dd399579b8eac7250173f28b35e | BoolFlag resolve around95-134 chooses requirement, CLI, environment, configuration, managed, remote, default in order, preserving explicit false. | +| xai-grok-shell/src/agent/config.rs | a430462ad3b8e0611368ad2051f093827d7babee | FeatureEntries around4224-4271 reads native TOML booleans;1492 maps them into feature_values. is_turn_summary_enabled1846 resolves Feature::TurnSummary; feature_sources2033 supplies the actual config tier. | +| xai-grok-shell/src/session/acp_session_impl/spawn.rs | 323fe32f305231bfac22aa60d43f3123efd1c38e | Actor captures effective_config.is_turn_summary_enabled() at2077; title_refresh is resolved separately around1778. | +| xai-grok-shell/src/session/acp_session_impl/turn_summary.rs | 84915ba9062b6fd394382e334cd35340133ad08e | restart_turn_summary returns before work if its gate is false or subagent. The helper is display-only and builds a tool-free request from the exact system/user source below. | +| xai-grok-shell/src/session/helpers/turn_summary.rs | 382718b80599b54c0bd985eb819a044ced13b8b3 | TURN_SUMMARY_SYSTEM and turn_summary_user_message match the measured second request byte-for-byte. last_turn keeps the actual last prompt and visible reply, excluding reasoning/tools/results. | +| xai-grok-shell/src/session/acp_session_impl/title_refresh.rs | e67a787a99e94cdc448276beba6018409158f45a | Later whole-conversation title refresh checks its own resolved title_refresh_enabled before starting. | +| xai-grok-shell/src/session/helpers/session_summary.rs | 0f2af81e78701043b8446a8bffb2272921f3802c | Initial session-title fast path starts around188 and is distinct from per-turn dashboard summary and later title refresh. | +| xai-grok-pager/docs/user-guide/26-config-reference.md | f5aa92e8599e821d81ca73b39049513bab4be2c0 | Row233 documents features.turn_summary boolean, default true, and GROK_TURN_SUMMARY. | + +The public tree is not proven equivalent to shipping Grok1.0.70/f50904eaee6c. Existing source research explicitly retains that limit. Exact prompt correspondence establishes the observed request's specific source match; it does not prove every config path in the binary. The setting below is a supported-source candidate requiring a fresh genuine hosted observation. Unsupported/ignored/overridden settings must remain a truthful failed observation; do not add a fallback or reinterpret the response. + +## 3. One fixed configuration change before original freeze + +Append exactly this native TOML suffix to the existing complete two-model configuration during `configureGrokTitleProvider`, before its only original HOME config write, authoredConfig clone and protected digest freeze: + +```toml +[features] +turn_summary = false +``` + +The exact appended bytes are one separating LF, `[features]`, LF, `turn_summary = false`, LF. The existing recipe already ends in LF. No dynamically discovered section/key, parser rewrite, config migration or file adoption is involved. This is a real TOML boolean, not the string false and not a `[turn_summary]` sampling-settings table. + +Keep the same `f.base.Home/.grok/config.toml`, ordinary0600 write, original parents, original HOME/CWD and both fixed model endpoint/key references. Author the complete recipe once; only then write, clone and hash it. Do not edit config after the native client starts, renew a baseline from native output, change originalConfig/protected/immutable maps after execution or introduce another HOME/profile. + +The existing child environment is constructed from a closed fixture environment and contains no GROK_TURN_SUMMARY or GROK_TITLE_REFRESH. Leave it byte-for-byte unchanged; do not add environment overrides as a second mechanism. Test that ambient true values remain excluded. No requirement/managed config override, remote fetch, credentials, trust, plugin enablement, tools or permission setting is introduced. + +### Acknowledge the later-title-refresh effect + +`Config::resolve_title_refresh` around2055 defaults to the current TurnSummary result when title_refresh is unset. Therefore false also makes later whole-conversation title refresh default false in this recipe. This incidental suppression is explicitly intended and bounded to optional post-turn display work. Do not claim that turn_summary exclusively controls one request. + +Keep the existing initial session-title route/model (`models.session_summary = uap-grok-title-fixture`) and all its request validation/response/counter/cleanup unchanged. Its independently observed first-title request remains expected; the setting must not be used to remove that fixture or admit a different title shape. Do not add features.title_refresh as a separate setting. Shipping behavior is measured by the next raw capture, including truthful partial/failure outcomes if it differs. + +## 4. Preserve every existing native/protocol authority + +There is no new native/public command, argument, retry, model, endpoint, request grammar, response, counter or artifact slot. Preserve all original main/title route/auth/type/body bounds and exact fixed first SSE bytes. Preserve the new refused-duplicate one-shot claim, original first-failure reservation, unlocked bounded read/privacy, done handoff/export race checks, HTTP400 and unconditional original cancellation. If any second main request still arrives, the existing diagnostic must refuse it and retain only its already-admitted bounded opaque evidence; no successful summary response is added. + +Keep original work origin150seconds, final origin180seconds, provider read/write5seconds, command contexts and ordinary full-descendant process supervision, original config/pin checks, raw privacy/resource bounds, no-follow filesystem/Prepared ownership checks, source/version/scanner pins, inspect command, no-probe/no-provider-qualification semantics and final all-false barrier. In particular, native exit0 still leads to the unchanged intentional raw-review fatal barrier; a clean startup does not activate or qualify anything. + +The current 27-member capture reflects a reached duplicate. The existing reader already permits its actual finite optional duplicate presence/absence. Do not require a fabricated duplicate when suppression works, or remove a real duplicate when it fails. Keep exact existing allowed member unions, summary and failed-only modes. No Python reader or contract is changed by this configuration amendment. + +## 5. Exact implementation surface and preservation exceptions + +After two exact-plan GOs and root's accepted pre-code commit, implementation is limited to: + +1. `repotests/agentplugins_grok_project_native_title_test.go`: append the fixed suffix to the complete original recipe at the existing configureGrokTitleProvider construction point, before the existing sole write/clone/hash. Preserve every other function in the file whole-byte, including title.configuration, all title request/response/serve/stop handlers and credential/listener setup. The minimal implementation can append the suffix directly to the existing returned recipe; no generalized configuration framework is warranted. +2. `repotests/agentplugins_grok_project_native_title_configuration_test.go`: add only `features: {turn_summary: false}` to the existing independently stated expected TOML map. This explicit one-literal expectation exception is necessary because the fixture recipe changes. Preserve the remainder of that test and every existing assertion; do not rename, skip or weaken it. +3. A new `repotests/agentplugins_grok_project_native_turn_summary_contract_test.go`: narrowly focused inert contracts for the fixed supported suffix, original freeze and ambient isolation described below. It may use the existing owned in-process fixture providers; it must not run acquired/native/public installer/scanner executables or impersonate a hosted environment. +4. Append concise factual plan/validation to the existing Grok validation record and accepted plan to `docs/plans/project-install-e2e/remaining-native-qualification.md`. Distinguish old failed evidence, local inert checks and future hosted results accurately. No raw capture/log/source vendor payload is committed. + +Everything else is whole-byte preserved: all main provider, title handlers/configuration helper, duplicate/privacy/route diagnostics and existing contracts, selected project/Prepared authority, shared command/environment/process functions, collectors, Python runtime/Grok readers/contracts, production adapters/shared/VSCode, workflow/selection/pins and all other client files. New feature data is authored before the old guard's original freeze; no guard predicate or exception changes. + +## 6. Meaningful inert controls and exact-source gates + +The existing independent whole-map configuration contract must pass with its one extra boolean subtree. New inert contracts should demonstrate these concrete boundaries: + +- Raw authored TOML equals the unchanged complete two-model recipe plus the one fixed suffix, and parses to the complete independently stated map with exactly one features table containing only boolean turn_summary=false. Preserve every original models/model/marketplace field, both distinct held listeners/keys, max_retries0 and env-key names. Reject accidental string false, true, duplicate section/key, extra feature or sampling-table substitution through the expected-recipe contract; do not make a new native parser. +- The same original HOME config is written and captured once with ordinary0600 metadata and protected digest covering the complete bytes; no project config is created. Keep every prior original-route/config-freeze assertion. Existing actual protected-config fatal-child controls remain unchanged and run in the focused gate; do not add a redundant new guard implementation or weaken fatal behavior. +- With ambient GROK_TURN_SUMMARY=true, GROK_TITLE_REFRESH=true and foreign HOME/model settings, the constructed child env still contains neither flag and keeps the original held HOME, both keys and existing shared-client optout. Parent process environment remains unchanged; other clients receive no Grok feature/env authority. Add these controls in the new file rather than changing old environment contracts. +- Existing strict first-main/title and refused-duplicate positive, malformed/privacy/Unicode/resource, partial/concurrent/third-request and closure tests run unchanged. A feature setting is not authority to alter provider behavior. No synthetic test claims that the vendor binary actually consumes the setting. + +Root owns the serialized gate slot. Execute meaningful focused `TestGrok`/`TestProjectNativeCLI`/`TestProjectNativeRuntime` contracts and affected Go package checks with no vendor/native/public installer/scanner, plus affected Python runtime/Grok contracts or precisely recorded whole-tree transfer if every relevant Python byte is unchanged. Run gofmt/diff, affected all-enabled lint against `a412ba5e137fdcb7e004393229b323211ed746e1`, and whole unfiltered revive/funlen/gocyclo/gocognit/dupl/depguard. Record actual exact source/tree/commands/exits/log SHAs and real test counts/skips; do not predict them or transfer an old pass as a new run. + +Verify whole-file/function preservation against the actual accepted pre-code base, including all Python modules/contracts and every protected handler/duplicate file. Replay actual b40/348 failed archives through original and candidate full readers with original source identities; raw ZIP/member/result hashes and all-false outcomes must remain identical. If root transfers a prior replay on a byte-identical complete Python tree, document that exact tree and receipt explicitly. No AST-extracted substitute reader or relabeled capture is allowed. + +Freeze the exact source manifest and get two independent full/delta CODE reviews before root publication. The author does not approve their own implementation. + +## 7. Composition, hosted observation and remaining qualification + +This plan's evidence/recipe base is exact b40. Root separately owns program-parent composition. The read-only composition receipt `/tmp/uap-grok-parent888-readonly-composition-kimi.json`, SHA256 `eae34e2d1eb5d25c4b3e0286226fcb4380ee5caf291de33229090e214758bff7`, identifies three documentation insertion conflicts and clean disjoint Python composition. If root composes qualified Kimi parent before this amendment, preserve both full leaf readers/tests and record the actual new pre-code base/tree; these b40 hashes remain historical anchors, not permission to replace combined files with the Grok side. No unpublished stale083b or named1109 baseline path is changed or discarded. Other clients' completed/in-flight evidence remains preserved. + +After reviews and measured gates, root alone publishes by the normal reviewed route and dispatches the unchanged genuine hosted workflow on the exact published source. Do not retry unchanged, supersede/cancel another pending capture, run the vendor locally or forge hosted eligibility. The next run must retain source/build/pin/API/closedZIP, public add and inspect raw, exact first main/title exchanges and ordinary cleanup. Independently audit whether a second main request exists and its truthful receipt/status. Absence after this supported setting is a new measured fact only on that fresh source; old b40/348 remain failed duplicate observations. + +If the setting is consumed and native exits cleanly, the existing intentional barrier still fails with every flag/runtime/auth/model false. If summary or any other unexpected request persists, existing refusal/cancellation/diagnostic applies and no fallback is authorized. If config or original process/authority guards fail, stop and retain that failure. No new request shape, title response, trust/enable transition, registry admission or runtime qualification follows from this amendment. Any later activation/tool/lifecycle success path requires its own actual evidence and two pre-code reviews. + +## Baseline preservation anchors + +Hashes below are from exact b40. The first two files have only the narrow recipe/expected-map exceptions above; remaining files are whole-byte anchors. A separately reviewed parent composition must establish its own complete combined pre-code anchors before implementation. + +| Path | SHA256 | +| --- | --- | +| `repotests/agentplugins_grok_project_native_title_test.go` | 76d595042a2fc2d3639cb4dad0568b8691485f342101689a76a97d844cf27658 | +| `repotests/agentplugins_grok_project_native_title_configuration_test.go` | 788c9ecd7c818f5477eff713960f3710d8d435d3e53b89e01af74ef1c16b0f87 | +| `repotests/agentplugins_grok_project_native_provider_test.go` | 90012dcf96cdfb882470e31a3feb52688d570ae64d9f356410ad7910c944e950 | +| `repotests/agentplugins_grok_project_native_provider_contract_test.go` | 6aeaf8af3953324b28075e042835b4875e3e66e2c780b831c4fb30bc93bba801 | +| `repotests/agentplugins_grok_project_native_privacy_test.go` | 4df4765a0caec14e4aa55963cb3000e1204ed0a9c1d1afe122d46cf6210c5164 | +| `repotests/agentplugins_grok_project_native_duplicate_test.go` | 53a29267624dd1e308f7a3fce50499c87308fec08581c9b1f7bbe807f7b91be3 | +| `repotests/agentplugins_grok_project_native_duplicate_evidence_test.go` | b2bcab1adcb962055232a72884505add1e8effdc32aaaf729cc0d3a608f91ea6 | +| `repotests/agentplugins_grok_project_native_duplicate_contract_test.go` | c81bc20643000614d26ef0c021b75bee56b4a7e6648325c9c91068f0420a98ff | +| `repotests/agentplugins_grok_project_native_title_contract_test.go` | f86600f85c5c24b7c7c08b10d6694d163b71aad092c3f3860c951a859a229b83 | +| `repotests/agentplugins_grok_project_native_environment_test.go` | dbbae16a28cfe9e893c30475ba8bfac343b41b06c3c264ad7dc0d2965b69cdb4 | +| `repotests/agentplugins_project_native_cli_attempt_test.go` | 07f40ed4e0def62f58698cd6d8ce839d407e6bc751e952680460affe39670791 | +| `repotests/agentplugins_grok_project_native_attempt_test.go` | ee4f42b4574190e5350a1dde9f0bc4f90517bd5d1da70d27d78916e317f1aea4 | +| `repotests/agentplugins_grok_config_contract_test.go` | 3153076a942ec8baf01c9b9d6e9ce328594e6dcbaa9f88f8e7eb25a2fd20ad47 | +| `repotests/agentplugins_grok_config_diagnostic_test.go` | 8c23fe40cf5fcddad742ae325f8c3cca5032dc00a88d2deaf278944e03fa5fc8 | +| `repotests/agentplugins_grok_config_refusal_test.go` | f32c416faeb9027bbed958317b563a23aff32a570e4170e2eea5dd24668139d4 | +| `scripts/project_native_runtime.py` | b8ab281512d562903f90a44fca1765a5d05b55bc977e9f0e71fd6c53de4f2d19 | +| `scripts/test_project_native_runtime.py` | d0ee55f930ed8ff790beb1b8a67baca894c7ee422f7b646949559a6c5136f82f | +| `scripts/project_native_collectors/grok.py` | 95ef4b4f3bad0c4dfbb82c652f834660ee0d994899ab0059a772d848517af56c | +| `scripts/test_project_native_grok.py` | 8e1d3c3267b55cfa29446a9d0509799fc9ff84d24256d1879d449e86b9d31fea | +| `.github/workflows/project-native-runtime.yml` | 11140878d0b2ca882a76b0a5830b25a1c5763247d76fec2c3dea9b22ba8096a1 | + +This draft author performed pure source/ZIP/hash/JSON reads and wrote scratch research/plan files only. No source implementation, Go/unittest/lint/vendor/native/public installer/scanner execution, Git/GitHub mutation or publication occurred for this proposal. + + +## Accepted measured turn-summary suppression plan before implementation + +Root R1 GO receipt SHA256 `a71549915420aa61f90a7ab05586e1e2280a516fa0b78eb3a0cfb2f48d45fd69` and independent Native R2 GO receipt SHA256 `a29a83c1a3b8fdf2ea5d70bc3e9e009ef88e58e974cc170d89b40ef07e90332d` approve the exact draft above, SHA256 `3bc56715f4b326634f99210fc7bde69a711c2edbe7c2880fece74ec950c4593e`, before implementation. Both independently reconstructed the captured summary messages from pinned primary source and verified all historical baseline anchors. + +This pre-code commit retains original published b40 as its code base; parent composition is separate. Only the fixed supported TOML feature suffix before the original configuration freeze, one complete-map expected literal, inert contracts and validation documentation are authorized. Original initial-title/main/duplicate handlers, environment, deadlines, config/pin guards, readers, privacy, workflow and failed-only review barrier remain unchanged. Fresh hosted evidence must establish behavior of the pinned shipping binary. Root alone commits and publishes after exact CODE reviews and measured gates. + + +## Accepted Grok exact project trust and selected skill diagnostic + +Pre-code base: `91402cd32f39342aaac9175c9e76bddbd360f2df`, tree +`c2f198f3ccf2bd8abfc4675c01cb2f153c1ad444`. Root and independent +other_clients PLAN GO were received before implementation, with the conditions +below. Publication still requires independent CODE reviews and actual gates. + +The genuine run40615904857 observed clean native exit with one strict main and +one initial-title fixture exchange, while the project remained untrusted and +the selected skill absent. This amendment only captures the next boundary. +All five qualifications, runtime completion, vendor authentication and real +model qualification remain false; the intentional raw-review failure remains. +Grok project MCP is unsupported. Full installed lifecycle is separate work. + +### Exact supported fixture setup + +Before any child, append `[plugins] enabled = ["uap-grok-attempt"]` to the +complete existing original-HOME two-model recipe, before its existing final +`[features] turn_summary = false` table and sole config write/clone/hash. +This is a supported name selector, not path-specific activation. The selected +physical ID depends on the subsequently generated installation UUID. The +existing manifest, binding, source and delivered-byte checks must still join +the single selected ordinary project package. + +Author original-HOME `.grok/trusted_folders.toml` once, exclusively, mode0600, +with only `[folders.""] trusted = true`. +Check the ordinary canonical project and original HOME, refuse HOME/root/B, +overlap and aliases, and freeze the complete trust bytes under the existing +protected digest before any child. Refuse existing trust/config entries rather +than replacing or adopting them. No timestamp is needed; the publisher makes +`decided_at` optional. No `--trust`, trust feature optout, environment override, +`plugin-dir`, `[plugins].paths`, registry mutation or native tool is added. + +### Measured selection and failed-only admission + +Preserve the old disabled/untrusted Prepared checker and its negative tests. +Share its identity checks through an explicit expected-selection boolean; the +new selected branch requires exact canonical project/plugin identity and both +trust booleans true. It additionally requires exactly one selected skill with +the authored name/description, `userInvocable=true`, disabled absent or literal +false, and exact source `{type: "plugin", plugin_name: , path: +}`. Reject folded identity aliases, +duplicates, foreign paths and a trust-only plugin entry without that skill. + +Use the same real `native-inspect` command, cwd, deadline and held source +authority, with an explicit selected-mode call. Add one finite Grok-only +`fixture_selection` marker before inspection. Record completed selected +`native_inspection` only after the exact raw check passes. The existing Python +reader gains only a strict optional marker branch: join completed observation +to raw inspection, successful exact inspect command/cwd, binding/profile/target +and installed skill. Preserve failure-before-inspection capture and old +unmarked archives, the closed members and every failed/all-false predicate. +Do not add an artifact slot or infer load from plugin `enabled` alone. + +The strict provider is unchanged. Loaded skills may change its next request; +an unrecognized shape is refused and captured under existing failed-only +diagnostics. No success shape, extra response, retry or protocol allowance is +introduced before genuine fresh raw review. + +### Bounded code and validation surface + +Change only Grok title recipe/whole-map expectation, Grok inspection helpers, +the shared attempt's Grok-only call, new focused Grok selection contracts, +the runtime reader and focused reader contracts. Existing turn-summary +contracts need only the complete raw recipe and protected count2 expectations, +plus creation of the now-required ordinary project in their inert constructors. +Putting `[plugins]` before `[features]` preserves their duplicate-key and +extra-feature negative controls. Preserve provider/title handlers, duplicate, +privacy, environment, production adapters, workflow, pins and all deadlines. + +Run meaningful inert Grok/shared Go and Python runtime contracts, gofmt, +affected all-enabled and complete six-linter gates. Replay actual old archives +through complete old/current readers with original source identities. Obtain +two independent exact CODE reviews before root publication and the next genuine +disposable hosted run. No vendor, public installer or scanner runs locally. + +### Pinned primary source guidance + +All publisher paths below are under `xai-org/grok-build` tree +`2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8`, prefix `crates/codegen/`. +The snapshot is not proven equivalent to shipping1.0.70/f50904eaee6c. + +| Path | Git blob | Relevant contract | +| --- | --- | --- | +| `xai-grok-permission-rules/src/trust.rs` | `9411e22ab04953bee3d9d68558e6b1bf0d896ab5` | Original-HOME exact folder TOML; optional timestamp; broad-root refusal | +| `xai-grok-workspace/src/plugins.rs` | `9cd0d8aef4b2336635db5627b3e77431b5378c45` | User config enabled names or IDs | +| `xai-grok-agent/src/plugins/discovery.rs` | `96b8caae336c6ae7a210141922e1454dacec7817` | Project trust; unlisted plugins disabled | +| `xai-grok-agent/src/plugins/registry.rs` | `31112af7aac163927f63e6c61f2e21268e5359f8` | Active requires enabled and trusted | +| `xai-grok-shell/src/inspect/mod.rs` | `bbbe3da44a5c73046ac0b514e3c124283f865afa` | Plugin enabled means trust; separate loaded skill source; camelCase userInvocable; false disabled omitted | +| `xai-grok-agent/src/prompt/skills.rs` | `d70dcb18f5845ced0d1129d490773703bcf0eac1` | Only active plugin skills get exact source stamp | +| `xai-grok-tools/src/types/config_source.rs` | `11f16ad8aee5243aa71c1697ac9c0f2a1645463a` | Plugin source serializes plugin_name and path | + +The separate native `plugin enable` command is unsuitable for this prepared +directory: `xai-grok-pager/src/plugin_cmd.rs` blob +`36b53be9bc0e22eed45bc437573595072289548a`, lines610-622, requires native +install-registry membership. No such registration is invented. + +### Implemented selection diagnostic validation + +Two independent Sol/xhigh CODE reviews and the root review accepted the frozen +nine-file candidate. Focused Go contracts passed (62 top-level runs, 271 pass +events; one existing child-only helper skip), all 61 Python contracts passed, +and affected all-enabled plus complete six-linter gates reported zero issues. +The full original 91402 runtime archive replays identically through the original +and current readers (25 members and the complete failed/all-false result). +The Grok-only reader branch also has explicit unchanged-result coverage for +Qoder, Kiro and Windsurf. No vendor executable ran locally. These are source +and inert-contract results; fresh genuine hosted evidence remains required for +selected-skill observation and subsequent complete lifecycle qualification. + +### Grok complete supported skills lifecycle (2026-10-28) + +Real hosted run40717447621 at f794d6d1 now confirms the exact selected loaded +skill/source/description/userInvocable serialization and canonical path; native +inspect/startup exit0, main/title each1request/response and ordinary shutdown. +Its only test failure is the existing independent-raw-review barrier; all +qualification flags remain false. Native artifact12834025060 outerSHA256 +bfdef920511b1c1c6200a349b2c4d86cbca85c053ccdbf203400b2010150e658 and inner +49fee32d67edd1f59297025f96a0bcfc6928abc3a03660ce775adbda2fd48b00 retain +the exact source/pins and25closed members. Two independent full raw audits +confirm this failed capture and its unchanged all-false result. + +Two independent pre-code reviews accepted one bounded skills-only +cycle: qualify only project-lifecycle/native-restart-removal; project MCP, +vendor authentication, real models and skill execution stay unsupported or +unclaimed. Persisted Grok bindings stay Prepared/package_validated. Reuse the +existing hosted admission, original HOME, pins/scanner/process supervision, +150s work/180s total origins,30s commands and closed archive bounds. + +Before any child, author/freeze original HOME config with supported exact +A/B/user enabled names and disabled foreign A/B names; trust only canonical +owned A+B. Hold independent original-user and foreign A/B native plugin/skill +bytes, inode/owner/mode/membership and caller data through every operation. +Distinct local A/B skills carry nonce/revisions; A-one updates to A-two at the +same version. Direct local repair keeps A-two frozen and requires its recorded +digest (repair_multi.go and installer/package.go), never invented remote pins. + +Exactly14 public commands cover3Git roots/version, addA/B, repeatadd, +unchangedupdate, intactrepair, same-versionupdate, missingpackagerepair, removeA, +repeatremoveA and removeB. The public CLI repair_many/RepairGroup route restages intact repair +(mutated=true: lifecycle.go75-105, repair_multi.go321-328 and group_plan.go162); +missing exact owned-package repair restores the recorded bytes (mutated=true). +Actual public inert execution confirms partial-leaf absence refuses unchanged, +while whole package absence repairs. Validate the sole managed_package_directory +receipt and derived A/.grok/plugins/ plus closed source/native bytes. +Nonrecursively remove only plugin.json, skills//SKILL.md, +.grok-plugin/plugin.json and their known empty directories, ending at that exact +active root; refuse any extra entry, never RemoveAll. Keep state/source and +parent/foreign/User/config/data authority, then require exact active-path absence. +Installed skill-only packages have zero data receipts; default final-binding +removal creates real retained owned data/marker/receipt, validated from +remove_prepare.go and PluginDataManager and preserved afterward. + +Exactly20 fresh `inspect --json` processes cover outside/A/B initial, installed, +stable, updated, damaged, repaired and removed/repeated/final states. Require +whole exact plugin/skill catalogs, authored description plus exact source path, +invocable/disabled semantics and unchanged User/foreign controls. Plugin.enabled +means trust only. Damaged A lacks its selected plugin and skill; final A/B/outside lack both +managed sources. No provider or extra native activation command is needed. + +Exactly4members contain summary/public/inspect/proof JSONL with bounded raw +bytes/hash/base64 records and source/state/binding/ownership/removal joins. +Apply the existing Grok privacy scanner before encoding and after strict +decoding (no provider exists to trigger the old conditional keep scanner). +Failed actual prefixes and every late failure keep all flagsfalse; only full +closure plus ordinary cleanup/preservation and actual test exit0 can qualify +the two applicable flags. Retain old diagnostic evidence/readers unchanged; +no artificial transition review fatal, new framework, workflow or client pin. +Obtain exact independent CODE reviews and focused inert Go/Python plus affected +and complete six-linter gates, then one genuine hosted full-cycle capture and +independent raw audit. No vendor executable runs locally. + +The implemented leaf24dcb5f0 has two independent exact-source CODE GOs and +preserves every existing failure/ownership gate. Its composition with qualified +program parent73208da2 has two independent reviews: all owned Go bytes remain +identical, and the two shared Python files retain each reviewed client branch. +On the composed source,64 focused top-level Go contracts pass (274 pass events; +one unchanged child-only helper skip), the real inert public CLI repair contract +passes,82 Python contracts pass, and affected all-enabled plus complete six-linter +gates for both Go modules report zero issues. The original f794 native25-member +and frontend25-member captures still replay to the identical failed/all-false +result. These local gates do not qualify native behavior; the published complete +cycle requires one fresh genuine hosted capture and two independent raw audits. + ## Gemini owned-MCP listing diagnostic amendment Accepted plan SHA256 `6c696cd8309f6da3ee9059ef887b14bb8358ed5e4fd57afe199a69b2655b3d5d` diff --git a/install/integrationctl/agentplugins/clients/grok/project_contract_test.go b/install/integrationctl/agentplugins/clients/grok/project_contract_test.go index c9c230a44..6151463f0 100644 --- a/install/integrationctl/agentplugins/clients/grok/project_contract_test.go +++ b/install/integrationctl/agentplugins/clients/grok/project_contract_test.go @@ -55,6 +55,7 @@ type projectTestRunner struct { calls int enabled bool bad string + version string } func (runner *projectTestRunner) Run(_ context.Context, command legacyports.Command) (legacyports.CommandResult, error) { @@ -63,17 +64,28 @@ func (runner *projectTestRunner) Run(_ context.Context, command legacyports.Comm if !reflect.DeepEqual(command.Argv, []string{runner.request.Client.ExecutablePath, "inspect", "--json"}) || command.Dir != plan.TargetContext.ProjectRoot || command.StdoutLimitBytes != 1<<20 { return legacyports.CommandResult{}, os.ErrPermission } - body, _ := json.Marshal(map[string]any{"grokVersion": "1.0.67", "cwd": command.Dir, "projectRoot": command.Dir + string(filepath.Separator), "projectTrusted": false, "skills": []any{}, "plugins": []any{map[string]any{"name": "demo", "scope": "project", "path": plan.ActivePath, "enabled": runner.enabled}}}) + version := runner.version + if version == "" { + version = "1.0.67" + } + body, _ := json.Marshal(map[string]any{"grokVersion": version, "cwd": command.Dir, "projectRoot": command.Dir + string(filepath.Separator), "projectTrusted": false, "skills": []any{}, "plugins": []any{map[string]any{"name": "demo", "scope": "project", "path": plan.ActivePath, "enabled": runner.enabled}}}) if runner.bad != "" { body = []byte(runner.bad) } return legacyports.CommandResult{Stdout: body}, nil } func TestProjectGrokPreparedForeverAndNoGlobalCommand(t *testing.T) { + for _, version := range []string{"1.0.67", "1.0.70"} { + t.Run(version, func(t *testing.T) { testProjectGrokPreparedForever(t, version) }) + } +} + +func testProjectGrokPreparedForever(t *testing.T, version string) { + t.Helper() request := projectTestRequest(t) projectTestWrite(t, filepath.Join(request.Plan.ActivePath, ".grok-plugin", "plugin.json"), `{"name":"demo"}`) for _, enabled := range []bool{false, true} { - runner := &projectTestRunner{request: request, enabled: enabled} + runner := &projectTestRunner{request: request, enabled: enabled, version: version} env := clients.Env{Runner: runner} out, err := New().Activate(t.Context(), env, request) if err != nil || out.Activation != domain.ActivationPrepared || out.Verification != domain.VerificationPackageValid || out.AuthoritativeObservation || runner.calls != 1 { @@ -99,8 +111,15 @@ func TestProjectGrokPreparedForeverAndNoGlobalCommand(t *testing.T) { } } func TestProjectGrokStrictObservedInspectShape(t *testing.T) { + for _, version := range []string{"1.0.67", "1.0.70"} { + t.Run(version, func(t *testing.T) { testProjectGrokStrictObservedInspectShape(t, version) }) + } +} + +func testProjectGrokStrictObservedInspectShape(t *testing.T, version string) { + t.Helper() request := projectTestRequest(t) - runner := &projectTestRunner{request: request} + runner := &projectTestRunner{request: request, version: version} command := legacyports.Command{Argv: []string{request.Client.ExecutablePath, "inspect", "--json"}, Dir: request.Plan.TargetContext.ProjectRoot, StdoutLimitBytes: 1 << 20} result, err := runner.Run(t.Context(), command) if err != nil { @@ -127,8 +146,11 @@ func TestProjectGrokStrictObservedInspectShape(t *testing.T) { t.Fatal("observed root spelling refused", err) } } - invalid := make([]string, 0, 10) - invalid = append(invalid, strings.Replace(body, `"grokVersion":"1.0.67"`, `"grokVersion":"1.0.68"`, 1), strings.Replace(body, `"cwd":`, `"Cwd":`, 1), strings.Replace(body, `"projectRoot":`, `"ProjectRoot":`, 1), strings.Replace(body, `"enabled":false`, `"enabled":null`, 1), body+` {}`, strings.Replace(body, `"skills":[]`, `"skills":null`, 1), strings.Replace(body, `"name":"demo"`, `"name":"demo","Name":"demo"`, 1)) + invalid := make([]string, 0, 12) + for _, unsupported := range []string{"1.0.68", "1.0.71", "1.0.70-dev"} { + invalid = append(invalid, strings.Replace(body, `"grokVersion":"`+version+`"`, `"grokVersion":"`+unsupported+`"`, 1)) + } + invalid = append(invalid, strings.Replace(body, `"cwd":`, `"Cwd":`, 1), strings.Replace(body, `"projectRoot":`, `"ProjectRoot":`, 1), strings.Replace(body, `"enabled":false`, `"enabled":null`, 1), body+` {}`, strings.Replace(body, `"skills":[]`, `"skills":null`, 1), strings.Replace(body, `"name":"demo"`, `"name":"demo","Name":"demo"`, 1)) for _, suffix := range []string{"//", "/./", "/../A/"} { invalid = append(invalid, replaceRoot(project+suffix)) } diff --git a/install/integrationctl/agentplugins/clients/grok/project_inspect.go b/install/integrationctl/agentplugins/clients/grok/project_inspect.go index b2a518122..3ea5247bf 100644 --- a/install/integrationctl/agentplugins/clients/grok/project_inspect.go +++ b/install/integrationctl/agentplugins/clients/grok/project_inspect.go @@ -20,7 +20,7 @@ type projectInspection struct{ plugins []projectPlugin } func parseProjectInspect(body []byte, project string) (projectInspection, error) { fail := func() (projectInspection, error) { - return projectInspection{}, fmt.Errorf("grok 1.0.67 project inspect contract is not recognized") + return projectInspection{}, fmt.Errorf("grok project inspect contract (1.0.67 or 1.0.70) is not recognized") } value, err := shared.DecodeStrictJSONObject(body) if err != nil { @@ -29,7 +29,7 @@ func parseProjectInspect(body []byte, project string) (projectInspection, error) if !exactProjectFields(value, []string{"grokVersion", "cwd", "projectRoot", "projectTrusted", "plugins", "skills"}) { return fail() } - if value["grokVersion"] != "1.0.67" || value["cwd"] != project || (value["projectRoot"] != project && value["projectRoot"] != project+string(filepath.Separator)) { + if (value["grokVersion"] != "1.0.67" && value["grokVersion"] != "1.0.70") || value["cwd"] != project || (value["projectRoot"] != project && value["projectRoot"] != project+string(filepath.Separator)) { return fail() } if _, ok := value["projectTrusted"].(bool); !ok { diff --git a/install/integrationctl/agentplugins/clients/grok/scope.go b/install/integrationctl/agentplugins/clients/grok/scope.go index 54326d16e..28a940c63 100644 --- a/install/integrationctl/agentplugins/clients/grok/scope.go +++ b/install/integrationctl/agentplugins/clients/grok/scope.go @@ -91,7 +91,7 @@ func (*Adapter) CheckPlanPrecondition(in clients.PlanInput, plan *domain.Deliver if in.Client.ExecutablePath == "" { plan.Status = domain.PlanUnsupported plan.Warnings = append(plan.Warnings, "pinned_grok_inspect_required") - plan.UserActions = append(plan.UserActions, "Grok Build 1.0.67 CLI is required for read-only project source checks") + plan.UserActions = append(plan.UserActions, "Grok Build 1.0.67 or 1.0.70 CLI is required for read-only project source checks") } return nil } @@ -108,7 +108,7 @@ func (*Adapter) QualifyPlan(in clients.PlanInput, plan *domain.DeliveryPlan) err for i := range plan.Components { if plan.Components[i].Kind == domain.ComponentMCPServer { plan.Components[i].Support = domain.SupportUnsupported - plan.Components[i].Reason = "Grok project bundled MCP initialization and tools are unqualified at pinned 1.0.67" + plan.Components[i].Reason = "Grok project bundled MCP initialization and tools are unqualified at pinned 1.0.67 and 1.0.70" } } return nil diff --git a/repotests/agentplugins_grok_config_contract_test.go b/repotests/agentplugins_grok_config_contract_test.go new file mode 100644 index 000000000..4b6ac0ea1 --- /dev/null +++ b/repotests/agentplugins_grok_config_contract_test.go @@ -0,0 +1,222 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/json" + "fmt" + "os" + "path/filepath" + "reflect" + "sort" + "strings" + "testing" + "time" +) + +func grokConfigFixture(t *testing.T) *projectNativeCLIAttempt { + t.Helper() + root := t.TempDir() + ctx, cancel := context.WithCancel(context.Background()) + f := &projectNativeCLIAttempt{spec: projectNativeCLISpec{client: "grok"}, ctx: ctx, cancel: cancel, started: time.Now(), + base: &nativeFixture{Root: root, Home: filepath.Join(root, "original-home")}, protected: map[string]string{}, + files: map[string][]byte{"native-attempt.stdout.log": {}, "native-attempt.stderr.log": {}, "native-attempt.command.json": []byte(`{"inert":true}`)}, + used: map[string]bool{"native-attempt": true}, evidence: map[string]any{}} + t.Cleanup(func() { + if f.grok != nil { + f.grok.stop() + } + cancel() + }) + f.configureGrokProvider(t) + return f +} + +// This deliberately changes only the file authored in this inert test fixture. +// The diagnostic itself never writes, repairs or adopts the changed file. +func grokConfigRewrite(t *testing.T, path string, body []byte) { + t.Helper() + if _, err := projectNativeRuntimeRead(path, 64<<10); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(path, body, 0600); err != nil { + t.Fatal(err) + } +} + +func grokConfigObservedFile(t *testing.T, f *projectNativeCLIAttempt, body []byte) grokConfigObservation { + t.Helper() + path := filepath.Join(f.base.Home, ".grok", "config.toml") + grokConfigRewrite(t, path, body) + before, err := os.Lstat(path) + if err != nil { + t.Fatal(err) + } + want := f.protected[path] + f.observeGrokConfigMutation(path, want, nativeSHA(t, path)) + observation, ok := f.evidence["grok_config_mutation"].(grokConfigObservation) + if !ok { + t.Fatal("eligible held-file observation missing") + } + raw, err := json.Marshal(observation) + if err != nil || len(raw) > 2048 || bytes.Contains(raw, []byte("PRIVATE-")) || bytes.Contains(raw, []byte("uap-grok-private-")) { + t.Fatal("finite diagnostic bound or private-value projection differs") + } + after, statErr := os.Lstat(path) + actual, readErr := os.ReadFile(path) + if statErr != nil || readErr != nil || !os.SameFile(before, after) || !projectNativeRuntimeStableStat(before.Sys(), after.Sys()) || + !bytes.Equal(body, actual) || f.protected[path] != want || !bytes.Equal(f.grok.authoredConfig, f.grok.configuration()) { + t.Fatal("observation changed original file identity, recipe or protected digest") + } + return observation +} + +func grokConfigRequireKnown(t *testing.T, comparison *grokConfigComparison, unequal string) { + t.Helper() + if comparison == nil || len(comparison.Known) != 8 { + t.Fatal("eight authored scalar facts missing") + } + for path, fact := range comparison.Known { + if fact.Equal != (path != unequal) { + t.Fatal("authored scalar equality differs", path) + } + } +} + +func TestGrokConfigRecipeAuthorsOnlyEmptyProfileMigrationSentinel(t *testing.T) { + f := grokConfigFixture(t) + value, status := grokConfigParse(f.grok.authoredConfig) + want := map[string]any{ + "models": map[string]any{"default": "uap-grok-fixture"}, + "model": map[string]any{"uap-grok-fixture": map[string]any{ + "model": "uap-grok-fixture", "base_url": "http://" + f.grok.listener.Addr().String() + "/v1", + "env_key": "UAP_GROK_TEST_PROVIDER_KEY", "api_backend": "chat_completions", + "supports_backend_search": false, "supports_reasoning_effort": false, "max_retries": int64(0), + }}, + "marketplace": map[string]any{"default_skills_installs_purged": true}, + } + if status != "captured" || !reflect.DeepEqual(value, want) { + t.Fatal("independently authored model and empty-profile migration recipe differs") + } + semantic, err := json.Marshal(value["marketplace"]) + if err != nil || fmt.Sprintf("%x", sha256.Sum256(semantic)) != "292bc27c01bd4c8e2df64aacd7e75d92db6d9e3f446970ea2d7d97f011250093" { + t.Fatal("source-defined migration table differs from independent semantic hash") + } + path := filepath.Join(f.base.Home, ".grok", "config.toml") + body, err := projectNativeRuntimeRead(path, 64<<10) + if err != nil || !bytes.Equal(body, f.grok.authoredConfig) || f.protected[path] != grokConfigSHA(body) { + t.Fatal("complete original recipe and protected digest differ before a child") + } +} + +func TestGrokConfigDiagnosticReportsOnlyIndependentlyEncodedAddedTypes(t *testing.T) { + for _, test := range []struct{ toml, kind, canonical string }{ + {`"PRIVATE-BODY"`, "string", `"PRIVATE-BODY"`}, {"false", "boolean", "false"}, {"1", "integer", "1"}, + {"1.25", "float", "1.25"}, {`["PRIVATE-BODY", 1]`, "array", `["PRIVATE-BODY",1]`}, + {"{ nested = true }", "table", `{"nested":true}`}, + } { + t.Run(test.kind, func(t *testing.T) { + f := grokConfigFixture(t) + body := append([]byte("PRIVATE-KEY = "+test.toml+"\n"), f.grok.authoredConfig...) + observation := grokConfigObservedFile(t, f, body) + comparison := observation.Comparison + grokConfigRequireKnown(t, comparison, "") + if observation.Status != "captured" || comparison.SemanticEqual || comparison.BeforeFields != 3 || comparison.AfterFields != 4 || comparison.ChangedCount != 1 || comparison.OmittedCount != 0 || len(comparison.Differences) != 1 { + t.Fatal("one added top-level member facts differ") + } + want := grokConfigDifference{KeySHA256: fmt.Sprintf("%x", sha256.Sum256([]byte("PRIVATE-KEY"))), BeforeType: "absent", AfterType: test.kind, + AfterSHA256: fmt.Sprintf("%x", sha256.Sum256([]byte(test.canonical)))} + if comparison.Differences[0] != want { + t.Fatal("literal semantic byte hash/type projection differs") + } + }) + } +} + +func TestGrokConfigDiagnosticPreservesKnownSafetyAndIntegerFloatDistinctions(t *testing.T) { + for _, test := range []struct{ before, after, path, kind string }{ + {"supports_backend_search = false", "supports_backend_search = true", "model.uap-grok-fixture.supports_backend_search", "boolean"}, + {"supports_reasoning_effort = false", "supports_reasoning_effort = true", "model.uap-grok-fixture.supports_reasoning_effort", "boolean"}, + {"max_retries = 0", "max_retries = 0.0", "model.uap-grok-fixture.max_retries", "float"}, + {"max_retries = 0\n", "", "model.uap-grok-fixture.max_retries", "absent"}, + } { + f := grokConfigFixture(t) + body := bytes.Replace(f.grok.authoredConfig, []byte(test.before), []byte(test.after), 1) + observation := grokConfigObservedFile(t, f, body) + grokConfigRequireKnown(t, observation.Comparison, test.path) + if observation.Status != "captured" || observation.Comparison.SemanticEqual || observation.Comparison.ChangedCount != 1 || observation.Comparison.Known[test.path].Type != test.kind { + t.Fatal("changed safety value or exact scalar type was adopted") + } + } +} + +func TestGrokConfigDiagnosticFormatAndRemovalArePresenceAware(t *testing.T) { + for _, mode := range []string{"format", "removed", "type-changed"} { + f := grokConfigFixture(t) + body := append([]byte("# PRIVATE-COMMENT\n"), f.grok.authoredConfig...) + if mode != "format" { + body = bytes.Replace(f.grok.authoredConfig, []byte("[models]\ndefault = \"uap-grok-fixture\"\n"), nil, 1) + if mode == "type-changed" { + body = append([]byte("models = \"PRIVATE-BODY\"\n"), body...) + } + } + observation := grokConfigObservedFile(t, f, body) + comparison := observation.Comparison + if mode == "format" { + grokConfigRequireKnown(t, comparison, "") + if !comparison.SemanticEqual || comparison.ChangedCount != 0 || len(comparison.Differences) != 0 { + t.Fatal("format-only rewrite fabricated a semantic change") + } + continue + } + grokConfigRequireKnown(t, comparison, "models.default") + if comparison.SemanticEqual || comparison.ChangedCount != 1 || len(comparison.Differences) != 1 || comparison.Known["models.default"].Present { + t.Fatal("removed scalar was confused with an equal existing value") + } + want := grokConfigDifference{KeySHA256: fmt.Sprintf("%x", sha256.Sum256([]byte("models"))), BeforeType: "table", AfterType: "absent", + BeforeSHA256: fmt.Sprintf("%x", sha256.Sum256([]byte(`{"default":"uap-grok-fixture"}`)))} + if mode == "type-changed" { + want.AfterType, want.AfterSHA256 = "string", fmt.Sprintf("%x", sha256.Sum256([]byte(`"PRIVATE-BODY"`))) + } + if comparison.Differences[0] != want { + t.Fatal("presence-aware removed/changed semantic hash differs") + } + } +} + +func TestGrokConfigDiagnosticSelectionAndBoundAreDeterministic(t *testing.T) { + var prior *grokConfigComparison + keys := []string{"PRIVATE-0", "PRIVATE-1", "PRIVATE-2", "PRIVATE-3", "PRIVATE-4", "PRIVATE-5", "PRIVATE-6"} + want := make([]string, 0, len(keys)) + for _, key := range keys { + want = append(want, fmt.Sprintf("%x", sha256.Sum256([]byte(key)))) + } + sort.Strings(want) + for _, reverse := range []bool{false, true} { + f := grokConfigFixture(t) + prefix := make([]string, 0, len(keys)) + for index := range keys { + if reverse { + index = len(keys) - index - 1 + } + prefix = append(prefix, keys[index]+" = \"PRIVATE-BODY\"") + } + observation := grokConfigObservedFile(t, f, append([]byte(strings.Join(prefix, "\n")+"\n"), f.grok.authoredConfig...)) + comparison := observation.Comparison + if observation.Status != "entry-limit" || comparison.ChangedCount != 7 || comparison.OmittedCount != 4 || len(comparison.Differences) != 3 { + t.Fatal("three-detail bound or exact omitted count differs") + } + for index, difference := range comparison.Differences { + if difference.KeySHA256 != want[index] || difference.BeforeType != "absent" || difference.AfterType != "string" || difference.AfterSHA256 != fmt.Sprintf("%x", sha256.Sum256([]byte(`"PRIVATE-BODY"`))) { + t.Fatal("selected deterministic semantic member differs") + } + } + if prior != nil && !reflect.DeepEqual(prior, comparison) { + t.Fatal("authored key order changed the bounded comparison") + } + prior = comparison + } +} diff --git a/repotests/agentplugins_grok_config_diagnostic_test.go b/repotests/agentplugins_grok_config_diagnostic_test.go new file mode 100644 index 000000000..afb2d4637 --- /dev/null +++ b/repotests/agentplugins_grok_config_diagnostic_test.go @@ -0,0 +1,347 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "math" + "os" + "path/filepath" + "reflect" + "sort" + "strings" + "syscall" + "time" + "unicode/utf8" + + "github.com/pelletier/go-toml/v2" +) + +type grokConfigObservation struct { + Status string `json:"status"` + ExpectedSHA256 string `json:"expected_sha256"` + ObservedSHA256 string `json:"observed_sha256"` + Bytes *int `json:"bytes,omitempty"` + Comparison *grokConfigComparison `json:"comparison,omitempty"` +} + +type grokConfigComparison struct { + BeforeFields int `json:"before_fields"` + AfterFields int `json:"after_fields"` + SemanticEqual bool `json:"semantic_equal"` + Known map[string]grokConfigKnown `json:"known"` + ChangedCount int `json:"changed_count"` + OmittedCount int `json:"omitted_count"` + SemanticEncoding string `json:"semantic_encoding"` + Differences []grokConfigDifference `json:"differences"` +} + +type grokConfigKnown struct { + Present bool `json:"present"` + Type string `json:"type"` + Equal bool `json:"equal"` +} + +type grokConfigDifference struct { + KeySHA256 string `json:"key_sha256"` + BeforeType string `json:"before_type"` + AfterType string `json:"after_type"` + BeforeSHA256 string `json:"before_sha256,omitempty"` + AfterSHA256 string `json:"after_sha256,omitempty"` +} + +func grokConfigSHA(body []byte) string { + sum := sha256.Sum256(body) + return hex.EncodeToString(sum[:]) +} + +func (f *projectNativeCLIAttempt) grokConfigEligible(path, want, got string) bool { + if f.spec.client != "grok" || f.base == nil || f.grok == nil || f.grok.listener == nil || f.evidence == nil || + !f.used["native-attempt"] || path != filepath.Join(f.base.Home, ".grok", "config.toml") || f.protected[path] != want || + len(want) != 64 || len(got) != 64 || strings.Trim(want+got, "0123456789abcdef") != "" || want == got { + return false + } + for _, name := range []string{"native-attempt.stdout.log", "native-attempt.stderr.log", "native-attempt.command.json"} { + if _, exists := f.files[name]; !exists { + return false + } + } + _, already := f.evidence["grok_config_mutation"] + return !already +} + +func (f *projectNativeCLIAttempt) observeGrokConfigMutation(path, want, got string) { + if f.grokConfigEligible(path, want, got) { + f.evidence["grok_config_mutation"] = f.grokReadConfigMutation(path, want, got, grokConfigHeldRead) + } +} + +func grokConfigHeldRead(path string, limit int64) ([]byte, error) { + return grokConfigStableRead(path, limit, projectNativeRuntimeRead) +} + +func grokConfigStableRead(path string, limit int64, read func(string, int64) ([]byte, error)) ([]byte, error) { + parents, err := grokConfigParentIdentities(path) + if err != nil { + return nil, err + } + before, err := os.Lstat(path) + if err != nil { + return nil, err + } + body, err := read(path, limit) + if err != nil { + return nil, err + } + after, err := os.Lstat(path) + if err != nil || !os.SameFile(before, after) || !projectNativeRuntimeStableStat(before.Sys(), after.Sys()) { + return nil, fmt.Errorf("Grok config held metadata changed") + } + for parent, held := range parents { + current, err := os.Lstat(parent) + if err != nil || !grokConfigSameParent(held, current) { + return nil, fmt.Errorf("Grok config parent identity changed") + } + } + return body, nil +} + +func grokConfigSameParent(held, current os.FileInfo) bool { + a, okA := held.Sys().(*syscall.Stat_t) + b, okB := current.Sys().(*syscall.Stat_t) + return okA && okB && current.IsDir() && current.Mode()&os.ModeSymlink == 0 && os.SameFile(held, current) && + held.Mode() == current.Mode() && a.Uid == b.Uid && a.Gid == b.Gid +} + +func grokConfigParentIdentities(path string) (map[string]os.FileInfo, error) { + if err := projectNativeRuntimeParents(path); err != nil { + return nil, err + } + parents := map[string]os.FileInfo{} + for parent := filepath.Dir(path); ; parent = filepath.Dir(parent) { + info, err := os.Lstat(parent) + if err != nil || !info.IsDir() || info.Mode()&os.ModeSymlink != 0 { + return nil, fmt.Errorf("Grok config parent is not ordinary") + } + parents[parent] = info + if filepath.Dir(parent) == parent { + return parents, nil + } + } +} + +func (f *projectNativeCLIAttempt) grokReadConfigMutation(path, want, got string, read func(string, int64) ([]byte, error)) grokConfigObservation { + observation := grokConfigObservation{ExpectedSHA256: want, ObservedSHA256: got, Status: "work-deadline"} + if !time.Now().Before(f.started.Add(150 * time.Second)) { + return observation + } + body, err := read(path, 64<<10) + if err != nil || projectNativeRuntimeParents(path) != nil { + observation.Status = "read-refused" + return observation + } + count := len(body) + observation.Bytes = &count + if !time.Now().Before(f.started.Add(150 * time.Second)) { + return observation + } + if len(body) > 64<<10 || grokConfigSHA(body) != got { + observation.Status = "observed-digest-mismatch" + return observation + } + if !bytes.Equal(f.grok.authoredConfig, f.grok.configuration()) || grokConfigSHA(f.grok.authoredConfig) != want { + observation.Status = "recipe-mismatch" + return observation + } + observation.Comparison, observation.Status = grokConfigProject(f.grok.authoredConfig, body) + if !time.Now().Before(f.started.Add(150 * time.Second)) { + observation.Comparison, observation.Status = nil, "work-deadline" + } + return grokConfigBound(observation) +} + +func grokConfigParse(body []byte) (map[string]any, string) { + if len(body) > 64<<10 || !utf8.Valid(body) { + return nil, "toml-syntax" + } + value := map[string]any{} + if err := toml.Unmarshal(body, &value); err != nil { + return nil, "toml-syntax" + } + nodes := 16384 + if !grokConfigValueValid(value, 0, &nodes) { + return nil, "toml-value" + } + canonical, err := json.Marshal(value) + if err != nil || len(canonical) > 64<<10 { + return nil, "semantic-bound" + } + if grokProjectPrivateCredential(canonical) { + return nil, "private-content" + } + return value, "captured" +} + +func grokConfigValueValid(value any, depth int, nodes *int) bool { + *nodes-- + if depth > 32 || *nodes < 0 { + return false + } + switch typed := value.(type) { + case map[string]any: + for key, item := range typed { + if len(key) > 4096 || !utf8.ValidString(key) || !grokConfigValueValid(key, depth+1, nodes) || !grokConfigValueValid(item, depth+1, nodes) { + return false + } + } + case []any: + for _, item := range typed { + if !grokConfigValueValid(item, depth+1, nodes) { + return false + } + } + case string: + return utf8.ValidString(typed) + case bool, int64: + return true + case float64: + return !math.IsInf(typed, 0) && !math.IsNaN(typed) + default: + return false + } + return true +} + +func grokConfigType(value any, present bool) string { + if !present { + return "absent" + } + switch value.(type) { + case map[string]any: + return "table" + case []any: + return "array" + case string: + return "string" + case bool: + return "boolean" + case int64: + return "integer" + case float64: + return "float" + } + return "unsupported" +} + +func grokConfigKnownFacts(before, after map[string]any) map[string]grokConfigKnown { + paths := []string{"models.default", "model.uap-grok-fixture.model", "model.uap-grok-fixture.base_url", "model.uap-grok-fixture.env_key", + "model.uap-grok-fixture.api_backend", "model.uap-grok-fixture.supports_backend_search", "model.uap-grok-fixture.supports_reasoning_effort", "model.uap-grok-fixture.max_retries"} + facts := make(map[string]grokConfigKnown, len(paths)) + for _, path := range paths { + expected, expectedPresent := grokConfigLookup(before, path) + actual, present := grokConfigLookup(after, path) + facts[path] = grokConfigKnown{Present: present, Type: grokConfigType(actual, present), Equal: expectedPresent == present && reflect.DeepEqual(expected, actual)} + } + return facts +} + +func grokConfigLookup(value map[string]any, path string) (any, bool) { + var current any = value + for _, key := range strings.Split(path, ".") { + table, ok := current.(map[string]any) + if !ok { + return nil, false + } + current, ok = table[key] + if !ok { + return nil, false + } + } + return current, true +} + +func grokConfigProject(beforeBody, afterBody []byte) (*grokConfigComparison, string) { + before, status := grokConfigParse(beforeBody) + if status != "captured" { + return nil, "recipe-" + status + } + after, status := grokConfigParse(afterBody) + if status != "captured" { + return nil, status + } + differences, ok := grokConfigDifferences(before, after) + if !ok { + return nil, "semantic-bound" + } + return &grokConfigComparison{BeforeFields: len(before), AfterFields: len(after), SemanticEqual: reflect.DeepEqual(before, after), + Known: grokConfigKnownFacts(before, after), ChangedCount: len(differences), SemanticEncoding: "standard Go encoding/json.Marshal", + Differences: differences}, "captured" +} + +func grokConfigDifferences(before, after map[string]any) ([]grokConfigDifference, bool) { + keys := make(map[string]bool, len(before)+len(after)) + for key := range before { + keys[key] = true + } + for key := range after { + keys[key] = true + } + differences := make([]grokConfigDifference, 0, len(keys)) + for key := range keys { + left, had := before[key] + right, has := after[key] + if had == has && reflect.DeepEqual(left, right) { + continue + } + difference := grokConfigDifference{KeySHA256: grokConfigSHA([]byte(key)), BeforeType: grokConfigType(left, had), AfterType: grokConfigType(right, has)} + var ok bool + if difference.BeforeSHA256, ok = grokConfigValueSHA(left, had); !ok { + return nil, false + } + if difference.AfterSHA256, ok = grokConfigValueSHA(right, has); !ok { + return nil, false + } + differences = append(differences, difference) + } + sort.Slice(differences, func(i, j int) bool { return differences[i].KeySHA256 < differences[j].KeySHA256 }) + return differences, true +} + +func grokConfigValueSHA(value any, present bool) (string, bool) { + if !present { + return "", true + } + body, err := json.Marshal(value) + if err != nil || len(body) > 64<<10 { + return "", false + } + return grokConfigSHA(body), true +} + +func grokConfigBound(observation grokConfigObservation) grokConfigObservation { + comparison := observation.Comparison + if comparison != nil && len(comparison.Differences) > 3 { + comparison.Differences = comparison.Differences[:3] + } + for { + if comparison != nil { + comparison.OmittedCount = comparison.ChangedCount - len(comparison.Differences) + if comparison.OmittedCount > 0 { + observation.Status = "entry-limit" + } + } + body, err := json.Marshal(observation) + if err == nil && len(body) <= 2<<10 { + return observation + } + if comparison == nil || len(comparison.Differences) == 0 { + observation.Comparison, observation.Status = nil, "encoded-bound" + return observation + } + comparison.Differences = comparison.Differences[:len(comparison.Differences)-1] + } +} diff --git a/repotests/agentplugins_grok_config_refusal_test.go b/repotests/agentplugins_grok_config_refusal_test.go new file mode 100644 index 000000000..8df5bb636 --- /dev/null +++ b/repotests/agentplugins_grok_config_refusal_test.go @@ -0,0 +1,274 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + "time" +) + +func TestGrokConfigDiagnosticRefusesSyntaxTypesPrivateValuesAndBoundsWithoutPartialFacts(t *testing.T) { + for _, test := range []struct{ prefix, status string }{ + {"x = 1\nx = 2\n", "toml-syntax"}, {"PRIVATE-MALFORMED = [\n", "toml-syntax"}, {"\xff\n", "toml-syntax"}, + {"PRIVATE-KEY = 1979-05-27T07:32:00Z\n", "toml-value"}, {"PRIVATE-KEY = 07:32:00\n", "toml-value"}, + {"PRIVATE-KEY = nan\n", "toml-value"}, {"PRIVATE-KEY = inf\n", "toml-value"}, + {strings.Repeat("a.", 34) + "key = 1\n", "toml-value"}, {strings.Repeat("k", 4097) + " = 1\n", "toml-value"}, + {"PRIVATE-KEY = [" + strings.Repeat("0,", 16384) + "0]\n", "toml-value"}, + {"PRIVATE-KEY = \"" + strings.Repeat("<", 12000) + "\"\n", "semantic-bound"}, + {"PRIVATE-KEY = \"\\u0075ap-grok-private-inert\"\n", "private-content"}, + {"\"\\u0075ap-grok-private-inert\" = true\n", "private-content"}, + {"PRIVATE-KEY = '{\"content\":\"\\u0075ap-grok-private-inert\"}'\n", "private-content"}, + } { + f := grokConfigFixture(t) + observation := grokConfigObservedFile(t, f, append([]byte(test.prefix), f.grok.authoredConfig...)) + if observation.Status != test.status || observation.Comparison != nil { + t.Fatalf("unsafe TOML emitted partial metadata: got %s, want %s", observation.Status, test.status) + } + } +} + +func TestGrokConfigDiagnosticReadsOnceWithinOriginalWorkTimeAfterEarlyCancellation(t *testing.T) { + f := grokConfigFixture(t) + path := filepath.Join(f.base.Home, ".grok", "config.toml") + body := append([]byte("# PRIVATE-COMMENT\n"), f.grok.authoredConfig...) + grokConfigRewrite(t, path, body) + want, got := f.protected[path], nativeSHA(t, path) + originalContext, originalStarted := f.ctx, f.started + f.cancel() + reads := 0 + read := func(name string, limit int64) ([]byte, error) { + reads++ + if name != path || limit != 64<<10 { + t.Fatal("diagnostic changed the fixed held-file boundary") + } + return grokConfigHeldRead(name, limit) + } + observation := f.grokReadConfigMutation(path, want, got, read) + if observation.Status != "captured" || observation.Comparison == nil || !observation.Comparison.SemanticEqual || reads != 1 || f.ctx != originalContext || f.started != originalStarted || f.ctx.Err() == nil { + t.Fatal("early cancellation changed the original context/budget or skipped bounded observation") + } + f.started = time.Now().Add(-151 * time.Second) + reads = 0 + observation = f.grokReadConfigMutation(path, want, got, read) + if observation.Status != "work-deadline" || reads != 0 || observation.Bytes != nil || observation.Comparison != nil { + t.Fatal("expired original work boundary performed a read or emitted partial facts") + } + f.observeGrokConfigMutation(path, want, got) + first := f.evidence["grok_config_mutation"] + f.started = originalStarted + f.observeGrokConfigMutation(path, want, got) + if !reflect.DeepEqual(first, f.evidence["grok_config_mutation"]) { + t.Fatal("a recorded deadline refusal was retried with fresh time") + } +} + +func TestGrokConfigDiagnosticDoesNotProjectChangedReadOrRecipeAndRetainsNoErrors(t *testing.T) { + for _, mode := range []string{"read-error", "digest", "recipe", "expired-after-read"} { + f := grokConfigFixture(t) + path := filepath.Join(f.base.Home, ".grok", "config.toml") + body := append([]byte("# changed\n"), f.grok.authoredConfig...) + grokConfigRewrite(t, path, body) + want, got := f.protected[path], nativeSHA(t, path) + if mode == "recipe" { + f.grok.authoredConfig = append([]byte("# changed expectation\n"), f.grok.authoredConfig...) + } + read := func(string, int64) ([]byte, error) { + switch mode { + case "read-error": + return nil, fmt.Errorf("PRIVATE-ERROR /PRIVATE-PATH") + case "digest": + return []byte("different = true\n"), nil + case "expired-after-read": + f.started = time.Now().Add(-151 * time.Second) + } + return bytes.Clone(body), nil + } + observation := f.grokReadConfigMutation(path, want, got, read) + wantStatus := map[string]string{"read-error": "read-refused", "digest": "observed-digest-mismatch", "recipe": "recipe-mismatch", "expired-after-read": "work-deadline"}[mode] + raw, err := json.Marshal(observation) + if err != nil || bytes.Contains(raw, []byte("PRIVATE-")) || observation.Status != wantStatus || observation.Comparison != nil || f.protected[path] != want { + t.Fatal("failed held read/recipe produced private or partial facts") + } + } +} + +func TestGrokConfigDiagnosticRequiresExactClientPathAndRecordedNativeTriple(t *testing.T) { + for _, mode := range []string{"wrong-client", "wrong-path", "not-used", "stdout", "stderr", "command", "same", "unprotected", "private-hash"} { + f := grokConfigFixture(t) + path := filepath.Join(f.base.Home, ".grok", "config.toml") + want, got := f.protected[path], strings.Repeat("a", 64) + switch mode { + case "wrong-client": + f.spec.client = "qoder" + case "wrong-path": + path = filepath.Join(f.base.Home, "PRIVATE-PATH") + case "not-used": + delete(f.used, "native-attempt") + case "stdout", "stderr": + delete(f.files, "native-attempt."+mode+".log") + case "command": + delete(f.files, "native-attempt.command.json") + case "same": + got = want + case "unprotected": + delete(f.protected, path) + case "private-hash": + got = "PRIVATE-HASH" + } + f.observeGrokConfigMutation(path, want, got) + if _, exists := f.evidence["grok_config_mutation"]; exists { + t.Fatal("unrelated client/path/attempt entered diagnostic") + } + } +} + +func TestGrokConfigDiagnosticRefusesAliasesLinksAndOversizedActualFiles(t *testing.T) { + for _, mode := range []string{"symlink", "hardlink", "parent-alias", "oversized"} { + f := grokConfigFixture(t) + path := filepath.Join(f.base.Home, ".grok", "config.toml") + body := append([]byte("# changed\n"), f.grok.authoredConfig...) + if mode == "oversized" { + body = append([]byte("#"+strings.Repeat("x", 64<<10)+"\n"), body...) + } + grokConfigRewrite(t, path, body) + got := nativeSHA(t, path) + switch mode { + case "symlink", "hardlink": + target := filepath.Join(f.base.Home, "PRIVATE-TARGET") + if err := os.Rename(path, target); err != nil { + t.Fatal(err) + } + link := os.Symlink + if mode == "hardlink" { + link = os.Link + } + if err := link(target, path); err != nil { + t.Fatal(err) + } + case "parent-alias": + directory, target := filepath.Dir(path), filepath.Join(f.base.Home, "PRIVATE-DIRECTORY") + if err := os.Rename(directory, target); err != nil { + t.Fatal(err) + } + if err := os.Symlink(target, directory); err != nil { + t.Fatal(err) + } + } + observation := f.grokReadConfigMutation(path, f.protected[path], got, grokConfigHeldRead) + if observation.Status != "read-refused" || observation.Comparison != nil { + t.Fatal("unsafe actual file was interpreted or given semantic hashes") + } + } +} + +func TestGrokConfigHeldReadRefusesFullMetadataAndParentChangesDuringRead(t *testing.T) { + for _, mode := range []string{"mode-restored", "link-restored", "parent-replaced"} { + f := grokConfigFixture(t) + path := filepath.Join(f.base.Home, ".grok", "config.toml") + read := func(name string, limit int64) ([]byte, error) { + body, err := projectNativeRuntimeRead(name, limit) + if err != nil { + return nil, err + } + switch mode { + case "mode-restored": + if err := os.Chmod(path, 0640); err != nil { + t.Fatal(err) + } + if err := os.Chmod(path, 0600); err != nil { + t.Fatal(err) + } + case "link-restored": + link := filepath.Join(f.base.Home, "PRIVATE-LINK") + if err := os.Link(path, link); err != nil { + t.Fatal(err) + } + if err := os.Remove(link); err != nil { + t.Fatal(err) + } + case "parent-replaced": + directory := filepath.Dir(path) + previous := filepath.Join(f.base.Home, "PRIVATE-PREVIOUS") + if err := os.Rename(directory, previous); err != nil { + t.Fatal(err) + } + if err := os.Mkdir(directory, 0700); err != nil { + t.Fatal(err) + } + if err := os.Rename(filepath.Join(previous, "config.toml"), path); err != nil { + t.Fatal(err) + } + } + return body, nil + } + if body, err := grokConfigStableRead(path, 64<<10, read); err == nil || body != nil { + t.Fatal("restored mode/link count or replaced parent escaped full metadata refusal") + } + } +} + +func TestGrokConfigDiagnosticCannotMakeFormatOnlyRewritePassProtectedGuard(t *testing.T) { + grokConfigRequireProtectedFatal(t, "format") +} + +func TestGrokConfigMigrationFlagChangesCannotPassProtectedGuard(t *testing.T) { + for _, mode := range []string{"removed", "false"} { + t.Run(mode, func(t *testing.T) { grokConfigRequireProtectedFatal(t, mode) }) + } +} + +func grokConfigRequireProtectedFatal(t *testing.T, mode string) { + t.Helper() + executable, err := os.Executable() + if err != nil { + t.Fatal(err) + } + result := captureProjectNative(context.Background(), executable, t.TempDir(), []string{"UAP_GROK_CONFIG_FATAL_CHILD=" + mode}, + []string{"-test.run=^TestGrokConfigProtectedFatalInertChild$"}, 4096, 5*time.Second) + if result.Exit != 1 || result.Deadline || result.StdoutLimit || result.StderrLimit || len(result.Stderr) != 0 || + !bytes.Contains(result.Stdout, []byte("CONFIG_FATAL_DIAGNOSTIC_VERIFIED")) || !bytes.Contains(result.Stdout, []byte("native CLI immutable/protected identity changed")) { + t.Fatalf("original fatal guard was not proved: exit=%d, error=%s", result.Exit, result.ErrorKind) + } +} + +func TestGrokConfigProtectedFatalInertChild(t *testing.T) { + mode := os.Getenv("UAP_GROK_CONFIG_FATAL_CHILD") + if mode == "" { + t.Skip("inert protected-guard child only") + } + f := grokConfigFixture(t) + path := filepath.Join(f.base.Home, ".grok", "config.toml") + body := f.grok.authoredConfig + switch mode { + case "format": + body = append([]byte("# PRIVATE-COMMENT\n"), body...) + case "removed": + body = bytes.Replace(body, []byte("\n[marketplace]\ndefault_skills_installs_purged = true\n"), nil, 1) + case "false": + body = bytes.Replace(body, []byte("default_skills_installs_purged = true"), []byte("default_skills_installs_purged = false"), 1) + default: + t.Fatal("unknown inert guard mutation") + } + grokConfigRewrite(t, path, body) + defer func() { + observation, ok := f.evidence["grok_config_mutation"].(grokConfigObservation) + if !ok || observation.Status != "captured" || observation.Comparison == nil || + observation.Comparison.SemanticEqual != (mode == "format") || f.protected[path] != grokConfigSHA(f.grok.authoredConfig) { + t.Error("CONFIG_DIAGNOSTIC_OR_AUTHORITY_DIFFERS") + return + } + if _, err := fmt.Fprint(os.Stdout, "CONFIG_FATAL_DIAGNOSTIC_VERIFIED\n"); err != nil { + t.Error("inert verification output failed") + } + }() + f.check(t) + t.Fatal("changed config unexpectedly passed") +} diff --git a/repotests/agentplugins_grok_cycle_contract_test.go b/repotests/agentplugins_grok_cycle_contract_test.go new file mode 100644 index 000000000..655e56a1d --- /dev/null +++ b/repotests/agentplugins_grok_cycle_contract_test.go @@ -0,0 +1,145 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "encoding/json" + "os" + "path/filepath" + "reflect" + "testing" +) + +func TestGrokCycleInspectRequiresExactProducerSkillSource(t *testing.T) { + for _, want := range []grokCycleNativePlugin{ + {Name: "user", Scope: "user", Path: "/owned/home/plugin", SkillPath: "/owned/home/plugin/./skills/user/SKILL.md", Description: "Original marker", Skills: 1, Loaded: true}, + {Name: "project", Scope: "project", Path: "/owned/A/plugin", SkillPath: "/owned/A/plugin/skills/project/SKILL.md", Description: "Owned marker", Skills: 1, Loaded: true}, + } { + for _, path := range []string{want.Path + "/./skills/" + want.Name + "/SKILL.md", want.Path + "/skills/" + want.Name + "/SKILL.md", want.Path + "/../plugin/skills/" + want.Name + "/SKILL.md", want.Path + "/././skills/" + want.Name + "/SKILL.md", "/foreign/skills/" + want.Name + "/SKILL.md"} { + row := map[string]any{"grokVersion": "1.0.70", "cwd": "/owned/A", "projectRoot": "/owned/A/", "projectTrusted": true, "mcpServers": []any{}, + "plugins": []any{map[string]any{"name": want.Name, "scope": want.Scope, "path": want.Path, "enabled": true, "provides": map[string]any{"skills": 1, "agents": 0, "hooks": false, "mcpServers": 0}}}, + "skills": []any{map[string]any{"name": want.Name, "description": want.Description, "userInvocable": true, "source": map[string]any{"type": "plugin", "plugin_name": want.Name, "path": path}}}} + raw, err := json.Marshal(row) + if err != nil { + t.Fatal(err) + } + if err := grokCycleInspect(raw, "/owned/A", true, []grokCycleNativePlugin{want}); (err == nil) != (path == want.SkillPath) { + t.Fatal("native producer skill source comparison differs", want.Name, path, err) + } + } + } +} + +func grokCycleRepeatAddContract(t *testing.T) (map[string]any, *grokCycleRecipe) { + t.Helper() + const body = `{"schema_version":1,"command":"add","result":"success","data":{"plugin":"held-grok","version":"1.0.0","source":"direct local source","tree_digest":"sha256:held-tree","manifest_digest":"sha256:held-manifest","dry_run":false,"result":{"installation_id":"held-installation","plan":{"client_id":"grok","scope":"project","status":"ready","package_mode":"native","activation":"prepared","authentication":"not_checked","policy":"allowed","verification":"package_validated","physical_artifact_id":"held-physical","components":[{"kind":"skill","name":"held-grok","support":"native"}]},"activation":{"activation":"prepared","authentication":"not_checked","policy":"allowed","verification":"package_validated"},"requires_confirmation":false,"mutated":false}}}` + value, err := projectNativeRuntimeJSON([]byte(body)) + if err != nil { + t.Fatal(err) + } + r := &grokCycleRecipe{Scope: "a", Source: "/held/source", Name: "held-grok", Digest: "sha256:held-tree"} + r.Installation.InstallationID = "held-installation" + r.Installation.Source.CanonicalSource, r.Installation.Source.TreeDigest = r.Source, r.Digest + r.Installation.Package.ManifestDigest = "sha256:held-manifest" + r.Binding.PhysicalArtifact = "held-physical" + return value.(map[string]any), r +} + +func TestGrokCycleRepeatAddRequiresHeldDirectPublicResult(t *testing.T) { + value, r := grokCycleRepeatAddContract(t) + if err := grokCycleRepeatAdd("repeat-add-a", "a", false, value, r); err != nil { + t.Fatal("held direct repeated add refused", err) + } + if _, err := commandsTargetOutcome(value); err == nil { + t.Fatal("shared batch helper unexpectedly accepts direct result") + } + for _, stage := range []string{"add-a", "add-b", "unchanged-update-a", "intact-repair-a", "update-a", "missing-repair-a", "remove-a", "repeat-remove-a"} { + if grokCycleRepeatAdd(stage, "a", false, value, r) == nil { + t.Fatal("unrelated stage accepted singleton", stage) + } + } + if grokCycleRepeatAdd("repeat-add-a", "b", false, value, r) == nil || grokCycleRepeatAdd("repeat-add-a", "a", true, value, r) == nil || grokCycleRepeatAdd("repeat-add-a", "a", false, value, nil) == nil { + t.Fatal("wrong scope/mutation/missing held authority accepted") + } +} + +func TestGrokCycleRepeatAddRefusesChangedPublicAuthority(t *testing.T) { + for _, path := range [][]string{{"schema_version"}, {"command"}, {"result"}, {"data"}, {"data", "plugin"}, {"data", "version"}, {"data", "source"}, {"data", "tree_digest"}, {"data", "manifest_digest"}, {"data", "dry_run"}, {"data", "result"}, {"data", "result", "installation_id"}, {"data", "result", "requires_confirmation"}, {"data", "result", "mutated"}, {"data", "result", "plan"}, {"data", "result", "activation"}, {"data", "result", "plan", "client_id"}, {"data", "result", "plan", "scope"}, {"data", "result", "plan", "status"}, {"data", "result", "plan", "package_mode"}, {"data", "result", "plan", "physical_artifact_id"}, {"data", "result", "plan", "components"}, {"data", "result", "plan", "activation"}, {"data", "result", "plan", "authentication"}, {"data", "result", "plan", "policy"}, {"data", "result", "plan", "verification"}, {"data", "result", "activation", "activation"}, {"data", "result", "activation", "authentication"}, {"data", "result", "activation", "policy"}, {"data", "result", "activation", "verification"}} { + for _, replacement := range []any{nil, "foreign", true, json.Number("1")} { + value, r := grokCycleRepeatAddContract(t) + parent := value + for _, key := range path[:len(path)-1] { + parent = parent[key].(map[string]any) + } + if reflect.DeepEqual(parent[path[len(path)-1]], replacement) { + continue + } + if replacement == nil { + delete(parent, path[len(path)-1]) + } else { + parent[path[len(path)-1]] = replacement + } + if grokCycleRepeatAdd("repeat-add-a", "a", false, value, r) == nil { + t.Fatal("changed singleton authority accepted", path, replacement) + } + } + } + for _, field := range []string{"batch", "targets", "operation_id"} { + value, r := grokCycleRepeatAddContract(t) + value["data"].(map[string]any)[field] = "invented" + if grokCycleRepeatAdd("repeat-add-a", "a", false, value, r) == nil { + t.Fatal("singleton adopted grouped output", field) + } + } +} + +func TestGrokCycleRawPrivacyDoesNotRequireProvider(t *testing.T) { + for _, raw := range [][]byte{[]byte("uap-grok-private-inert"), []byte(`{"value":"\u0075ap-grok-private-inert"}`), []byte("{}\n{\"value\":\"\\u0075ap-grok-private-inert\"}\n")} { + if _, err := grokCycleStream(raw); err == nil { + t.Fatal("provider-free cycle encoded private raw input") + } + } + value, err := grokCycleStream([]byte("ordinary fixture\n")) + if err != nil || string(value.Body) != "ordinary fixture\n" || value.Bytes != len(value.Body) || value.SHA256 != clineAttemptSHA(value.Body) { + t.Fatal("provider-free raw byte receipt differs", err) + } +} + +func TestGrokCycleSnapshotHoldsOriginalIdentityAndRefusesLinks(t *testing.T) { + root := t.TempDir() + path := filepath.Join(root, "original") + projectNativeRuntimeWrite(t, path, []byte("original bytes\n"), 0600) + before, err := grokCycleSnapshot(root) + if err != nil { + t.Fatal(err) + } + after, err := grokCycleSnapshot(root) + if err != nil || !reflect.DeepEqual(before, after) { + t.Fatal("ordinary held original changed", err) + } + replacement := filepath.Join(t.TempDir(), "replacement") + projectNativeRuntimeWrite(t, replacement, []byte("original bytes\n"), 0600) + if err := os.Rename(replacement, path); err != nil { + t.Fatal(err) + } + after, err = grokCycleSnapshot(root) + if err != nil || reflect.DeepEqual(before, after) { + t.Fatal("same bytes adopted a replacement original inode", err) + } + link := filepath.Join(root, "alias") + if err := os.Link(path, link); err != nil { + t.Fatal(err) + } + if _, err := grokCycleSnapshot(root); err == nil { + t.Fatal("hard-linked original was adopted") + } + if err := os.Remove(link); err != nil { + t.Fatal(err) + } + if err := os.Symlink(path, link); err != nil { + t.Fatal(err) + } + if _, err := grokCycleSnapshot(root); err == nil { + t.Fatal("symlinked original was adopted") + } +} diff --git a/repotests/agentplugins_grok_cycle_engine_contract_test.go b/repotests/agentplugins_grok_cycle_engine_contract_test.go new file mode 100644 index 000000000..edfe925e9 --- /dev/null +++ b/repotests/agentplugins_grok_cycle_engine_contract_test.go @@ -0,0 +1,138 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/clients" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/clients/grok" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/domain" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/installer" + "github.com/777genius/plugin-kit-ai/install/integrationctl/ports" +) + +// An inert in-memory registry answer around the actual Engine/Store/Stager. +// No native executable or command runner is invoked by this contract. +type grokCycleInertRegistry struct{ project, executable string } + +func (r grokCycleInertRegistry) Run(_ context.Context, command ports.Command) (ports.CommandResult, error) { + if command.Dir != r.project || !reflect.DeepEqual(command.Argv, []string{r.executable, "inspect", "--json"}) { + return ports.CommandResult{}, fmt.Errorf("unregistered inert Grok command") + } + plugins := []any{} + root := filepath.Join(r.project, ".grok", "plugins") + entries, err := os.ReadDir(root) + if err != nil && !os.IsNotExist(err) { + return ports.CommandResult{}, err + } + for _, entry := range entries { + path := filepath.Join(root, entry.Name()) + raw, err := projectNativeRuntimeRead(filepath.Join(path, ".grok-plugin", "plugin.json"), 1<<20) + if err != nil { + return ports.CommandResult{}, err + } + manifest, err := projectNativeRuntimeJSON(raw) + if err != nil { + return ports.CommandResult{}, err + } + plugins = append(plugins, map[string]any{"name": manifest.(map[string]any)["name"], "scope": "project", "path": path, "enabled": true}) + } + raw, err := json.Marshal(map[string]any{"grokVersion": "1.0.70", "cwd": r.project, "projectRoot": r.project + "/", "projectTrusted": true, "plugins": plugins, "skills": []any{}}) + return ports.CommandResult{Stdout: raw}, err +} + +func TestGrokCycleActualEngineNoopAndMissingLeafRepair(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + root := t.TempDir() + project, user, source := filepath.Join(root, "project"), filepath.Join(root, "home", ".grok"), filepath.Join(root, "source") + for _, path := range []string{project, user} { + if err := os.MkdirAll(path, 0700); err != nil { + t.Fatal(err) + } + } + name := "uap-grok-cycle-a" + manifest := []byte(`{"$schema":"https://agent-plugins.org/schemas/1.0.0/plugin.schema.json","description":"Disposable Grok skills cycle","name":"uap-grok-cycle-a","version":"1.0.0"}`) + projectNativeRuntimeWrite(t, filepath.Join(source, "plugin.json"), manifest, 0600) + skill := []byte("---\nname: " + name + "\ndescription: Actual inert repair contract\n---\nHeld source.\n") + projectNativeRuntimeWrite(t, filepath.Join(source, "skills", name, "SKILL.md"), skill, 0600) + helper := filepath.Join(root, "inert-executable") + projectNativeRuntimeWrite(t, helper, []byte("inert bytes; never executed\n"), 0700) + registry, err := clients.NewRegistry(grok.New()) + if err != nil { + t.Fatal(err) + } + engine, err := installer.New(installer.Config{ProjectRoot: project, StateRoot: filepath.Join(project, ".uap"), Registry: registry, TrustedLocalPackages: true, HelperExecutable: helper, Runner: grokCycleInertRegistry{project, helper}}) + if err != nil { + t.Fatal(err) + } + req := installer.Request{Scope: domain.ScopeProject, ProjectRoot: project, ClientID: "grok", ClientConfigRoot: user, ClientExecutable: helper, ClientVersion: "1.0.70", PackageRoot: source, InstallationID: "00000000-0000-4000-8000-000000000070", RequiredComponents: []string{"skills"}} + sequence := 0 + apply := func(operation installer.Operation, mutated bool) { + t.Helper() + sequence++ + req.Operation, req.OperationID = operation, fmt.Sprintf("grok-inert-%d", sequence) + handle, err := engine.Prepare(ctx, req) + if err != nil { + t.Fatal(operation, err) + } + defer func() { + if err := handle.Close(); err != nil { + t.Error(err) + } + }() + result, err := engine.Apply(ctx, handle, installer.Decision{Confirmed: true}) + if err != nil || result.Mutated != mutated || (operation == installer.OpRepair && !mutated && !result.NoChange) { + t.Fatalf("actual Grok %s mutation=%t: %+v %v", operation, mutated, result, err) + } + } + apply(installer.OpInstall, true) + statePath := filepath.Join(project, ".uap", "state-v2.json") + before, err := projectNativeRuntimeRead(statePath, 1<<20) + if err != nil { + t.Fatal(err) + } + state, err := clineCycleState(before) + if err != nil || len(state.Installations) != 1 { + t.Fatal(err) + } + i := state.Installations[0] + r := &grokCycleRecipe{Scope: "a", Project: project, Source: source, Name: name, Digest: i.Source.TreeDigest, Authored: map[string][]byte{"plugin.json": manifest, "skills/" + name + "/SKILL.md": skill}} + if err := grokCycleSource(i, r); err != nil { + t.Fatal(err) + } + var binding domain.ClientBinding + for _, b := range i.Clients { + binding = b + } + payload, err := grokCycleSnapshot(binding.TargetLocator) + if err != nil || grokCyclePayload(payload, grokCycleProjected(r)) != nil { + t.Fatal("cycle source/native payload recipe differs from actual Engine projection", err) + } + if binding.Activation != domain.ActivationPrepared || binding.Verification != domain.VerificationPackageValid || binding.DataReceiptID != "" || len(i.DataReceipts) != 0 { + t.Fatal("actual Grok Prepared/zero-data contract differs") + } + for _, operation := range []installer.Operation{installer.OpInstall, installer.OpUpdate, installer.OpRepair} { + apply(operation, false) + after, err := projectNativeRuntimeRead(statePath, 1<<20) + if err != nil || string(after) != string(before) { + t.Fatal("actual Grok no-op changed state", operation, err) + } + } + path := filepath.Join(binding.TargetLocator, "skills", name, "SKILL.md") + if err := os.Remove(path); err != nil { + t.Fatal(err) + } + apply(installer.OpRepair, true) + restored, err := projectNativeRuntimeRead(path, 1<<20) + if err != nil || string(restored) != string(skill) { + t.Fatal("actual missing single owned leaf was not restored", err) + } +} diff --git a/repotests/agentplugins_grok_cycle_fixture_test.go b/repotests/agentplugins_grok_cycle_fixture_test.go new file mode 100644 index 000000000..af34aa1e4 --- /dev/null +++ b/repotests/agentplugins_grok_cycle_fixture_test.go @@ -0,0 +1,263 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "path/filepath" + "reflect" + "syscall" + "testing" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/packagedigest" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/domain" +) + +var grokCyclePublic = []string{"git-a", "git-b", "git-outside", "version", "add-a", "add-b", "repeat-add-a", "unchanged-update-a", "intact-repair-a", "update-a", "missing-repair-a", "remove-a", "repeat-remove-a", "remove-b"} +var grokCycleInspections = []string{"empty-outside", "empty-a", "empty-b", "installed-a", "installed-b", "installed-outside", "stable-a", "updated-a", "updated-b", "updated-outside", "damaged-a", "repaired-a", "removed-a", "remaining-b", "removed-outside", "repeat-removed-a", "removed-b", "final-a", "final-b", "final-outside"} +var grokCycleProofs = []string{"source-a-a-one", "source-b-b-one", "fixture", "add-a", "add-b", "repeat-add-a", "unchanged-update-a", "intact-repair-a", "before-source-revision-a", "source-a-a-two", "update-a", "before-missing-a", "damaged-a", "missing-repair-a", "remove-a", "repeat-remove-a", "remove-b", "final"} +var grokCycleMembers = []string{"grok-cycle-public.jsonl", "grok-cycle-inspect.jsonl", "grok-cycle-proof.jsonl"} + +type grokCycleRecipe struct { + Scope, Project, Source, Name, Revision, Digest string + Authored map[string][]byte + Installation domain.Installation + Binding domain.ClientBinding + Active, Data string + Removed bool +} + +type grokCycleFile struct { + Kind string `json:"kind"` + Mode uint32 `json:"mode"` + UID uint32 `json:"uid"` + GID uint32 `json:"gid"` + Device uint64 `json:"device"` + Inode uint64 `json:"inode"` + Modified int64 `json:"modified_ns"` + Changed int64 `json:"changed_ns"` + Content *clineCycleBytes `json:"content,omitempty"` +} + +type grokProjectCycle struct { + f *projectNativeCLIAttempt + recipes map[string]*grokCycleRecipe + aggregates map[string][]byte + guards, fixed map[string]map[string]grokCycleFile + absent map[string]bool + public, inspections []string + proofs []string + complete bool +} + +func grokCycleStream(raw []byte) (clineCycleBytes, error) { + if grokProjectPrivateCredential(raw) { + return clineCycleBytes{}, fmt.Errorf("Grok cycle private raw stream refused before encoding") + } + return clineCycleBytes{Bytes: len(raw), SHA256: clineAttemptSHA(raw), Body: append([]byte{}, raw...)}, nil +} + +func (c *grokProjectCycle) stream(t *testing.T, raw []byte) clineCycleBytes { + t.Helper() + value, err := grokCycleStream(raw) + if err != nil { + t.Fatal(err) + } + return value +} + +func grokCycleSnapshot(root string) (map[string]grokCycleFile, error) { + if !filepath.IsAbs(root) || filepath.Clean(root) != root || projectNativeRuntimeParents(root) != nil { + return nil, fmt.Errorf("Grok cycle snapshot root authority differs") + } + result, total := map[string]grokCycleFile{}, 0 + err := filepath.WalkDir(root, func(path string, entry os.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + relative, err := filepath.Rel(root, path) + if err != nil || len(result) >= 128 { + return fmt.Errorf("Grok cycle finite snapshot bound") + } + info, err := os.Lstat(path) + if err != nil { + return err + } + stat, ok := info.Sys().(*syscall.Stat_t) + if !ok || stat.Uid != uint32(os.Getuid()) || info.Mode()&os.ModeSymlink != 0 || (!info.IsDir() && !info.Mode().IsRegular()) { + return fmt.Errorf("Grok cycle held path is not ordinary originally owned") + } + value := grokCycleFile{Kind: "directory", Mode: uint32(info.Mode().Perm()), UID: stat.Uid, GID: stat.Gid, Device: stat.Dev, Inode: stat.Ino} + if entry.IsDir() { + if err := projectNativeRuntimeParents(filepath.Join(path, "leaf")); err != nil { + return err + } + } else { + body, err := projectNativeRuntimeRead(path, 1<<20) + if err != nil { + return err + } + total += len(body) + after, err := os.Lstat(path) + if err != nil || total > 1<<20 || !os.SameFile(info, after) || !projectNativeRuntimeStableStat(info.Sys(), after.Sys()) { + return fmt.Errorf("Grok cycle held file changed during bounded capture") + } + stream, err := grokCycleStream(body) + if err != nil { + return err + } + value.Kind, value.Content = "file", &stream + value.Modified, value.Changed = stat.Mtim.Nano(), stat.Ctim.Nano() + } + result[filepath.ToSlash(relative)] = value + return nil + }) + return result, err +} + +func (c *grokProjectCycle) freeze(t *testing.T, path string, fixed bool) { + t.Helper() + value, err := grokCycleSnapshot(path) + if err != nil { + t.Fatal(err) + } + c.guards[path] = value + if fixed { + c.fixed[path] = value + } +} + +func (c *grokProjectCycle) check(t *testing.T) { + t.Helper() + c.f.check(t) + for path, expected := range c.guards { + actual, err := grokCycleSnapshot(path) + if err != nil || !reflect.DeepEqual(actual, expected) { + t.Fatalf("Grok cycle original held authority changed: %s (%v)", path, err) + } + } + for path := range c.absent { + if err := projectNativeRuntimeParents(path); err != nil { + t.Fatal(err) + } + if _, err := os.Lstat(path); !os.IsNotExist(err) { + t.Fatal("Grok cycle independently absent path appeared") + } + } +} + +func (c *grokProjectCycle) append(t *testing.T, member, stage string, value any) { + t.Helper() + known := false + for _, name := range grokCycleMembers { + known = known || name == member + } + if !known { + t.Fatal("unregistered Grok aggregate") + } + raw, err := json.Marshal(map[string]any{"stage": stage, "value": value}) + if err != nil { + t.Fatal(err) + } + raw = append(raw, '\n') + total := len(raw) + for _, body := range c.aggregates { + total += len(body) + } + if len(c.aggregates[member])+len(raw) > 1<<20 || total > 16<<20 { + t.Fatal("Grok cycle live aggregate bound exceeded") + } + c.aggregates[member] = append(c.aggregates[member], raw...) +} + +func (c *grokProjectCycle) author(t *testing.T, r *grokCycleRecipe, replace bool) { + t.Helper() + r.Authored = grokCycleAuthored(r.Name, r.Revision, c.f.base.Nonce) + for relative, body := range r.Authored { + path := filepath.Join(r.Source, filepath.FromSlash(relative)) + if replace { + if err := os.WriteFile(path, body, 0600); err != nil { + t.Fatal(err) + } + } else { + projectNativeRuntimeWrite(t, path, body, 0600) + } + } + entries, err := clineAttemptPayload(r.Source) + if err != nil { + t.Fatal(err) + } + r.Digest, err = packagedigest.DigestCaptured(c.f.ctx, entries) + if err != nil { + t.Fatal(err) + } + c.freeze(t, r.Source, false) + c.append(t, "grok-cycle-proof.jsonl", "source-"+r.Scope+"-"+r.Revision, map[string]any{"scope": r.Scope, "source": r.Source, "revision": r.Revision, "digest": r.Digest, "files": c.guards[r.Source]}) + c.proofs = append(c.proofs, "source-"+r.Scope+"-"+r.Revision) +} + +func grokCycleAuthored(name, revision, nonce string) map[string][]byte { + manifest, _ := json.Marshal(map[string]any{"$schema": domain.PluginSchemaV1, "name": name, "version": "1.0.0", "description": "Disposable Grok skills cycle"}) + skill := []byte("---\nname: " + name + "\ndescription: " + revision + " " + nonce + "\n---\nUAP_GROK_" + revision + "_" + nonce + "\n") + return map[string][]byte{"plugin.json": manifest, "skills/" + name + "/SKILL.md": skill} +} + +func (c *grokProjectCycle) prepare(t *testing.T) { + t.Helper() + f := c.f + if f.spec.client != "grok" || f.base.Project != filepath.Join(f.base.Root, "project") || f.other != filepath.Join(f.base.Root, "project-b") || f.outside != filepath.Join(f.base.Root, "outside") || f.base.Home != filepath.Join(f.base.Root, "home") { + t.Fatal("Grok cycle original sibling roots differ") + } + for _, path := range []string{f.base.Root, f.base.Project, f.other, f.outside, f.base.Home} { + if err := projectNativeRuntimeParents(filepath.Join(path, "leaf")); err != nil { + t.Fatal(err) + } + info, err := os.Lstat(path) + if err != nil || !info.IsDir() || info.Sys().(*syscall.Stat_t).Uid != uint32(os.Getuid()) { + t.Fatal("Grok cycle roots must be original ordinary owned directories") + } + } + for _, scope := range []string{"a", "b"} { + project := f.base.Project + if scope == "b" { + project = f.other + } + r := &grokCycleRecipe{Scope: scope, Project: project, Source: filepath.Join(f.base.Root, "cycle-source-"+scope), Name: "uap-grok-cycle-" + scope, Revision: scope + "-one"} + c.recipes[scope] = r + c.author(t, r, false) + name := "uap-grok-foreign-" + scope + path := filepath.Join(project, ".grok", "plugins", name) + c.seedPlugin(t, path, name, "Foreign inert marker", "FOREIGN_"+scope+"_"+f.base.Nonce) + caller := filepath.Join(project, ".uap", "plugin-data", "caller-owned") + projectNativeRuntimeWrite(t, filepath.Join(caller, "keep.txt"), []byte("CALLER_"+scope+"_"+f.base.Nonce+"\n"), 0600) + c.freeze(t, caller, true) + } + name := "uap-grok-original-user" + c.seedPlugin(t, filepath.Join(f.base.Home, ".grok", "plugins", name), name, "Original user "+f.base.Nonce, "USER_"+f.base.Nonce) + var trust bytes.Buffer + for _, root := range []string{f.base.Project, f.other} { + quoted, _ := json.Marshal(root) + trust.WriteString("[folders." + string(quoted) + "]\ntrusted = true\n") + } + config := []byte("[marketplace]\ndefault_skills_installs_purged = true\n[plugins]\nenabled = [\"uap-grok-cycle-a\", \"uap-grok-cycle-b\", \"uap-grok-original-user\"]\ndisabled = [\"uap-grok-foreign-a\", \"uap-grok-foreign-b\"]\n[features]\nturn_summary = false\n") + for name, body := range map[string][]byte{"config.toml": config, "trusted_folders.toml": trust.Bytes()} { + path := filepath.Join(f.base.Home, ".grok", name) + projectNativeRuntimeWrite(t, path, body, 0600) + f.protected[path] = nativeSHA(t, path) + c.freeze(t, path, true) + } + c.append(t, "grok-cycle-proof.jsonl", "fixture", map[string]any{"root": f.base.Root, "home": f.base.Home, "project_a": f.base.Project, "project_b": f.other, "outside": f.outside, "nonce": f.base.Nonce, "fixed": c.fixed}) + c.proofs = append(c.proofs, "fixture") + c.check(t) +} + +func (c *grokProjectCycle) seedPlugin(t *testing.T, path, name, description, body string) { + t.Helper() + manifest, _ := json.Marshal(map[string]any{"name": name, "version": "0.9.0", "skills": "./skills/"}) + projectNativeRuntimeWrite(t, filepath.Join(path, ".grok-plugin", "plugin.json"), manifest, 0600) + projectNativeRuntimeWrite(t, filepath.Join(path, "skills", name, "SKILL.md"), []byte("---\nname: "+name+"\ndescription: "+description+"\n---\n"+body+"\n"), 0600) + c.freeze(t, path, true) +} diff --git a/repotests/agentplugins_grok_cycle_inspect_test.go b/repotests/agentplugins_grok_cycle_inspect_test.go new file mode 100644 index 000000000..ba6210b8b --- /dev/null +++ b/repotests/agentplugins_grok_cycle_inspect_test.go @@ -0,0 +1,98 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "encoding/json" + "fmt" + "path/filepath" + "reflect" + "strings" +) + +type grokCycleNativePlugin struct { + Name, Scope, Path, SkillPath, Description string + Skills int + Loaded bool +} + +func (c *grokProjectCycle) expectedInspection(scope string, damaged bool) []grokCycleNativePlugin { + name := "uap-grok-original-user" + user := filepath.Join(c.f.base.Home, ".grok", "plugins", name) + // The retained User producer preserves /./; the owned producer reports the + // exact installed skill path. Neither spelling is an interchangeable alias. + result := []grokCycleNativePlugin{{Name: name, Scope: "user", Path: user, SkillPath: user + "/./skills/" + name + "/SKILL.md", Description: "Original user " + c.f.base.Nonce, Skills: 1, Loaded: true}} + if scope == "outside" { + return result + } + r := c.recipes[scope] + foreign := "uap-grok-foreign-" + scope + result = append(result, grokCycleNativePlugin{Name: foreign, Scope: "project", Path: filepath.Join(r.Project, ".grok", "plugins", foreign), Description: "Foreign inert marker", Skills: 1}) + if r.Active != "" && !r.Removed && !damaged { + result = append(result, grokCycleNativePlugin{Name: r.Name, Scope: "project", Path: r.Active, SkillPath: filepath.Join(r.Active, "skills", r.Name, "SKILL.md"), Description: r.Revision + " " + c.f.base.Nonce, Skills: 1, Loaded: true}) + } + return result +} + +func grokCycleInspect(raw []byte, cwd string, trusted bool, expected []grokCycleNativePlugin) error { + value, err := projectNativeRuntimeJSON(raw) + if err != nil { + return err + } + object, ok := value.(map[string]any) + if !ok || !projectGrokIdentityKeys(object, "grokVersion cwd projectRoot projectTrusted plugins skills mcpServers") || object["grokVersion"] != "1.0.70" || object["cwd"] != cwd || (object["projectRoot"] != cwd && object["projectRoot"] != cwd+"/") || object["projectTrusted"] != trusted { + return fmt.Errorf("Grok cycle fresh inspection project identity differs") + } + plugins, p := object["plugins"].([]any) + skills, s := object["skills"].([]any) + mcp, m := object["mcpServers"].([]any) + if !p || !s || !m || len(mcp) != 0 || len(plugins) != len(expected) { + return fmt.Errorf("Grok cycle complete native inventory differs") + } + wanted := map[string]grokCycleNativePlugin{} + for _, plugin := range expected { + wanted[plugin.Name] = plugin + } + seen := map[string]bool{} + for _, value := range plugins { + entry, ok := value.(map[string]any) + name, named := entry["name"].(string) + want, found := wanted[name] + provides := map[string]any{"skills": json.Number(fmt.Sprint(want.Skills)), "agents": json.Number("0"), "hooks": false, "mcpServers": json.Number("0")} + identity := map[string]any{"name": name, "scope": want.Scope, "path": want.Path, "enabled": true, "provides": provides} + if !ok || !named || !found || seen[name] || !reflect.DeepEqual(entry, identity) { + return fmt.Errorf("Grok cycle exact plugin source/catalog differs") + } + seen[name] = true + } + seen = map[string]bool{} + loaded := 0 + for _, plugin := range expected { + if plugin.Loaded { + loaded++ + } + } + if len(skills) != loaded { + return fmt.Errorf("Grok cycle complete loaded skill set differs") + } + for _, value := range skills { + entry, ok := value.(map[string]any) + name, named := entry["name"].(string) + want, found := wanted[name] + if !ok || !named || !found || !want.Loaded || seen[name] || strings.TrimSpace(name) == "" { + return fmt.Errorf("Grok cycle foreign/duplicate/disabled skill loaded") + } + expectedSkill := map[string]any{"name": name, "description": want.Description, "source": map[string]any{"type": "plugin", "plugin_name": name, "path": want.SkillPath}, "userInvocable": true} + if disabled, exists := entry["disabled"]; exists { + if disabled != false { + return fmt.Errorf("Grok cycle loaded skill is disabled") + } + expectedSkill["disabled"] = false + } + if !reflect.DeepEqual(entry, expectedSkill) { + return fmt.Errorf("Grok cycle loaded skill source or nonce/revision differs") + } + seen[name] = true + } + return nil +} diff --git a/repotests/agentplugins_grok_cycle_lifecycle_test.go b/repotests/agentplugins_grok_cycle_lifecycle_test.go new file mode 100644 index 000000000..cbd4ab45e --- /dev/null +++ b/repotests/agentplugins_grok_cycle_lifecycle_test.go @@ -0,0 +1,332 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "reflect" + "strings" + "syscall" + "testing" + "time" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/adapters/process" + "github.com/777genius/plugin-kit-ai/install/integrationctl/ports" +) + +func (c *grokProjectCycle) execute(t *testing.T, member, label, cwd string, argv []string) ports.CommandResult { + t.Helper() + c.check(t) + ctx, cancel := context.WithTimeout(c.f.ctx, 30*time.Second) + defer cancel() + started := time.Now() + result, err := (process.OS{}).RunWithDescendantExitGrace(ctx, ports.Command{Argv: argv, Env: c.f.env(), Dir: cwd, StdoutLimitBytes: 1 << 20}, 5*time.Second) + meta := map[string]any{"argv": argv, "cwd": cwd, "exit": result.ExitCode, "error": err != nil, "deadline": ctx.Err() != nil, "elapsed_ms": time.Since(started).Milliseconds(), "cleanup": "full descendant supervision; forced cleanup fails", "stdout": c.stream(t, result.Stdout), "stderr": c.stream(t, result.Stderr)} + c.append(t, member, label, meta) + if member == "grok-cycle-public.jsonl" { + c.public = append(c.public, label) + } else { + c.inspections = append(c.inspections, label) + } + c.check(t) + if err != nil || ctx.Err() != nil || len(result.Stdout) > 1<<20 || bytes.Contains(result.Stderr, []byte("stderr bytes omitted")) || result.ExitCode < 0 { + t.Fatal("Grok cycle child/capture/ordinary cleanup failed") + } + if label == "repeat-remove-a" { + if result.ExitCode != 1 || len(result.Stdout) != 0 || string(result.Stderr) != "agentplugins: plugin is not installed for target \"grok\" in project scope; no target was changed\n" { + t.Fatal("Grok cycle exact repeated-removal refusal differs") + } + } else if result.ExitCode != 0 { + t.Fatal("Grok cycle command exited nonzero") + } + return result +} + +func (c *grokProjectCycle) publicCommand(t *testing.T, label string) ports.CommandResult { + t.Helper() + if len(c.public) >= len(grokCyclePublic) || grokCyclePublic[len(c.public)] != label { + t.Fatal("Grok fixed public command order differs") + } + f := c.f + scope := "a" + if strings.HasSuffix(label, "-b") { + scope = "b" + } + r, cwd := c.recipes[scope], c.recipes[scope].Project + var argv []string + switch label { + case "git-a", "git-b", "git-outside": + if label == "git-outside" { + cwd = f.outside + } + argv = []string{f.git, "-c", "core.hooksPath=/dev/null", "init", "--quiet"} + case "version": + cwd, argv = f.outside, []string{f.client, "--version"} + default: + verb, selector := strings.Split(label, "-")[0], r.Name + switch label { + case "repeat-add-a": + verb = "add" + case "unchanged-update-a": + verb = "update" + case "intact-repair-a", "missing-repair-a": + verb, selector = "repair", r.Installation.InstallationID + case "repeat-remove-a": + verb = "remove" + } + if verb == "add" || label == "update-a" { + selector = r.Source + } + argv = []string{f.installer, verb, selector} + if verb == "add" { + argv = append(argv, "--no-save") + } + argv = append(argv, "--scope", "project", "--project-root", r.Project, "--target", "grok", "--format", "json") + } + return c.execute(t, "grok-cycle-public.jsonl", label, cwd, argv) +} + +func (c *grokProjectCycle) inspect(t *testing.T, stage, scope string) { + t.Helper() + if len(c.inspections) >= len(grokCycleInspections) || grokCycleInspections[len(c.inspections)] != stage { + t.Fatal("Grok fixed fresh inspection order differs") + } + cwd := c.f.outside + if scope != "outside" { + cwd = c.recipes[scope].Project + } + result := c.execute(t, "grok-cycle-inspect.jsonl", stage, cwd, []string{c.f.client, "inspect", "--json"}) + // A/B have exact recorded grants; outside has the pinned native gate's + // provisional no-repository-config allow, without a stored trust grant. + if len(result.Stderr) != 0 || grokCycleInspect(result.Stdout, cwd, true, c.expectedInspection(scope, stage == "damaged-a")) != nil { + t.Fatal("Grok exact fresh skill/source inspection differs") + } +} + +func grokCycleProgress(stage, name string) string { + switch stage { + case "add-a", "add-b": + return "Resolving and validating one Agent Plugin package for every selected target...\nInstalling " + name + " for 1 selected clients...\n" + case "repeat-add-a": + return "Resolving and validating one Agent Plugin package for every selected target...\nApplying transactional client package...\n" + case "update-a", "unchanged-update-a": + return "Resolving and validating one updated Agent Plugin package for every selected target...\n" + case "intact-repair-a", "missing-repair-a": + return "Resolving and validating each unique exact installed package revision once...\n" + default: + return "" + } +} + +// The existing single-target repeated add returns the direct public DTO. Its +// authority is the earlier held installation, never an identity learned here. +func grokCycleRepeatAdd(stage, scope string, mutated bool, document map[string]any, r *grokCycleRecipe) error { + refused := fmt.Errorf("Grok repeated add differs from held direct result") + if stage != "repeat-add-a" || scope != "a" || mutated || r == nil || r.Scope != "a" || r.Name == "" || r.Source == "" || r.Digest == "" || r.Installation.InstallationID == "" || r.Binding.PhysicalArtifact == "" || r.Installation.Source.CanonicalSource != r.Source || r.Installation.Source.TreeDigest != r.Digest { + return refused + } + if projectNativeRuntimeKeys(document, "schema_version command result data", "") != nil || document["schema_version"] != json.Number("1") || document["command"] != "add" || document["result"] != "success" { + return refused + } + data, ok := document["data"].(map[string]any) + if !ok || projectNativeRuntimeKeys(data, "plugin version source tree_digest manifest_digest dry_run result", "security next_action") != nil { + return refused + } + for key, value := range map[string]any{"plugin": r.Name, "version": "1.0.0", "source": "direct local source", "tree_digest": r.Digest, "manifest_digest": r.Installation.Package.ManifestDigest, "dry_run": false} { + if data[key] != value { + return refused + } + } + outcome, ok := data["result"].(map[string]any) + if !ok || projectNativeRuntimeKeys(outcome, "installation_id plan activation requires_confirmation mutated", "") != nil || outcome["installation_id"] != r.Installation.InstallationID || outcome["requires_confirmation"] != false || outcome["mutated"] != false { + return refused + } + plan, p := outcome["plan"].(map[string]any) + activation, a := outcome["activation"].(map[string]any) + if !p || !a || projectNativeRuntimeKeys(plan, "client_id scope status package_mode activation authentication policy verification physical_artifact_id components", "user_actions warnings") != nil || projectNativeRuntimeKeys(activation, "activation authentication policy verification", "user_actions") != nil { + return refused + } + for key, value := range map[string]any{"client_id": "grok", "scope": "project", "status": "ready", "package_mode": "native", "physical_artifact_id": r.Binding.PhysicalArtifact} { + if plan[key] != value { + return refused + } + } + for key, value := range map[string]any{"activation": "prepared", "authentication": "not_checked", "policy": "allowed", "verification": "package_validated"} { + if plan[key] != value || activation[key] != value { + return refused + } + } + if !reflect.DeepEqual(plan["components"], []any{map[string]any{"kind": "skill", "name": r.Name, "support": "native"}}) { + return refused + } + return nil +} + +func (c *grokProjectCycle) apply(t *testing.T, stage, scope string, mutated bool) { + t.Helper() + r := c.recipes[scope] + c.check(t) + statePath := filepath.Join(r.Project, ".uap", "state-v2.json") + stableState, stableActive := c.guards[statePath], c.guards[r.Active] + delete(c.guards, statePath) + delete(c.absent, statePath) + if r.Active != "" { + delete(c.guards, r.Active) + } + if stage == "missing-repair-a" { + delete(c.absent, r.Active) + } + if strings.HasPrefix(stage, "remove-") { + delete(c.absent, filepath.Join(r.Project, ".uap", "plugin-data", r.Binding.PhysicalArtifact)) + } + result := c.publicCommand(t, stage) + if stage != "repeat-remove-a" { + value, err := projectNativeRuntimeJSON(result.Stdout) + document, ok := value.(map[string]any) + if err != nil || !ok || nativeValidateInstallerJSON(result.Stdout) != nil || string(result.Stderr) != grokCycleProgress(stage, r.Name) { + t.Fatal("Grok exact public success/progress differs") + } + if stage == "repeat-add-a" { + if err := grokCycleRepeatAdd(stage, scope, mutated, document, r); err != nil { + t.Fatal(err) + } + } else { + commandsRequireMutated(t, stage, document, mutated) + } + } + if strings.HasPrefix(stage, "remove-") { + r.Removed = true + } + c.proof(t, r, stage) + if !mutated { + if !reflect.DeepEqual(c.guards[statePath], stableState) || !reflect.DeepEqual(c.guards[r.Active], stableActive) { + t.Fatal("Grok no-op/repeated removal changed exact held state/package identity") + } + } +} + +func (c *grokProjectCycle) reviseA(t *testing.T) { + t.Helper() + r := c.recipes["a"] + c.proof(t, r, "before-source-revision-a") + c.check(t) + delete(c.guards, r.Source) + r.Revision = "a-two" + c.author(t, r, true) + c.check(t) +} + +func (c *grokProjectCycle) missingA(t *testing.T) { + t.Helper() + r := c.recipes["a"] + c.proof(t, r, "before-missing-a") + c.check(t) + before, err := grokCycleSnapshot(r.Active) + if err != nil || !reflect.DeepEqual(before, c.guards[r.Active]) || grokCyclePayload(before, grokCycleProjected(r)) != nil { + t.Fatal("Grok sole owned package fault authority differs", err) + } + delete(c.guards, r.Active) + for _, relative := range []string{"plugin.json", "skills/" + r.Name + "/SKILL.md", ".grok-plugin/plugin.json", "skills/" + r.Name, "skills", ".grok-plugin", "."} { + path := filepath.Join(r.Active, filepath.FromSlash(relative)) + entry := before[relative] + info, err := os.Lstat(path) + if err != nil { + t.Fatal(err) + } + stat := info.Sys().(*syscall.Stat_t) + if info.Mode()&os.ModeSymlink != 0 || stat.Ino != entry.Inode || stat.Dev != entry.Device || stat.Uid != entry.UID || stat.Gid != entry.GID || uint32(info.Mode().Perm()) != entry.Mode { + t.Fatal("Grok held owned fault entry identity changed") + } + if entry.Content != nil { + body, err := projectNativeRuntimeRead(path, 1<<20) + if err != nil || !bytes.Equal(body, entry.Content.Body) { + t.Fatal("Grok held owned fault file bytes changed", err) + } + } + // Every directory removal is nonrecursive and refuses an extra entry. + if err := os.Remove(path); err != nil { + t.Fatal("Grok exact owned package absence fault", err) + } + } + c.absent[r.Active] = true + c.append(t, "grok-cycle-proof.jsonl", "damaged-a", map[string]any{"scope": "a", "removed_path": r.Active, "before": before, "absent": true, "preserved": c.preserved(t)}) + c.proofs = append(c.proofs, "damaged-a") + c.check(t) +} + +func (c *grokProjectCycle) finish(t *testing.T) { + t.Helper() + defer c.f.finish(t) + defer func() { + for _, member := range grokCycleMembers { + c.f.keep(t, member, c.aggregates[member]) + } + c.f.evidence["cycle_public_stages"], c.f.evidence["cycle_inspect_stages"], c.f.evidence["cycle_proof_stages"] = c.public, c.inspections, c.proofs + c.f.evidence["cycle_observed_complete"] = c.complete && reflect.DeepEqual(c.public, grokCyclePublic) && reflect.DeepEqual(c.inspections, grokCycleInspections) && reflect.DeepEqual(c.proofs, grokCycleProofs) + }() + c.check(t) + if c.f.ctx.Err() != nil { + t.Error("Grok original work deadline expired") + } +} + +func TestAgentpluginsGrokNativeProjectCompleteCycle(t *testing.T) { + if os.Getenv("AGENTPLUGINS_GROK_PROJECT_RUNTIME_ATTEMPT") != "1" { + t.Skip("opt-in genuine hosted complete Grok skills cycle") + } + spec, err := projectNativeCLISpecification("grok") + if err != nil { + t.Fatal(err) + } + f := newProjectNativeCLIAttempt(t, spec) + c := &grokProjectCycle{f: f, recipes: map[string]*grokCycleRecipe{}, aggregates: map[string][]byte{}, guards: map[string]map[string]grokCycleFile{}, fixed: map[string]map[string]grokCycleFile{}, absent: map[string]bool{}, public: []string{}, inspections: []string{}, proofs: []string{}} + f.evidence["capture_mode"], f.evidence["attempt_stage"] = "grok-complete-skills-cycle", "complete-cycle" + defer c.finish(t) + c.prepare(t) + for _, stage := range []string{"git-a", "git-b", "git-outside", "version"} { + result := c.publicCommand(t, stage) + if len(result.Stderr) != 0 || (stage != "version" && len(result.Stdout) != 0) || (stage == "version" && string(result.Stdout) != spec.versionOutput) { + t.Fatal("Grok exact Git/version setup differs") + } + } + for _, scope := range []string{"outside", "a", "b"} { + c.inspect(t, "empty-"+scope, scope) + } + c.apply(t, "add-a", "a", true) + c.apply(t, "add-b", "b", true) + for _, scope := range []string{"a", "b", "outside"} { + c.inspect(t, "installed-"+scope, scope) + } + for _, stage := range []string{"repeat-add-a", "unchanged-update-a"} { + c.apply(t, stage, "a", false) + } + c.apply(t, "intact-repair-a", "a", true) + c.inspect(t, "stable-a", "a") + c.reviseA(t) + c.apply(t, "update-a", "a", true) + for _, scope := range []string{"a", "b", "outside"} { + c.inspect(t, "updated-"+scope, scope) + } + c.missingA(t) + c.inspect(t, "damaged-a", "a") + c.apply(t, "missing-repair-a", "a", true) + c.inspect(t, "repaired-a", "a") + c.apply(t, "remove-a", "a", true) + for _, pair := range [][2]string{{"removed-a", "a"}, {"remaining-b", "b"}, {"removed-outside", "outside"}} { + c.inspect(t, pair[0], pair[1]) + } + c.apply(t, "repeat-remove-a", "a", false) + c.inspect(t, "repeat-removed-a", "a") + c.apply(t, "remove-b", "b", true) + for _, pair := range [][2]string{{"removed-b", "b"}, {"final-a", "a"}, {"final-b", "b"}, {"final-outside", "outside"}} { + c.inspect(t, pair[0], pair[1]) + } + c.append(t, "grok-cycle-proof.jsonl", "final", map[string]any{"preserved": c.preserved(t), "retained_a": c.guards[c.recipes["a"].Data], "retained_b": c.guards[c.recipes["b"].Data], "absent_a": c.recipes["a"].Active, "absent_b": c.recipes["b"].Active}) + c.proofs = append(c.proofs, "final") + c.complete = true +} diff --git a/repotests/agentplugins_grok_cycle_proof_test.go b/repotests/agentplugins_grok_cycle_proof_test.go new file mode 100644 index 000000000..1335b3e35 --- /dev/null +++ b/repotests/agentplugins_grok_cycle_proof_test.go @@ -0,0 +1,194 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/packagedigest" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/domain" +) + +func grokCycleSource(i domain.Installation, r *grokCycleRecipe) error { + source := domain.SourceBinding{SourceBindingID: "src_" + clineAttemptSHA([]byte(r.Source + "\x00\x00"))[:24], RequestedSource: r.Source, CanonicalSource: r.Source, TreeDigest: r.Digest} + pack := domain.PackageBinding{LoaderKind: domain.LoaderKindAgentPlugins, FormatID: domain.FormatIDAgentPluginsV1, SchemaURI: domain.PluginSchemaV1, DeclaredName: r.Name, Version: "1.0.0", ManifestDigest: "sha256:" + clineAttemptSHA(r.Authored["plugin.json"]), Inventory: domain.ComponentInventory{Skills: []string{r.Name}}} + if i.InstallationID == "" || i.DeclaredName != r.Name || !reflect.DeepEqual(i.Source, source) || !reflect.DeepEqual(i.Package, pack) { + return fmt.Errorf("Grok cycle source/package differs from independent authored recipe") + } + return nil +} + +func grokCycleProjected(r *grokCycleRecipe) map[string][]byte { + manifest, _ := json.MarshalIndent(map[string]any{"name": r.Name, "version": "1.0.0", "description": "Disposable Grok skills cycle", "skills": "./skills/"}, "", " ") + return map[string][]byte{"plugin.json": r.Authored["plugin.json"], "skills/" + r.Name + "/SKILL.md": r.Authored["skills/"+r.Name+"/SKILL.md"], ".grok-plugin/plugin.json": append(manifest, '\n')} +} + +func grokCyclePayload(snapshot map[string]grokCycleFile, expected map[string][]byte) error { + directories := map[string]bool{".": true} + for path := range expected { + for dir := filepath.ToSlash(filepath.Dir(path)); dir != "."; dir = filepath.ToSlash(filepath.Dir(dir)) { + directories[dir] = true + } + } + count := len(expected) + len(directories) + if len(snapshot) != count { + return fmt.Errorf("Grok cycle complete package membership differs") + } + for path, entry := range snapshot { + if directories[path] { + if entry.Kind != "directory" || entry.Mode != 0755 || entry.Content != nil { + return fmt.Errorf("Grok cycle package directory mode differs") + } + continue + } + body, ok := expected[path] + if !ok || entry.Kind != "file" || entry.Mode != 0644 || entry.Content == nil || !bytes.Equal(entry.Content.Body, body) { + return fmt.Errorf("Grok cycle package bytes or file mode differ") + } + } + return nil +} + +func (c *grokProjectCycle) bindingProof(t *testing.T, r *grokCycleRecipe, i domain.Installation, stage string) map[string]grokCycleFile { + t.Helper() + if len(i.Clients) != 1 || len(i.DataReceipts) != 0 || i.DataRetained { + t.Fatal("Grok skill-only installation must have one binding and zero data") + } + var b domain.ClientBinding + for _, value := range i.Clients { + b = value + } + physical := r.Name + "-" + clineAttemptSHA([]byte(i.InstallationID))[:12] + active := filepath.Join(r.Project, ".grok", "plugins", physical) + id := "client_" + clineAttemptSHA([]byte(strings.Join([]string{i.InstallationID, "grok", "project", active}, "\x00")))[:24] + revision := &domain.ClientPackageRevision{Version: "1.0.0", TreeDigest: r.Digest, ManifestDigest: "sha256:" + clineAttemptSHA(r.Authored["plugin.json"])} + if b.ClientID != "grok" || b.Scope != "project" || b.TargetLocator != active || b.PhysicalArtifact != physical || b.ClientBindingID != id || b.NativeProfileRoot != filepath.Join(r.Project, ".grok") || b.Materialization != domain.MaterializationMaterialized || b.Activation != domain.ActivationPrepared || b.Verification != domain.VerificationPackageValid || b.Authentication != domain.AuthenticationNotChecked || b.Policy != domain.PolicyAllowed || !reflect.DeepEqual(b.PackageRevision, revision) || b.DataReceiptID != "" || b.NativeActivationAttempt != "" || b.PendingNativeIntent != nil || !b.SelectedDelivery.IsZero() || b.InstallIntent != "" || b.ProfileNamespace != "" || b.ProfileAuthority != nil || b.LocalEntryObservation != nil || !reflect.DeepEqual(b.AffectedSurfaces, []string{"grok"}) { + t.Fatal("Grok exact Prepared project binding differs") + } + if r.Binding.ClientBindingID != "" && (r.Binding.ClientBindingID != id || r.Binding.PhysicalArtifact != physical || r.Installation.InstallationID != i.InstallationID) { + t.Fatal("Grok cycle update/repair changed logical binding authority") + } + for scope, other := range c.recipes { + if scope != r.Scope && other.Installation.InstallationID != "" && (other.Installation.InstallationID == i.InstallationID || other.Binding.ClientBindingID == id || other.Binding.PhysicalArtifact == physical) { + t.Fatal("Grok A/B installation authority overlaps") + } + } + snapshot, err := grokCycleSnapshot(active) + if err != nil || grokCyclePayload(snapshot, grokCycleProjected(r)) != nil { + t.Fatal("Grok selected payload differs from independent projection", err) + } + entries := []packagedigest.CapturedEntry{} + for path, entry := range snapshot { + if path == "." { + continue + } + captured := packagedigest.CapturedEntry{Path: path, Kind: entry.Kind} + if entry.Content != nil { + captured.Content = entry.Content.Body + } + entries = append(entries, captured) + } + digest, err := projectNativeRuntimeInstalledDigest(c.f.ctx, entries) + want := []domain.NativeObjectOwnership{{ObjectID: "package:grok:" + physical, Kind: "managed_package_directory", LogicalName: r.Name, Path: active, ManagedDigest: digest, ProtectionClass: "managed"}} + if err != nil || !reflect.DeepEqual(b.NativeObjects, want) { + t.Fatal("Grok package ownership digest differs from held expected bytes", err) + } + if err := c.swapReceipts(r, b, stage); err != nil || len(b.Receipts) == 0 || i.OperationGroupID != b.Receipts[len(b.Receipts)-1].OperationGroupID { + t.Fatal("Grok public grouped receipt history differs", err) + } + r.Binding, r.Active = b, active + data := filepath.Join(r.Project, ".uap", "plugin-data", physical) + if _, err := os.Lstat(data); !os.IsNotExist(err) { + t.Fatal("Grok skill-only package adopted allocated data") + } + c.absent[data] = true + c.guards[active] = snapshot + return snapshot +} + +func (c *grokProjectCycle) retainedProof(t *testing.T, r *grokCycleRecipe, i domain.Installation) map[string]grokCycleFile { + t.Helper() + physical := r.Name + "-" + clineAttemptSHA([]byte(i.InstallationID))[:12] + id := "data_" + clineAttemptSHA([]byte(strings.Join([]string{i.InstallationID, physical, "project"}, "\x00")))[:24] + path := filepath.Join(r.Project, ".uap", "plugin-data", physical) + data := i.DataReceipts[id] + wantDigest := "sha256:" + clineAttemptSHA([]byte("agentplugins-plugin-data-v1\x00"+id+"\x00"+path)) + if !i.DataRetained || len(i.Clients) != 0 || len(i.DataReceipts) != 1 || data.DataReceiptID != id || data.PhysicalBackend != physical || data.Scope != "project" || data.Locator != path || data.OwnershipDigest != wantDigest || data.State != domain.DataReceiptOwned || r.Binding.PhysicalArtifact != physical { + t.Fatal("Grok actual default-removal retained ownership receipt differs") + } + if r.Data != "" && !reflect.DeepEqual(r.Installation.DataReceipts, i.DataReceipts) { + t.Fatal("Grok retained receipt changed after removal") + } + snapshot, err := grokCycleSnapshot(path) + marker := snapshot[".agentplugins-data-owner.json"] + if err != nil || len(snapshot) != 2 || snapshot["."].Kind != "directory" || snapshot["."].Mode != 0700 || marker.Kind != "file" || marker.Mode != 0600 || marker.Content == nil { + t.Fatal("Grok retained initial data membership differs", err) + } + value, err := projectNativeRuntimeJSON(marker.Content.Body) + want, _ := json.Marshal(data) + expected, _ := projectNativeRuntimeJSON(want) + if err != nil || !reflect.DeepEqual(value, expected) { + t.Fatal("Grok retained marker bytes differ from actual owned receipt") + } + if _, err := os.Lstat(r.Active); !os.IsNotExist(err) { + t.Fatal("Grok removed managed package still exists") + } + c.absent[r.Active] = true + r.Data = path + c.guards[path] = snapshot + return snapshot +} + +func (c *grokProjectCycle) preserved(t *testing.T) map[string]map[string]grokCycleFile { + t.Helper() + result := map[string]map[string]grokCycleFile{} + for path, expected := range c.fixed { + actual, err := grokCycleSnapshot(path) + if err != nil || !reflect.DeepEqual(actual, expected) { + t.Fatal("Grok original fixed authority changed", err) + } + result[path] = actual + } + return result +} + +func (c *grokProjectCycle) proof(t *testing.T, r *grokCycleRecipe, stage string) { + t.Helper() + path := filepath.Join(r.Project, ".uap", "state-v2.json") + raw, err := projectNativeRuntimeRead(path, 1<<20) + if err != nil { + t.Fatal(err) + } + state, err := clineCycleState(raw) + if err != nil || len(state.Installations) != 1 { + t.Fatal("Grok exact single current installation missing", err) + } + i := state.Installations[0] + if err := grokCycleSource(i, r); err != nil { + t.Fatal(err) + } + if r.Installation.InstallationID != "" && i.InstallationID != r.Installation.InstallationID { + t.Fatal("Grok stable installation identity changed") + } + var payload map[string]grokCycleFile + if r.Removed { + payload = c.retainedProof(t, r, i) + } else { + payload = c.bindingProof(t, r, i, stage) + } + if err := c.transactionReceipts(r, i, state.TransactionReceipts); err != nil { + t.Fatal(err) + } + r.Installation = i + c.freeze(t, path, false) + c.append(t, "grok-cycle-proof.jsonl", stage, map[string]any{"scope": r.Scope, "revision": r.Revision, "removed": r.Removed, "state": c.stream(t, raw), "binding": r.Binding, "source_digest": r.Digest, "payload": payload, "preserved": c.preserved(t)}) + c.proofs = append(c.proofs, stage) + c.check(t) +} diff --git a/repotests/agentplugins_grok_cycle_receipt_test.go b/repotests/agentplugins_grok_cycle_receipt_test.go new file mode 100644 index 000000000..5a31e6c83 --- /dev/null +++ b/repotests/agentplugins_grok_cycle_receipt_test.go @@ -0,0 +1,102 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "context" + "fmt" + "path/filepath" + "reflect" + "regexp" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/packagedigest" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/domain" +) + +func grokCycleReceiptCount(stage, scope string) int { + if scope == "b" { + return 1 + } + switch stage { + case "intact-repair-a", "before-source-revision-a": + return 2 + case "update-a", "before-missing-a": + return 3 + case "missing-repair-a", "remove-a", "repeat-remove-a": + return 4 + default: + return 1 + } +} + +func grokCycleExpectedDigest(ctx context.Context, name, revision, nonce string) (string, error) { + r := &grokCycleRecipe{Name: name, Authored: grokCycleAuthored(name, revision, nonce)} + files := grokCycleProjected(r) + entries := make([]packagedigest.CapturedEntry, 0, 3+len(files)) + entries = append(entries, packagedigest.CapturedEntry{Path: ".grok-plugin", Kind: "directory"}, packagedigest.CapturedEntry{Path: "skills", Kind: "directory"}, packagedigest.CapturedEntry{Path: "skills/" + name, Kind: "directory"}) + for path, body := range files { + entries = append(entries, packagedigest.CapturedEntry{Path: path, Kind: "file", Content: body}) + } + return projectNativeRuntimeInstalledDigest(ctx, entries) +} + +func grokCycleReceipt(row domain.MutationReceipt, b domain.ClientBinding, sequence int, kind, before, after string) error { + if !regexp.MustCompile(`^op-[0-9a-f]{32}$`).MatchString(row.OperationGroupID) || row.OperationID != row.OperationGroupID+"-001" { + return fmt.Errorf("Grok grouped receipt operation/group differs") + } + suffix := clineAttemptSHA([]byte(row.OperationID))[:16] + want := domain.MutationReceipt{OperationID: row.OperationID, OperationGroupID: row.OperationGroupID, Sequence: sequence, MutationType: kind, ClientBindingID: b.ClientBindingID, ActivePath: b.TargetLocator, BackupPath: filepath.Join(filepath.Dir(b.TargetLocator), ".agentplugins-backup-"+suffix), BeforeDigest: before, AfterDigest: after, Phase: "committed"} + if kind == "directory_swap" { + want.StagingPath = filepath.Join(b.NativeProfileRoot, ".agentplugins-staging-"+suffix) + } + if !reflect.DeepEqual(row, want) { + return fmt.Errorf("Grok exact receipt type/path/sequence/digest/authority differs") + } + return nil +} + +func (c *grokProjectCycle) swapReceipts(r *grokCycleRecipe, b domain.ClientBinding, stage string) error { + if len(b.Receipts) != grokCycleReceiptCount(stage, r.Scope) { + return fmt.Errorf("Grok grouped receipt stage count differs") + } + one, err := grokCycleExpectedDigest(c.f.ctx, r.Name, r.Scope+"-one", c.f.base.Nonce) + if err != nil { + return err + } + two := one + if r.Scope == "a" && len(b.Receipts) > 2 { + two, err = grokCycleExpectedDigest(c.f.ctx, r.Name, "a-two", c.f.base.Nonce) + if err != nil { + return err + } + } + before, after := []string{"", one, one, ""}, []string{one, one, two, two} + seen := map[string]bool{} + for index, receipt := range b.Receipts { + if seen[receipt.OperationID] || grokCycleReceipt(receipt, b, index+1, "directory_swap", before[index], after[index]) != nil { + return fmt.Errorf("Grok grouped receipt history differs") + } + seen[receipt.OperationID] = true + } + if len(r.Binding.Receipts) > len(b.Receipts) || len(r.Binding.Receipts) > 0 && !reflect.DeepEqual(r.Binding.Receipts, b.Receipts[:len(r.Binding.Receipts)]) { + return fmt.Errorf("Grok prior receipt history changed") + } + return nil +} + +func (c *grokProjectCycle) transactionReceipts(r *grokCycleRecipe, i domain.Installation, rows []domain.MutationReceipt) error { + if !r.Removed { + if len(rows) != 0 { + return fmt.Errorf("Grok active package has unexpected state-level receipt") + } + return nil + } + if len(rows) != 1 || rows[0].OperationGroupID != i.OperationGroupID { + return fmt.Errorf("Grok retained removal has no exact state-level receipt") + } + digest, err := grokCycleExpectedDigest(c.f.ctx, r.Name, r.Revision, c.f.base.Nonce) + if err != nil { + return err + } + return grokCycleReceipt(rows[0], r.Binding, len(r.Binding.Receipts)+1, "directory_remove", digest, "") +} diff --git a/repotests/agentplugins_grok_project_native_attempt_test.go b/repotests/agentplugins_grok_project_native_attempt_test.go new file mode 100644 index 000000000..bf4b84580 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_attempt_test.go @@ -0,0 +1,197 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "reflect" + "strings" + "syscall" + "testing" + "time" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/adapters/pathpolicy" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/clients/shared" +) + +func TestAgentpluginsGrokNativeProjectRuntimeAttempt(t *testing.T) { + projectNativeCLIRuntimeAttempt(t, "grok") +} + +// This reads candidate inventory only. A disabled Prepared entry is not skill +// loading, native execution, source revision or completed lifecycle evidence. +func projectGrokPreparedInspect(body []byte, project, name, active string) error { + return projectGrokInspect(body, project, name, active, false) +} + +func projectGrokInspect(body []byte, project, name, active string, selected bool) error { + if !filepath.IsAbs(project) || filepath.Clean(project) != project || pathpolicy.ValidateLeafID(name) != nil || !filepath.IsAbs(active) || filepath.Clean(active) != active || filepath.Dir(active) != filepath.Join(project, ".grok", "plugins") || pathpolicy.ValidateLeafID(filepath.Base(active)) != nil { + return fmt.Errorf("Grok inspection expectation is outside the selected project") + } + value, err := projectNativeRuntimeJSON(body) + if err != nil { + return err + } + object, ok := value.(map[string]any) + if !ok || !projectGrokIdentityKeys(object, "grokVersion cwd projectRoot projectTrusted skills plugins") || object["grokVersion"] != "1.0.70" || object["cwd"] != project || (object["projectRoot"] != project && object["projectRoot"] != project+string(filepath.Separator)) || object["projectTrusted"] != selected { + return fmt.Errorf("measured Grok project selection identity differs") + } + if _, ok := object["skills"].([]any); !ok { + return fmt.Errorf("Grok skill inventory is not an array") + } + plugins, ok := object["plugins"].([]any) + if !ok { + return fmt.Errorf("Grok plugin inventory is not an array") + } + seen, found := map[string]bool{}, false + for _, value := range plugins { + entry, ok := value.(map[string]any) + if !ok || !projectGrokIdentityKeys(entry, "name scope path enabled") { + return fmt.Errorf("Grok plugin identity object differs") + } + entryName, n := entry["name"].(string) + scope, s := entry["scope"].(string) + path, p := entry["path"].(string) + enabled, e := entry["enabled"].(bool) + folded := shared.FoldJSONKey(entryName) + if !n || strings.TrimSpace(entryName) == "" || seen[folded] || !s || (scope != "project" && scope != "user") || !p || !filepath.IsAbs(path) || filepath.Clean(path) != path || !e { + return fmt.Errorf("ambiguous or noncanonical Grok plugin identity") + } + seen[folded] = true + if folded == shared.FoldJSONKey(name) { + if entryName != name || scope != "project" || path != active || enabled != selected { + return fmt.Errorf("selected Grok project candidate differs") + } + found = true + } + } + if !found { + return fmt.Errorf("selected Grok Prepared candidate is absent") + } + if selected { + return grokProjectSelectedSkill(object["skills"].([]any), name, filepath.Join(active, "skills", name, "SKILL.md")) + } + return nil +} + +func projectGrokIdentityKeys(object map[string]any, identity string) bool { + for key := range object { + for _, expected := range strings.Fields(identity) { + if key != expected && shared.FoldJSONKey(key) == shared.FoldJSONKey(expected) { + return false + } + } + } + return true +} + +// Freeze only the finite selected ordinary paths. No returned native pathname +// can select a file; system ancestors retain their existing no-alias checks. +func projectGrokPreparedAuthority(f *projectNativeCLIAttempt) (func() error, error) { + project, active := f.base.Project, f.binding.TargetLocator + profile := filepath.Join(project, ".grok") + if f.spec.client != "grok" || f.binding.Scope != "project" || f.binding.NativeProfileRoot != profile || pathpolicy.ValidateLeafID(f.name) != nil || pathpolicy.ValidateLeafID(f.binding.PhysicalArtifact) != nil || !filepath.IsAbs(active) || filepath.Clean(active) != active || filepath.Dir(active) != filepath.Join(profile, "plugins") || filepath.Base(active) != f.binding.PhysicalArtifact || f.skill != filepath.Join(active, "skills", f.name, "SKILL.md") { + return nil, fmt.Errorf("Grok selected Prepared package authority differs") + } + directories := []string{project, profile, filepath.Dir(active), active, filepath.Join(active, ".grok-plugin"), filepath.Join(active, "skills"), filepath.Dir(f.skill), f.base.Package, filepath.Join(f.base.Package, "skills"), filepath.Join(f.base.Package, "skills", f.name)} + dirs := map[string]os.FileInfo{} + for _, path := range directories { + if err := projectNativeRuntimeParents(filepath.Join(path, "leaf")); err != nil { + return nil, err + } + info, err := os.Lstat(path) + if err != nil { + return nil, err + } + stat, ok := info.Sys().(*syscall.Stat_t) + if !ok || !info.IsDir() || info.Mode()&os.ModeSymlink != 0 || stat.Uid != uint32(os.Getuid()) { + return nil, fmt.Errorf("Grok selected directory must be ordinary and originally owned") + } + dirs[path] = info + } + skill := []byte("---\nname: " + f.name + "\ndescription: Harmless installed startup marker\n---\nUAP_NATIVE_INSTALLED_" + f.base.Nonce + "\n") + sourceSkill := filepath.Join(f.base.Package, "skills", f.name, "SKILL.md") + manifest := filepath.Join(active, ".grok-plugin", "plugin.json") + files := map[string][]byte{sourceSkill: skill, f.skill: skill, manifest: nil} + identities := map[string]os.FileInfo{} + for path, expected := range files { + before, err := os.Lstat(path) + if err != nil { + return nil, err + } + body, err := projectNativeRuntimeRead(path, 1<<20) + if err != nil { + return nil, err + } + after, err := os.Lstat(path) + if err != nil || !os.SameFile(before, after) || !projectNativeRuntimeStableStat(before.Sys(), after.Sys()) { + return nil, fmt.Errorf("Grok selected held-file identity changed") + } + if path == manifest { + value, err := projectNativeRuntimeJSON(body) + if err != nil || !reflect.DeepEqual(value, map[string]any{"name": f.name, "version": "1.0.0", "description": "Disposable native project startup observation", "skills": "./skills/"}) { + return nil, fmt.Errorf("Grok selected native manifest differs from authored projection") + } + } else if !bytes.Equal(body, expected) { + return nil, fmt.Errorf("Grok source or delivered skill differs from authored recipe") + } + files[path], identities[path] = bytes.Clone(body), before + } + return func() error { + for path, before := range dirs { + if err := projectNativeRuntimeParents(filepath.Join(path, "leaf")); err != nil { + return err + } + after, err := os.Lstat(path) + if err != nil || !os.SameFile(before, after) || before.Mode() != after.Mode() || before.Sys().(*syscall.Stat_t).Uid != after.Sys().(*syscall.Stat_t).Uid || before.Sys().(*syscall.Stat_t).Gid != after.Sys().(*syscall.Stat_t).Gid { + return fmt.Errorf("Grok selected directory authority changed") + } + } + for path, expected := range files { + body, err := projectNativeRuntimeRead(path, 1<<20) + after, statErr := os.Lstat(path) + if err != nil || statErr != nil || !bytes.Equal(body, expected) || !os.SameFile(identities[path], after) || !projectNativeRuntimeStableStat(identities[path].Sys(), after.Sys()) { + return fmt.Errorf("Grok selected held-file authority changed") + } + } + return nil + }, nil +} + +func (f *projectNativeCLIAttempt) inspectGrokSelected(t *testing.T) { + f.evidence["fixture_selection"] = "owned-project-trust-and-selected-plugin-name" + f.inspectGrok(t, true) +} + +func (f *projectNativeCLIAttempt) inspectGrok(t *testing.T, selected bool) { + t.Helper() + verify, err := projectGrokPreparedAuthority(f) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := verify(); err != nil { + t.Error(err) + } + }() + f.evidence["attempt_stage"] = "native-installed-inspection" + r := f.command(t, "native-inspect", f.base.Project, []string{f.client, "inspect", "--json"}, 30*time.Second) + if err := verify(); err != nil { + t.Fatal(err) + } + if len(r.Stderr) != 0 { + t.Fatal("Grok installed inspection emitted diagnostics") + } + if err := projectGrokInspect(r.Stdout, f.base.Project, f.name, f.binding.TargetLocator, selected); err != nil { + t.Fatal(err) + } + observation := map[string]any{"name": f.name, "scope": "project", "path": f.binding.TargetLocator, "enabled": selected, "project_trusted": selected, "interpretation": "visible disabled Prepared candidate only; no skill loading or lifecycle/runtime qualification"} + if selected { + observation["installed_skill"] = f.skill + observation["interpretation"] = "exact selected plugin skill listed; no skill execution or lifecycle/runtime qualification" + } + f.evidence["native_inspection"] = observation +} diff --git a/repotests/agentplugins_grok_project_native_contract_test.go b/repotests/agentplugins_grok_project_native_contract_test.go new file mode 100644 index 000000000..422c1ac89 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_contract_test.go @@ -0,0 +1,204 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/domain" +) + +func grokPreparedInspectFixture() (map[string]any, string, string, string) { + project, name := "/owned/project-a", "uap-grok-research-a" + active := filepath.Join(project, ".grok", "plugins", "selected-artifact") + // The measured 1.0.70 identity subset; extension data is deliberately opaque. + object := map[string]any{"grokVersion": "1.0.70", "cwd": project, "projectRoot": project + "/", "projectTrusted": false, "skills": []any{}, "channel": "unknown", "plugins": []any{ + map[string]any{"name": name, "scope": "project", "path": active, "enabled": false, "provides": map[string]any{"skills": 1, "agents": 0, "hooks": false, "mcpServers": 0}}, + map[string]any{"name": "uap-grok-research-user", "scope": "user", "path": "/owned/home/.grok/plugins/user", "enabled": true}, + }} + return object, project, name, active +} + +func TestGrokPreparedInspectRequiresExactMeasuredDisabledProjectCandidate(t *testing.T) { + for _, trailing := range []string{"", "/"} { + object, project, name, active := grokPreparedInspectFixture() + object["projectRoot"] = project + trailing + body, _ := json.Marshal(object) + if err := projectGrokPreparedInspect(body, project, name, active); err != nil { + t.Fatal(err) + } + } + mutations := []string{"version", "cwd", "root", "root-extra-separator", "trusted", "trust-type", "skills", "plugins", "missing", "duplicate-name", "folded-name", "name", "scope", "path", "noncanonical-path", "enabled", "enabled-type", "folded-key", "missing-key", "plugin-type", "foreign-type"} + for _, mutation := range mutations { + t.Run(mutation, func(t *testing.T) { + object, project, name, active := grokPreparedInspectFixture() + plugins := object["plugins"].([]any) + entry := plugins[0].(map[string]any) + switch mutation { + case "version": + object["grokVersion"] = "1.0.67" + case "cwd": + object["cwd"] = "/other/project" + case "root": + object["projectRoot"] = "/other/project" + case "root-extra-separator": + object["projectRoot"] = project + "//" + case "trusted": + object["projectTrusted"] = true + case "trust-type": + object["projectTrusted"] = "false" + case "skills": + object["skills"] = map[string]any{} + case "plugins": + object["plugins"] = nil + case "missing": + object["plugins"] = plugins[1:] + case "duplicate-name": + object["plugins"] = append(plugins, entry) + case "folded-name": + entry["name"] = strings.ToUpper(name) + case "name": + entry["name"] = "other" + case "scope": + entry["scope"] = "user" + case "path": + entry["path"] = "/other/package" + case "noncanonical-path": + entry["path"] = active + "/../selected-artifact" + case "enabled": + entry["enabled"] = true + case "enabled-type": + entry["enabled"] = 0 + case "folded-key": + entry["Scope"] = entry["scope"] + delete(entry, "scope") + case "missing-key": + delete(entry, "enabled") + case "plugin-type": + plugins[0] = "unknown" + case "foreign-type": + plugins[1].(map[string]any)["path"] = "relative" + } + body, _ := json.Marshal(object) + if err := projectGrokPreparedInspect(body, project, name, active); err == nil { + t.Fatal("changed native inspection admitted") + } + }) + } + object, project, name, active := grokPreparedInspectFixture() + body, _ := json.Marshal(object) + for _, bad := range [][]byte{append(body, body...), []byte(`{"cwd":"a","cwd":"b"}`), {0xff}, []byte(`[]`), []byte(`{"x":NaN}`)} { + if err := projectGrokPreparedInspect(bad, project, name, active); err == nil { + t.Fatal("unsafe JSON admitted") + } + } + if projectGrokPreparedInspect(body, project, name, "/foreign/selected-artifact") == nil { + t.Fatal("unanchored expected package admitted") + } +} + +func grokPreparedAuthorityFixture(t *testing.T) *projectNativeCLIAttempt { + t.Helper() + root := t.TempDir() + spec, _ := projectNativeCLISpecification("grok") + project, source, name := filepath.Join(root, "project-a"), filepath.Join(root, "source"), "uap-grok-attempt" + active := filepath.Join(project, ".grok", "plugins", "selected-artifact") + f := &projectNativeCLIAttempt{spec: spec, base: &nativeFixture{Project: project, Package: source, Nonce: "inert-nonce"}, name: name, client: "/owned/grok", skill: filepath.Join(active, "skills", name, "SKILL.md"), binding: domain.ClientBinding{Scope: "project", NativeProfileRoot: filepath.Join(project, ".grok"), PhysicalArtifact: "selected-artifact", TargetLocator: active}} + skill := []byte("---\nname: " + name + "\ndescription: Harmless installed startup marker\n---\nUAP_NATIVE_INSTALLED_inert-nonce\n") + for _, path := range []string{f.skill, filepath.Join(source, "skills", name, "SKILL.md")} { + projectNativeRuntimeWrite(t, path, skill, 0600) + } + manifest, _ := json.Marshal(map[string]any{"name": name, "version": "1.0.0", "description": "Disposable native project startup observation", "skills": "./skills/"}) + projectNativeRuntimeWrite(t, filepath.Join(active, ".grok-plugin", "plugin.json"), manifest, 0644) + return f +} + +func TestGrokPreparedAuthorityRefusesSubstitutionBeforeFirstBaselineAndAfterInspection(t *testing.T) { + for _, after := range []bool{false, true} { + for _, mutation := range []string{"skill-content", "source-content", "manifest", "manifest-alias", "skill-hardlink", "parent-alias", "directory-replacement", "mode"} { + t.Run(mutation+"-after-"+map[bool]string{false: "no", true: "yes"}[after], func(t *testing.T) { + f := grokPreparedAuthorityFixture(t) + verify, err := projectGrokPreparedAuthority(f) + if err != nil || verify() != nil { + t.Fatalf("ordinary original authority failed: %v", err) + } + active := f.binding.TargetLocator + manifest := filepath.Join(active, ".grok-plugin", "plugin.json") + switch mutation { + case "skill-content": + err = os.WriteFile(f.skill, []byte("foreign skill"), 0600) + case "source-content": + err = os.WriteFile(filepath.Join(f.base.Package, "skills", f.name, "SKILL.md"), []byte("foreign source"), 0600) + case "manifest": + err = os.WriteFile(manifest, []byte(`{"name":"foreign"}`), 0644) + case "manifest-alias": + if err = os.Remove(manifest); err == nil { + err = os.Symlink(f.skill, manifest) + } + case "skill-hardlink": + err = os.Link(f.skill, filepath.Join(active, "extra-link")) + case "parent-alias": + if err = os.Rename(filepath.Dir(manifest), filepath.Dir(manifest)+"-original"); err == nil { + err = os.Symlink(filepath.Dir(manifest)+"-original", filepath.Dir(manifest)) + } + case "directory-replacement": + if err = os.Rename(active, active+"-original"); err == nil { + err = os.Mkdir(active, 0700) + } + case "mode": + err = os.Chmod(f.skill, 0700) + } + if err != nil { + t.Fatal(err) + } + if after { + err = verify() + } else { + _, err = projectGrokPreparedAuthority(f) + } + // Before the first snapshot the authored byte recipe, ordinary + // paths and single-link rule refuse corruption. Mode is frozen + // across the inspection, without inventing a projection mode. + if mutation != "mode" || after { + if err == nil { + t.Fatal("selected path substitution admitted") + } + } + }) + } + } + f := grokPreparedAuthorityFixture(t) + f.binding.TargetLocator = filepath.Join(f.base.Project, "foreign", f.binding.PhysicalArtifact) + if _, err := projectGrokPreparedAuthority(f); err == nil { + t.Fatal("binding selected a foreign package anchor") + } +} + +func TestGrokPreparedInspectCommandCannotAuthorizeOtherClientsArgvScopeOrDeadline(t *testing.T) { + f := grokPreparedAuthorityFixture(t) + good := []string{f.client, "inspect", "--json"} + if err := f.selectedCommand("native-inspect", f.base.Project, good, 30*time.Second); err != nil { + t.Fatal(err) + } + for _, client := range []string{"qoder", "kiro", "windsurf"} { + f.spec.client = client + if f.selectedCommand("native-inspect", f.base.Project, good, 30*time.Second) == nil { + t.Fatal("another client gained native inspection") + } + } + f.spec.client = "grok" + for _, changed := range []struct { + cwd string + argv []string + timeout time.Duration + }{{"/foreign/project", good, 30 * time.Second}, {f.base.Project, []string{f.client, "plugin", "enable"}, 30 * time.Second}, {f.base.Project, good, 60 * time.Second}} { + if f.selectedCommand("native-inspect", changed.cwd, changed.argv, changed.timeout) == nil { + t.Fatal("unreviewed command/cwd/deadline admitted") + } + } +} diff --git a/repotests/agentplugins_grok_project_native_duplicate_contract_test.go b/repotests/agentplugins_grok_project_native_duplicate_contract_test.go new file mode 100644 index 000000000..a711d7ff9 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_duplicate_contract_test.go @@ -0,0 +1,375 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "strings" + "testing" + "time" +) + +func grokDuplicateContractProvider(t *testing.T) *grokProjectProvider { + t.Helper() + p, _ := grokTitleContractProvider(t) + first := []byte(`{"model":"uap-grok-fixture","stream":true,"messages":[{"role":"user","content":"independent inert first request"}]}`) + status, reply := grokProjectContractSend(t, grokProjectContractRequest(t, p, first)) + p.mu.Lock() + defer p.mu.Unlock() + if status != http.StatusOK || !bytes.Equal(reply, grokProjectProviderResponse()) || !bytes.Equal(p.request, first) || p.receipt.Requests != 1 || p.receipt.Responses != 1 { + t.Fatal("first original exchange changed before refused diagnostic") + } + return p +} + +func grokDuplicateContractEncode(t *testing.T, value any) []byte { + t.Helper() + raw, err := json.Marshal(value) + if err != nil { + t.Fatal(err) + } + return raw +} + +func grokDuplicateContractFiles(t *testing.T, p *grokProjectProvider) map[string][]byte { + t.Helper() + p.stop() + p.title.transport.stop() + p.mu.Lock() + defer p.mu.Unlock() + files := map[string][]byte{ + "runtime-attempt.json": grokDuplicateContractEncode(t, map[string]any{"client": "grok", "activation": "prepared", "runtime_attempt_complete": false, "test_failed": true, + "vendor_authentication_qualified": false, "real_model_qualified": false, "qualification": map[string]bool{"project-lifecycle": false, "native-restart-removal": false, "mcp-initialize": false, "mcp-tools-list": false, "mcp-tools-call": false}}), + "grok-provider.json": grokDuplicateContractEncode(t, p.receipt), "grok-provider.request.json": bytes.Clone(p.request), "grok-provider.response.sse": bytes.Clone(p.reply), + } + p.title.transport.mu.Lock() + files["grok-title-provider.json"] = grokDuplicateContractEncode(t, struct { + Role string `json:"role"` + grokProjectProviderReceipt + }{"session-title", p.title.transport.receipt}) + p.title.transport.mu.Unlock() + if p.duplicate != nil { + select { + case <-p.duplicate.done: + default: + t.Fatal("duplicate handler not settled after original stop") + } + files[grokDuplicateReceiptName] = grokDuplicateContractEncode(t, p.duplicate.receipt) + if p.duplicate.receipt.Status == "captured" { + files[grokDuplicateRequestName] = bytes.Clone(p.duplicate.raw) + } + } + return files +} + +func TestGrokProjectDuplicateOpaqueCaptureNeverRespondsOrQualifies(t *testing.T) { + p := grokDuplicateContractProvider(t) + second := []byte(`{"unfamiliar_model":"ordinary refused data","messages":null}`) + status, response := grokProjectContractSend(t, grokProjectContractRequest(t, p, second)) + if status != http.StatusBadRequest || !bytes.Equal(response, []byte("closed Grok provider request refused\n")) || p.ctx.Err() == nil { + t.Fatal("observed duplicate gained a response or escaped original cancellation") + } + p.mu.Lock() + claimed := p.duplicate + p.mu.Unlock() + if third, _ := grokProjectContractSend(t, grokProjectContractRequest(t, p, []byte(`{"third":"refused"}`))); third != http.StatusBadRequest { + t.Fatal("third request was accepted") + } + files := grokDuplicateContractFiles(t, p) + if err := grokProjectDuplicateMembers(files, "grok"); err != nil { + t.Fatal("coherent failed diagnostic refused", err) + } + if p.duplicate != claimed || !bytes.Equal(files[grokDuplicateRequestName], second) || p.receipt.Failure != "duplicate" || p.receipt.Requests != 1 || p.receipt.Responses != 1 || !bytes.Equal(p.reply, grokProjectProviderResponse()) { + t.Fatal("diagnostic replaced first exchange, slot or original failure") + } +} + +func TestGrokProjectDuplicateStructureAndPrivacyRefuseBeforeRetention(t *testing.T) { + for _, kind := range []string{"empty", "oversized", "array", "null", "scalar", "invalid", "duplicate", "trailing", "nonfinite", "depth", "nodes", "key", "utf8", "unicode-high", "unicode-low", "unicode-invalid-pair", "unicode-key", "unicode-nested-string", "unicode-nested-key", "unicode-nested-scalar", "unicode-nested-lines", "main-key", "title-key", "escaped-main", "escaped-title", "nested-main", "nested-title"} { + t.Run(kind, func(t *testing.T) { + p := grokDuplicateContractProvider(t) + var raw []byte + expected := "json-refused" + switch kind { + case "empty": + raw, expected = nil, "body-refused" + case "oversized": + raw, expected = bytes.Repeat([]byte{' '}, (1<<20)+1), "body-refused" + case "array", "null", "scalar": + raw = []byte(map[string]string{"array": "[]", "null": "null", "scalar": "42"}[kind]) + case "invalid": + raw = []byte(`{"inert":`) + case "duplicate": + raw = []byte(`{"inert":1,"inert":2}`) + case "trailing": + raw = []byte(`{} {}`) + case "nonfinite": + raw = []byte(`{"number":1e9999}`) + case "depth": + raw = []byte(`{"nested":` + strings.Repeat("[", 34) + "0" + strings.Repeat("]", 34) + "}") + case "nodes": + raw = []byte(`{"nodes":[` + strings.Repeat("0,", 16384) + "0]}") + case "key": + raw = grokDuplicateContractEncode(t, map[string]bool{strings.Repeat("é", 2049): true}) + case "utf8": + raw = []byte{255} + case "unicode-high", "unicode-low", "unicode-invalid-pair", "unicode-key": + raw = []byte(map[string]string{"unicode-high": `{"opaque":"\ud800"}`, "unicode-low": `{"opaque":"\udc00"}`, + "unicode-invalid-pair": `{"opaque":"\ud800\u0041"}`, "unicode-key": `{"\ud800":"ordinary"}`}[kind]) + case "unicode-nested-string", "unicode-nested-key", "unicode-nested-scalar", "unicode-nested-lines": + inner := `{"opaque":"\ud800"}` + switch kind { + case "unicode-nested-scalar": + inner = `"\ud800"` + case "unicode-nested-lines": + inner = "{}\n" + inner + } + value := map[string]string{"opaque": inner} + if kind == "unicode-nested-key" { + value = map[string]string{inner: "ordinary"} + } + raw = grokDuplicateContractEncode(t, value) + default: + key := p.key + if strings.Contains(kind, "title") { + key = p.title.transport.key + } + expected = "privacy-refused" + raw = []byte(`{"opaque":"` + key + `"}`) + if strings.HasPrefix(kind, "escaped") || strings.HasPrefix(kind, "nested") { + raw = []byte(`{"\u0075` + key[1:] + `":"ordinary"}`) + } + if strings.HasPrefix(kind, "nested") { + raw = grokDuplicateContractEncode(t, map[string]string{"opaque": string(raw)}) + } + } + status, _ := grokProjectContractSend(t, grokProjectContractRequest(t, p, raw)) + files := grokDuplicateContractFiles(t, p) + if status != http.StatusBadRequest || p.ctx.Err() == nil || p.receipt.Failure != "duplicate" || p.duplicate == nil || p.duplicate.receipt.Status != expected || files[grokDuplicateRequestName] != nil { + t.Fatal("refused body was retained or changed original failure", expected) + } + if err := grokProjectDuplicateMembers(files, "grok"); err != nil { + t.Fatal("truthful finite refusal failed artifact admission", err) + } + }) + } +} + +func TestGrokProjectDuplicateUnicodeScalarsPreserveRawOrdinaryData(t *testing.T) { + for _, raw := range []string{`{"opaque":"\ud83d\ude00"}`, `{"opaque":"\ufffd"}`, `{"opaque":"�"}`, `{"opaque":"é😀"}`, + `{"opaque":"\\ud800"}`, `{"opaque":"\/path"}`, `{"opaque":"\"\\ud800"}`, `{"\ud83d\ude00":"ordinary"}`} { + p := grokDuplicateContractProvider(t) + status, _ := grokProjectContractSend(t, grokProjectContractRequest(t, p, []byte(raw))) + files := grokDuplicateContractFiles(t, p) + if status != http.StatusBadRequest || p.duplicate.receipt.Status != "captured" || !bytes.Equal(files[grokDuplicateRequestName], []byte(raw)) { + t.Fatal("valid Unicode data was normalized, refused or answered") + } + if err := grokProjectDuplicateMembers(files, "grok"); err != nil { + t.Fatal("valid scalar failed artifact admission", err) + } + } +} + +func grokDuplicateContractPartial(t *testing.T, p *grokProjectProvider, duplicate bool) net.Conn { + t.Helper() + conn, err := (&net.Dialer{Timeout: time.Second}).DialContext(p.ctx, "tcp4", p.listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = conn.Close() }) + if _, err := fmt.Fprintf(conn, "POST /v1/chat/completions HTTP/1.1\r\nHost: %s\r\nAuthorization: Bearer %s\r\nContent-Type: application/json\r\nContent-Length: 1000\r\n\r\n{", p.listener.Addr(), p.key); err != nil { + t.Fatal(err) + } + end := time.Now().Add(time.Second) + for { + p.mu.Lock() + claimed := p.duplicate != nil + if !duplicate { + claimed = p.attempted + } + p.mu.Unlock() + if claimed { + return conn + } + if time.Now().After(end) { + t.Fatal("partial duplicate never reached one-shot decision") + } + time.Sleep(time.Millisecond) + } +} + +func TestGrokProjectDuplicateConcurrentRefusalCannotBlockOriginalStop(t *testing.T) { + p := grokDuplicateContractProvider(t) + grokDuplicateContractPartial(t, p, true) + p.mu.Lock() + claimed := p.duplicate + p.mu.Unlock() + status, _ := grokProjectContractSend(t, grokProjectContractRequest(t, p, []byte(`{"third":"ordinary"}`))) + start := time.Now() + files := grokDuplicateContractFiles(t, p) + if status != http.StatusBadRequest || p.duplicate != claimed || p.duplicate.receipt.Status != "context-refused" || files[grokDuplicateRequestName] != nil || p.receipt.Failure != "duplicate" || !p.receipt.ForcedClose || !p.receipt.ShutdownComplete || time.Since(start) > 2500*time.Millisecond { + t.Fatal("concurrent refusal stalled stop, renewed origin or retained partial bytes") + } + if err := grokProjectDuplicateMembers(files, "grok"); err != nil { + t.Fatal("truthful concurrent/forced failed artifact refused", err) + } +} + +func TestGrokProjectDuplicatePartialReadKeepsOriginalFiveSecondLimit(t *testing.T) { + p := grokDuplicateContractProvider(t) + start := time.Now() + conn := grokDuplicateContractPartial(t, p, true) + // This is a client-side test observation bound, never a provider deadline. + if err := conn.SetReadDeadline(start.Add(7 * time.Second)); err != nil { + t.Fatal(err) + } + response, _ := io.ReadAll(io.LimitReader(conn, 2049)) + files := grokDuplicateContractFiles(t, p) + if time.Since(start) > 6500*time.Millisecond || p.server.ReadTimeout != 5*time.Second || p.duplicate.receipt.Status != "body-refused" || files[grokDuplicateRequestName] != nil || p.ctx.Err() == nil || bytes.Contains(response, []byte("200 OK")) || bytes.Contains(response, []byte("data:")) { + t.Fatal("partial diagnostic escaped original read/refusal boundary") + } + if err := grokProjectDuplicateMembers(files, "grok"); err != nil { + t.Fatal(err) + } +} + +func TestGrokProjectDuplicateRequiresOriginalEligibility(t *testing.T) { + for _, kind := range []string{"query", "method", "host", "credential", "duplicate-credential", "content-type", "duplicate-content-type", "late", "earlier", "missing-title", "incomplete-first"} { + t.Run(kind, func(t *testing.T) { + var p *grokProjectProvider + switch kind { + case "incomplete-first": + p, _ = grokTitleContractProvider(t) + grokDuplicateContractPartial(t, p, false) + case "missing-title": + p = grokProjectContractProvider(t) + body := []byte(`{"model":"uap-grok-fixture","stream":true,"messages":[{}]}`) + if status, _ := grokProjectContractSend(t, grokProjectContractRequest(t, p, body)); status != http.StatusOK { + t.Fatal("first inert main-only request failed") + } + default: + p = grokDuplicateContractProvider(t) + } + switch kind { + case "late": + p.cancel() + case "earlier": + p.fail("serve") + } + r := grokProjectContractRequest(t, p, []byte(`{"ordinary":"refused"}`)) + grokProjectContractMutateHTTP(r, kind) + status, _ := grokProjectContractSend(t, r) + p.stop() + if status != http.StatusBadRequest || p.ctx.Err() == nil || p.duplicate != nil || kind == "earlier" && p.receipt.Failure != "serve" { + t.Fatal("ineligible refusal acquired a duplicate capture or replaced first failure") + } + }) + } + main, title := grokTitleContractProvider(t) + if status, _ := grokProjectContractSend(t, grokProjectContractRequest(t, title.transport, []byte(grokTitleContractGolden))); status != http.StatusOK { + t.Fatal("first inert title exchange failed") + } + status, _ := grokProjectContractSend(t, grokProjectContractRequest(t, title.transport, []byte(`{"second":"title-refused"}`))) + title.transport.stop() + main.stop() + if status != http.StatusBadRequest || title.transport.duplicate != nil || main.duplicate != nil || title.transport.receipt.Failure != "duplicate" { + t.Fatal("title duplicate acquired main-only diagnostic") + } +} + +func TestGrokProjectDuplicateArtifactJoinsRefuseCoherentForgery(t *testing.T) { + p := grokDuplicateContractProvider(t) + grokProjectContractSend(t, grokProjectContractRequest(t, p, []byte(`{"independent":"ordinary refused data"}`))) + original := grokDuplicateContractFiles(t, p) + for _, kind := range []string{"client", "missing-receipt", "missing-raw", "missing-title", "missing-first", "unknown-file", "role", "index", "status", "extra", "response", "bytes", "hash", "refused-body", "main-count", "main-failure", "success", "qualification", "private", "nested-private", "invalid-json", "unicode-high", "unicode-low", "unicode-invalid-pair", "unicode-nested-key"} { + t.Run(kind, func(t *testing.T) { + files := map[string][]byte{} + for name, body := range original { + files[name] = bytes.Clone(body) + } + value, err := projectNativeRuntimeJSON(files[grokDuplicateReceiptName]) + if err != nil { + t.Fatal(err) + } + row := value.(map[string]any) + client := "grok" + switch kind { + case "client": + client = "qoder" + case "missing-receipt": + delete(files, grokDuplicateReceiptName) + case "missing-raw": + delete(files, grokDuplicateRequestName) + case "missing-title": + delete(files, "grok-title-provider.json") + case "missing-first": + delete(files, "grok-provider.request.json") + case "unknown-file": + files["grok-provider.duplicate-request-2.json"] = []byte(`{}`) + case "role": + row["role"] = "session-title" + case "index": + row["attempt_index"] = true + case "status": + row["status"] = "accepted" + case "extra": + row["private_error"] = "not permitted" + case "response": + row["response_issued"] = true + case "bytes": + row["captured_bytes"] = 1 + case "hash": + row["captured_sha256"] = strings.Repeat("0", 64) + case "refused-body": + row["status"], row["captured_bytes"], row["captured_sha256"] = "privacy-refused", 0, "" + case "main-count", "main-failure": + value, _ := projectNativeRuntimeJSON(files["grok-provider.json"]) + parent := value.(map[string]any) + if kind == "main-count" { + parent["requests"] = 0 + } else { + parent["failure"] = "none" + } + files["grok-provider.json"] = grokDuplicateContractEncode(t, parent) + case "success", "qualification": + value, _ := projectNativeRuntimeJSON(files["runtime-attempt.json"]) + result := value.(map[string]any) + if kind == "success" { + result["runtime_attempt_complete"] = true + } else { + result["qualification"].(map[string]any)["mcp-tools-call"] = true + } + files["runtime-attempt.json"] = grokDuplicateContractEncode(t, result) + case "private", "nested-private", "invalid-json", "unicode-high", "unicode-low", "unicode-invalid-pair", "unicode-nested-key": + raw := []byte(`{"opaque":"uap-grok-private-inert"}`) + switch kind { + case "nested-private": + raw = grokDuplicateContractEncode(t, map[string]string{"opaque": `{"\u0075ap-grok-private-inert":"ordinary"}`}) + case "invalid-json": + raw = []byte(`{"unclosed":`) + case "unicode-high": + raw = []byte(`{"opaque":"\ud800"}`) + case "unicode-low": + raw = []byte(`{"opaque":"\udc00"}`) + case "unicode-invalid-pair": + raw = []byte(`{"opaque":"\ud800\u0041"}`) + case "unicode-nested-key": + raw = grokDuplicateContractEncode(t, map[string]string{`{"opaque":"\ud800"}`: "ordinary"}) + } + files[grokDuplicateRequestName] = raw + row["captured_bytes"], row["captured_sha256"] = len(raw), grokProjectDuplicateSHA(raw) + } + if kind != "missing-receipt" { + files[grokDuplicateReceiptName] = grokDuplicateContractEncode(t, row) + } + if err := grokProjectDuplicateMembers(files, client); err == nil { + t.Fatal("incoherent or unsafe duplicate evidence admitted") + } + }) + } +} diff --git a/repotests/agentplugins_grok_project_native_duplicate_evidence_test.go b/repotests/agentplugins_grok_project_native_duplicate_evidence_test.go new file mode 100644 index 000000000..1199728a4 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_duplicate_evidence_test.go @@ -0,0 +1,226 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" +) + +func grokProjectDuplicateJSON(raw []byte, required, optional string) (map[string]any, error) { + value, err := projectNativeRuntimeJSON(raw) + row, ok := value.(map[string]any) + if err != nil || !ok || projectNativeRuntimeKeys(row, required, optional) != nil { + return nil, fmt.Errorf("Grok duplicate closed object differs") + } + return row, nil +} + +func grokProjectDuplicateParent(files map[string][]byte, title bool) error { + name, role, model, transport := "grok-provider", "", grokProjectProviderModel, grokProjectProviderTransport + required := "transport model started requests responses credential_matched failure shutdown_complete forced_close" + if title { + name, role, model, transport = "grok-title-provider", "session-title", grokProjectTitleModel, grokProjectTitleTransport + required += " role" + } + row, err := grokProjectDuplicateJSON(files[name+".json"], required, "route_observation") + if err != nil || len(files[name+".json"]) > 2048 || row["model"] != model || row["transport"] != transport || + row["started"] != true || title && row["role"] != role { + return fmt.Errorf("Grok duplicate held main/title identity differs") + } + for _, key := range []string{"started", "credential_matched", "shutdown_complete", "forced_close"} { + if _, ok := row[key].(bool); !ok { + return fmt.Errorf("Grok duplicate parent Boolean differs") + } + } + counts := map[string]int64{} + for _, key := range []string{"requests", "responses"} { + number, ok := row[key].(json.Number) + if !ok { + return fmt.Errorf("Grok duplicate parent integer differs") + } + count, err := number.Int64() + if err != nil || count < 0 || count > 1 { + return fmt.Errorf("Grok duplicate parent count differs") + } + counts[key] = count + _, present := files[name+map[string]string{"requests": ".request.json", "responses": ".response.sse"}[key]] + if present != (count == 1) { + return fmt.Errorf("Grok duplicate parent member join differs") + } + } + if counts["responses"] > counts["requests"] || counts["requests"] > 0 && row["credential_matched"] != true { + return fmt.Errorf("Grok duplicate parent exchange differs") + } + if !title && (counts["requests"] != 1 || counts["responses"] != 1 || row["failure"] != "duplicate" || row["route_observation"] != nil) { + return fmt.Errorf("Grok duplicate completed first main exchange absent") + } + failure, isFailure := row["failure"].(string) + failures := map[string]bool{"none": true, "listen": true, "serve": true, "deadline": true, "route": true, "credential": true, + "duplicate": true, "body": true, "request-shape": true, "privacy": true, "response-write": true, "cleanup": true} + if title && (!isFailure || !failures[failure]) { + return fmt.Errorf("Grok duplicate title failure differs") + } + if observation, present := row["route_observation"]; present { + if failure != "route" || grokProjectDuplicateRoute(observation) != nil { + return fmt.Errorf("Grok duplicate parent route observation differs") + } + } + if title && (row["failure"] == "listen" || row["failure"] == "none" && + (counts["requests"] != counts["responses"] || row["forced_close"] != false || row["shutdown_complete"] != true) || + (row["failure"] == "body" || row["failure"] == "privacy") && counts["requests"] != 0 || + map[string]bool{"request-shape": true, "privacy": true, "body": true, "response-write": true}[failure] && counts["responses"] != 0) { + return fmt.Errorf("Grok duplicate title partial/cleanup state differs") + } + if counts["requests"] == 1 && grokProjectDuplicateObject(files[name+".request.json"]) != "captured" { + return fmt.Errorf("Grok duplicate parent safe request absent") + } + if !title { + value, err := projectNativeRuntimeJSON(files[name+".request.json"]) + request, ok := value.(map[string]any) + if err != nil || !ok { + return fmt.Errorf("Grok duplicate first request absent") + } + messages, ok := request["messages"].([]any) + if request["model"] != grokProjectProviderModel || request["stream"] != true || !ok || len(messages) == 0 || len(messages) > 128 { + return fmt.Errorf("Grok duplicate first request predicate differs") + } + } + if counts["responses"] == 1 { + response := grokProjectProviderResponse() + if title { + response = grokProjectTitleResponse() + value, err := projectNativeRuntimeJSON(files[name+".request.json"]) + if err != nil || !reflect.DeepEqual(value, grokProjectTitleExpected()) { + return fmt.Errorf("Grok duplicate title response/request join differs") + } + } + if !bytes.Equal(files[name+".response.sse"], response) { + return fmt.Errorf("Grok duplicate first response changed") + } + } + return nil +} + +func grokProjectDuplicateRoute(value any) error { + row, ok := value.(map[string]any) + flags := []string{"peer_loopback", "host_matches", "relative_url", "empty_url_host", "empty_raw_path", "content_type_exact"} + if !ok || projectNativeRuntimeKeys(row, "method uri peer_loopback host_matches relative_url empty_url_host empty_raw_path content_type_exact content_type_count content_type_kind", "") != nil { + return fmt.Errorf("Grok duplicate route shape differs") + } + all := true + for _, key := range flags { + value, ok := row[key].(bool) + if !ok { + return fmt.Errorf("Grok duplicate route Boolean differs") + } + all = all && value + } + choices := map[string]map[string]bool{ + "method": {"POST": true, "GET": true, "other": true}, + "uri": {"/": true, "/v1/chat/completions": true, "/chat/completions": true, "/v1/models": true, "/models": true, "/v1/responses": true, "/responses": true, "other": true}, + "content_type_count": {"none": true, "one": true, "multiple": true}, + "content_type_kind": {"missing": true, "json-bare": true, "json-utf8": true, "other": true}, + } + for key, allowed := range choices { + value, ok := row[key].(string) + if !ok || !allowed[value] { + return fmt.Errorf("Grok duplicate route enum differs") + } + } + count, kind := row["content_type_count"], row["content_type_kind"] + if (count == "none") != (kind == "missing") || count == "multiple" && kind != "other" || + (kind == "json-bare" || kind == "json-utf8") && count != "one" || + row["content_type_exact"] == true && (count != "one" || kind != "json-bare") || + row["method"] == "POST" && row["uri"] == "/v1/chat/completions" && all { + return fmt.Errorf("Grok duplicate route contradiction") + } + return nil +} + +func grokProjectDuplicateFailedTuple(raw []byte) error { + value, err := projectNativeRuntimeJSON(raw) + row, ok := value.(map[string]any) + if err != nil || !ok || row["client"] != "grok" || row["activation"] != "prepared" || row["runtime_attempt_complete"] != false || row["test_failed"] != true || + row["vendor_authentication_qualified"] != false || row["real_model_qualified"] != false { + return fmt.Errorf("Grok duplicate must remain failed and unqualified") + } + flags, ok := row["qualification"].(map[string]any) + if !ok || projectNativeRuntimeKeys(flags, "project-lifecycle native-restart-removal mcp-initialize mcp-tools-list mcp-tools-call", "") != nil { + return fmt.Errorf("Grok duplicate qualification shape differs") + } + for _, value := range flags { + if value != false { + return fmt.Errorf("Grok duplicate cannot qualify any surface") + } + } + return nil +} + +func grokProjectDuplicateMembers(files map[string][]byte, client string) error { + receipt, present := files[grokDuplicateReceiptName] + raw, captured := files[grokDuplicateRequestName] + if !present && !captured { + return nil + } + if client != "grok" || !present || len(receipt) > 2048 { + return fmt.Errorf("Grok duplicate member isolation differs") + } + if err := grokProjectDuplicateClosedFiles(files); err != nil { + return err + } + row, err := grokProjectDuplicateJSON(receipt, "schema_version role attempt_index status original_failure response_issued captured_bytes captured_sha256", "") + if err != nil || row["schema_version"] != json.Number("1") || row["role"] != "main-duplicate-diagnostic" || + row["attempt_index"] != json.Number("2") || row["original_failure"] != "duplicate" || row["response_issued"] != false { + return fmt.Errorf("Grok duplicate receipt identity differs") + } + if err := grokProjectDuplicateFailedTuple(files["runtime-attempt.json"]); err != nil { + return err + } + for _, title := range []bool{false, true} { + if err := grokProjectDuplicateParent(files, title); err != nil { + return err + } + } + switch row["status"] { + case "captured": + if !captured || len(raw) == 0 || len(raw) > 1<<20 || row["captured_bytes"] != json.Number(fmt.Sprint(len(raw))) || + row["captured_sha256"] != grokProjectDuplicateSHA(raw) || grokProjectDuplicateObject(raw) != "captured" { + return fmt.Errorf("Grok duplicate raw/privacy join differs") + } + case "body-refused", "json-refused", "privacy-refused", "context-refused": + if captured || row["captured_bytes"] != json.Number("0") || row["captured_sha256"] != "" { + return fmt.Errorf("Grok duplicate refusal retained raw") + } + default: + return fmt.Errorf("Grok duplicate status differs") + } + return nil +} + +func grokProjectDuplicateClosedFiles(files map[string][]byte) error { + allowed := map[string]bool{"runtime-attempt.json": true, "probe-events.jsonl": true, grokDuplicateReceiptName: true, grokDuplicateRequestName: true} + for _, prefix := range []string{"grok-provider", "grok-title-provider"} { + for _, suffix := range []string{".json", ".request.json", ".response.sse"} { + allowed[prefix+suffix] = true + } + } + for _, label := range []string{"git-a", "git-b", "version", "add-a", "native-inspect", "native-attempt"} { + for _, suffix := range []string{".command.json", ".stdout.log", ".stderr.log"} { + allowed[label+suffix] = true + } + } + total := 0 + for name, body := range files { + total += len(body) + if !allowed[name] || len(body) > 1<<20 || grokProjectPrivateCredential(body) { + return fmt.Errorf("Grok duplicate closed/privacy member differs") + } + } + if len(files) > 80 || total > 16<<20 { + return fmt.Errorf("Grok duplicate archive bound differs") + } + return nil +} diff --git a/repotests/agentplugins_grok_project_native_duplicate_test.go b/repotests/agentplugins_grok_project_native_duplicate_test.go new file mode 100644 index 000000000..26607c847 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_duplicate_test.go @@ -0,0 +1,262 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "crypto/sha256" + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" + "testing" +) + +const grokDuplicateReceiptName = "grok-provider.duplicate.json" +const grokDuplicateRequestName = "grok-provider.duplicate-request.json" + +func grokProjectDuplicateSHA(raw []byte) string { return fmt.Sprintf("%x", sha256.Sum256(raw)) } + +type grokProjectDuplicateReceipt struct { + SchemaVersion int `json:"schema_version"` + Role string `json:"role"` + AttemptIndex int `json:"attempt_index"` + Status string `json:"status"` + OriginalFailure string `json:"original_failure"` + ResponseIssued bool `json:"response_issued"` + CapturedBytes int `json:"captured_bytes"` + CapturedSHA256 string `json:"captured_sha256"` +} + +type grokProjectDuplicateCapture struct { + receipt grokProjectDuplicateReceipt + raw []byte + done chan struct{} +} + +// Caller holds p.mu. Reserve both first failure and the sole observation before +// releasing the lock; all later attempts keep their original immediate refusal. +func (p *grokProjectProvider) claimDuplicate() *grokProjectDuplicateCapture { + if p.ctx.Err() != nil || p.title == nil || p.title.transport == nil || p.title.transport == p || + p.key == "" || p.title.transport.key == "" || p.title.transport.key == p.key || + p.receipt.Failure != "none" || p.receipt.Requests != 1 || p.receipt.Responses != 1 || + len(p.request) == 0 || !bytes.Equal(p.reply, grokProjectProviderResponse()) || p.duplicate != nil { + return nil + } + p.receipt.Failure = "duplicate" + p.duplicate = &grokProjectDuplicateCapture{done: make(chan struct{})} + return p.duplicate +} + +// No provider lock is held during body reading or parsing. The existing server +// ReadTimeout and original work/total contexts are the only time origins. +func (p *grokProjectProvider) observeDuplicate(w http.ResponseWriter, r *http.Request, capture *grokProjectDuplicateCapture) { + status := "body-refused" + var retained []byte + defer func() { + p.mu.Lock() + if p.ctx.Err() != nil { + status, retained = "context-refused", nil + } + capture.receipt = grokProjectDuplicateReceipt{SchemaVersion: 1, Role: "main-duplicate-diagnostic", AttemptIndex: 2, + Status: status, OriginalFailure: "duplicate"} + if status == "captured" { + capture.raw = bytes.Clone(retained) + capture.receipt.CapturedBytes = len(retained) + capture.receipt.CapturedSHA256 = grokProjectDuplicateSHA(retained) + } + close(capture.done) + p.mu.Unlock() + http.Error(w, "closed Grok provider request refused", http.StatusBadRequest) + p.failWithRequest("duplicate", r) + }() + if p.ctx.Err() != nil { + status = "context-refused" + return + } + raw, err := io.ReadAll(io.LimitReader(r.Body, (1<<20)+1)) + if err != nil || len(raw) == 0 || len(raw) > 1<<20 { + return + } + status = grokProjectDuplicateObject(raw, p.key, p.title.transport.key) + if status == "captured" { + retained = raw + } +} + +func grokProjectDuplicateObject(raw []byte, keys ...string) string { + value, err := projectNativeRuntimeJSON(raw) + object, ok := value.(map[string]any) + if err != nil || !ok { + return "json-refused" + } + decoded, err := json.Marshal(object) + if err != nil || grokProjectPrivateCredential(raw) || grokProjectPrivateCredential(decoded) { + return "privacy-refused" + } + for _, key := range keys { + if key == "" || bytes.Contains(raw, []byte(key)) || bytes.Contains(decoded, []byte(key)) { + return "privacy-refused" + } + } + budget := grokProjectPrivacyBudget{text: 4 * len(raw), nodes: 16384} + if !budget.scalarJSON(raw, 0) { + return "json-refused" + } + return "captured" +} + +// encoding/json replaces lone UTF16 escapes. This new diagnostic retains the +// original bytes, so refuse such scalars instead of normalizing its raw input. +// Whole strict JSON parsing always precedes this lexical string preflight. +func grokProjectDuplicateScalars(raw []byte) bool { + quoted := false + for index := 0; index < len(raw); index++ { + if raw[index] == '"' { + quoted = !quoted + continue + } + if !quoted || raw[index] != '\\' { + continue + } + index++ + if index >= len(raw) { + return false + } + if raw[index] != 'u' { + continue + } + if index+4 >= len(raw) { + return false + } + unit, err := strconv.ParseUint(string(raw[index+1:index+5]), 16, 16) + if err != nil { + return false + } + index += 4 + if unit >= 0xdc00 && unit <= 0xdfff { + return false + } + if unit < 0xd800 || unit > 0xdbff { + continue + } + if index+6 >= len(raw) || raw[index+1] != '\\' || raw[index+2] != 'u' { + return false + } + low, err := strconv.ParseUint(string(raw[index+3:index+7]), 16, 16) + if err != nil || low < 0xdc00 || low > 0xdfff { + return false + } + index += 6 + } + return !quoted +} + +// Match only complete nested JSON values/lines already traversed by the +// existing privacy scanner, with its unchanged shared text/node/depth bounds. +// Ordinary backslash-u text that is not complete JSON stays ordinary data. +func (b *grokProjectPrivacyBudget) scalarJSON(body []byte, depth int) bool { + if depth > 64 || len(body) > b.text { + return false + } + b.text -= len(body) + value, err := projectNativeRuntimeJSON(body) + if err == nil { + return grokProjectDuplicateScalars(body) && b.scalarValue(value, depth) + } + if b.parseRefusal(body, err) { + return false + } + if !bytes.ContainsRune(body, '\n') { + return true + } + for len(body) != 0 { + line, rest, _ := bytes.Cut(body, []byte{'\n'}) + if len(line) > b.text { + return false + } + b.text -= len(line) + value, err := projectNativeRuntimeJSON(line) + if b.parseRefusal(line, err) || err == nil && (!grokProjectDuplicateScalars(line) || !b.scalarValue(value, depth)) { + return false + } + body = rest + } + return true +} + +func (b *grokProjectPrivacyBudget) scalarValue(value any, depth int) bool { + b.nodes-- + if depth > 64 || b.nodes < 0 { + return false + } + switch node := value.(type) { + case string: + return b.scalarJSON([]byte(node), depth+1) + case map[string]any: + for key, item := range node { + if !b.scalarValue(key, depth+1) || !b.scalarValue(item, depth+1) { + return false + } + } + case []any: + for _, item := range node { + if !b.scalarValue(item, depth+1) { + return false + } + } + } + return true +} + +// Runs after the original finish unlocks. Snapshot only while holding p.mu; +// never wait, parse, scan privacy or persist evidence under that lock. +func (p *grokProjectProvider) finishDuplicate(t *testing.T, f *projectNativeCLIAttempt) { + t.Helper() + p.mu.Lock() + if p.duplicate == nil { + p.mu.Unlock() + return + } + select { + case <-p.duplicate.done: + default: + p.mu.Unlock() + t.Error("Grok duplicate observation did not settle before export") + return + } + receipt, body := p.duplicate.receipt, bytes.Clone(p.duplicate.raw) + p.mu.Unlock() + raw, err := json.Marshal(receipt) + if err != nil || len(raw) > 2048 { + t.Error("Grok duplicate finite receipt refused") + return + } + summary := make(map[string]any, len(f.evidence)+1) + for key, value := range f.evidence { + summary[key] = value + } + summary["test_failed"] = t.Failed() + result, err := json.Marshal(summary) + if err != nil { + t.Error("Grok duplicate failed boundary refused") + return + } + candidate := make(map[string][]byte, len(f.files)+3) + for name, body := range f.files { + candidate[name] = body + } + candidate["runtime-attempt.json"], candidate[grokDuplicateReceiptName] = result, raw + if receipt.Status == "captured" { + candidate[grokDuplicateRequestName] = body + } + if err := grokProjectDuplicateMembers(candidate, f.spec.client); err != nil { + t.Error(fmt.Errorf("Grok duplicate artifact refused: %w", err)) + return + } + f.keep(t, grokDuplicateReceiptName, raw) + if receipt.Status == "captured" { + f.keep(t, grokDuplicateRequestName, body) + } +} diff --git a/repotests/agentplugins_grok_project_native_environment_test.go b/repotests/agentplugins_grok_project_native_environment_test.go new file mode 100644 index 000000000..6c02e1319 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_environment_test.go @@ -0,0 +1,80 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" +) + +func grokContractEnvironment(t *testing.T, entries []string) map[string]string { + t.Helper() + values := make(map[string]string, len(entries)) + for _, entry := range entries { + key, value, ok := strings.Cut(entry, "=") + if _, duplicate := values[key]; !ok || key == "" || duplicate { + t.Fatal("fixture environment contains an invalid or duplicate entry") + } + values[key] = value + } + return values +} + +func assertGrokContractEnvironment(t *testing.T, f *projectNativeCLIAttempt) { + t.Helper() + values := grokContractEnvironment(t, f.env()) + for key, want := range map[string]string{ + "HOME": f.base.Home, "CODEX_HOME": f.base.CodexHome, + "AGENTPLUGINS_HOME": filepath.Join(f.base.Root, "installer-state"), + "NO_COLOR": "1", "GIT_CONFIG_NOSYSTEM": "1", "GIT_CONFIG_GLOBAL": os.DevNull, "GIT_TERMINAL_PROMPT": "0", + } { + if values[key] != want { + t.Fatalf("held environment field %s changed", key) + } + } + for _, key := range []string{"GROK_DISABLE_AUTOUPDATER", "GROK_SAMPLER_SHARED_CLIENT", "UAP_GROK_TEST_PROVIDER_KEY", "UAP_GROK_AMBIENT_SECRET"} { + want, exists := "", false + if key == "GROK_DISABLE_AUTOUPDATER" && f.spec.client == "grok" { + want, exists = "1", true + } else if f.spec.client == "grok" && f.grok != nil { + switch key { + case "GROK_SAMPLER_SHARED_CLIENT": + want, exists = "0", true + case "UAP_GROK_TEST_PROVIDER_KEY": + want, exists = f.grok.key, true + } + } + if value, present := values[key]; value != want || present != exists { + t.Fatalf("client/provider routing or ambient isolation changed for %s", key) + } + } +} + +func TestGrokProjectNativeSharedClientOptOutRequiresHeldFixtureProvider(t *testing.T) { + for key, value := range map[string]string{ + "GROK_SAMPLER_SHARED_CLIENT": "true", "UAP_GROK_TEST_PROVIDER_KEY": "ambient-provider-key", + "GROK_DISABLE_AUTOUPDATER": "0", "UAP_GROK_AMBIENT_SECRET": "ambient-private-value", "HOME": "/ambient-user-home", + } { + t.Setenv(key, value) + } + root := t.TempDir() + for _, client := range []string{"grok", "qoder", "kiro", "windsurf"} { + for _, held := range []bool{false, true} { + t.Run(fmt.Sprintf("%s/provider-%t", client, held), func(t *testing.T) { + f := &projectNativeCLIAttempt{spec: projectNativeCLISpec{client: client}, bin: filepath.Join(root, "bin"), + base: &nativeFixture{Root: root, Home: filepath.Join(root, "original-home"), + Project: filepath.Join(root, "project"), CodexHome: filepath.Join(root, "original-codex")}} + if held { + f.grok = &grokProjectProvider{key: "held-fixture-key"} + } + assertGrokContractEnvironment(t, f) + }) + } + } + if os.Getenv("GROK_SAMPLER_SHARED_CLIENT") != "true" || os.Getenv("UAP_GROK_TEST_PROVIDER_KEY") != "ambient-provider-key" { + t.Fatal("constructing a child environment mutated process-global settings") + } +} diff --git a/repotests/agentplugins_grok_project_native_privacy_test.go b/repotests/agentplugins_grok_project_native_privacy_test.go new file mode 100644 index 000000000..e8752e2d5 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_privacy_test.go @@ -0,0 +1,241 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "strings" + "testing" + "time" + "unicode/utf8" +) + +type grokProjectPrivacyBudget struct{ text, nodes int } + +func grokProjectPrivateCredential(body []byte) bool { + if len(body) > 1<<20 || !utf8.Valid(body) || bytes.Contains(body, []byte("uap-grok-private-")) { + return true + } + budget := grokProjectPrivacyBudget{text: 4 * len(body), nodes: 16384} + return budget.inspectJSON(body, 0) +} + +func (b *grokProjectPrivacyBudget) inspectJSON(body []byte, depth int) bool { + if depth > 64 || len(body) > b.text { + return true + } + b.text -= len(body) + value, err := projectNativeRuntimeJSON(body) + if err == nil { + return b.inspectValue(value, depth) + } + if b.parseRefusal(body, err) { + return true + } + if !bytes.Contains(body, []byte{'\n'}) { + return false + } + for len(body) != 0 { + line, rest, _ := bytes.Cut(body, []byte{'\n'}) + if len(line) > b.text { + return true + } + b.text -= len(line) + value, err := projectNativeRuntimeJSON(line) + if b.parseRefusal(line, err) || (err == nil && b.inspectValue(value, depth)) { + return true + } + body = rest + } + return false +} + +func (b *grokProjectPrivacyBudget) parseRefusal(body []byte, err error) bool { + if err == nil { + return false + } + switch err.Error() { + case "JSON structure limit", "JSON key", "duplicate JSON key": + return true + case "nonfinite JSON number": + return b.nonfiniteRefused(body) + } + return false +} + +func (b *grokProjectPrivacyBudget) nonfiniteRefused(body []byte) bool { + if len(body) > b.text { + return true + } + b.text -= len(body) + decoder := json.NewDecoder(bytes.NewReader(body)) + var raw json.RawMessage + if err := decoder.Decode(&raw); err != nil { + return grokProjectPrivacySyntaxResource(err) + } + err := decoder.Decode(&raw) + return errors.Is(err, io.EOF) || grokProjectPrivacySyntaxResource(err) +} + +func grokProjectPrivacySyntaxResource(err error) bool { + if err == nil || errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return false + } + var syntax *json.SyntaxError + if errors.As(err, &syntax) { + return strings.Contains(syntax.Error(), "exceeded max depth") + } + return true +} + +func (b *grokProjectPrivacyBudget) inspectValue(value any, depth int) bool { + b.nodes-- + if depth > 64 || b.nodes < 0 { + return true + } + switch node := value.(type) { + case string: + if strings.Contains(node, "uap-grok-private-") { + return true + } + return b.inspectJSON([]byte(node), depth+1) + case map[string]any: + for key, item := range node { + if b.inspectValue(key, depth+1) || b.inspectValue(item, depth+1) { + return true + } + } + case []any: + for _, item := range node { + if b.inspectValue(item, depth+1) { + return true + } + } + } + return false +} + +func grokProjectPrivacyJSON(t *testing.T, value any) []byte { + t.Helper() + body, err := json.Marshal(value) + if err != nil { + t.Fatal(err) + } + return body +} + +func TestGrokProjectNativeProviderPrivacyInspectsNestedSemanticStrings(t *testing.T) { + for _, seed := range []string{`{"key":"\u0075ap-grok-private-inert"}`, `{"\u0075ap-grok-private-inert":"ordinary"}`, `["\u0075ap-grok-private-inert"]`} { + nested := []byte(seed) + for range 8 { + nested = grokProjectPrivacyJSON(t, map[string]string{"content": string(nested)}) + for _, raw := range [][]byte{nested, bytes.Join([][]byte{[]byte(`{"first":true}`), nested, {}}, []byte{'\n'})} { + before := bytes.Clone(raw) + if bytes.Contains(raw, []byte("uap-grok-private-")) || !grokProjectPrivateCredential(raw) || !bytes.Equal(raw, before) { + t.Fatal("nested encoded credential escaped or changed input") + } + } + } + } + benign := grokProjectPrivacyJSON(t, map[string]string{"content": "{\"key\":\"ordinary\"}\n{\"tail\":true}"}) + if grokProjectPrivateCredential(benign) || grokProjectPrivateCredential([]byte("ordinary non-JSON diagnostic")) { + t.Fatal("bounded ordinary text was misclassified") + } +} + +func TestGrokProjectNativeProviderPrivacyBudgetsSpanWholeMember(t *testing.T) { + for _, raw := range [][]byte{{0xff}, []byte(`{"same":1,"same":2}`), []byte(strings.Repeat("[", 33) + "0" + strings.Repeat("]", 33)), + []byte("[" + strings.Repeat("0,", 17000) + "0]"), grokProjectPrivacyJSON(t, map[string]bool{strings.Repeat("k", 4097): true}), + []byte(`{"n":` + strings.Repeat("9", 5000) + `,"key":"\u0075ap-grok-private-inert"}`)} { + if !grokProjectPrivateCredential(raw) { + t.Fatal("ambiguous syntax or privacy resource limit escaped refusal") + } + } + nested := grokProjectPrivacyJSON(t, strings.Repeat("x", 4096)) + for range 2 { + nested = grokProjectPrivacyJSON(t, map[string]string{"content": string(nested)}) + } + if grokProjectPrivateCredential(nested) || !grokProjectPrivateCredential(bytes.Join([][]byte{nested, nested, {}}, []byte{'\n'})) { + t.Fatal("JSONL renewed the shared text budget or rejected bounded nested text") + } + for _, keys := range [][2]string{{"first", "last"}, {"last", "first"}} { + if !grokProjectPrivateCredential(grokProjectPrivacyJSON(t, map[string]string{keys[0]: string(nested), keys[1]: string(nested)})) { + t.Fatal("siblings renewed the shared text budget") + } + } + if grokProjectPrivateCredential(bytes.Repeat([]byte{' '}, 1<<20)) { + t.Fatal("bounded ordinary whitespace was refused") + } + budget := grokProjectPrivacyBudget{text: 100, nodes: 16384} + if !budget.inspectJSON([]byte(`"ordinary"`), 65) || budget.text != 100 { + t.Fatal("depth exhaustion parsed additional text") + } + budget.nodes = 0 + if !budget.inspectValue(nil, 0) { + t.Fatal("shared node limit was ignored") + } +} + +func TestGrokProjectNativeProviderPrivacyRequiresCompleteNumericJSON(t *testing.T) { + text := "1e999" + strings.Repeat("a", 59) + for _, raw := range [][]byte{ + []byte(text), []byte("-1e999ordinary"), []byte("1e999 []"), []byte("1e999 1e999"), []byte("NaN"), []byte("Infinity"), + grokProjectPrivacyJSON(t, map[string]string{"metadata": text}), grokProjectPrivacyJSON(t, map[string]string{text: "ordinary"}), + []byte(text + "\n{\"event\":\"ordinary\"}\n"), + } { + original := bytes.Clone(raw) + if grokProjectPrivateCredential(raw) || !bytes.Equal(raw, original) { + t.Fatal("ordinary non-JSON numeric prefix was refused or rewritten") + } + } + for _, raw := range [][]byte{[]byte("1e999"), []byte(`{"n":1e999}`), []byte(`[1e999]`), grokProjectPrivacyJSON(t, "1e999"), + []byte(text + "\n{\"opaque\":\"\\u0075ap-grok-private-inert\"}\n"), + []byte("[1e999," + strings.Repeat("[", 10050) + strings.Repeat("]", 10050) + "]")} { + if !grokProjectPrivateCredential(raw) { + t.Fatal("complete nonfinite value, later JSONL credential or syntax resource failure escaped refusal") + } + } + budget := grokProjectPrivacyBudget{text: len(text), nodes: 16384} + if budget.nonfiniteRefused([]byte(text)) || budget.text != 0 { + t.Fatal("complete-syntax attempt did not debit the shared budget") + } + budget.text = len(text) - 1 + if !budget.nonfiniteRefused([]byte(text)) { + t.Fatal("numeric syntax attempt exceeded the shared budget") + } +} + +func TestGrokProjectPrivateNestedCaptureRefusesBeforePersistence(t *testing.T) { + if os.Getenv("UAP_GROK_PRIVATE_CAPTURE_CHILD") == "1" { + f := &projectNativeCLIAttempt{grok: &grokProjectProvider{key: "uap-grok-private-inert"}, files: map[string][]byte{}} + defer func() { + if len(f.files) != 0 { + t.Error("PRIVATE_CAPTURE_WAS_STORED") + return + } + if _, err := fmt.Fprint(os.Stdout, "EMPTY_CAPTURE_VERIFIED\n"); err != nil { + t.Error("inert verification output failed") + } + }() + body := grokProjectPrivacyJSON(t, map[string]string{"content": `{"key":"\u0075ap-grok-private-inert"}`}) + f.keep(t, "probe-events.jsonl", body) + t.Fatal("private capture unexpectedly returned") + } + executable, err := os.Executable() + if err != nil { + t.Fatal(err) + } + result := captureProjectNative(context.Background(), executable, t.TempDir(), []string{"UAP_GROK_PRIVATE_CAPTURE_CHILD=1"}, + []string{"-test.run=^TestGrokProjectPrivateNestedCaptureRefusesBeforePersistence$"}, 2048, 5*time.Second) + if result.Exit != 1 || result.Deadline || result.StdoutLimit || result.StderrLimit || + !bytes.Contains(result.Stdout, []byte("EMPTY_CAPTURE_VERIFIED")) || !bytes.Contains(result.Stdout, []byte("unredacted Grok fixture credential refused")) || + bytes.Contains(result.Stdout, []byte("PRIVATE_CAPTURE_WAS_STORED")) || bytes.Contains(result.Stdout, []byte("uap-grok-private-")) || len(result.Stderr) != 0 { + t.Fatalf("actual inert capture refusal was not proved: exit=%d, error=%s", result.Exit, result.ErrorKind) + } +} diff --git a/repotests/agentplugins_grok_project_native_provider_contract_test.go b/repotests/agentplugins_grok_project_native_provider_contract_test.go new file mode 100644 index 000000000..55fb1c771 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_provider_contract_test.go @@ -0,0 +1,270 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "os" + "path/filepath" + "strings" + "testing" + "time" +) + +func grokProjectContractProvider(t *testing.T) *grokProjectProvider { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) + p, err := newGrokProjectProvider(ctx, cancel, time.Now().Add(10*time.Second)) + if err != nil { + cancel() + t.Fatal(err) + } + t.Cleanup(func() { p.stop(); cancel() }) + if err := p.start(); err != nil { + t.Fatal(err) + } + return p +} + +func grokProjectContractRequest(t *testing.T, p *grokProjectProvider, body []byte) *http.Request { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + t.Cleanup(cancel) + r, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://"+p.listener.Addr().String()+"/v1/chat/completions", bytes.NewReader(body)) + if err != nil { + t.Fatal(err) + } + r.Header.Set("Content-Type", "application/json") + r.Header.Set("Authorization", "Bearer "+p.key) + return r +} + +func grokProjectContractSend(t *testing.T, r *http.Request) (int, []byte) { + t.Helper() + transport := &http.Transport{DisableKeepAlives: true} + defer transport.CloseIdleConnections() + response, err := (&http.Client{Transport: transport, Timeout: 3 * time.Second}).Do(r) + if err != nil { + t.Fatal(err) + } + body, err := io.ReadAll(io.LimitReader(response.Body, 2049)) + closeErr := response.Body.Close() + if err != nil || closeErr != nil || len(body) > 2048 { + t.Fatal("bounded inert HTTP response failed") + } + return response.StatusCode, body +} + +func grokProjectContractBody(t *testing.T, p *grokProjectProvider) []byte { + t.Helper() + body, err := json.Marshal(map[string]any{"model": "uap-grok-fixture", "stream": true, + "messages": []any{map[string]string{"role": "user", "content": "inert request"}}, "opaque_research_field": p.key}) + if err != nil { + t.Fatal(err) + } + return body +} + +func TestGrokProjectNativeProviderOneOpaqueExchangeKeepsClosedRedactedEvidence(t *testing.T) { + p := grokProjectContractProvider(t) + status, body := grokProjectContractSend(t, grokProjectContractRequest(t, p, grokProjectContractBody(t, p))) + if status != http.StatusOK || !bytes.Equal(body, grokProjectProviderResponse()) || bytes.Contains(body, []byte("tool_calls")) { + t.Fatal("fixed text-only provider response differs") + } + f := &projectNativeCLIAttempt{grok: p, files: map[string][]byte{}} + f.keep(t, "native-attempt.stderr.log", []byte("private diagnostic "+p.key)) + p.finish(t, f) + if len(f.files) != 4 || p.receipt.Requests != 1 || p.receipt.Responses != 1 || !p.receipt.CredentialMatched || !p.receipt.ShutdownComplete || p.receipt.ForcedClose || p.receipt.Failure != "none" { + t.Fatal("actual completed request/response/cleanup receipt differs") + } + for _, captured := range f.files { + if bytes.Contains(captured, []byte(p.key)) || bytes.Contains(captured, []byte("uap-grok-private-")) { + t.Fatal("private credential escaped into a captured member") + } + } + if !bytes.Contains(f.files["grok-provider.request.json"], []byte("opaque_research_field")) || !bytes.Contains(f.files["grok-provider.request.json"], []byte("[redacted-grok-fixture-key]")) { + t.Fatal("opaque first request or its redaction was lost") + } +} + +func TestGrokProjectNativeProviderRefusesWrongHTTPAndBodyWithoutFurtherResponse(t *testing.T) { + for _, kind := range []string{"query", "method", "host", "credential", "duplicate-credential", "content-type", "duplicate-content-type", "model", "stream", "empty-messages", "invalid-json", "duplicate-json", "oversized", "late", "duplicate"} { + t.Run(kind, func(t *testing.T) { + p := grokProjectContractProvider(t) + body := grokProjectContractBody(t, p) + switch kind { + case "model": + body = bytes.ReplaceAll(body, []byte("uap-grok-fixture"), []byte("foreign")) + case "stream": + body = bytes.ReplaceAll(body, []byte(`"stream":true`), []byte(`"stream":false`)) + case "empty-messages": + body = []byte(`{"model":"uap-grok-fixture","stream":true,"messages":[]}`) + case "invalid-json": + body = []byte(`{"model":`) + case "duplicate-json": + body = []byte(`{"model":"uap-grok-fixture","model":"uap-grok-fixture"}`) + case "oversized": + body = bytes.Repeat([]byte{' '}, (1<<20)+1) + case "late": + p.cancel() + case "duplicate": + if status, _ := grokProjectContractSend(t, grokProjectContractRequest(t, p, body)); status != http.StatusOK { + t.Fatal("first request refused before duplicate control") + } + } + r := grokProjectContractRequest(t, p, body) + grokProjectContractMutateHTTP(r, kind) + status, _ := grokProjectContractSend(t, r) + p.stop() + wantResponses := 0 + if kind == "duplicate" { + wantResponses = 1 + } + if status != http.StatusBadRequest || p.receipt.Failure == "none" || p.receipt.Responses != wantResponses || !p.receipt.ShutdownComplete || p.connections != 0 { + t.Fatal("invalid request continued or actual cleanup was not proved") + } + }) + } +} + +func grokProjectContractMutateHTTP(r *http.Request, kind string) { + switch kind { + case "query": + r.URL.RawQuery = "extra=1" + case "method": + r.Method = http.MethodPut + case "host": + r.Host = "foreign.invalid" + case "credential": + r.Header.Set("Authorization", "Bearer foreign") + case "duplicate-credential": + r.Header.Add("Authorization", r.Header.Get("Authorization")) + case "content-type": + r.Header.Set("Content-Type", "text/plain") + case "duplicate-content-type": + r.Header.Add("Content-Type", "application/json") + } +} + +func TestGrokProjectNativeProviderRouteRequiresLiteralOriginAndLoopback(t *testing.T) { + for _, kind := range []string{"valid", "absolute", "escaped", "foreign-peer", "missing-peer"} { + r, err := http.NewRequestWithContext(context.Background(), http.MethodPost, "/v1/chat/completions", http.NoBody) + if err != nil { + t.Fatal(err) + } + r.Host, r.RemoteAddr, r.RequestURI = "127.0.0.1:12345", "127.0.0.1:12346", "/v1/chat/completions" + r.Header.Set("Content-Type", "application/json") + switch kind { + case "absolute": + r.RequestURI = "http://127.0.0.1:12345/v1/chat/completions" + case "escaped": + r.URL.RawPath = "/v1/chat/%63ompletions" + case "foreign-peer": + r.RemoteAddr = "192.0.2.1:12346" + case "missing-peer": + r.RemoteAddr = "" + } + if grokProjectProviderRoute(r, "127.0.0.1:12345") != (kind == "valid") { + t.Fatal("literal route or peer boundary differs", kind) + } + } +} + +func TestGrokProjectNativeProviderEncodedCredentialIsRefusedBeforeCapture(t *testing.T) { + p := grokProjectContractProvider(t) + encoded := []byte(`{"opaque":"\u0075` + p.key[1:] + `"}`) + if bytes.Contains(encoded, []byte(p.key)) || !grokProjectPrivateCredential(p.redact(encoded)) { + t.Fatal("encoded private fixture credential escaped final refusal") + } + for _, stream := range [][]byte{ + bytes.Join([][]byte{[]byte(`{"event":"start"}`), encoded}, []byte{'\n'}), + bytes.Join([][]byte{encoded, []byte(`{"event":"end"}`), {}}, []byte{'\n'}), + } { + if bytes.Contains(stream, []byte("uap-grok-private-")) || !grokProjectPrivateCredential(p.redact(stream)) { + t.Fatal("escaped private credential in native JSONL escaped final refusal") + } + } + if grokProjectPrivateCredential(p.redact([]byte(p.key))) { + t.Fatal("exact fixture credential was not redacted") + } + if grokProjectPrivateCredential([]byte("{\"event\":\"start\"}\n{\"event\":\"end\"}\n")) { + t.Fatal("ordinary bounded native JSONL falsely reported a credential") + } + if !grokProjectPrivateCredential(bytes.Repeat([]byte{' '}, (1<<20)+1)) { + t.Fatal("privacy scan exceeded the existing capture member bound") + } +} + +func TestGrokProjectNativeProviderPartialBodyIsClosedWithinOriginalCleanupBudget(t *testing.T) { + p := grokProjectContractProvider(t) + conn, err := (&net.Dialer{Timeout: time.Second}).DialContext(p.ctx, "tcp4", p.listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer func() { _ = conn.Close() }() + if _, err := fmt.Fprintf(conn, "POST /v1/chat/completions HTTP/1.1\r\nHost: %s\r\nAuthorization: Bearer %s\r\nContent-Type: application/json\r\nContent-Length: 1000\r\n\r\n{", p.listener.Addr(), p.key); err != nil { + t.Fatal("inert partial request failed") + } + deadline := time.Now().Add(time.Second) + for { + p.mu.Lock() + entered := p.attempted + p.mu.Unlock() + if entered { + break + } + if time.Now().After(deadline) { + t.Fatal("partial request never reached the actual handler") + } + time.Sleep(time.Millisecond) + } + start := time.Now() + ordinary := p.stop() + if ordinary || !p.receipt.ForcedClose || !p.receipt.ShutdownComplete || p.connections != 0 || time.Since(start) > 2500*time.Millisecond { + t.Fatal("partial-input cleanup was unbounded or mislabeled ordinary") + } + select { + case <-p.serveDone: + default: + t.Fatal("owned HTTP serve goroutine survived cleanup") + } +} + +func TestGrokProjectNativeProviderUsesOriginalHomeAndPreservesConfigAuthority(t *testing.T) { + root := t.TempDir() + ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) + f := &projectNativeCLIAttempt{spec: projectNativeCLISpec{client: "grok"}, ctx: ctx, cancel: cancel, started: time.Now(), + base: &nativeFixture{Root: root, Home: filepath.Join(root, "original-home"), Project: filepath.Join(root, "project")}, + protected: map[string]string{}, evidence: map[string]any{}} + t.Cleanup(func() { + if f.grok != nil { + f.grok.stop() + } + cancel() + }) + f.configureGrokProvider(t) + path := filepath.Join(f.base.Home, ".grok", "config.toml") + body, err := os.ReadFile(path) + if err != nil || !bytes.Equal(body, f.grok.configuration()) || bytes.Contains(body, []byte(f.grok.key)) || f.protected[path] != nativeSHA(t, path) { + t.Fatal("original user config or immutable baseline differs") + } + if _, err := os.Stat(filepath.Join(f.base.Project, ".grok", "config.toml")); !os.IsNotExist(err) { + t.Fatal("project was substituted for original user profile") + } + joined := strings.Join(f.env(), "\n") + if !strings.Contains(joined, "HOME="+f.base.Home+"\n") || !strings.Contains(joined, "UAP_GROK_TEST_PROVIDER_KEY="+f.grok.key) { + t.Fatal("held original HOME or explicit private environment missing") + } + for _, client := range []string{"qoder", "kiro", "windsurf"} { + f.spec.client = client + if strings.Contains(strings.Join(f.env(), "\n"), "UAP_GROK_TEST_PROVIDER_KEY=") { + t.Fatal("another client inherited Grok provider authority") + } + } +} diff --git a/repotests/agentplugins_grok_project_native_provider_test.go b/repotests/agentplugins_grok_project_native_provider_test.go new file mode 100644 index 000000000..0fee45c62 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_provider_test.go @@ -0,0 +1,296 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "net/http" + "path/filepath" + "sync" + "testing" + "time" +) + +const grokProjectProviderModel = "uap-grok-fixture" +const grokProjectProviderTransport = "scripted loopback OpenAI chat-completions SSE; no real model" + +type grokProjectProviderReceipt struct { + Transport string `json:"transport"` + Model string `json:"model"` + Started bool `json:"started"` + Requests int `json:"requests"` + Responses int `json:"responses"` + CredentialMatched bool `json:"credential_matched"` + Failure string `json:"failure"` + ShutdownComplete bool `json:"shutdown_complete"` + ForcedClose bool `json:"forced_close"` + RouteObservation *grokProjectRouteObservation `json:"route_observation,omitempty"` +} + +type grokProjectProvider struct { + mu sync.Mutex + ctx context.Context + cancel context.CancelFunc + totalEnd time.Time + key string + server *http.Server + listener net.Listener + serveDone chan struct{} + stateChanged chan struct{} + connections int + attempted bool + request, reply []byte + authoredConfig []byte + receipt grokProjectProviderReceipt + title *grokProjectTitleProvider + duplicate *grokProjectDuplicateCapture +} + +func newGrokProjectProvider(ctx context.Context, cancel context.CancelFunc, totalEnd time.Time) (*grokProjectProvider, error) { + var secret [16]byte + if _, err := rand.Read(secret[:]); err != nil { + return nil, err + } + return &grokProjectProvider{ctx: ctx, cancel: cancel, totalEnd: totalEnd, + key: "uap-grok-private-" + hex.EncodeToString(secret[:]), serveDone: make(chan struct{}), stateChanged: make(chan struct{}, 1), + receipt: grokProjectProviderReceipt{Transport: grokProjectProviderTransport, Model: grokProjectProviderModel, Failure: "none"}}, nil +} + +func (f *projectNativeCLIAttempt) configureGrokProvider(t *testing.T) { + t.Helper() + var err error + f.grok, err = newGrokProjectProvider(f.ctx, f.cancel, f.started.Add(180*time.Second)) + if err != nil { + t.Fatal("Grok fixture credential generation failed") + } + if err = f.grok.start(); err != nil { + t.Fatal("Grok loopback listener could not start") + } + path := filepath.Join(f.base.Home, ".grok", "config.toml") + configuration := f.grok.configuration() + projectNativeRuntimeWrite(t, path, configuration, 0600) + f.grok.authoredConfig = bytes.Clone(configuration) + f.protected[path] = nativeSHA(t, path) + f.evidence["provider_mode"] = "documented original-HOME custom provider; first opaque request observation; no vendor account" +} + +func (p *grokProjectProvider) start() error { + listener, err := (&net.ListenConfig{}).Listen(p.ctx, "tcp4", "127.0.0.1:0") + if err != nil { + p.fail("listen") + return err + } + p.listener = listener + p.server = &http.Server{Handler: http.HandlerFunc(p.serve), ReadHeaderTimeout: 2 * time.Second, + ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second, IdleTimeout: 2 * time.Second, MaxHeaderBytes: 16 << 10, + BaseContext: func(net.Listener) context.Context { return p.ctx }, ConnState: p.connectionState} + p.receipt.Started = true + go func() { + defer close(p.serveDone) + if err := p.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) { + p.fail("serve") + } + }() + return nil +} + +func (p *grokProjectProvider) configuration() []byte { + // This fresh original HOME has no legacy marketplace installs to purge. + // Author the source-defined migration sentinel before freezing the full file. + return []byte(fmt.Sprintf("[models]\ndefault = %q\n[model.%s]\nmodel = %q\nbase_url = %q\nenv_key = \"UAP_GROK_TEST_PROVIDER_KEY\"\napi_backend = \"chat_completions\"\nsupports_backend_search = false\nsupports_reasoning_effort = false\nmax_retries = 0\n\n[marketplace]\ndefault_skills_installs_purged = true\n", grokProjectProviderModel, grokProjectProviderModel, grokProjectProviderModel, "http://"+p.listener.Addr().String()+"/v1")) +} + +func (p *grokProjectProvider) connectionState(_ net.Conn, state http.ConnState) { + p.mu.Lock() + switch state { + case http.StateNew: + p.connections++ + case http.StateClosed, http.StateHijacked: + p.connections-- + } + p.mu.Unlock() + select { + case p.stateChanged <- struct{}{}: + default: + } +} + +func (p *grokProjectProvider) fail(reason string) { + p.failWithRequest(reason, nil) +} + +func grokProjectProviderRoute(r *http.Request, host string) bool { + peer, _, err := net.SplitHostPort(r.RemoteAddr) + return err == nil && peer == "127.0.0.1" && r.Method == http.MethodPost && r.Host == host && + r.RequestURI == "/v1/chat/completions" && !r.URL.IsAbs() && r.URL.Host == "" && r.URL.RawPath == "" && + r.Header.Get("Content-Type") == "application/json" && len(r.Header.Values("Content-Type")) == 1 +} + +func (p *grokProjectProvider) admit(w http.ResponseWriter, r *http.Request) bool { + reason := "none" + switch { + case p.ctx.Err() != nil: + reason = "deadline" + case !grokProjectProviderRoute(r, p.listener.Addr().String()): + reason = "route" + case len(r.Header.Values("Authorization")) != 1 || r.Header.Get("Authorization") != "Bearer "+p.key: + reason = "credential" + } + p.mu.Lock() + var duplicate *grokProjectDuplicateCapture + if reason == "none" && p.attempted { + reason = "duplicate" + duplicate = p.claimDuplicate() + } + if reason == "none" { + p.attempted, p.receipt.CredentialMatched = true, true + } + p.mu.Unlock() + if duplicate != nil { + p.observeDuplicate(w, r, duplicate) + return false + } + if reason != "none" { + http.Error(w, "closed Grok provider request refused", http.StatusBadRequest) + p.failWithRequest(reason, r) + return false + } + return true +} + +func (p *grokProjectProvider) captureRequest(raw []byte) bool { + value, err := projectNativeRuntimeJSON(raw) + object, ok := value.(map[string]any) + if err != nil || !ok { + return false + } + p.mu.Lock() + p.request, p.receipt.Requests = bytes.Clone(raw), 1 + p.mu.Unlock() + messages, ok := object["messages"].([]any) + return object["model"] == grokProjectProviderModel && object["stream"] == true && ok && len(messages) > 0 && len(messages) <= 128 +} + +func grokProjectProviderResponse() []byte { + return []byte("data: {\"id\":\"uap-grok-capture\",\"object\":\"chat.completion.chunk\",\"created\":1,\"model\":\"uap-grok-fixture\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"UAP_GROK_PROVIDER_CAPTURE\"},\"finish_reason\":null}]}\n\ndata: {\"id\":\"uap-grok-capture\",\"object\":\"chat.completion.chunk\",\"created\":1,\"model\":\"uap-grok-fixture\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}]}\n\ndata: [DONE]\n\n") +} + +func (p *grokProjectProvider) serve(w http.ResponseWriter, r *http.Request) { + if !p.admit(w, r) { + return + } + raw, err := io.ReadAll(io.LimitReader(r.Body, (1<<20)+1)) + if err != nil || len(raw) > 1<<20 { + http.Error(w, "Grok provider body bound", http.StatusBadRequest) + p.fail("body") + return + } + if !p.captureRequest(raw) { + http.Error(w, "Grok provider request shape refused", http.StatusBadRequest) + p.fail("request-shape") + return + } + response := grokProjectProviderResponse() + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + if count, err := w.Write(response); err != nil || count != len(response) { + p.fail("response-write") + return + } + p.mu.Lock() + p.reply, p.receipt.Responses = bytes.Clone(response), 1 + p.mu.Unlock() +} + +func (p *grokProjectProvider) stop() bool { + if p.server == nil { + return false + } + p.mu.Lock() + closed, ordinary := p.receipt.ShutdownComplete, !p.receipt.ForcedClose + p.mu.Unlock() + if closed { + return ordinary + } + end := time.Now().Add(2 * time.Second) + if end.After(p.totalEnd) { + end = p.totalEnd + } + cleanup, cancel := context.WithDeadline(context.Background(), end) + defer cancel() + grace, stopGrace := context.WithTimeout(p.ctx, time.Second) + err := p.server.Shutdown(grace) + stopGrace() + if err != nil { + p.mu.Lock() + p.receipt.ForcedClose = true + p.mu.Unlock() + p.fail("cleanup") + if closeErr := p.server.Close(); closeErr != nil { + p.fail("cleanup") + } + } + select { + case <-p.serveDone: + case <-cleanup.Done(): + p.fail("cleanup") + return false + } + for { + p.mu.Lock() + closed := p.connections == 0 + p.receipt.ShutdownComplete = closed + ordinary := closed && !p.receipt.ForcedClose + p.mu.Unlock() + if closed { + return ordinary + } + select { + case <-p.stateChanged: + case <-cleanup.Done(): + p.fail("cleanup") + return false + } + } +} + +func (p *grokProjectProvider) redact(body []byte) []byte { + body = bytes.ReplaceAll(body, []byte(p.key), []byte("[redacted-grok-fixture-key]")) + if p.title != nil { + body = bytes.ReplaceAll(body, []byte(p.title.transport.key), []byte("[redacted-grok-fixture-key]")) + } + return body +} + +func (p *grokProjectProvider) finish(t *testing.T, f *projectNativeCLIAttempt) { + t.Helper() + if p.title != nil { + p.title.finish(t, f) + } + if !p.stop() { + t.Error("Grok test provider ordinary shutdown did not complete") + } + defer p.finishDuplicate(t, f) + p.mu.Lock() + defer p.mu.Unlock() + if len(p.request) != 0 { + f.keep(t, "grok-provider.request.json", p.request) + } + if len(p.reply) != 0 { + f.keep(t, "grok-provider.response.sse", p.reply) + } + raw, err := json.Marshal(p.receipt) + if err != nil || len(raw) > 2<<10 { + t.Error("Grok provider finite receipt refused") + return + } + f.keep(t, "grok-provider.json", raw) +} diff --git a/repotests/agentplugins_grok_project_native_route_test.go b/repotests/agentplugins_grok_project_native_route_test.go new file mode 100644 index 000000000..0fdf86d87 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_route_test.go @@ -0,0 +1,236 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "encoding/json" + "mime" + "net" + "net/http" + "reflect" + "strings" + "testing" +) + +type grokProjectRouteObservation struct { + Method string `json:"method"` + URI string `json:"uri"` + PeerLoopback bool `json:"peer_loopback"` + HostMatches bool `json:"host_matches"` + RelativeURL bool `json:"relative_url"` + EmptyURLHost bool `json:"empty_url_host"` + EmptyRawPath bool `json:"empty_raw_path"` + ContentTypeExact bool `json:"content_type_exact"` + ContentTypeCount string `json:"content_type_count"` + ContentTypeKind string `json:"content_type_kind"` +} + +func (p *grokProjectProvider) failWithRequest(reason string, request *http.Request) { + p.mu.Lock() + if p.receipt.Failure == "none" { + p.receipt.Failure = reason + if reason == "route" && request != nil { + observation := grokProjectObserveRoute(request, p.listener.Addr().String()) + p.receipt.RouteObservation = &observation + } + } + p.mu.Unlock() + p.cancel() +} + +func grokProjectObserveRoute(request *http.Request, host string) grokProjectRouteObservation { + observation := grokProjectRouteObservation{Method: "other", URI: "other"} + switch request.Method { + case http.MethodPost, http.MethodGet: + observation.Method = request.Method + } + switch request.RequestURI { + case "/", "/v1/chat/completions", "/chat/completions", "/v1/models", "/models", "/v1/responses", "/responses": + observation.URI = request.RequestURI + } + peer, _, err := net.SplitHostPort(request.RemoteAddr) + observation.PeerLoopback = err == nil && peer == "127.0.0.1" + observation.HostMatches = request.Host == host + if request.URL != nil { + observation.RelativeURL = !request.URL.IsAbs() + observation.EmptyURLHost = request.URL.Host == "" + observation.EmptyRawPath = request.URL.RawPath == "" + } + values := request.Header.Values("Content-Type") + observation.ContentTypeExact = len(values) == 1 && values[0] == "application/json" + observation.ContentTypeCount, observation.ContentTypeKind = grokProjectObserveContentType(values) + return observation +} + +func grokProjectObserveContentType(values []string) (string, string) { + if len(values) == 0 { + return "none", "missing" + } + if len(values) != 1 { + return "multiple", "other" + } + if len(values[0]) > 128 { + return "one", "other" + } + media, parameters, err := mime.ParseMediaType(values[0]) + if err != nil || media != "application/json" { + return "one", "other" + } + if len(parameters) == 0 { + return "one", "json-bare" + } + if len(parameters) == 1 && strings.EqualFold(parameters["charset"], "utf-8") { + return "one", "json-utf8" + } + return "one", "other" +} + +func grokProjectRouteTestRequest(t *testing.T, method, uri string) *http.Request { + t.Helper() + request, err := http.NewRequestWithContext(context.Background(), method, uri, http.NoBody) + if err != nil { + t.Fatal(err) + } + request.RequestURI = uri + return request +} + +func TestGrokProjectRouteObservationIsFiniteAndNeverAdmitsMIMEVariants(t *testing.T) { + for _, test := range []struct { + values []string + count, kind string + exact bool + }{ + {nil, "none", "missing", false}, + {[]string{"application/json"}, "one", "json-bare", true}, + {[]string{"Application/JSON"}, "one", "json-bare", false}, + {[]string{"application/json; charset=UTF-8"}, "one", "json-utf8", false}, + {[]string{"application/json; charset=ascii"}, "one", "other", false}, + {[]string{"application/json; private=secret"}, "one", "other", false}, + {[]string{"application/json; charset=utf-8; extra=secret"}, "one", "other", false}, + {[]string{"application/json", "application/json"}, "multiple", "other", false}, + {[]string{"application/json" + strings.Repeat(" ", 113)}, "one", "other", false}, + {[]string{""}, "one", "other", false}, + } { + request := grokProjectRouteTestRequest(t, http.MethodPost, "/v1/chat/completions") + request.Host, request.RemoteAddr = "127.0.0.1:12345", "127.0.0.1:12346" + request.Header["Content-Type"] = test.values + observation := grokProjectObserveRoute(request, request.Host) + if observation.ContentTypeCount != test.count || observation.ContentTypeKind != test.kind || observation.ContentTypeExact != test.exact || grokProjectProviderRoute(request, request.Host) != test.exact { + t.Fatal("diagnostic classification widened the unchanged literal route") + } + } +} + +func TestGrokProjectRouteObservationKeepsOnlyClosedMethodPathAndPredicates(t *testing.T) { + request := grokProjectRouteTestRequest(t, http.MethodPost, "/v1/chat/completions") + request.Host, request.RemoteAddr = "127.0.0.1:12345", "127.0.0.1:12346" + request.Header.Set("Content-Type", "application/json") + want := grokProjectRouteObservation{Method: "POST", URI: "/v1/chat/completions", PeerLoopback: true, HostMatches: true, + RelativeURL: true, EmptyURLHost: true, EmptyRawPath: true, ContentTypeExact: true, ContentTypeCount: "one", ContentTypeKind: "json-bare"} + if got := grokProjectObserveRoute(request, request.Host); got != want { + t.Fatal("literal route facts differ") + } + for _, uri := range []string{"/v1/chat/completions", "/chat/completions", "/v1/models", "/models", "/v1/responses", "/responses"} { + request.RequestURI = uri + if got := grokProjectObserveRoute(request, request.Host); got.URI != uri { + t.Fatal("fixed route label differs") + } + } + request.Method = http.MethodGet + if grokProjectObserveRoute(request, request.Host).Method != "GET" { + t.Fatal("fixed GET label differs") + } + request.Method, request.RequestURI = "PRIVATE-METHOD", "/private-path?credential=private-query" + request.Host, request.RemoteAddr = "private-host", "192.0.2.1:12346" + request.URL.Scheme, request.URL.Host, request.URL.RawPath = "private-scheme", "private-url-host", "/private-raw-path" + request.Header.Set("Content-Type", "private-content-type") + request.Header.Set("Authorization", "Bearer private-credential") + got := grokProjectObserveRoute(request, "127.0.0.1:12345") + want = grokProjectRouteObservation{Method: "other", URI: "other", ContentTypeCount: "one", ContentTypeKind: "other"} + if got != want { + t.Fatal("unknown route must expose only finite false predicates") + } + raw, err := json.Marshal(got) + if err != nil || bytes.Contains(raw, []byte("private")) || len(raw) > 512 { + t.Fatal("private route attribute escaped bounded observation") + } +} + +func TestGrokProjectRouteFailureRecordsOnlyTheFirstActualRejectedExchange(t *testing.T) { + p := grokProjectContractProvider(t) + r := grokProjectContractRequest(t, p, grokProjectContractBody(t, p)) + r.Header.Set("Content-Type", "application/json; charset=utf-8") + status, _ := grokProjectContractSend(t, r) + p.stop() + if status != http.StatusBadRequest || p.receipt.Failure != "route" || p.receipt.RouteObservation == nil || p.receipt.Requests != 0 || p.receipt.Responses != 0 || p.receipt.CredentialMatched || p.request != nil { + t.Fatal("refused real route changed request admission or omitted its observation") + } + want := *p.receipt.RouteObservation + if want.ContentTypeKind != "json-utf8" || want.ContentTypeExact || !want.PeerLoopback || !want.HostMatches || !want.RelativeURL || !want.EmptyURLHost || !want.EmptyRawPath { + t.Fatal("actual inert loopback refusal facts differ") + } + p.failWithRequest("route", grokProjectRouteTestRequest(t, http.MethodGet, "/models")) + p.fail("cleanup") + if p.receipt.Failure != "route" || !reflect.DeepEqual(*p.receipt.RouteObservation, want) { + t.Fatal("later failures replaced the original route facts") + } +} + +func TestGrokProjectNonRouteFirstFailureCannotAcquireRouteMetadata(t *testing.T) { + for _, reason := range []string{"listen", "serve", "deadline", "credential", "duplicate", "body", "request-shape", "response-write", "cleanup"} { + p := grokProjectContractProvider(t) + p.fail(reason) + p.failWithRequest("route", grokProjectRouteTestRequest(t, http.MethodGet, "/models")) + if p.receipt.Failure != reason || p.receipt.RouteObservation != nil { + t.Fatal("later route failure replaced an earlier failure") + } + p.stop() + } +} + +func TestGrokProjectOriginGETIsObservedWhileAdmissionStillRefuses(t *testing.T) { + p := grokProjectContractProvider(t) + r := grokProjectContractRequest(t, p, grokProjectContractBody(t, p)) + r.Method, r.URL.Path = http.MethodGet, "/" + r.Body, r.ContentLength = http.NoBody, 0 + r.Header.Del("Content-Type") + r.Header.Set("Authorization", "Bearer private-origin-header") + status, _ := grokProjectContractSend(t, r) + p.stop() + if status != http.StatusBadRequest || p.ctx.Err() == nil || p.receipt.Failure != "route" || p.receipt.Requests != 0 || p.receipt.Responses != 0 || p.receipt.CredentialMatched || p.request != nil || p.reply != nil { + t.Fatal("origin diagnostic changed rejection, cancellation or provider admission") + } + want := grokProjectRouteObservation{Method: "GET", URI: "/", PeerLoopback: true, HostMatches: true, + RelativeURL: true, EmptyURLHost: true, EmptyRawPath: true, ContentTypeCount: "none", ContentTypeKind: "missing"} + if p.receipt.RouteObservation == nil || *p.receipt.RouteObservation != want { + t.Fatal("actual inert rejected origin request lost its finite facts") + } + p.failWithRequest("route", grokProjectRouteTestRequest(t, http.MethodGet, "/models")) + p.fail("cleanup") + if p.receipt.Failure != "route" || *p.receipt.RouteObservation != want { + t.Fatal("later failure replaced the first rejected origin request") + } + raw, err := json.Marshal(p.receipt.RouteObservation) + if err != nil || len(raw) > 512 || bytes.Contains(raw, []byte("private")) { + t.Fatal("origin observation exceeded bounds or exposed a private header") + } +} + +func TestGrokProjectOriginObservationDoesNotNormalizePrivateRootLikeURIs(t *testing.T) { + for _, uri := range []string{"/?credential=private-query", "//", "/v1/", "/%2F", "http://private-host/", "/private-root"} { + r := grokProjectRouteTestRequest(t, http.MethodGet, uri) + r.Host, r.RemoteAddr = "127.0.0.1:12345", "127.0.0.1:12346" + r.Header.Set("Authorization", "Bearer private-root-header") + got := grokProjectObserveRoute(r, r.Host) + if got.URI != "other" || grokProjectProviderRoute(r, r.Host) { + t.Fatal("root-like request was normalized or admitted") + } + raw, err := json.Marshal(got) + if err != nil || len(raw) > 512 || bytes.Contains(raw, []byte("private")) { + t.Fatal("root-like request exposed private attributes") + } + } +} diff --git a/repotests/agentplugins_grok_project_native_selection_contract_test.go b/repotests/agentplugins_grok_project_native_selection_contract_test.go new file mode 100644 index 000000000..fed2f6871 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_selection_contract_test.go @@ -0,0 +1,170 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "encoding/json" + "os" + "path/filepath" + "reflect" + "strings" + "testing" +) + +func TestGrokSelectionFreezesOnlyOriginalHomeExactProjectTrust(t *testing.T) { + f := grokTurnSummaryContractFixture(t) + path := filepath.Join(f.base.Home, ".grok", "trusted_folders.toml") + body, err := projectNativeRuntimeRead(path, 4096) + info, statErr := os.Lstat(path) + want := []byte("[folders.\"" + f.base.Project + "\"]\ntrusted = true\n") + parsed, status := grokConfigParse(body) + if err != nil || statErr != nil || info.Mode().Perm() != 0600 || !bytes.Equal(body, want) || + status != "captured" || !reflect.DeepEqual(parsed, map[string]any{"folders": map[string]any{f.base.Project: map[string]any{"trusted": true}}}) || + f.protected[path] != grokConfigSHA(want) || len(f.protected) != 2 { + t.Fatal("exact original HOME owned trust recipe was not frozen") + } + config, status := grokConfigParse(f.grok.authoredConfig) + if status != "captured" || !reflect.DeepEqual(config["plugins"], map[string]any{"enabled": []any{"uap-grok-attempt"}}) { + t.Fatal("supported name selector changed or gained alternate plugin paths") + } + if _, err := os.Lstat(filepath.Join(f.base.Project, ".grok")); !os.IsNotExist(err) { + t.Fatal("fixture trust acquired project configuration authority") + } + f.check(t) +} + +func grokSelectionRootFixture(t *testing.T) *projectNativeCLIAttempt { + t.Helper() + root := t.TempDir() + f := &projectNativeCLIAttempt{spec: projectNativeCLISpec{client: "grok"}, other: filepath.Join(root, "project-b"), + base: &nativeFixture{Root: root, Home: filepath.Join(root, "home"), Project: filepath.Join(root, "project")}} + for _, path := range []string{f.base.Home, f.base.Project, f.other} { + if err := os.Mkdir(path, 0700); err != nil { + t.Fatal(err) + } + } + return f +} + +func TestGrokSelectionTrustRefusesForeignRootsAliasesAndExistingAuthority(t *testing.T) { + for _, kind := range []string{"home", "root", "b", "relative", "noncanonical", "alias", "other-client", "trust", "config", "dangling-trust"} { + t.Run(kind, func(t *testing.T) { + f := grokSelectionRootFixture(t) + if _, err := grokProjectSelectionTrust(f); err != nil { + t.Fatal(err) + } + mutateGrokSelectionRoot(t, f, kind) + if _, err := grokProjectSelectionTrust(f); err == nil { + t.Fatal("foreign/alias/existing trust authority was adopted") + } + }) + } +} + +func mutateGrokSelectionRoot(t *testing.T, f *projectNativeCLIAttempt, kind string) { + t.Helper() + switch kind { + case "home": + f.base.Project = f.base.Home + case "root": + f.base.Project = f.base.Root + case "b": + f.base.Project = f.other + case "relative": + f.base.Project = "project" + case "noncanonical": + f.base.Home += "/../home" + case "alias": + if err := os.Rename(f.base.Project, f.base.Project+"-original"); err != nil { + t.Fatal(err) + } + if err := os.Symlink(f.base.Project+"-original", f.base.Project); err != nil { + t.Fatal(err) + } + case "other-client": + f.spec.client = "qoder" + default: + name := "trusted_folders.toml" + if kind == "config" { + name = "config.toml" + } + path := filepath.Join(f.base.Home, ".grok", name) + if kind == "dangling-trust" { + if err := os.Mkdir(filepath.Dir(path), 0700); err != nil { + t.Fatal(err) + } + if err := os.Symlink(filepath.Join(f.base.Root, "absent"), path); err != nil { + t.Fatal(err) + } + } else { + projectNativeRuntimeWrite(t, path, []byte("original authority\n"), 0600) + } + } +} + +func grokSelectedInspectFixture() (map[string]any, string, string, string) { + object, project, name, active := grokPreparedInspectFixture() + object["projectTrusted"] = true + object["plugins"].([]any)[0].(map[string]any)["enabled"] = true + object["skills"] = []any{map[string]any{"name": name, "description": "Harmless installed startup marker", "userInvocable": true, + "source": map[string]any{"type": "plugin", "plugin_name": name, "path": filepath.Join(active, "skills", name, "SKILL.md")}}} + return object, project, name, active +} + +func TestGrokSelectedInspectRequiresSeparateExactLoadedSkill(t *testing.T) { + for _, kind := range []string{"ordinary", "false-disabled", "trust-only", "untrusted", "disabled-plugin", "duplicate", "folded-name", "description", "user-invocable", "disabled", "disabled-type", "source-type", "source-name", "source-path", "source-alias", "folded-key"} { + t.Run(kind, func(t *testing.T) { + object, project, name, active := grokSelectedInspectFixture() + mutateGrokSelectedInspect(object, name, project, kind) + body, err := json.Marshal(object) + if err != nil { + t.Fatal(err) + } + valid := kind == "ordinary" || kind == "false-disabled" + if (projectGrokInspect(body, project, name, active, true) == nil) != valid { + t.Fatal("selected loaded skill identity control differs") + } + if projectGrokPreparedInspect(body, project, name, active) == nil { + t.Fatal("selected observation changed the disabled Prepared contract") + } + }) + } +} + +func mutateGrokSelectedInspect(object map[string]any, name, project, kind string) { + entry := object["skills"].([]any)[0].(map[string]any) + source := entry["source"].(map[string]any) + switch kind { + case "false-disabled": + entry["disabled"] = false + case "trust-only": + object["skills"] = []any{} + case "untrusted": + object["projectTrusted"] = false + case "disabled-plugin": + object["plugins"].([]any)[0].(map[string]any)["enabled"] = false + case "duplicate": + object["skills"] = append(object["skills"].([]any), entry) + case "folded-name": + entry["name"] = strings.ToUpper(name) + case "description": + entry["description"] = "foreign marker" + case "user-invocable": + entry["userInvocable"] = false + case "disabled": + entry["disabled"] = true + case "disabled-type": + entry["disabled"] = "false" + case "source-type": + source["type"] = "user" + case "source-name": + source["plugin_name"] = "foreign-plugin" + case "source-path": + source["path"] = filepath.Join(project, "foreign", "SKILL.md") + case "source-alias": + source["plugin_Name"] = name + case "folded-key": + entry["Name"] = name + } +} diff --git a/repotests/agentplugins_grok_project_native_selection_test.go b/repotests/agentplugins_grok_project_native_selection_test.go new file mode 100644 index 000000000..d5dbdd0c1 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_selection_test.go @@ -0,0 +1,85 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "reflect" + "syscall" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/clients/shared" +) + +// This is fixture authorship in the original disposable HOME, before any child +// or protected baseline. It does not import or renew a native trust decision. +func grokProjectSelectionTrust(f *projectNativeCLIAttempt) ([]byte, error) { + if f.spec.client != "grok" || f.base == nil { + return nil, fmt.Errorf("Grok selection requires its original fixture") + } + root, home, project := f.base.Root, f.base.Home, f.base.Project + if filepath.Dir(root) == root || project != filepath.Join(root, "project") || filepath.Dir(home) != root || home == project || home == root || project == f.other { + return nil, fmt.Errorf("Grok trust must select only the original project A") + } + for _, path := range []string{root, home, project} { + if !filepath.IsAbs(path) || filepath.Clean(path) != path { + return nil, fmt.Errorf("Grok trust requires canonical absolute roots") + } + if err := projectNativeRuntimeParents(filepath.Join(path, "leaf")); err != nil { + return nil, err + } + info, err := os.Lstat(path) + if err != nil { + return nil, err + } + stat, ok := info.Sys().(*syscall.Stat_t) + if !ok || !info.IsDir() || stat.Uid != uint32(os.Getuid()) { + return nil, fmt.Errorf("Grok trust requires original ordinary owned roots") + } + } + for _, name := range []string{"trusted_folders.toml", "config.toml"} { + path := filepath.Join(home, ".grok", name) + if _, err := os.Lstat(path); !os.IsNotExist(err) { + return nil, fmt.Errorf("Grok selection refuses an existing or unreadable config entry") + } + } + quoted, err := json.Marshal(project) + if err != nil { + return nil, err + } + return []byte("[folders." + string(quoted) + "]\ntrusted = true\n"), nil +} + +// The publisher's plugin enabled bit means trust. Only the separate loaded +// skill record can join the selected source to the enabled plugin registry. +func grokProjectSelectedSkill(skills []any, name, path string) error { + wantSource := map[string]any{"type": "plugin", "plugin_name": name, "path": path} + found := false + for _, value := range skills { + entry, ok := value.(map[string]any) + if !ok || !projectGrokIdentityKeys(entry, "name description source userInvocable disabled") { + return fmt.Errorf("Grok loaded skill identity is ambiguous") + } + entryName, named := entry["name"].(string) + source, sourced := entry["source"].(map[string]any) + if !named || entryName == "" || !sourced || !projectGrokIdentityKeys(source, "type plugin_name path") { + return fmt.Errorf("Grok loaded skill name/source differs") + } + pluginName, _ := source["plugin_name"].(string) + if shared.FoldJSONKey(entryName) != shared.FoldJSONKey(name) && shared.FoldJSONKey(pluginName) != shared.FoldJSONKey(name) { + continue + } + disabled, present := entry["disabled"] + if found || entryName != name || entry["description"] != "Harmless installed startup marker" || entry["userInvocable"] != true || + present && disabled != false || !reflect.DeepEqual(source, wantSource) { + return fmt.Errorf("Grok selected loaded skill differs from its owned source") + } + found = true + } + if !found { + return fmt.Errorf("Grok selected loaded skill is absent") + } + return nil +} diff --git a/repotests/agentplugins_grok_project_native_title_configuration_test.go b/repotests/agentplugins_grok_project_native_title_configuration_test.go new file mode 100644 index 000000000..420eebc99 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_title_configuration_test.go @@ -0,0 +1,92 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "os" + "path/filepath" + "reflect" + "testing" + "time" +) + +func TestGrokProjectTitleConfigurationFreezesBothHeldRoutesInOriginalHome(t *testing.T) { + for key, value := range map[string]string{ + "UAP_GROK_TITLE_PROVIDER_KEY": "ambient-title-private", "UAP_GROK_TEST_PROVIDER_KEY": "ambient-main-private", + "GROK_SESSION_SUMMARY_MODEL": "ambient-model", "GROK_TITLE_REFRESH": "false", "HOME": "/ambient-user-home", + } { + t.Setenv(key, value) + } + root := t.TempDir() + ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) + f := &projectNativeCLIAttempt{spec: projectNativeCLISpec{client: "grok"}, ctx: ctx, cancel: cancel, started: time.Now(), + base: &nativeFixture{Root: root, Home: filepath.Join(root, "original-home"), Project: filepath.Join(root, "project"), + CodexHome: filepath.Join(root, "original-codex")}, protected: map[string]string{}, evidence: map[string]any{}} + for _, path := range []string{f.base.Home, f.base.Project} { + if err := os.Mkdir(path, 0700); err != nil { + t.Fatal(err) + } + } + t.Cleanup(func() { + if f.grok != nil { + if f.grok.title != nil { + f.grok.title.transport.stop() + } + f.grok.stop() + } + cancel() + }) + f.configureGrokTitleProvider(t) + main, title := f.grok, f.grok.title.transport + if main.key == title.key || main.listener.Addr().String() == title.listener.Addr().String() { + t.Fatal("title and primary must hold distinct listener/key authority") + } + model := func(name, address, key string) map[string]any { + return map[string]any{"model": name, "base_url": "http://" + address + "/v1", "env_key": key, + "api_backend": "chat_completions", "supports_backend_search": false, "supports_reasoning_effort": false, "max_retries": int64(0)} + } + want := map[string]any{ + "models": map[string]any{"default": "uap-grok-fixture", "session_summary": "uap-grok-title-fixture"}, + "model": map[string]any{ + "uap-grok-fixture": model("uap-grok-fixture", main.listener.Addr().String(), "UAP_GROK_TEST_PROVIDER_KEY"), + "uap-grok-title-fixture": model("uap-grok-title-fixture", title.listener.Addr().String(), "UAP_GROK_TITLE_PROVIDER_KEY"), + }, + "marketplace": map[string]any{"default_skills_installs_purged": true}, + "features": map[string]any{"turn_summary": false}, + "plugins": map[string]any{"enabled": []any{"uap-grok-attempt"}}, + } + parsed, status := grokConfigParse(main.authoredConfig) + if status != "captured" || !reflect.DeepEqual(parsed, want) { + t.Fatal("independently stated two-model original HOME configuration differs") + } + path := filepath.Join(f.base.Home, ".grok", "config.toml") + body, err := os.ReadFile(path) + if err != nil || !bytes.Equal(body, main.authoredConfig) || f.protected[path] != nativeSHA(t, path) || + bytes.Contains(body, []byte(main.key)) || bytes.Contains(body, []byte(title.key)) { + t.Fatal("config was not frozen once from the complete independently authored recipe") + } + if _, err := os.Lstat(filepath.Join(f.base.Project, ".grok", "config.toml")); !os.IsNotExist(err) { + t.Fatal("project replaced original HOME configuration") + } + env := grokContractEnvironment(t, f.env()) + if env["HOME"] != f.base.Home || env["UAP_GROK_TEST_PROVIDER_KEY"] != main.key || env["UAP_GROK_TITLE_PROVIDER_KEY"] != title.key || env["GROK_SAMPLER_SHARED_CLIENT"] != "0" { + t.Fatal("held original HOME, two keys or existing client optout differs") + } + for _, key := range []string{"GROK_SESSION_SUMMARY_MODEL", "GROK_TITLE_REFRESH"} { + if _, exists := env[key]; exists { + t.Fatal("ambient auxiliary model/refresh setting entered the child") + } + } + for _, client := range []string{"qoder", "kiro", "windsurf"} { + f.spec.client = client + other := grokContractEnvironment(t, f.env()) + if _, exists := other["UAP_GROK_TITLE_PROVIDER_KEY"]; exists { + t.Fatal("another client inherited title fixture authority") + } + } + if os.Getenv("UAP_GROK_TITLE_PROVIDER_KEY") != "ambient-title-private" || os.Getenv("GROK_SESSION_SUMMARY_MODEL") != "ambient-model" { + t.Fatal("constructing fixture configuration changed ambient environment") + } +} diff --git a/repotests/agentplugins_grok_project_native_title_contract_test.go b/repotests/agentplugins_grok_project_native_title_contract_test.go new file mode 100644 index 000000000..35a515507 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_title_contract_test.go @@ -0,0 +1,269 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "net" + "net/http" + "strings" + "testing" + "time" +) + +// Actual611 title bytes, changing only the prospective explicitly configured +// auxiliary model. This inert input is not successful native evidence. +const grokTitleContractGolden = `{"model":"uap-grok-title-fixture","messages":[{"role":"system","content":"You are tasked with generating the session title. The user is asking almost always software engineering related questions on their codebase.\nWe describe the session title below\n# Session Title\nA short and distinctive 5-10 word descriptive title for the session. Super info dense, no filler.\n\nYou will be given the user query below encapsulated in .\n\nJust generate the session_title and nothing else"},{"role":"user","content":"\nDescribe only the installed uap-grok-attempt skill marker. Do not run any tool or change any file.\n"}],"temperature":1.0,"max_tokens":100,"tools":[{"type":"function","function":{"name":"session_title","description":"Generate the session_title which we use for the user_message","parameters":{"type":"object","required":["session_title"],"properties":{"session_title":{"type":"string","description":"Final session title, just 5-10 word descriptive title for the session. Super info dense, no filler."}},"additionalProperties":false}}}],"tool_choice":{"type":"function","function":{"name":"session_title"}},"stream":true,"stream_options":{"include_usage":true}}` + +func grokTitleContractProvider(t *testing.T) (*grokProjectProvider, *grokProjectTitleProvider) { + t.Helper() + main := grokProjectContractProvider(t) + transport, err := newGrokProjectProvider(main.ctx, main.cancel, main.totalEnd) + if err != nil { + t.Fatal(err) + } + title := &grokProjectTitleProvider{transport: transport, mainKey: main.key} + main.title = title + transport.receipt.Transport, transport.receipt.Model = grokProjectTitleTransport, grokProjectTitleModel + if err := title.start(); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { transport.stop() }) + return main, title +} + +func grokTitleContractInput(t *testing.T, kind string, main *grokProjectProvider, title *grokProjectTitleProvider) []byte { + t.Helper() + body := []byte(grokTitleContractGolden) + switch kind { + case "array": + return []byte(`[]`) + case "null": + return []byte(`null`) + case "depth": + return []byte(strings.Repeat("[", 34) + "0" + strings.Repeat("]", 34)) + case "utf8": + return []byte{255} + case "invalid": + return []byte(`{"model":`) + case "duplicate": + return []byte(`{"model":"one","model":"two"}`) + case "trailing": + return append(body, []byte("{}")...) + case "nonfinite": + return []byte(`{"temperature":1e9999}`) + case "oversized": + return bytes.Repeat([]byte{' '}, (1<<20)+1) + case "privacy-main": + return []byte(`{"opaque":"` + main.key + `"}`) + case "privacy-title": + return []byte(`{"opaque":"` + title.transport.key + `"}`) + case "encoded-main": + return []byte(`{"opaque":"\u0075` + main.key[1:] + `"}`) + case "encoded-title": + return []byte(`{"opaque":"\u0075` + title.transport.key[1:] + `"}`) + } + var value map[string]any + decoder := json.NewDecoder(bytes.NewReader(body)) + decoder.UseNumber() + if err := decoder.Decode(&value); err != nil { + t.Fatal(err) + } + switch kind { + case "main-model": + value["model"] = "uap-grok-fixture" + case "unknown-model": + value["model"] = "grok-4.6" + case "extra": + value["unknown"] = "opaque but safe" + case "missing": + delete(value, "stream") + case "system": + value["messages"].([]any)[0].(map[string]any)["content"] = "changed system" + case "prompt": + value["messages"].([]any)[1].(map[string]any)["content"] = "changed user" + case "messages": + value["messages"] = append(value["messages"].([]any), map[string]any{"role": "tool", "content": "extra"}) + case "schema": + value["tools"].([]any)[0].(map[string]any)["function"].(map[string]any)["parameters"].(map[string]any)["additionalProperties"] = true + case "tool": + value["tools"].([]any)[0].(map[string]any)["function"].(map[string]any)["name"] = "inspect_runtime" + case "options": + value["stream_options"].(map[string]any)["unknown"] = true + case "temperature": + value["temperature"] = true + case "tokens": + value["max_tokens"] = float64(101) + case "choice": + value["tool_choice"] = "auto" + } + body, err := json.Marshal(value) + if err != nil { + t.Fatal(err) + } + return body +} + +func TestGrokProjectTitleFixtureKeepsOneExactSourceDefinedExchange(t *testing.T) { + if len(grokTitleContractGolden) != 1220 || fmt.Sprintf("%x", sha256.Sum256([]byte(grokTitleContractGolden))) != "6310901ba3d8138dbf06eb01e447139f815a42424513ed048263f39b1ced658a" { + t.Fatal("independently frozen measured-title input differs") + } + main, title := grokTitleContractProvider(t) + status, body := grokProjectContractSend(t, grokProjectContractRequest(t, title.transport, []byte(grokTitleContractGolden))) + if status != http.StatusOK || len(body) != 570 || fmt.Sprintf("%x", sha256.Sum256(body)) != "eea6c4c389c7e27cf0e6d1cc9e705bc84a4d77ae8c5afddaa846ec8255e6ed68" { + t.Fatal("fixed title response bytes differ") + } + events := bytes.Split(body, []byte("\n\n")) + if len(events) != 4 || !bytes.Equal(events[2], []byte("data: [DONE]")) || len(events[3]) != 0 { + t.Fatal("title SSE termination differs") + } + var first, last map[string]any + if json.Unmarshal(bytes.TrimPrefix(events[0], []byte("data: ")), &first) != nil || json.Unmarshal(bytes.TrimPrefix(events[1], []byte("data: ")), &last) != nil { + t.Fatal("title SSE JSON differs") + } + choice := first["choices"].([]any)[0].(map[string]any) + delta := choice["delta"].(map[string]any) + call := delta["tool_calls"].([]any)[0].(map[string]any) + function := call["function"].(map[string]any) + if first["model"] != "uap-grok-title-fixture" || first["id"] != "uap-grok-title-capture" || call["id"] != "uap-grok-title-call" || function["name"] != "session_title" || function["arguments"] != `{"session_title":"Disposable plugin startup observation"}` || last["choices"].([]any)[0].(map[string]any)["finish_reason"] != "tool_calls" || len(delta) != 2 { + t.Fatal("title function identity/result differs") + } + f := &projectNativeCLIAttempt{grok: main, files: map[string][]byte{}} + f.keep(t, "native-attempt.stderr.log", []byte(main.key+" "+title.transport.key)) + main.finish(t, f) + if len(f.files) != 5 || title.transport.receipt.Requests != 1 || title.transport.receipt.Responses != 1 || !title.transport.receipt.ShutdownComplete || title.transport.receipt.ForcedClose || title.transport.receipt.Failure != "none" || main.receipt.Requests != 0 || main.receipt.Responses != 0 { + t.Fatal("title contaminated primary counters or failed ordinary closure") + } + for _, raw := range f.files { + if grokProjectPrivateCredential(raw) || bytes.Contains(raw, []byte(main.key)) || bytes.Contains(raw, []byte(title.transport.key)) { + t.Fatal("either private fixture key escaped retention") + } + } + var receipt map[string]any + if json.Unmarshal(f.files["grok-title-provider.json"], &receipt) != nil || receipt["role"] != "session-title" || receipt["model"] != "uap-grok-title-fixture" || len(f.files["grok-title-provider.json"]) > 2048 { + t.Fatal("separate finite title role receipt differs") + } +} + +func TestGrokProjectTitleFixtureRefusesUnmeasuredSchemaAndPrivateBodies(t *testing.T) { + kinds := []string{"main-model", "unknown-model", "extra", "missing", "system", "prompt", "messages", "schema", "tool", "options", "temperature", "tokens", "choice", "array", "null", "depth", "utf8", "invalid", "duplicate", "trailing", "nonfinite", "oversized", "privacy-main", "privacy-title", "encoded-main", "encoded-title"} + for _, kind := range kinds { + t.Run(kind, func(t *testing.T) { + main, title := grokTitleContractProvider(t) + raw := grokTitleContractInput(t, kind, main, title) + status, _ := grokProjectContractSend(t, grokProjectContractRequest(t, title.transport, raw)) + title.transport.stop() + uncaptured := kind == "array" || kind == "null" || kind == "depth" || kind == "utf8" || kind == "invalid" || kind == "duplicate" || kind == "trailing" || kind == "nonfinite" || kind == "oversized" || strings.HasPrefix(kind, "privacy-") || strings.HasPrefix(kind, "encoded-") + requests := 1 + if uncaptured { + requests = 0 + } + if status != http.StatusBadRequest || title.transport.receipt.Failure == "none" || title.transport.receipt.Requests != requests || title.transport.receipt.Responses != 0 || (len(title.transport.request) == 0) != uncaptured || main.receipt.Requests != 0 || main.ctx.Err() == nil { + t.Fatal("unknown/private body escaped failure or retained forbidden bytes") + } + if !uncaptured && !bytes.Equal(title.transport.request, raw) { + t.Fatal("safe shape-refused body was not retained exactly") + } + }) + } +} + +func TestGrokProjectTitleFixtureRetainsOriginalHTTPAndSingleAttemptBoundary(t *testing.T) { + for _, kind := range []string{"query", "method", "get-root", "host", "credential", "main-credential", "duplicate-credential", "content-type", "duplicate-content-type", "deadline", "second"} { + t.Run(kind, func(t *testing.T) { + main, title := grokTitleContractProvider(t) + if kind == "second" { + status, _ := grokProjectContractSend(t, grokProjectContractRequest(t, title.transport, []byte(grokTitleContractGolden))) + if status != 200 { + t.Fatal("first title request failed") + } + } + request := grokProjectContractRequest(t, title.transport, []byte(grokTitleContractGolden)) + grokProjectContractMutateHTTP(request, kind) + switch kind { + case "main-credential": + request.Header.Set("Authorization", "Bearer "+main.key) + case "get-root": + request.Method = http.MethodGet + request.URL.Path = "/" + case "deadline": + main.cancel() + } + status, _ := grokProjectContractSend(t, request) + title.transport.stop() + responses := 0 + if kind == "second" { + responses = 1 + } + if status != 400 || title.transport.receipt.Failure == "none" || title.transport.receipt.Responses != responses || main.receipt.Requests != 0 { + t.Fatal("title route/key/deadline/one-attempt boundary widened") + } + }) + } +} + +type grokTitleContractWriteFailure struct{ headers http.Header } + +func (w grokTitleContractWriteFailure) Header() http.Header { return w.headers } +func (grokTitleContractWriteFailure) WriteHeader(int) {} +func (grokTitleContractWriteFailure) Write([]byte) (int, error) { + return 0, errors.New("inert title write refusal") +} + +func TestGrokProjectTitleFixtureResponseWriteFailureCannotRecordCompletion(t *testing.T) { + _, title := grokTitleContractProvider(t) + request, err := http.NewRequestWithContext(title.transport.ctx, http.MethodPost, "/v1/chat/completions", strings.NewReader(grokTitleContractGolden)) + if err != nil { + t.Fatal(err) + } + request.RequestURI = "/v1/chat/completions" + request.Host = title.transport.listener.Addr().String() + request.RemoteAddr = "127.0.0.1:12345" + request.Header.Set("Content-Type", "application/json") + request.Header.Set("Authorization", "Bearer "+title.transport.key) + title.serve(grokTitleContractWriteFailure{headers: http.Header{}}, request) + title.transport.stop() + if title.transport.receipt.Requests != 1 || title.transport.receipt.Responses != 0 || title.transport.receipt.Failure != "response-write" || len(title.transport.reply) != 0 { + t.Fatal("failed response write claimed a completed title") + } +} + +func TestGrokProjectTitleFixtureUsesOriginalForcedCleanupRefusal(t *testing.T) { + _, title := grokTitleContractProvider(t) + p := title.transport + connection, err := (&net.Dialer{Timeout: time.Second}).DialContext(p.ctx, "tcp4", p.listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer func() { _ = connection.Close() }() + if _, err := fmt.Fprintf(connection, "POST /v1/chat/completions HTTP/1.1\r\nHost: %s\r\nAuthorization: Bearer %s\r\nContent-Type: application/json\r\nContent-Length: 1000\r\n\r\n{", p.listener.Addr(), p.key); err != nil { + t.Fatal("inert partial title request failed") + } + end := time.Now().Add(time.Second) + for { + p.mu.Lock() + entered := p.attempted + p.mu.Unlock() + if entered { + break + } + if time.Now().After(end) { + t.Fatal("actual title handler never received partial body") + } + time.Sleep(time.Millisecond) + } + began := time.Now() + ordinary := p.stop() + if ordinary || !p.receipt.ForcedClose || !p.receipt.ShutdownComplete || p.connections != 0 || time.Since(began) > 2500*time.Millisecond || p.receipt.Failure != "cleanup" { + t.Fatal("incomplete title body was not bounded and failed on forced cleanup") + } + select { + case <-p.serveDone: + default: + t.Fatal("title HTTP serve goroutine survived cleanup") + } +} diff --git a/repotests/agentplugins_grok_project_native_title_test.go b/repotests/agentplugins_grok_project_native_title_test.go new file mode 100644 index 000000000..ff8f1ad86 --- /dev/null +++ b/repotests/agentplugins_grok_project_native_title_test.go @@ -0,0 +1,201 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "net/http" + "path/filepath" + "reflect" + "testing" + "time" +) + +const grokProjectTitleModel = "uap-grok-title-fixture" +const grokProjectTitleTransport = "scripted loopback OpenAI session-title SSE; no real model" +const grokProjectTitlePrompt = "Describe only the installed uap-grok-attempt skill marker. Do not run any tool or change any file." +const grokProjectTitleSystem = `You are tasked with generating the session title. The user is asking almost always software engineering related questions on their codebase. +We describe the session title below +# Session Title +A short and distinctive 5-10 word descriptive title for the session. Super info dense, no filler. + +You will be given the user query below encapsulated in . + +Just generate the session_title and nothing else` + +// The source-defined auxiliary title has a separate endpoint, key and one-call +// counter. It never changes the original primary provider's model admission. +type grokProjectTitleProvider struct { + transport *grokProjectProvider + mainKey string +} + +func (f *projectNativeCLIAttempt) configureGrokTitleProvider(t *testing.T) { + t.Helper() + if f.spec.client != "grok" { + t.Fatal("fixed Grok title fixture requires Grok") + } + var err error + f.grok, err = newGrokProjectProvider(f.ctx, f.cancel, f.started.Add(180*time.Second)) + if err != nil { + t.Fatal("Grok main fixture credential generation failed") + } + if err = f.grok.start(); err != nil { + t.Fatal("Grok main fixture listener could not start") + } + transport, err := newGrokProjectProvider(f.ctx, f.cancel, f.started.Add(180*time.Second)) + if err != nil { + t.Fatal("Grok title fixture credential generation failed") + } + if transport.key == f.grok.key { + t.Fatal("Grok title and main fixture keys must differ") + } + f.grok.title = &grokProjectTitleProvider{transport: transport, mainKey: f.grok.key} + transport.receipt.Transport, transport.receipt.Model = grokProjectTitleTransport, grokProjectTitleModel + if err = f.grok.title.start(); err != nil { + t.Fatal("Grok title fixture listener could not start") + } + trust, err := grokProjectSelectionTrust(f) + if err != nil { + t.Fatal(err) + } + trustPath := filepath.Join(f.base.Home, ".grok", "trusted_folders.toml") + projectNativeRuntimeWrite(t, trustPath, trust, 0600) + f.protected[trustPath] = nativeSHA(t, trustPath) + // Both held endpoints and the supported name selector precede the only freeze. + configuration := append(f.grok.title.configuration(f.grok.configuration()), []byte("\n[plugins]\nenabled = [\"uap-grok-attempt\"]\n\n[features]\nturn_summary = false\n")...) + path := filepath.Join(f.base.Home, ".grok", "config.toml") + projectNativeRuntimeWrite(t, path, configuration, 0600) + f.grok.authoredConfig = bytes.Clone(configuration) + f.protected[path] = nativeSHA(t, path) + f.evidence["provider_mode"] = "documented original-HOME custom main/title fixtures; bounded startup capture; no vendor account" +} + +func (p *grokProjectTitleProvider) configuration(primary []byte) []byte { + model := []byte(fmt.Sprintf("session_summary = %q\n", grokProjectTitleModel)) + configuration := bytes.Replace(primary, []byte("[models]\n"), append([]byte("[models]\n"), model...), 1) + return append(configuration, []byte(fmt.Sprintf("\n[model.%s]\nmodel = %q\nbase_url = %q\nenv_key = \"UAP_GROK_TITLE_PROVIDER_KEY\"\napi_backend = \"chat_completions\"\nsupports_backend_search = false\nsupports_reasoning_effort = false\nmax_retries = 0\n", grokProjectTitleModel, grokProjectTitleModel, "http://"+p.transport.listener.Addr().String()+"/v1"))...) +} + +func (p *grokProjectTitleProvider) start() error { + t := p.transport + listener, err := (&net.ListenConfig{}).Listen(t.ctx, "tcp4", "127.0.0.1:0") + if err != nil { + t.fail("listen") + return err + } + t.listener = listener + t.server = &http.Server{Handler: http.HandlerFunc(p.serve), ReadHeaderTimeout: 2 * time.Second, + ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second, IdleTimeout: 2 * time.Second, MaxHeaderBytes: 16 << 10, + BaseContext: func(net.Listener) context.Context { return t.ctx }, ConnState: t.connectionState} + t.receipt.Started = true + go func() { + defer close(t.serveDone) + if err := t.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) { + t.fail("serve") + } + }() + return nil +} + +func grokProjectTitleExpected() map[string]any { + return map[string]any{ + "model": grokProjectTitleModel, "stream": true, "stream_options": map[string]any{"include_usage": true}, + "temperature": json.Number("1.0"), "max_tokens": json.Number("100"), + "messages": []any{map[string]any{"role": "system", "content": grokProjectTitleSystem}, + map[string]any{"role": "user", "content": "\n" + grokProjectTitlePrompt + "\n"}}, + "tools": []any{map[string]any{"type": "function", "function": map[string]any{ + "name": "session_title", "description": "Generate the session_title which we use for the user_message", + "parameters": map[string]any{"type": "object", "required": []any{"session_title"}, "additionalProperties": false, + "properties": map[string]any{"session_title": map[string]any{"type": "string", "description": "Final session title, just 5-10 word descriptive title for the session. Super info dense, no filler."}}}}}}, + "tool_choice": map[string]any{"type": "function", "function": map[string]any{"name": "session_title"}}, + } +} + +func (p *grokProjectTitleProvider) capture(raw []byte) string { + value, err := projectNativeRuntimeJSON(raw) + object, ok := value.(map[string]any) + if err != nil || !ok { + return "request-shape" + } + // Refuse both literal and decoded credentials before retaining an opaque + // shape refusal. The existing bounded scanner also catches nested encodings. + decoded, err := json.Marshal(object) + if err != nil || grokProjectPrivateCredential(raw) || grokProjectPrivateCredential(decoded) { + return "privacy" + } + for _, key := range []string{p.mainKey, p.transport.key} { + if key == "" || bytes.Contains(raw, []byte(key)) || bytes.Contains(decoded, []byte(key)) { + return "privacy" + } + } + p.transport.mu.Lock() + p.transport.request, p.transport.receipt.Requests = bytes.Clone(raw), 1 + p.transport.mu.Unlock() + if !reflect.DeepEqual(object, grokProjectTitleExpected()) { + return "request-shape" + } + return "none" +} + +func grokProjectTitleResponse() []byte { + return []byte("data: {\"id\":\"uap-grok-title-capture\",\"object\":\"chat.completion.chunk\",\"created\":1,\"model\":\"uap-grok-title-fixture\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"tool_calls\":[{\"index\":0,\"id\":\"uap-grok-title-call\",\"type\":\"function\",\"function\":{\"name\":\"session_title\",\"arguments\":\"{\\\"session_title\\\":\\\"Disposable plugin startup observation\\\"}\"}}]},\"finish_reason\":null}]}\n\ndata: {\"id\":\"uap-grok-title-capture\",\"object\":\"chat.completion.chunk\",\"created\":1,\"model\":\"uap-grok-title-fixture\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"tool_calls\"}]}\n\ndata: [DONE]\n\n") +} + +func (p *grokProjectTitleProvider) serve(w http.ResponseWriter, r *http.Request) { + t := p.transport + if !t.admit(w, r) { + return + } + raw, err := io.ReadAll(io.LimitReader(r.Body, (1<<20)+1)) + if err != nil || len(raw) > 1<<20 { + http.Error(w, "Grok title body bound", http.StatusBadRequest) + t.fail("body") + return + } + if reason := p.capture(raw); reason != "none" { + http.Error(w, "Grok title request refused", http.StatusBadRequest) + t.fail(reason) + return + } + response := grokProjectTitleResponse() + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + if count, err := w.Write(response); err != nil || count != len(response) { + t.fail("response-write") + return + } + t.mu.Lock() + t.reply, t.receipt.Responses = bytes.Clone(response), 1 + t.mu.Unlock() +} + +func (p *grokProjectTitleProvider) finish(t *testing.T, f *projectNativeCLIAttempt) { + t.Helper() + transport := p.transport + if !transport.stop() { + t.Error("Grok title fixture ordinary shutdown did not complete") + } + transport.mu.Lock() + defer transport.mu.Unlock() + for name, body := range map[string][]byte{"grok-title-provider.request.json": transport.request, "grok-title-provider.response.sse": transport.reply} { + if len(body) != 0 { + f.keep(t, name, body) + } + } + raw, err := json.Marshal(struct { + Role string `json:"role"` + grokProjectProviderReceipt + }{Role: "session-title", grokProjectProviderReceipt: transport.receipt}) + if err != nil || len(raw) > 2<<10 { + t.Error("Grok title finite receipt refused") + return + } + f.keep(t, "grok-title-provider.json", raw) +} diff --git a/repotests/agentplugins_grok_project_native_turn_summary_contract_test.go b/repotests/agentplugins_grok_project_native_turn_summary_contract_test.go new file mode 100644 index 000000000..f625907dd --- /dev/null +++ b/repotests/agentplugins_grok_project_native_turn_summary_contract_test.go @@ -0,0 +1,139 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "os" + "path/filepath" + "reflect" + "testing" + "time" +) + +func grokTurnSummaryContractFixture(t *testing.T) *projectNativeCLIAttempt { + t.Helper() + root := t.TempDir() + ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) + f := &projectNativeCLIAttempt{spec: projectNativeCLISpec{client: "grok"}, ctx: ctx, cancel: cancel, started: time.Now(), + base: &nativeFixture{Root: root, Home: filepath.Join(root, "original-home"), Project: filepath.Join(root, "project"), + CodexHome: filepath.Join(root, "original-codex")}, protected: map[string]string{}, evidence: map[string]any{}, + files: map[string][]byte{"native-attempt.stdout.log": {}, "native-attempt.stderr.log": {}, "native-attempt.command.json": []byte(`{"inert":true}`)}, + used: map[string]bool{"native-attempt": true}} + for _, path := range []string{f.base.Home, f.base.Project} { + if err := os.Mkdir(path, 0700); err != nil { + t.Fatal(err) + } + } + t.Cleanup(func() { + if f.grok != nil { + if f.grok.title != nil { + f.grok.title.transport.stop() + } + f.grok.stop() + } + cancel() + }) + f.configureGrokTitleProvider(t) + return f +} + +func TestGrokTurnSummarySettingIsFrozenOnceInCompleteOriginalHomeRecipe(t *testing.T) { + f := grokTurnSummaryContractFixture(t) + want := append(f.grok.title.configuration(f.grok.configuration()), []byte("\n[plugins]\nenabled = [\"uap-grok-attempt\"]\n\n[features]\nturn_summary = false\n")...) + parsed, status := grokConfigParse(f.grok.authoredConfig) + if status != "captured" || !reflect.DeepEqual(parsed["features"], map[string]any{"turn_summary": false}) || + !bytes.Equal(f.grok.authoredConfig, want) || bytes.Count(want, []byte("[features]")) != 1 { + t.Fatal("fixed native boolean suffix changed the complete two-model recipe") + } + path := filepath.Join(f.base.Home, ".grok", "config.toml") + body, err := os.ReadFile(path) + info, statErr := os.Lstat(path) + if err != nil || statErr != nil || !info.Mode().IsRegular() || info.Mode().Perm() != 0600 || + !bytes.Equal(body, want) || len(f.protected) != 2 || f.protected[path] != grokConfigSHA(want) { + t.Fatal("complete recipe was not the original ordinary protected config") + } + if _, err := os.Lstat(filepath.Join(f.base.Project, ".grok", "config.toml")); !os.IsNotExist(err) { + t.Fatal("feature setting acquired project configuration authority") + } + f.check(t) +} + +func TestGrokTurnSummaryRecipeChangesRemainObservedRefusalsWithoutAdoption(t *testing.T) { + for _, kind := range []string{"true", "quoted", "duplicate-table", "duplicate-key", "extra-feature", "sampling-table"} { + t.Run(kind, func(t *testing.T) { + f := grokTurnSummaryContractFixture(t) + original := bytes.Clone(f.grok.authoredConfig) + body := bytes.Clone(original) + switch kind { + case "true": + body = bytes.Replace(body, []byte("turn_summary = false"), []byte("turn_summary = true"), 1) + case "quoted": + body = bytes.Replace(body, []byte("turn_summary = false"), []byte(`turn_summary = "false"`), 1) + case "duplicate-table": + body = append(body, []byte("\n[features]\nturn_summary = false\n")...) + case "duplicate-key": + body = append(body, []byte("turn_summary = true\n")...) + case "extra-feature": + body = append(body, []byte("ordinary_extra = true\n")...) + case "sampling-table": + body = bytes.Replace(body, []byte("[features]"), []byte("[turn_summary]"), 1) + } + originalValue, originalStatus := grokConfigParse(original) + changedValue, changedStatus := grokConfigParse(body) + captured := kind != "duplicate-table" && kind != "duplicate-key" + if originalStatus != "captured" || (changedStatus == "captured") != captured || + captured && reflect.DeepEqual(originalValue, changedValue) || !captured && changedValue != nil { + t.Fatal("changed native boolean recipe escaped complete-map or strict TOML distinction") + } + path := filepath.Join(f.base.Home, ".grok", "config.toml") + before := f.protected[path] + grokConfigRewrite(t, path, body) + f.observeGrokConfigMutation(path, before, nativeSHA(t, path)) + observation, ok := f.evidence["grok_config_mutation"].(grokConfigObservation) + if !ok || bytes.Equal(body, original) || f.protected[path] != before || !bytes.Equal(f.grok.authoredConfig, original) { + t.Fatal("changed feature data renewed original authority") + } + if observation.Status != "recipe-mismatch" || observation.Comparison != nil || bytes.Equal(original, f.grok.configuration()) { + t.Fatal("changed feature data bypassed the existing finite diagnostic refusal") + } + actual, err := os.ReadFile(path) + if err != nil || !bytes.Equal(actual, body) || nativeSHA(t, path) == before { + t.Fatal("diagnostic rewrote observed bytes or adopted their digest") + } + }) + } +} + +func TestGrokTurnSummaryAmbientOverridesCannotEnterOriginalFixture(t *testing.T) { + for key, value := range map[string]string{ + "GROK_TURN_SUMMARY": "true", "GROK_TITLE_REFRESH": "true", "GROK_SESSION_SUMMARY_MODEL": "ambient-model", + "HOME": "/ambient-home", "UAP_GROK_TEST_PROVIDER_KEY": "ambient-main", "UAP_GROK_TITLE_PROVIDER_KEY": "ambient-title", + } { + t.Setenv(key, value) + } + f := grokTurnSummaryContractFixture(t) + env := grokContractEnvironment(t, f.env()) + for _, key := range []string{"GROK_TURN_SUMMARY", "GROK_TITLE_REFRESH", "GROK_SESSION_SUMMARY_MODEL"} { + if _, exists := env[key]; exists { + t.Fatal("ambient feature or model setting entered native child environment") + } + } + if env["HOME"] != f.base.Home || env["UAP_GROK_TEST_PROVIDER_KEY"] != f.grok.key || + env["UAP_GROK_TITLE_PROVIDER_KEY"] != f.grok.title.transport.key || env["GROK_SAMPLER_SHARED_CLIENT"] != "0" { + t.Fatal("supported feature setting changed original HOME or held route authority") + } + for _, client := range []string{"qoder", "kiro", "windsurf"} { + f.spec.client = client + other := grokContractEnvironment(t, f.env()) + for _, key := range []string{"GROK_TURN_SUMMARY", "GROK_TITLE_REFRESH", "UAP_GROK_TEST_PROVIDER_KEY", "UAP_GROK_TITLE_PROVIDER_KEY"} { + if _, exists := other[key]; exists { + t.Fatal("another client inherited Grok feature or fixture authority") + } + } + } + if os.Getenv("GROK_TURN_SUMMARY") != "true" || os.Getenv("HOME") != "/ambient-home" || os.Getenv("GROK_SESSION_SUMMARY_MODEL") != "ambient-model" { + t.Fatal("constructing fixture configuration changed parent environment") + } +} diff --git a/repotests/agentplugins_project_native_cli_attempt_test.go b/repotests/agentplugins_project_native_cli_attempt_test.go new file mode 100644 index 000000000..f1c530783 --- /dev/null +++ b/repotests/agentplugins_project_native_cli_attempt_test.go @@ -0,0 +1,753 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "archive/zip" + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "reflect" + "runtime" + "sort" + "strings" + "testing" + "time" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/adapters/process" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/nativeconfig" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/adapters/packagedigest" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/domain" + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/managedstdio" + "github.com/777genius/plugin-kit-ai/install/integrationctl/ports" +) + +// A closed four-client research table, not a native client/plugin framework. +// Every first native session remains failed/unqualified until its genuine +// output and exact authentication/trust/protocol boundary have been reviewed. +type projectNativeCLISpec struct { + client, binEnv, optin, version, archiveSHA, member, executableSHA, versionOutput string + executableBytes int64 + mixed bool +} + +func projectNativeCLISpecification(client string) (projectNativeCLISpec, error) { + switch client { + case "qoder": + return projectNativeCLISpec{client, "AGENTPLUGINS_QODER_BIN", "AGENTPLUGINS_QODER_PROJECT_RUNTIME_ATTEMPT", "1.1.78", "5e3213d2d4256c86c5ad2c8bf73fd392f92ba80d0d38f822806542805a48a8ea", "qodercli", "0da0976e13fe00bc28f725d6fe7c2f0fe9fa9a8c37eeb2ee4e80ad4c0fc58953", "1.1.78\n", 182982112, false}, nil + case "grok": + return projectNativeCLISpec{client, "AGENTPLUGINS_GROK_BIN", "AGENTPLUGINS_GROK_PROJECT_RUNTIME_ATTEMPT", "1.0.70", "11d27a30adfb75c4b9b180072ab27412a1916f054ff5497ab70fb9a77d304bcc", "grok", "22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9", "grok 1.0.70 (f50904eaee6c)\n", 195700192, false}, nil + case "kiro": + return projectNativeCLISpec{client, "AGENTPLUGINS_KIRO_BIN", "AGENTPLUGINS_KIRO_PROJECT_RUNTIME_ATTEMPT", "2.34.0", "11dce9286cf7401079cf4eac02a3d5982daac16a346d880109ec7b4a0b941040", "kirocli/bin/kiro-cli", "94cae7e349f76869a19eae335a5b392fba8515743b1c2822b9ea33128c067cc6", "kiro-cli 2.34.0\n", 43027744, true}, nil + case "windsurf": + return projectNativeCLISpec{client, "AGENTPLUGINS_WINDSURF_BIN", "AGENTPLUGINS_WINDSURF_PROJECT_RUNTIME_ATTEMPT", "3000.11.205", "2e65c8b93682258fc91f5e4cead92e3698338cebf1b31dbc32a3201cb63705c4", "bin/devin", "642c82e32cb3dec90cf5ab01975520b425307872c7a650c9fb5b0653f37681f4", "devin 3000.11.205 (6dea39440ae3)\n", 217494824, true}, nil + default: + return projectNativeCLISpec{}, fmt.Errorf("unregistered native CLI attempt") + } +} +func projectNativeCLIArguments(client, executable, view, prompt string) ([]string, error) { + switch client { + case "qoder": + if !filepath.IsAbs(view) || filepath.Clean(view) != view || filepath.Base(view) != ".agentplugins-qoder-native" { + return nil, fmt.Errorf("receipt-selected closed Qoder view required") + } + return []string{executable, "--print", "--output-format", "stream-json", "--no-session-persistence", "--permission-mode", "default", "--tools", "", "--plugin-dir", view, prompt}, nil + case "grok": + // Source-guided candidate: unlike --tools "" (which parses as None), the + // strict permission parser maps --deny "" to Deny/Any/no pattern. + return []string{executable, "--single", prompt, "--output-format", "streaming-json", "--permission-mode", "default", "--deny", "", "--no-subagents", "--disable-web-search", "--max-turns", "1", "--model", grokProjectProviderModel}, nil + case "kiro": + return []string{executable, "chat", "--v2", "--no-interactive", prompt}, nil + case "windsurf": + return []string{executable, "--print", prompt, "--respect-workspace-trust", "true"}, nil + default: + return nil, fmt.Errorf("unregistered native CLI argv") + } +} +func projectNativeCLIUpdater(client string) (string, []byte) { + switch client { + case "qoder": + return ".qoder/settings.json", []byte("{\"general\":{\"enableAutoUpdate\":false}}\n") + case "windsurf": + return "devin/config.json", []byte("{\"auto_update\":false,\"permissions\":{\"allow\":[],\"deny\":[\"read\",\"edit\",\"write\",\"grep\",\"glob\",\"exec\",\"fetch\",\"web_fetch\",\"mcp__*\"],\"ask\":[]}}\n") + default: + return "", nil + } +} + +type projectNativeCLIAttempt struct { + spec projectNativeCLISpec + base *nativeFixture + client, installer, git, other, outside, output, bin, name, skill, view, data string + started time.Time + ctx context.Context + cancel context.CancelFunc + files map[string][]byte + immutable, protected map[string]string + used map[string]bool + evidence map[string]any + binding domain.ClientBinding + grok *grokProjectProvider +} + +func (f *projectNativeCLIAttempt) keep(t *testing.T, name string, body []byte) { + t.Helper() + if f.grok != nil { + body = f.grok.redact(body) + if grokProjectPrivateCredential(body) { + t.Fatal("unredacted Grok fixture credential refused") + } + } + total := len(body) + for _, b := range f.files { + total += len(b) + } + if filepath.Base(name) != name || f.files[name] != nil || len(f.files) >= 80 || len(body) > 1<<20 || total > 16<<20 { + t.Fatal("closed CLI evidence bound") + } + f.files[name] = bytes.Clone(body) +} +func (f *projectNativeCLIAttempt) check(t *testing.T) { + t.Helper() + for index, inventory := range []map[string]string{f.immutable, f.protected} { + for path, want := range inventory { + got, err := projectNativeRuntimeDigest(path) + if err != nil || got != want { + if index == 1 && err == nil { + f.observeGrokConfigMutation(path, want, got) + } + t.Fatalf("native CLI immutable/protected identity changed: %s (%v)", path, err) + } + } + } + if f.spec.client == "qoder" && f.bin != "" { + path := filepath.Join(f.bin, "qoder") + info, err := os.Lstat(path) + target, linkErr := os.Readlink(path) + if err != nil || linkErr != nil || info.Mode()&os.ModeSymlink == 0 || target != f.client { + t.Fatal("recorded genuine Qoder command symlink changed") + } + } +} +func (f *projectNativeCLIAttempt) env() []string { + env := append(f.base.env(f.bin), "NO_COLOR=1", "GIT_CONFIG_NOSYSTEM=1", "GIT_CONFIG_GLOBAL="+os.DevNull, "GIT_TERMINAL_PROMPT=0") + if f.spec.client == "grok" { + env = append(env, "GROK_DISABLE_AUTOUPDATER=1") + if f.grok != nil { + env = append(env, "UAP_GROK_TEST_PROVIDER_KEY="+f.grok.key, "GROK_SAMPLER_SHARED_CLIENT=0") + if f.grok.title != nil { + env = append(env, "UAP_GROK_TITLE_PROVIDER_KEY="+f.grok.title.transport.key) + } + } + } + return env +} +func (f *projectNativeCLIAttempt) selectedCommand(label, cwd string, argv []string, timeout time.Duration) error { + expectedCWD, expectedTimeout := f.base.Project, 30*time.Second + var expected []string + switch label { + case "git-a": + expected = []string{f.git, "-c", "core.hooksPath=/dev/null", "init", "--quiet"} + case "git-b": + expectedCWD = f.other + expected = []string{f.git, "-c", "core.hooksPath=/dev/null", "init", "--quiet"} + case "version": + expectedCWD = f.outside + expected = []string{f.client, "--version"} + case "add-a": + expected = []string{f.installer, "add", f.base.Package, "--no-save", "--scope", "project", "--project-root", f.base.Project, "--target", f.spec.client, "--format", "json"} + case "native-attempt": + expectedTimeout = 60 * time.Second + var err error + expected, err = projectNativeCLIArguments(f.spec.client, f.client, f.view, "Describe only the installed "+f.name+" skill marker. Do not run any tool or change any file.") + if err != nil { + return err + } + case "native-inspect": + if f.spec.client != "grok" { + return fmt.Errorf("only Grok has the measured installed inspection") + } + expected = []string{f.client, "inspect", "--json"} + default: + return fmt.Errorf("unregistered native CLI command label") + } + if cwd != expectedCWD || timeout != expectedTimeout || !reflect.DeepEqual(argv, expected) { + return fmt.Errorf("fixed native CLI command/cwd/deadline differs") + } + return nil +} +func (f *projectNativeCLIAttempt) command(t *testing.T, label, cwd string, argv []string, timeout time.Duration) ports.CommandResult { + t.Helper() + allowed := map[string]bool{"git-a": true, "git-b": true, "version": true, "add-a": true, "native-attempt": true} + if f.spec.client == "grok" { + allowed["native-inspect"] = true + } + if !allowed[label] || f.used[label] { + t.Fatal("unregistered/repeated CLI attempt command") + } + f.used[label] = true + if err := f.selectedCommand(label, cwd, argv, timeout); err != nil { + t.Fatal(err) + } + f.check(t) + ctx, cancel := context.WithTimeout(f.ctx, timeout) + defer cancel() + start := time.Now() + r, err := (process.OS{}).RunWithDescendantExitGrace(ctx, ports.Command{Argv: argv, Env: f.env(), Dir: cwd, StdoutLimitBytes: 1 << 20}, 5*time.Second) + f.keep(t, label+".stdout.log", r.Stdout) + f.keep(t, label+".stderr.log", r.Stderr) + meta, _ := json.Marshal(map[string]any{"argv": argv, "cwd": cwd, "exit": r.ExitCode, "error": err != nil, "deadline": ctx.Err() != nil, "elapsed_ms": time.Since(start).Milliseconds(), "cleanup": "full descendant supervision; forced cleanup fails"}) + f.keep(t, label+".command.json", meta) + f.check(t) + if err != nil || ctx.Err() != nil || len(r.Stdout) > 1<<20 || bytes.Contains(r.Stderr, []byte("stderr bytes omitted")) || r.ExitCode < 0 || (label != "native-attempt" && r.ExitCode != 0) { + t.Fatalf("%s child/capture/cleanup failed; see bounded evidence", label) + } + return r +} +func projectNativeCLIHosted(goos, arch, actions, environment, optin string) bool { + return goos == "linux" && arch == "amd64" && actions == "true" && environment == "github-hosted" && optin == "1" +} +func newProjectNativeCLIAttempt(t *testing.T, spec projectNativeCLISpec) *projectNativeCLIAttempt { + t.Helper() + started := time.Now() + if !projectNativeCLIHosted(runtime.GOOS, runtime.GOARCH, os.Getenv("GITHUB_ACTIONS"), os.Getenv("RUNNER_ENVIRONMENT"), os.Getenv("AGENTPLUGINS_NATIVE_DISPOSABLE_HOSTED")) { + t.Fatal("native CLI attempts require actual opted-in GitHub-hosted Linux amd64") + } + f := &projectNativeCLIAttempt{spec: spec, started: started, files: map[string][]byte{}, immutable: map[string]string{}, protected: map[string]string{}, used: map[string]bool{}, evidence: map[string]any{"client": spec.client, "version": spec.version, "runtime_attempt_complete": false, "activation": "prepared", "vendor_authentication_qualified": false, "real_model_qualified": false, "qualification": map[string]bool{"project-lifecycle": false, "native-restart-removal": false, "mcp-initialize": false, "mcp-tools-list": false, "mcp-tools-call": false}}} + f.ctx, f.cancel = context.WithDeadline(context.Background(), started.Add(150*time.Second)) + f.output = os.Getenv("AGENTPLUGINS_PROJECT_NATIVE_OUTPUT") + if err := projectNativeRuntimeParents(filepath.Join(f.output, "leaf")); err != nil { + t.Fatal(err) + } + entries, err := os.ReadDir(f.output) + if err != nil || len(entries) != 0 { + t.Fatal("existing empty closed output directory required") + } + f.client = nativeBinary(t, spec.binEnv) + f.installer = nativeBinary(t, "AGENTPLUGINS_INSTALLER_BIN") + f.git, err = exec.LookPath("git") + if err != nil { + t.Fatal(err) + } + f.git, err = filepath.EvalSymlinks(f.git) + if err != nil { + t.Fatal(err) + } + raw, err := projectNativeRuntimeRead(os.Getenv("AGENTPLUGINS_PROJECT_NATIVE_IDENTITY"), 1<<20) + if err != nil { + t.Fatal(err) + } + if _, err := projectNativeRuntimeJSON(raw); err != nil { + t.Fatal(err) + } + var identity struct { + Client string `json:"client"` + Acquisition struct { + SHA string `json:"sha256"` + Pin bool `json:"pin_verified"` + Members []struct { + Name, Kind string + Bytes int64 + SHA string `json:"sha256"` + } `json:"members"` + } `json:"acquisition"` + Node json.RawMessage `json:"node"` + RuntimeFiles []struct { + Path string `json:"path"` + SHA string `json:"sha256"` + } `json:"runtime_files"` + } + if json.Unmarshal(raw, &identity) != nil || identity.Client != spec.client || !identity.Acquisition.Pin || identity.Acquisition.SHA != spec.archiveSHA || len(identity.Node) != 0 || len(identity.Acquisition.Members) > 4096 || len(identity.RuntimeFiles) == 0 || len(identity.RuntimeFiles) > 4096 { + t.Fatal("fixed CLI acquisition receipt differs") + } + suffix := filepath.FromSlash(spec.member) + if !strings.HasSuffix(f.client, string(filepath.Separator)+suffix) { + t.Fatal("exact native entrypoint suffix differs") + } + root := strings.TrimSuffix(f.client, suffix) + expected := map[string]string{} + main := false + for _, member := range identity.Acquisition.Members { + if member.Kind == "directory" { + continue + } + if member.Kind != "file" || member.Name == "" || member.Name == "." || member.Name == ".." || strings.ContainsAny(member.Name, "\\:\x00") || filepath.ToSlash(filepath.Clean(member.Name)) != member.Name || filepath.IsAbs(member.Name) || strings.HasPrefix(member.Name, "../") || len(member.SHA) != 64 || member.Bytes < 0 || member.Bytes > 512<<20 { + t.Fatal("native archive member refused") + } + path := filepath.Join(root, filepath.FromSlash(member.Name)) + if expected[path] != "" { + t.Fatal("duplicate native archive member") + } + expected[path] = member.SHA + if member.Name == spec.member { + if member.SHA != spec.executableSHA || member.Bytes != spec.executableBytes { + t.Fatal("exact native executable pin differs") + } + main = true + } + } + if !main || len(expected) != len(identity.RuntimeFiles) { + t.Fatal("complete regular runtime closure required") + } + for _, file := range identity.RuntimeFiles { + if expected[file.Path] == "" || expected[file.Path] != file.SHA || f.immutable[file.Path] != "" { + t.Fatal("runtime closure path/hash differs") + } + f.immutable[file.Path] = file.SHA + } + for _, path := range []string{f.installer, f.git, nativeBinary(t, "AGENTPLUGINS_NATIVE_PROBE_BIN"), nativeBinary(t, "AGENTPLUGINS_LINTAI_BIN")} { + digest, err := projectNativeRuntimeDigest(path) + if err != nil { + t.Fatal(err) + } + f.immutable[path] = digest + } + source, err := nativeSourceIdentity(os.Getenv("AGENTPLUGINS_INSTALLER_COMMIT"), os.Getenv("AGENTPLUGINS_INSTALLER_TREE"), os.Getenv("AGENTPLUGINS_INSTALLER_PATCH_SHA256")) + if err != nil { + t.Fatal(err) + } + f.evidence["source_identity"], f.evidence["acquisition"] = source, identity + f.check(t) + f.base = newNativeFixture(t, false) + f.other = filepath.Join(f.base.Root, "project-b") + f.outside = filepath.Join(f.base.Root, "outside") + for _, path := range []string{f.other, f.outside} { + if err := os.Mkdir(path, 0700); err != nil { + t.Fatal(err) + } + } + f.bin = filepath.Dir(f.client) + if spec.client == "qoder" { + f.bin = filepath.Join(f.base.Root, "native-bin") + if err := os.Mkdir(f.bin, 0700); err != nil { + t.Fatal(err) + } + if err := os.Symlink(f.client, filepath.Join(f.bin, "qoder")); err != nil { + t.Fatal(err) + } + } + f.base.ClientBinDir = f.bin + f.immutable[f.base.Probe] = nativeSHA(t, f.base.Probe) + f.evidence["scanner"] = nativeProvisionScanner(t, f.base) + marker := filepath.Join(f.base.Home, "original-user-marker.txt") + projectNativeRuntimeWrite(t, marker, []byte("original independent user authority\n"), 0600) + f.protected[marker] = nativeSHA(t, marker) + if relative, body := projectNativeCLIUpdater(spec.client); relative != "" { + base := f.base.Home + if spec.client == "windsurf" { + base = filepath.Join(f.base.Root, "xdg-config") + } + path := filepath.Join(base, filepath.FromSlash(relative)) + projectNativeRuntimeWrite(t, path, body, 0600) + f.protected[path] = nativeSHA(t, path) + f.evidence["fixture_authored_updater_config"] = path + } + return f +} +func (f *projectNativeCLIAttempt) setup(t *testing.T) { + t.Helper() + for _, project := range []struct{ label, path string }{{"a", f.base.Project}, {"b", f.other}} { + r := f.command(t, "git-"+project.label, project.path, []string{f.git, "-c", "core.hooksPath=/dev/null", "init", "--quiet"}, 30*time.Second) + if len(r.Stdout) != 0 || len(r.Stderr) != 0 { + t.Fatal("real Git project setup diagnostics") + } + } + r := f.command(t, "version", f.outside, []string{f.client, "--version"}, 30*time.Second) + if string(r.Stdout) != f.spec.versionOutput || len(r.Stderr) != 0 { + t.Fatal("exact frozen native version differs") + } + f.name = "uap-" + f.spec.client + "-attempt" + manifest, _ := json.Marshal(map[string]any{"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": f.name, "version": "1.0.0", "description": "Disposable native project startup observation"}) + projectNativeRuntimeWrite(t, filepath.Join(f.base.Package, "plugin.json"), manifest, 0600) + skill := []byte("---\nname: " + f.name + "\ndescription: Harmless installed startup marker\n---\nUAP_NATIVE_INSTALLED_" + f.base.Nonce + "\n") + projectNativeRuntimeWrite(t, filepath.Join(f.base.Package, "skills", f.name, "SKILL.md"), skill, 0600) + if f.spec.mixed { + probe, err := projectNativeRuntimeRead(f.base.Probe, 32<<20) + if err != nil { + t.Fatal(err) + } + projectNativeRuntimeWrite(t, filepath.Join(f.base.Package, "bin", "probe"), probe, 0700) + server := map[string]any{"type": "stdio", "command": "./bin/probe", "args": []string{"Owned " + f.spec.client, "${PLUGIN_ROOT}/argument"}, "cwd": "${PLUGIN_ROOT}", "env": map[string]string{"UAP_TEST_ROOT": f.base.Root, "UAP_TEST_EVENTS": f.base.Events, "UAP_TEST_NONCE": f.base.Nonce, "UAP_TEST_REVISION": "a-one"}} + mcp, _ := json.Marshal(map[string]any{"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", "mcpServers": map[string]any{f.name: server}}) + projectNativeRuntimeWrite(t, filepath.Join(f.base.Package, "mcp.json"), mcp, 0600) + } + f.evidence["attempt_stage"] = "public-project-install" + r = f.command(t, "add-a", f.base.Project, []string{f.installer, "add", f.base.Package, "--no-save", "--scope", "project", "--project-root", f.base.Project, "--target", f.spec.client, "--format", "json"}, 30*time.Second) + if _, err := projectNativeRuntimeJSON(r.Stdout); err != nil { + t.Fatal(err) + } + if err := nativeValidateInstallerJSON(r.Stdout); err != nil { + t.Fatal(err) + } + raw, err := projectNativeRuntimeRead(filepath.Join(f.base.Project, ".uap", "state-v2.json"), 1<<20) + if err != nil { + t.Fatal(err) + } + if _, err := projectNativeRuntimeJSON(raw); err != nil { + t.Fatal(err) + } + var state domain.StateFileV2 + if json.Unmarshal(raw, &state) != nil || len(state.Installations) != 1 { + t.Fatal("exact public project installation missing") + } + installation := state.Installations[0] + if installation.DeclaredName != f.name || installation.Source.CanonicalSource != f.base.Package || installation.Source.SourceBindingID == "" || installation.Source.TreeDigest == "" || installation.Package.DeclaredName != f.name || installation.Package.Version != "1.0.0" || installation.Package.ManifestDigest == "" || len(installation.Clients) != 1 { + t.Fatal("installed source/package/receipt identity differs") + } + for _, binding := range installation.Clients { + if binding.ClientID != f.spec.client || binding.Scope != "project" || binding.Activation != domain.ActivationPrepared || binding.PhysicalArtifact == "" || binding.TargetLocator == "" { + t.Fatal("Prepared project binding differs") + } + f.binding = binding + } + b := f.binding + wantVerification := domain.VerificationInstalled + if f.spec.client == "grok" { + wantVerification = domain.VerificationPackageValid + } + if b.Verification != wantVerification { + t.Fatal("exact Prepared binding verification differs") + } + profile := filepath.Join(f.base.Project, "."+f.spec.client) + if f.spec.client == "windsurf" { + profile = filepath.Join(f.base.Project, ".devin") + } + if f.spec.client == "qoder" { + profile = filepath.Join(f.base.Project, ".uap", "managed", "clients", "qoder") + } + if b.NativeProfileRoot != profile { + t.Fatal("original profile versus selected project differs") + } + active := filepath.Join(f.base.Project, ".uap", "managed", "clients", f.spec.client, b.PhysicalArtifact) + if f.spec.client == "grok" { + active = filepath.Join(profile, "plugins", b.PhysicalArtifact) + } + if b.TargetLocator != active { + t.Fatal("exact receipt package locator differs") + } + f.skill = filepath.Join(profile, "skills", f.name, "SKILL.md") + if f.spec.client == "qoder" { + f.view = filepath.Join(active, ".agentplugins-qoder-native") + f.skill = filepath.Join(f.view, "skills", f.name, "SKILL.md") + if err := projectNativeCLIClosedView(f.view, f.name, skill); err != nil { + t.Fatal(err) + } + } + if f.spec.client == "grok" { + f.skill = filepath.Join(active, "skills", f.name, "SKILL.md") + } + delivered, err := projectNativeRuntimeRead(f.skill, 1<<20) + if err != nil || !bytes.Equal(delivered, skill) { + t.Fatal("receipt-selected delivered skill differs from independent source") + } + f.protected[f.skill] = nativeSHA(t, f.skill) + packageDigest, err := projectNativeCLIPackageDigest(f.ctx, active) + if err != nil { + t.Fatal(err) + } + expectedOwned := map[string]domain.NativeObjectOwnership{ + "managed_package_directory": {ObjectID: "package:" + f.spec.client + ":" + b.PhysicalArtifact, Kind: "managed_package_directory", LogicalName: f.name, Path: active, ManagedDigest: packageDigest}, + } + if f.spec.mixed { + kind := "kiro_global_skill_directory" + id := "kiro-skill:" + f.name + if f.spec.client == "windsurf" { + kind = "devin_project_skill_directory" + id = kind + ":" + f.name + } + digest := sha256.Sum256(append(append([]byte("f\x00SKILL.md\x00-\x00"), skill...), 0)) + expectedOwned[kind] = domain.NativeObjectOwnership{ObjectID: id, Kind: kind, LogicalName: f.name, Path: filepath.Dir(f.skill), ManagedDigest: "sha256:" + hex.EncodeToString(digest[:])} + } + f.evidence["installation_source"], f.evidence["binding"], f.evidence["installed_skill"] = installation.Source, b, f.skill + if f.spec.mixed { + data := installation.DataReceipts[b.DataReceiptID] + if data.DataReceiptID != b.DataReceiptID || data.State != domain.DataReceiptOwned || data.Scope != "project" || data.PhysicalBackend != b.PhysicalArtifact || data.OwnershipDigest == "" { + t.Fatal("exact owned project data receipt differs") + } + nativeRequireContained(t, filepath.Join(f.base.Project, ".uap", "plugin-data"), data.Locator) + f.data = data.Locator + config := filepath.Join(profile, "settings", "mcp.json") + if f.spec.client == "windsurf" { + config = filepath.Join(profile, "mcp_config.json") + } + raw, err := projectNativeRuntimeRead(config, 1<<20) + if err != nil { + t.Fatal(err) + } + value, err := projectNativeRuntimeJSON(raw) + if err != nil { + t.Fatal(err) + } + document, ok := value.(map[string]any) + if !ok { + t.Fatal("native MCP object missing") + } + entries, ok := document["mcpServers"].(map[string]any) + if !ok { + t.Fatal("native MCP collection missing") + } + entry, ok := entries[f.name].(map[string]any) + if !ok { + t.Fatal("exact installed native server missing") + } + expectedArgs := []any{"--internal-stdio-v1", active, f.data, active, "plugin", "--", "./bin/probe", "Owned " + f.spec.client, active + "/argument"} + expectedEnv := map[string]any{"PLUGIN_ROOT": active, "PLUGIN_DATA": f.data, "UAP_TEST_ROOT": f.base.Root, "UAP_TEST_EVENTS": f.base.Events, "UAP_TEST_NONCE": f.base.Nonce, "UAP_TEST_REVISION": "a-one"} + if err := projectNativeCLIMixedEntry(entry, filepath.Join(active, filepath.FromSlash(managedstdio.RelativeDirectory), managedstdio.ExecutableName), expectedArgs, expectedEnv); err != nil { + t.Fatal(err) + } + mcpKind, mcpID := "kiro_global_mcp_server", "kiro-mcp:"+f.name + canonical, _ := json.Marshal(entry) + sum := sha256.Sum256(canonical) + mcpDigest := "sha256:" + hex.EncodeToString(sum[:]) + if f.spec.client == "windsurf" { + mcpKind = "devin_project_mcp_server" + mcpID = mcpKind + ":" + f.name + args := make([]string, len(expectedArgs)) + for i, value := range expectedArgs { + args[i] = value.(string) + } + env := map[string]string{} + for key, value := range expectedEnv { + env[key] = value.(string) + } + receipt, err := nativeconfig.DesiredReceipt(config, nativeconfig.CodecDevin, f.name, nativeconfig.Server{Type: "stdio", Command: entry["command"].(string), Args: args, Env: env, StdioValuesResolved: true, CWDResolved: true}, nativeconfig.Placeholders{}) + if err != nil { + t.Fatal(err) + } + mcpDigest = receipt.Digest + } + expectedOwned[mcpKind] = domain.NativeObjectOwnership{ObjectID: mcpID, Kind: mcpKind, LogicalName: f.name, Path: config, ManagedDigest: mcpDigest} + f.protected[config] = nativeSHA(t, config) + f.evidence["installed_mcp"] = entry + f.evidence["data_locator"] = f.data + for _, path := range []string{filepath.Join(active, "bin", "probe"), filepath.Join(active, filepath.FromSlash(managedstdio.RelativeDirectory), managedstdio.ExecutableName)} { + digest, err := projectNativeRuntimeDigest(path) + if err != nil { + t.Fatal(err) + } + f.immutable[path] = digest + } + } + if err := projectNativeCLIOwnership(b.NativeObjects, expectedOwned); err != nil { + t.Fatal(err) + } + f.check(t) +} +func projectNativeCLIMixedEntry(actual map[string]any, command string, args []any, env map[string]any) error { + if projectNativeRuntimeKeys(actual, "command args env", "") != nil || actual["command"] != command || !reflect.DeepEqual(actual["args"], args) || !reflect.DeepEqual(actual["env"], env) { + return fmt.Errorf("native stdio command/args/env differs or includes an unqualified transport/cwd/field") + } + return nil +} +func projectNativeCLIOwnership(actual []domain.NativeObjectOwnership, expected map[string]domain.NativeObjectOwnership) error { + if len(actual) != len(expected) { + return fmt.Errorf("installed native ownership count differs") + } + seen := map[string]bool{} + for _, object := range actual { + want, ok := expected[object.Kind] + if !ok || seen[object.Kind] || object.ObjectID != want.ObjectID || object.LogicalName != want.LogicalName || object.Path != want.Path || object.ManagedDigest == "" || object.ManagedDigest != want.ManagedDigest { + return fmt.Errorf("installed native ownership identity/path/digest differs") + } + seen[object.Kind] = true + } + return nil +} +func projectNativeCLIPackageDigest(ctx context.Context, root string) (string, error) { + if err := projectNativeRuntimeParents(filepath.Join(root, "leaf")); err != nil { + return "", err + } + captured := []packagedigest.CapturedEntry{} + var total int64 + count := 0 + err := filepath.WalkDir(root, func(path string, entry os.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + if err := ctx.Err(); err != nil { + return err + } + count++ + if count > 4096 { + return fmt.Errorf("owned package entry bound") + } + rel, err := filepath.Rel(root, path) + if err != nil { + return err + } + if len(strings.Split(rel, string(filepath.Separator))) > 32 { + return fmt.Errorf("owned package depth bound") + } + if entry.IsDir() { + if err := projectNativeRuntimeParents(filepath.Join(path, "leaf")); err != nil { + return err + } + if rel != "." { + captured = append(captured, packagedigest.CapturedEntry{Path: filepath.ToSlash(rel), Kind: "directory"}) + } + return nil + } + before, err := os.Lstat(path) + if err != nil { + return err + } + total += before.Size() + if total > 256<<20 { + return fmt.Errorf("owned package byte bound") + } + body, err := projectNativeRuntimeRead(path, 64<<20) + if err != nil { + return err + } + captured = append(captured, packagedigest.CapturedEntry{Path: filepath.ToSlash(rel), Kind: "file", Executable: before.Mode().Perm()&0111 != 0, Content: body}) + return nil + }) + if err != nil { + return "", err + } + return projectNativeRuntimeInstalledDigest(ctx, captured) +} +func projectNativeCLIClosedView(root, name string, skill []byte) error { + expected := map[string]bool{".": true, ".qoder-plugin": true, ".qoder-plugin/plugin.json": true, "skills": true, "skills/" + name: true, "skills/" + name + "/SKILL.md": true} + seen := map[string]bool{} + if err := projectNativeRuntimeParents(filepath.Join(root, "leaf")); err != nil { + return err + } + err := filepath.WalkDir(root, func(path string, entry os.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + rel, err := filepath.Rel(root, path) + if err != nil { + return err + } + rel = filepath.ToSlash(rel) + if !expected[rel] || seen[rel] { + return fmt.Errorf("Qoder view contains an unqualified native path") + } + seen[rel] = true + if entry.IsDir() { + return projectNativeRuntimeParents(filepath.Join(path, "leaf")) + } + body, err := projectNativeRuntimeRead(path, 1<<20) + if err != nil { + return err + } + if rel == ".qoder-plugin/plugin.json" { + value, err := projectNativeRuntimeJSON(body) + if err != nil { + return err + } + m, ok := value.(map[string]any) + if !ok || projectNativeRuntimeKeys(m, "name version description skills", "") != nil || m["name"] != name || m["version"] != "1.0.0" || m["skills"] != "./skills/" || m["description"] != "Disposable native project startup observation" { + return fmt.Errorf("Qoder view manifest differs from selected source") + } + } else if !bytes.Equal(body, skill) { + return fmt.Errorf("Qoder selected view skill differs") + } + return nil + }) + if err != nil { + return fmt.Errorf("closed receipt-selected Qoder native view failed: %w", err) + } + if !reflect.DeepEqual(seen, expected) { + return fmt.Errorf("closed receipt-selected Qoder native view membership differs") + } + return nil +} +func (f *projectNativeCLIAttempt) finish(t *testing.T) { + t.Helper() + defer f.cancel() + if f.grok != nil { + f.grok.finish(t, f) + } + if time.Since(f.started) > 180*time.Second { + t.Error("original CLI attempt total deadline exceeded") + } + f.evidence["test_failed"], f.evidence["elapsed_ms"] = t.Failed(), time.Since(f.started).Milliseconds() + if f.spec.client == "grok" && f.evidence["capture_mode"] == "grok-complete-skills-cycle" { + complete := !t.Failed() && f.ctx.Err() == nil && f.evidence["cycle_observed_complete"] == true + f.evidence["runtime_attempt_complete"] = complete + f.evidence["qualification"] = map[string]bool{"project-lifecycle": complete, "native-restart-removal": complete, "mcp-initialize": false, "mcp-tools-list": false, "mcp-tools-call": false} + } + if f.base != nil { + if body, err := projectNativeRuntimeRead(f.base.Events, 1<<20); err == nil { + f.keep(t, "probe-events.jsonl", body) + } + } + raw, _ := json.MarshalIndent(f.evidence, "", " ") + f.keep(t, "runtime-attempt.json", append(raw, '\n')) + if f.grok != nil || f.spec.client == "grok" && f.evidence["capture_mode"] == "grok-complete-skills-cycle" { + for _, body := range f.files { + if grokProjectPrivateCredential(body) { + t.Error("unredacted Grok fixture credential refused") + return + } + } + } + file, err := os.OpenFile(filepath.Join(f.output, "research.zip"), os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600) + if err != nil { + t.Error(err) + return + } + archive := zip.NewWriter(file) + names := make([]string, 0, len(f.files)) + for name := range f.files { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + entry, err := archive.Create(name) + if err != nil { + t.Error(err) + break + } + if _, err := entry.Write(f.files[name]); err != nil { + t.Error(err) + break + } + } + if err := archive.Close(); err != nil { + t.Error(err) + } + if err := file.Close(); err != nil { + t.Error(err) + } +} +func projectNativeCLIRuntimeAttempt(t *testing.T, client string) { + t.Helper() + spec, err := projectNativeCLISpecification(client) + if err != nil { + t.Fatal(err) + } + if os.Getenv(spec.optin) != "1" { + t.Skip("opt-in genuine installed native startup research") + } + f := newProjectNativeCLIAttempt(t, spec) + defer f.finish(t) + if client == "grok" { + // finish owns cleanup before any listener or native child can start. + f.configureGrokTitleProvider(t) + } + f.setup(t) + if client == "grok" { + f.inspectGrokSelected(t) + } + prompt := "Describe only the installed " + f.name + " skill marker. Do not run any tool or change any file." + argv, err := projectNativeCLIArguments(client, f.client, f.view, prompt) + if err != nil { + t.Fatal(err) + } + f.evidence["attempt_stage"] = "native-installed-startup" + result := f.command(t, "native-attempt", f.base.Project, argv, 60*time.Second) + f.evidence["native_exit"], f.evidence["native_output_captured"] = result.ExitCode, true + // Even zero exit is not proof of plugin selection, permissions or model use. + // Raw genuine output must freeze an exact supported protocol/auth boundary. + t.Fatal("genuine installed native startup captured; exact trust/authentication/session boundary requires independent raw review; runtime remains unqualified") +} diff --git a/repotests/agentplugins_project_native_cli_contract_test.go b/repotests/agentplugins_project_native_cli_contract_test.go new file mode 100644 index 000000000..d0fd15683 --- /dev/null +++ b/repotests/agentplugins_project_native_cli_contract_test.go @@ -0,0 +1,224 @@ +//go:build linux + +package pluginkitairepo_test + +import ( + "bytes" + "context" + "encoding/json" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + "time" + + "github.com/777genius/plugin-kit-ai/install/integrationctl/agentplugins/domain" + "github.com/777genius/plugin-kit-ai/install/integrationctl/packagesnapshot" +) + +func TestProjectNativeCLIAttemptRetainsRealHostedBoundary(t *testing.T) { + if !projectNativeCLIHosted("linux", "amd64", "true", "github-hosted", "1") { + t.Fatal("genuine hosted candidate rejected") + } + for _, input := range [][5]string{{"linux", "amd64", "", "", "1"}, {"linux", "amd64", "true", "self-hosted", "1"}, {"linux", "amd64", "false", "github-hosted", "1"}, {"linux", "arm64", "true", "github-hosted", "1"}, {"darwin", "amd64", "true", "github-hosted", "1"}, {"linux", "amd64", "true", "github-hosted", "0"}} { + if projectNativeCLIHosted(input[0], input[1], input[2], input[3], input[4]) { + t.Fatalf("inert unsafe boundary admitted: %v", input) + } + } +} +func TestProjectNativeCLIAttemptToolRestrictionsCannotBecomeApproval(t *testing.T) { + for _, client := range []string{"qoder", "grok", "kiro", "windsurf"} { + spec, err := projectNativeCLISpecification(client) + if err != nil || spec.client != client || len(spec.archiveSHA) != 64 || len(spec.executableSHA) != 64 { + t.Fatal("finite client identity missing") + } + argv, err := projectNativeCLIArguments(client, "/owned/native", "/owned/receipt/.agentplugins-qoder-native", "owned prompt") + if err != nil { + t.Fatal(err) + } + joined := strings.Join(argv, "\x00") + for _, forbidden := range []string{"--always-approve", "--trust-all-tools", "--trust-tools", "--dangerously-skip-permissions", "bypass_permissions", "bypassPermissions", "--respect-workspace-trust\x00false", "--permission-mode\x00auto"} { + if strings.Contains(joined, forbidden) { + t.Fatalf("%s gained approval: %v", client, argv) + } + } + switch client { + case "qoder": + if !strings.Contains(joined, "--tools\x00\x00--plugin-dir\x00/owned/receipt/.agentplugins-qoder-native") { + t.Fatal("Qoder measured empty tool restriction absent") + } + case "grok": + if strings.Contains(joined, "--tools") || !strings.Contains(joined, "--deny\x00\x00--no-subagents") || !strings.HasSuffix(joined, "--max-turns\x001\x00--model\x00uap-grok-fixture") { + t.Fatal("Grok empty allowlist bug or missing unconditional deny") + } + case "kiro": + if !reflect.DeepEqual(argv, []string{"/owned/native", "chat", "--v2", "--no-interactive", "owned prompt"}) { + t.Fatal("Kiro gained unreviewed mode") + } + case "windsurf": + if !strings.Contains(joined, "--respect-workspace-trust\x00true") { + t.Fatal("native trust wall omitted") + } + } + } + if _, err := projectNativeCLISpecification("cursor"); err == nil { + t.Fatal("403-blocked Cursor silently entered runtime table") + } + if _, err := projectNativeCLIArguments("unknown", "/native", "", "prompt"); err == nil { + t.Fatal("unknown client argv accepted") + } + for _, view := range []string{"", "relative/.agentplugins-qoder-native", "/owned/whole-package", "/owned/../receipt/.agentplugins-qoder-native"} { + if _, err := projectNativeCLIArguments("qoder", "/native", view, "prompt"); err == nil { + t.Fatal("nonreceipt native view accepted", view) + } + } +} +func TestProjectNativeCLIAttemptOriginalUpdaterAndDenySettingsAreFrozen(t *testing.T) { + _, qoder := projectNativeCLIUpdater("qoder") + if !bytes.Equal(qoder, []byte("{\"general\":{\"enableAutoUpdate\":false}}\n")) { + t.Fatal("Qoder updater suppression changed") + } + _, devin := projectNativeCLIUpdater("windsurf") + value, err := projectNativeRuntimeJSON(devin) + if err != nil { + t.Fatal(err) + } + object := value.(map[string]any) + permissions := object["permissions"].(map[string]any) + if object["auto_update"] != false || !reflect.DeepEqual(permissions["allow"], []any{}) || !reflect.DeepEqual(permissions["ask"], []any{}) || !reflect.DeepEqual(permissions["deny"], []any{"read", "edit", "write", "grep", "glob", "exec", "fetch", "web_fetch", "mcp__*"}) { + t.Fatal("Devin deny-only profile became an approval") + } +} +func TestProjectNativeCLIClosedQoderViewRefusesExtraNativeSurfacesAndAliases(t *testing.T) { + for _, kind := range []string{"valid", "mcp", "empty-dir", "alias", "hardlink", "wrong-manifest", "wrong-skill"} { + t.Run(kind, func(t *testing.T) { + root := filepath.Join(t.TempDir(), ".agentplugins-qoder-native") + name := "uap-qoder-attempt" + skill := []byte("inert selected skill\n") + manifest, _ := json.Marshal(map[string]any{"name": name, "version": "1.0.0", "description": "Disposable native project startup observation", "skills": "./skills/"}) + projectNativeRuntimeWrite(t, filepath.Join(root, ".qoder-plugin", "plugin.json"), manifest, 0600) + path := filepath.Join(root, "skills", name, "SKILL.md") + projectNativeRuntimeWrite(t, path, skill, 0600) + switch kind { + case "mcp": + projectNativeRuntimeWrite(t, filepath.Join(root, ".mcp.json"), []byte("{}"), 0600) + case "empty-dir": + if err := os.Mkdir(filepath.Join(root, "hooks"), 0700); err != nil { + t.Fatal(err) + } + case "alias": + if err := os.Rename(path, path+".saved"); err != nil { + t.Fatal(err) + } + if err := os.Symlink(path+".saved", path); err != nil { + t.Fatal(err) + } + case "hardlink": + if err := os.Link(path, filepath.Join(t.TempDir(), "other")); err != nil { + t.Fatal(err) + } + case "wrong-manifest": + if err := os.WriteFile(filepath.Join(root, ".qoder-plugin", "plugin.json"), []byte(`{"name":"foreign","version":"1.0.0","skills":"./skills/"}`), 0600); err != nil { + t.Fatal(err) + } + case "wrong-skill": + if err := os.WriteFile(path, []byte("foreign skill"), 0600); err != nil { + t.Fatal(err) + } + } + err := projectNativeCLIClosedView(root, name, skill) + if kind == "valid" && err != nil { + t.Fatal(err) + } + if kind != "valid" && err == nil { + t.Fatal("unsafe closed view admitted") + } + }) + } +} + +func TestProjectNativeCLICommandLabelsCannotAuthorizeDifferentArgvOrScope(t *testing.T) { + spec, _ := projectNativeCLISpecification("qoder") + f := &projectNativeCLIAttempt{spec: spec, base: &nativeFixture{Project: "/owned/A", Package: "/owned/source"}, client: "/owned/qodercli", installer: "/owned/agentplugins", git: "/usr/bin/git", outside: "/owned/outside", other: "/owned/B", view: "/owned/A/receipt/.agentplugins-qoder-native", name: "uap-qoder-attempt"} + good := []string{f.client, "--version"} + if err := f.selectedCommand("version", f.outside, good, 30*time.Second); err != nil { + t.Fatal(err) + } + for _, change := range []struct { + label, cwd string + argv []string + timeout time.Duration + }{{"version", f.base.Project, good, 30 * time.Second}, {"version", f.outside, []string{f.client, "login"}, 30 * time.Second}, {"version", f.outside, good, 60 * time.Second}, {"native-other", f.outside, good, 30 * time.Second}} { + if f.selectedCommand(change.label, change.cwd, change.argv, change.timeout) == nil { + t.Fatal("different native command/scope admitted") + } + } +} + +func TestProjectNativeCLIMixedServerRefusesExtraTransportCWDAndUnknownFields(t *testing.T) { + args := []any{"--internal-stdio-v1", "/owned/package", "/owned/data", "/owned/package", "plugin", "--", "./bin/probe"} + env := map[string]any{"PLUGIN_ROOT": "/owned/package", "PLUGIN_DATA": "/owned/data"} + good := map[string]any{"command": "/owned/package/launcher", "args": args, "env": env} + if err := projectNativeCLIMixedEntry(good, "/owned/package/launcher", args, env); err != nil { + t.Fatal(err) + } + for _, key := range []string{"cwd", "type", "transport", "disabled", "unknown"} { + wrong := map[string]any{"command": good["command"], "args": args, "env": env, key: "unqualified"} + if projectNativeCLIMixedEntry(wrong, "/owned/package/launcher", args, env) == nil { + t.Fatal("extra native field accepted", key) + } + } +} +func TestProjectNativeCLIOwnershipRequiresExactReceiptKindNamePathAndDigest(t *testing.T) { + good := domain.NativeObjectOwnership{ObjectID: "package:qoder:physical", Kind: "managed_package_directory", LogicalName: "owned", Path: "/owned/package", ManagedDigest: "sha256:recorded"} + expected := map[string]domain.NativeObjectOwnership{good.Kind: good} + if err := projectNativeCLIOwnership([]domain.NativeObjectOwnership{good}, expected); err != nil { + t.Fatal(err) + } + for _, field := range []string{"id", "kind", "name", "path", "digest", "empty-digest"} { + bad := good + switch field { + case "id": + bad.ObjectID = "package:qoder:foreign" + case "kind": + bad.Kind = "foreign" + case "name": + bad.LogicalName = "foreign" + case "path": + bad.Path = "/other/project/package" + case "digest": + bad.ManagedDigest = "sha256:other" + case "empty-digest": + bad.ManagedDigest = "" + } + if projectNativeCLIOwnership([]domain.NativeObjectOwnership{bad}, expected) == nil { + t.Fatal("wrong native ownership admitted", field) + } + } + if projectNativeCLIOwnership(nil, expected) == nil || projectNativeCLIOwnership([]domain.NativeObjectOwnership{good, good}, expected) == nil { + t.Fatal("missing/duplicate ownership admitted") + } +} +func TestProjectNativeCLIPackageDigestMatchesInstallerFramingFromHeldBytes(t *testing.T) { + root := t.TempDir() + projectNativeRuntimeWrite(t, filepath.Join(root, "plugin.json"), []byte(`{"name":"inert"}`), 0600) + projectNativeRuntimeWrite(t, filepath.Join(root, "bin", "probe"), []byte("inert executable bytes"), 0700) + got, err := projectNativeCLIPackageDigest(context.Background(), root) + if err != nil { + t.Fatal(err) + } + want, err := (packagesnapshot.Builder{}).Digest(context.Background(), root) + if err != nil { + t.Fatal(err) + } + if got != want { + t.Fatalf("held installed package digest differs from selected lifecycle framing: %s %s", got, want) + } + if err := os.Link(filepath.Join(root, "plugin.json"), filepath.Join(t.TempDir(), "alias")); err != nil { + t.Fatal(err) + } + if _, err := projectNativeCLIPackageDigest(context.Background(), root); err == nil { + t.Fatal("aliased installed package digest accepted") + } +} diff --git a/scripts/project_native_collectors/grok.py b/scripts/project_native_collectors/grok.py new file mode 100644 index 000000000..df128c1d6 --- /dev/null +++ b/scripts/project_native_collectors/grok.py @@ -0,0 +1,286 @@ +"""Measured Grok 1.0.70 acquisition and fixed native help research only. + +The shared runner owns genuine hosted admission, immutable acquisition, +resource limits and capture. The explicit observe phase adds only read-only +opaque catalogs around text-only skill fixtures; no trust, doctor, provider or +MCP connection is invoked and no native discovery qualification is inferred. +""" +import hashlib +import json +import math +import os +from pathlib import Path +import re +import stat + +import native_client_project_artifacts as artifacts + +CLIENT = "grok" +VERSION = "1.0.70" +AVAILABLE_PHASES = ("acquire", "help", "observe") +ACQUISITION = { + "url": "https://x.ai/cli/grok-1.0.70-linux-x86_64.gz", + # Genuine run 40235269281/attempt 2, artifact 12615677952; reviewed freeze + # b157c54c086a3eb17f285c6013749de59e26ed2f. No 1.0.67 identity is reused. + "integrity": "sha256:11d27a30adfb75c4b9b180072ab27412a1916f054ff5497ab70fb9a77d304bcc", + "expected_bytes": 76_760_282, + "binary_sha256": "22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9", + "max_bytes": 96 << 20, + "max_expanded_bytes": 256 << 20, + "format": "gzip", + # A gzip stream has no archive member: this is the shared runner's fixed + # synthetic output name, never an authority derived from its gzip header. + "entrypoint": "grok", + "runtime": "native", +} + +# This periodically synced September snapshot guides candidate commands; +# it is not shipping-source provenance for the measured executable. +COMMAND_SOURCE = "https://github.com/xai-org/grok-build/tree/2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8" +PROBES = ( + ("grok-version", ("--version",), None), + ("grok-help", ("--help",), ""), + ("grok-inspect-help", ("inspect", "--help"), "inspect"), + ("grok-mcp-help", ("mcp", "--help"), "mcp"), + ("grok-mcp-list-help", ("mcp", "list", "--help"), "mcp list"), + ("grok-mcp-doctor-help", ("mcp", "doctor", "--help"), "mcp doctor"), + ("grok-plugin-help", ("plugin", "--help"), "plugin"), + ("grok-plugin-list-help", ("plugin", "list", "--help"), "plugin list"), + ("grok-doctor-help", ("doctor", "--help"), "doctor"), +) + + +def _streams(record, stdout, stderr): + if (not isinstance(record, dict) or type(record.get("exit")) is not int + or record["exit"] != 0 or "error" not in record + or record["error"] is not None): + raise ValueError("Grok fixed help probe failed its bounded capture") + if not isinstance(stdout, bytes) or not isinstance(stderr, bytes) or stderr: + raise ValueError("Grok fixed help probe has unexpected stream data") + try: + return stdout.decode("utf-8", "strict") + except UnicodeError: + raise ValueError("Grok fixed help probe is not UTF-8") from None + + +def _version(stdout): + match = re.fullmatch(rb"grok 1\.0\.70 \(([0-9a-f]{7,40})\)( \[stable\])?\n", stdout) + if not match: + raise ValueError("Grok version differs from the frozen 1.0.70 grammar") + return {"version": VERSION, "compiled_commit_observed": match[1].decode("ascii"), + "channel_observed": "stable" if match[2] else None, + "shipping_source_equivalence_claimed": False} + + +def _help(text, command): + name = "grok" + (" " + command if command else "") + # Complete Clap metavariables only, including a variadic suffix. A parent + # is legitimate; different commands or unclosed delimiters are not. + metavar = r"(?:\[[A-Za-z0-9_.:|= /-]+\]|<[A-Za-z0-9_.:|= /-]+>)(?:\.\.\.)?" + usage = re.findall(r"^Usage: ([^\r\n]+)$", text, re.MULTILINE) + if (len(usage) != 1 or not re.fullmatch(re.escape(name) + + r"(?:[ \t]+" + metavar + r")*[ \t]*", usage[0])): + raise ValueError("Grok help Usage does not identify the requested command") + if not re.search(r"^[ \t]+(?:-h,[ \t]+)?--help(?:[ \t]+[^\r\n]*)?$", text, re.MULTILINE): + raise ValueError("Grok fixed command help is missing its help option") + if not command: + if not re.search(r"^Commands:$", text, re.MULTILINE) or any( + not re.search(r"^[ \t]{2,}" + word + r"(?:[ \t]+[^\r\n]*)?$", text, re.MULTILINE) + for word in ("inspect", "mcp", "plugin")): + raise ValueError("Grok main help is missing the selected command surface") + + + +UPDATER_ENV = {"GROK_DISABLE_AUTOUPDATER": "1"} +CATALOGS = (("inspect", ("inspect", "--json")), + ("plugins", ("plugin", "list", "--json")), + ("mcp", ("mcp", "list", "--json"))) +NAMES = {scope: "uap-grok-research-" + scope for scope in ("user", "a", "b")} + + +def _json(body): + if not isinstance(body, bytes) or not 0 < len(body) <= 1 << 20: + raise ValueError("Grok opaque JSON size/type refused") + + def unique(pairs): + result = {} + for key, value in pairs: + if key in result: + raise ValueError("Grok duplicate JSON key") + result[key] = value + return result + + def constant(value): + raise ValueError("Grok nonfinite JSON number: " + value) + + try: + result = json.loads(body.decode("utf-8", "strict"), object_pairs_hook=unique, parse_constant=constant) + if not isinstance(result, (dict, list)): + raise ValueError("Grok opaque catalog must be a JSON object or list") + pending, count = [(result, 0)], 0 + while pending: + value, depth = pending.pop() + count += 1 + if count > 8192 or depth > 32: + raise ValueError("Grok opaque JSON structure limit") + if isinstance(value, dict): + pending.extend((entry, depth + 1) for pair in value.items() for entry in pair) + elif isinstance(value, list): + pending.extend((entry, depth + 1) for entry in value) + elif isinstance(value, float) and not math.isfinite(value): + raise ValueError("Grok nonfinite JSON number") + return result + except (UnicodeError, RecursionError) as error: + raise ValueError("Grok opaque catalog is malformed or too deep") from error + + +def _directory(root, parts, create=False): + # Every component below the fixed fresh root is opened descriptor-relative; + # no authored path is followed through an alias, including during creation. + descriptor = os.open(root, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW) + try: + if not artifacts.same_opened(root.lstat(), os.fstat(descriptor)): + raise ValueError("Grok fixture root identity differs") + for part in parts: + if create: + try: + os.mkdir(part, 0o700, dir_fd=descriptor) + except FileExistsError: + pass + child = os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=descriptor) + os.close(descriptor) + descriptor = child + if os.fstat(descriptor).st_uid != os.getuid(): + raise ValueError("Grok fixture directory owner differs") + return descriptor + except BaseException: + os.close(descriptor) + raise + + +def _read_owned(root, relative, expected, original=None): + parent = _directory(root, relative.parts[:-1]) + try: + before = os.stat(relative.name, dir_fd=parent, follow_symlinks=False) + if (not stat.S_ISREG(before.st_mode) or before.st_nlink != 1 + or before.st_uid != os.getuid() or stat.S_IMODE(before.st_mode) != 0o600 + or before.st_size != len(expected) or before.st_size > 8192 + or (original is not None and artifacts.identity(before) != original)): + raise ValueError("Grok authored fixture type/size/mode differs") + descriptor = os.open(relative.name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=parent) + try: + held = os.fstat(descriptor) + if not artifacts.same_opened(before, held): + raise ValueError("Grok authored fixture opened identity differs") + body = os.read(descriptor, len(expected) + 1) + if body != expected or artifacts.identity(os.fstat(descriptor)) != artifacts.identity(held): + raise ValueError("Grok authored fixture bytes/held identity differs") + finally: + os.close(descriptor) + if artifacts.identity(os.stat(relative.name, dir_fd=parent, follow_symlinks=False)) != artifacts.identity(before): + raise ValueError("Grok authored fixture path identity differs") + # Reopen the namespace after the held read to reject replaced parents. + current = _directory(root, relative.parts[:-1]) + try: + left, right = os.fstat(parent), os.fstat(current) + if (left.st_dev, left.st_ino) != (right.st_dev, right.st_ino): + raise ValueError("Grok authored fixture parent replaced") + finally: + os.close(current) + return artifacts.identity(before) + finally: + os.close(parent) + + +def _fixture(root, scope): + name = NAMES[scope] + plugin = Path(".grok/plugins") / name + manifest = {"name": name, "version": "1.0.0", "description": "Inert native scope research", "skills": "./skills/"} + files = {plugin / ".grok-plugin/plugin.json": (json.dumps(manifest, sort_keys=True) + "\n").encode(), + plugin / "skills" / name / "SKILL.md": ( + "---\nname: " + name + "\ndescription: Inert native scope research marker\n---\n\nText-only marker for " + scope + ".\n").encode()} + owned = [] + for relative, body in files.items(): + parent = _directory(root, relative.parts[:-1], create=True) + try: + descriptor = os.open(relative.name, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, + 0o600, dir_fd=parent) + try: + if os.write(descriptor, body) != len(body): + raise ValueError("Grok authored fixture short write") + finally: + os.close(descriptor) + finally: + os.close(parent) + original = _read_owned(root, relative, body) + owned.append((root, relative, body, original)) + return owned + + +def _observe(ctx): + root, home = Path(ctx.root), Path(ctx.home) + projects = {key: Path(ctx.projects[key]) for key in ("a", "b", "user")} + roots = [home, *projects.values()] + if (not root.is_absolute() or root.resolve() != root or len(set(roots)) != 4 + or any(path.resolve() != path or not path.is_relative_to(root) or path == root for path in roots) + or any(left.is_relative_to(right) for left in roots for right in roots if left != right)): + raise ValueError("Grok requires separate real projects and original home") + owned, rows = [], [] + + def catalog(stage, project, kind, arguments): + for folder, relative, body, original in owned: + _read_owned(folder, relative, body, original) + record, stdout, stderr = ctx.probe("grok-" + stage + "-" + project + "-" + kind, + list(arguments), project=project, extra_env=UPDATER_ENV, timeout=30) + text = _streams(record, stdout, stderr) + value = _json(stdout) + for folder, relative, body, original in owned: + _read_owned(folder, relative, body, original) + rows.append({"stage": stage, "cwd_selection": project, "catalog": kind, + "root_kind": "object" if isinstance(value, dict) else "list", + "stdout_sha256": hashlib.sha256(stdout).hexdigest(), + "marker_tokens": [name for name in NAMES.values() if name in text], + "interpretation": "opaque native catalog; no source schema or loading qualification"}) + + for project in ("a", "user"): + for kind, arguments in CATALOGS: + catalog("empty", project, kind, arguments) + for scope, directory in (("user", home), ("a", projects["a"]), ("b", projects["b"])): + owned.extend(_fixture(directory, scope)) + for project in ("a", "b", "user"): + for kind, arguments in CATALOGS: + catalog("populated", project, kind, arguments) + return {"catalogs": rows, "protected_files": [ + {"root": str(folder), "path": str(relative), "sha256": hashlib.sha256(body).hexdigest()} + for folder, relative, body, _original in owned], + "scope_discovery_qualified": False, "uap_lifecycle_qualified": False, + "mcp_initialization_qualified": False, "mcp_tools_list_qualified": False, + "mcp_tools_call_qualified": False, "authentication_qualified": False, "real_model_qualified": False, + "remaining": "Freeze observed native schemas before any source-selection, trust or lifecycle claim"} + + +def collect(ctx): + """Run fixed help and only the explicitly selected opaque research phase.""" + if ctx.phase not in ("help", "observe"): + raise ValueError("Grok collector requires the explicit help or observe phase") + observations = {} + for label, arguments, command in PROBES: + record, stdout, stderr = ctx.probe(label, list(arguments), project="a", extra_env=UPDATER_ENV, timeout=30) + text = _streams(record, stdout, stderr) + row = {"surface": "version" if command is None else "help", "arguments": list(arguments)} + if command is None: + row.update(_version(stdout)) + else: + _help(text, command) + observations[label] = row + cases = [{"id": "grok.help", "status": "passed", + "reason": "Measured pinned Grok 1.0.70 fixed version/help table observed; help alone makes no discovery or runtime claim"}] + unobserved = ["grok.scope-observation", "grok.project-lifecycle", + "grok.native-restart-removal", "grok.mcp-initialize", + "grok.mcp-tools-list", "grok.mcp-tools-call"] + if ctx.phase == "observe": + observations["opaque-scope-catalogs"] = _observe(ctx) + cases.append({"id": "grok.scope-observation", "status": "passed", + "reason": "All fifteen read-only opaque JSON catalogs completed with exact text-only fixture preservation; source schema and every product qualification remain unqualified"}) + unobserved.remove("grok.scope-observation") + return {"observations": observations, "cases": cases, "unobserved": unobserved} diff --git a/scripts/project_native_grok_cycle.py b/scripts/project_native_grok_cycle.py new file mode 100644 index 000000000..287e97284 --- /dev/null +++ b/scripts/project_native_grok_cycle.py @@ -0,0 +1,508 @@ +"""Closed skills-only Grok lifecycle evidence; no filesystem access or child.""" +import base64 +from datetime import datetime +import hashlib +import json +from pathlib import PurePosixPath +import re + +MODE = "grok-complete-skills-cycle" +PUBLIC = tuple("git-a git-b git-outside version add-a add-b repeat-add-a unchanged-update-a intact-repair-a update-a missing-repair-a remove-a repeat-remove-a remove-b".split()) +INSPECT = tuple("empty-outside empty-a empty-b installed-a installed-b installed-outside stable-a updated-a updated-b updated-outside damaged-a repaired-a removed-a remaining-b removed-outside repeat-removed-a removed-b final-a final-b final-outside".split()) +PROOF = tuple("source-a-a-one source-b-b-one fixture add-a add-b repeat-add-a unchanged-update-a intact-repair-a before-source-revision-a source-a-a-two update-a before-missing-a damaged-a missing-repair-a remove-a repeat-remove-a remove-b final".split()) +MEMBERS = ("grok-cycle-public.jsonl", "grok-cycle-inspect.jsonl", "grok-cycle-proof.jsonl") +FLAGS = tuple("project-lifecycle native-restart-removal mcp-initialize mcp-tools-list mcp-tools-call".split()) +SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json" +CLEANUP = "full descendant supervision; forced cleanup fails" + + +def require(condition): + if not condition: + raise ValueError("Grok complete skills-cycle evidence join differs") + + +def load(raw): + from project_native_runtime import strict_json + return strict_json(raw) + + +def private(raw): + from project_native_runtime import grok_private_credential + return grok_private_credential(raw) + + +def same(left, right): + if type(left) is not type(right): + return False + if type(left) is dict: + return set(left) == set(right) and all(same(left[key], value) for key, value in right.items()) + if type(left) is list: + return len(left) == len(right) and all(same(a, b) for a, b in zip(left, right)) + return left == right + + +def sha(raw): + return hashlib.sha256(raw).hexdigest() + + +def compact(value): + return json.dumps(value, separators=(",", ":"), sort_keys=True, ensure_ascii=False).encode() + + +def canonical(value): + require(type(value) is str and value.startswith("/") and value != "/" and str(PurePosixPath(value)) == value + and ".." not in PurePosixPath(value).parts and "\0" not in value) + return value + + +def stream(value): + require(type(value) is dict and set(value) == {"bytes", "sha256", "body"} + and type(value["bytes"]) is int and 0 <= value["bytes"] <= 1 << 20 + and type(value["sha256"]) is str and re.fullmatch(r"[0-9a-f]{64}", value["sha256"]) + and type(value["body"]) is str and len(value["body"]) <= ((1 << 20) + 2) // 3 * 4) + raw = base64.b64decode(value["body"], validate=True) + require(len(raw) == value["bytes"] and sha(raw) == value["sha256"] + and base64.b64encode(raw).decode() == value["body"] and not private(raw)) + return raw + + +def embedded(value): + if type(value) is dict: + if {"body", "sha256"} <= set(value): + stream(value) + else: + for item in value.values(): + embedded(item) + elif type(value) is list: + for item in value: + embedded(item) + + +def records(raw, order): + require(type(raw) is bytes and len(raw) <= 1 << 20 and (not raw or raw.endswith(b"\n")) and not private(raw)) + result = [load(line) for line in raw.splitlines()] + require(len(result) <= len(order)) + for index, row in enumerate(result): + require(type(row) is dict and set(row) == {"stage", "value"} and row["stage"] == order[index] + and type(row["value"]) is dict) + embedded(row["value"]) + return result + + +def snapshot(value, files, mode, directories=0o700): + expected = {"."} + for path in files: + expected.add(path) + expected.update(str(parent) for parent in PurePosixPath(path).parents) + require(type(value) is dict and set(value) == expected) + for path, entry in value.items(): + keys = set("kind mode uid gid device inode modified_ns changed_ns".split()) + is_file = path in files + require(type(entry) is dict and set(entry) == keys | ({"content"} if is_file else set())) + for field in keys - {"kind"}: + require(type(entry[field]) is int and 0 <= entry[field] < 1 << 64) + require(entry["inode"] > 0 and entry["mode"] == (mode if is_file else directories)) + require(entry["kind"] == ("file" if is_file else "directory")) + if is_file: + require(entry["modified_ns"] > 0 and entry["changed_ns"] > 0 and stream(entry["content"]) == files[path]) + else: + require(entry["modified_ns"] == entry["changed_ns"] == 0) + + +def tree_digest(files, source): + directories = {str(parent) for path in files for parent in PurePosixPath(path).parents} - {"."} + out = bytearray() + def frame(raw): + out.extend(len(raw).to_bytes(8, "big")) + out.extend(raw) + if source: + frame(b"agentplugins.package-tree\0sha256\0v1") + for path in sorted(set(files) | directories): + raw, is_file = files.get(path, b""), path in files + if source: + for item in ("entry", path, "file" if is_file else "directory", "100644" if is_file else "040000", ""): + frame(item.encode()) + out.extend(len(raw).to_bytes(8, "big")) + else: + out.extend(f'{"file" if is_file else "dir"}\0{path}\0false\0{len(raw)}\0'.encode()) + out.extend(raw) + return "sha256:" + sha(out) + + +def authored(scope, revision, nonce): + name = "uap-grok-cycle-" + scope + manifest = compact({"$schema": SCHEMA, "name": name, "version": "1.0.0", "description": "Disposable Grok skills cycle"}) + skill = f"---\nname: {name}\ndescription: {revision} {nonce}\n---\nUAP_GROK_{revision}_{nonce}\n".encode() + return {"plugin.json": manifest, "skills/" + name + "/SKILL.md": skill} + + +def projected(scope, revision, nonce): + files = authored(scope, revision, nonce) + manifest = {"name": "uap-grok-cycle-" + scope, "version": "1.0.0", "description": "Disposable Grok skills cycle", "skills": "./skills/"} + files[".grok-plugin/plugin.json"] = (json.dumps(manifest, sort_keys=True, ensure_ascii=False, indent=2) + "\n").encode() + return files + + +def fixture(value): + require(set(value) == set("root home project_a project_b outside nonce fixed".split())) + root, nonce = canonical(value["root"]), value["nonce"] + require(type(nonce) is str and re.fullmatch(r"[0-9a-f]{32}", nonce)) + paths = {"home": root + "/home", "project_a": root + "/project", "project_b": root + "/project-b", "outside": root + "/outside"} + require(all(value[key] == path for key, path in paths.items())) + config = b'[marketplace]\ndefault_skills_installs_purged = true\n[plugins]\nenabled = ["uap-grok-cycle-a", "uap-grok-cycle-b", "uap-grok-original-user"]\ndisabled = ["uap-grok-foreign-a", "uap-grok-foreign-b"]\n[features]\nturn_summary = false\n' + trust = "".join("[folders." + json.dumps(paths[key], ensure_ascii=False) + "]\ntrusted = true\n" for key in ("project_a", "project_b")).encode() + fixed = {paths["home"] + "/.grok/config.toml": {".": config}, paths["home"] + "/.grok/trusted_folders.toml": {".": trust}} + for scope in ("a", "b", "user"): + name = "uap-grok-original-user" if scope == "user" else "uap-grok-foreign-" + scope + native = paths["home"] if scope == "user" else paths["project_" + scope] + description = "Original user " + nonce if scope == "user" else "Foreign inert marker" + body = "USER_" + nonce if scope == "user" else "FOREIGN_" + scope + "_" + nonce + fixed[native + "/.grok/plugins/" + name] = {".grok-plugin/plugin.json": compact({"name": name, "version": "0.9.0", "skills": "./skills/"}), "skills/" + name + "/SKILL.md": f"---\nname: {name}\ndescription: {description}\n---\n{body}\n".encode()} + if scope != "user": + fixed[native + "/.uap/plugin-data/caller-owned"] = {"keep.txt": f"CALLER_{scope}_{nonce}\n".encode()} + require(type(value["fixed"]) is dict and set(value["fixed"]) == set(fixed)) + for path, files in fixed.items(): + snapshot(value["fixed"][path], files, 0o600) + return paths, nonce, value["fixed"] + + +def stamp(value): + require(type(value) is str and re.fullmatch(r"\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d{1,9})?Z", value)) + return datetime.fromisoformat(value[:-1] + "+00:00") + + +def binding(value, ins, scope, revision, digest, paths, nonce): + iid, name = ins["installation_id"], "uap-grok-cycle-" + scope + physical = name + "-" + sha(iid.encode())[:12] + active = paths["project_" + scope] + "/.grok/plugins/" + physical + bid = "client_" + sha("\0".join((iid, "grok", "project", active)).encode())[:24] + required = set("client_binding_id client_id scope target_locator physical_artifact_id materialization activation authentication policy verification package_revision affected_surfaces native_profile_root native_objects receipts updated_at".split()) + require(type(value) is dict and set(value) == required) + expected = {"client_binding_id": bid, "client_id": "grok", "scope": "project", "target_locator": active, "physical_artifact_id": physical, + "materialization": "materialized", "activation": "prepared", "authentication": "not_checked", "policy": "allowed", "verification": "package_validated", "affected_surfaces": ["grok"], + "native_profile_root": paths["project_" + scope] + "/.grok", "package_revision": {"version": "1.0.0", "tree_digest": digest, "manifest_digest": ins["package"]["manifest_digest"]}} + require(all(same(value[key], item) for key, item in expected.items())) + managed = tree_digest(projected(scope, revision, nonce), False) + require(same(value["native_objects"], [{"object_id": "package:grok:" + physical, "kind": "managed_package_directory", "logical_name": name, "path": active, "managed_digest": managed, "protection_class": "managed"}])) + stamp(value["updated_at"]) + return active, physical + + +def check_receipt(row, bound, sequence, kind, before, after): + require(type(row) is dict and type(row.get("operation_group_id")) is str + and re.fullmatch(r"op-[0-9a-f]{32}", row["operation_group_id"])) + operation = row["operation_group_id"] + "-001" + suffix = sha(operation.encode())[:16] + expected = {"operation_id": operation, "operation_group_id": row["operation_group_id"], "sequence": sequence, + "mutation_type": kind, "client_binding_id": bound["client_binding_id"], "phase": "committed", + "active_path": bound["target_locator"], "backup_path": str(PurePosixPath(bound["target_locator"]).parent / (".agentplugins-backup-" + suffix))} + if kind == "directory_swap": + expected["staging_path"] = bound["native_profile_root"] + "/.agentplugins-staging-" + suffix + if before: + expected["before_digest"] = before + if after: + expected["after_digest"] = after + require(same(row, expected)) + + +def check_swap_receipts(bound, scope, stage, nonce, prior): + count = 1 if scope == "b" else 2 if stage in ("intact-repair-a", "before-source-revision-a") else 3 if stage in ("update-a", "before-missing-a") else 4 if stage in ("missing-repair-a", "remove-a", "repeat-remove-a") else 1 + rows = bound["receipts"] + require(type(rows) is list and len(rows) == count) + one = tree_digest(projected(scope, scope + "-one", nonce), False) + two = tree_digest(projected(scope, "a-two", nonce), False) if scope == "a" else one + before, after, seen = ("", one, one, ""), (one, one, two, two), set() + for index, row in enumerate(rows): + check_receipt(row, bound, index + 1, "directory_swap", before[index], after[index]) + require(row["operation_id"] not in seen) + seen.add(row["operation_id"]) + if prior is not None: + previous = prior["binding"]["receipts"] + require(same(rows[:len(previous)], previous)) + + +def proofs(rows): + by_stage = {row["stage"]: row["value"] for row in rows} + paths, nonce, fixed = fixture(by_stage["fixture"]) + sources = {} + for scope, revision in (("a", "a-one"), ("b", "b-one"), ("a", "a-two")): + row = by_stage["source-" + scope + "-" + revision] + files = authored(scope, revision, nonce) + digest = tree_digest(files, True) + require(set(row) == {"scope", "source", "revision", "digest", "files"} and row["scope"] == scope and row["revision"] == revision + and row["source"] == str(PurePosixPath(paths["home"]).parent / ("cycle-source-" + scope)) and row["digest"] == digest) + snapshot(row["files"], files, 0o600) + sources[scope, revision] = row + current, result, identities = {}, {}, {} + for stage in PROOF: + if stage in ("fixture", "final") or stage.startswith("source-"): + continue + row = by_stage[stage] + require(same(row["preserved"], fixed)) + if stage == "damaged-a": + require(set(row) == {"scope", "removed_path", "before", "absent", "preserved"} and row["scope"] == "a" and row["absent"] is True) + require(same(row["before"], current["a"]["payload"]) and row["removed_path"] == current["a"]["binding"]["target_locator"]) + continue + require(set(row) == set("scope revision removed state binding source_digest payload preserved".split())) + scope = "b" if stage.endswith("-b") else "a" + revision = "b-one" if scope == "b" else "a-two" if PROOF.index(stage) >= PROOF.index("update-a") else "a-one" + removed = stage in ("remove-a", "repeat-remove-a", "remove-b") + require(row["scope"] == scope and row["revision"] == revision and row["removed"] is removed) + source = sources[scope, revision] + require(row["source_digest"] == source["digest"]) + state = load(stream(row["state"])) + require(type(state) is dict and {"schema_version", "installations"} <= set(state) <= {"schema_version", "installations", "transaction_receipts"} + and type(state["schema_version"]) is int and state["schema_version"] == 5 and type(state["installations"]) is list and len(state["installations"]) == 1) + ins = state["installations"][0] + required = set("installation_id declared_name source package origin_mode operation_group_id clients created_at updated_at".split()) + require(type(ins) is dict and required <= set(ins) <= required | {"data_receipts", "data_retained"}) + iid, name = ins["installation_id"], "uap-grok-cycle-" + scope + require(type(iid) is str and re.fullmatch(r"[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}", iid)) + require(ins["declared_name"] == name and ins["origin_mode"] == "direct" and stamp(ins["created_at"]) <= stamp(ins["updated_at"])) + require(re.fullmatch(r"op-[0-9a-f]{32}", ins["operation_group_id"])) + require(same(ins["source"], {"source_binding_id": "src_" + sha((source["source"] + "\0\0").encode())[:24], "requested_source": source["source"], "canonical_source": source["source"], "resolved_revision": "", "tree_digest": source["digest"]})) + require(same(ins["package"], {"loader_kind": "agent_plugins", "format_id": "agent-plugins/1.0.0", "schema_uri": SCHEMA, "declared_name": name, "version": "1.0.0", "manifest_digest": "sha256:" + sha(authored(scope, revision, nonce)["plugin.json"]), "inventory": {"mcp_present": False, "mcp_enabled": False, "skills": [name]}})) + active, physical = binding(row["binding"], ins, scope, revision, source["digest"], paths, nonce) + bound = row["binding"] + check_swap_receipts(bound, scope, stage, nonce, current.get(scope)) + transaction = state.get("transaction_receipts", []) + require(type(transaction) is list) + if removed: + require(len(transaction) == 1) + check_receipt(transaction[0], bound, len(bound["receipts"]) + 1, "directory_remove", tree_digest(projected(scope, revision, nonce), False), "") + require(transaction[0]["operation_id"] not in {item["operation_id"] for item in bound["receipts"]}) + require(ins["operation_group_id"] == transaction[0]["operation_group_id"]) + else: + require(transaction == [] and ins["operation_group_id"] == bound["receipts"][-1]["operation_group_id"]) + identity = iid, bound["client_binding_id"], physical + require(identities.setdefault(scope, identity) == identity) + require(same(ins["clients"], {} if removed else {identity[1]: row["binding"]}) and ins.get("data_retained", False) is removed) + if not removed: + require(ins.get("data_receipts", {}) == {}) + snapshot(row["payload"], projected(scope, revision, nonce), 0o644, 0o755) + else: + require(same(row["binding"], current[scope]["binding"])) + data_id = "data_" + sha("\0".join((iid, physical, "project")).encode())[:24] + data_path = paths["project_" + scope] + "/.uap/plugin-data/" + physical + receipts = ins.get("data_receipts") + require(type(receipts) is dict and set(receipts) == {data_id}) + receipt = receipts[data_id] + expected = {"data_receipt_id": data_id, "physical_backend_id": physical, "scope": "project", "locator": data_path, "ownership_digest": "sha256:" + sha(("agentplugins-plugin-data-v1\0" + data_id + "\0" + data_path).encode()), "state": "owned"} + require(type(receipt) is dict and set(receipt) == set(expected) | {"created_at", "updated_at"} and all(same(receipt[key], item) for key, item in expected.items())) + require(stamp(receipt["created_at"]) <= stamp(receipt["updated_at"])) + marker = stream(row["payload"][".agentplugins-data-owner.json"]["content"]) + require(same(load(marker), receipt)) + snapshot(row["payload"], {".agentplugins-data-owner.json": marker}, 0o600) + if stage in ("repeat-add-a", "unchanged-update-a", "before-source-revision-a", "before-missing-a", "repeat-remove-a"): + require(same(row["state"], current[scope]["state"]) and same(row["payload"], current[scope]["payload"])) + current[scope], result[stage] = row, row + require(len(set(item[0] for item in identities.values())) == len(set(item[1] for item in identities.values())) == 2) + final = by_stage["final"] + require(set(final) == {"preserved", "retained_a", "retained_b", "absent_a", "absent_b"} and same(final["preserved"], fixed)) + for scope in ("a", "b"): + require(final["absent_" + scope] == current[scope]["binding"]["target_locator"] and same(final["retained_" + scope], current[scope]["payload"])) + return paths, nonce, result + + +def command(row): + require(type(row) is dict and set(row) == set("argv cwd exit error deadline elapsed_ms cleanup stdout stderr".split())) + require(type(row["argv"]) is list and 1 <= len(row["argv"]) <= 64 and all(type(arg) is str and len(arg) <= 4096 and "\0" not in arg for arg in row["argv"])) + canonical(row["cwd"]) + require(type(row["exit"]) is int and -1 <= row["exit"] <= 255 and type(row["error"]) is bool and type(row["deadline"]) is bool + and type(row["elapsed_ms"]) is int and 0 <= row["elapsed_ms"] <= 45000 and row["cleanup"] == CLEANUP) + return stream(row["stdout"]), stream(row["stderr"]) + + +def inspect(raw, cwd, trusted, expected): + row = load(raw) + from project_native_runtime import grok_selection_identity_keys + require(grok_selection_identity_keys(row, "grokVersion cwd projectRoot projectTrusted plugins skills mcpServers")) + require(type(row) is dict and row.get("grokVersion") == "1.0.70" and row.get("cwd") == cwd + and row.get("projectRoot") in (cwd, cwd + "/") and row.get("projectTrusted") is trusted and same(row.get("mcpServers"), [])) + wanted_plugins, wanted_skills = {}, {} + for name, scope, path, description, count, loaded, skill_path in expected: + wanted_plugins[name] = {"name": name, "scope": scope, "path": path, "enabled": True, "provides": {"skills": count, "agents": 0, "hooks": False, "mcpServers": 0}} + if loaded: + wanted_skills[name] = {"name": name, "description": description, "source": {"type": "plugin", "plugin_name": name, "path": skill_path}, "userInvocable": True} + for key, wanted in (("plugins", wanted_plugins), ("skills", wanted_skills)): + actual = row.get(key) + require(type(actual) is list and len(actual) == len(wanted)) + seen = set() + for value in actual: + require(type(value) is dict and type(value.get("name")) is str and value["name"] in wanted and value["name"] not in seen) + name = value["name"] + expected_row = wanted[name] + if key == "skills" and "disabled" in value: + require(value["disabled"] is False) + expected_row = {**expected_row, "disabled": False} + require(same(value, expected_row)) + seen.add(name) + + +def public_result(raw, stage, verb, selected): + row = load(raw) + require(type(row) is dict and set(row) == {"schema_version", "command", "result", "data"} + and same(row["schema_version"], 1) and row["command"] == verb and row["result"] == "success") + installation = load(stream(selected["state"]))["installations"][0] + scope, binding_row = selected["scope"], selected["binding"] + name, digest = "uap-grok-cycle-" + scope, selected["source_digest"] + data = row["data"] + if stage == "repeat-add-a": + required = {"plugin", "version", "source", "tree_digest", "manifest_digest", "dry_run", "result"} + require(verb == "add" and type(data) is dict and required <= set(data) <= required | {"security", "next_action"}) + output, group = data, "" + else: + required = {"batch", "status", "succeeded", "failed", "plugin", "dry_run", "targets"} + require(type(data) is dict and required <= set(data)) + require(all(same(data[key], value) for key, value in {"batch": True, "succeeded": 1, "failed": 0, "plugin": name, "dry_run": False}.items())) + require(data["status"] == ("data_retained" if verb == "remove" else "completed") and type(data["targets"]) is list and len(data["targets"]) == 1) + target = data["targets"][0] + require(type(target) is dict and target.get("target") == "grok" and target.get("status") == "external_completed") + if verb == "update": + require(target.get("selected") is True) + output, group = target["output"], data.get("operation_id") + expected = {"plugin": name, "version": "1.0.0", "source": "local" if verb in ("repair", "remove") else "direct local source", "tree_digest": digest, "manifest_digest": installation["package"]["manifest_digest"], "dry_run": False} + require(type(output) is dict and all(same(output.get(key), value) for key, value in expected.items())) + outcome, changed = output["result"], stage not in ("repeat-add-a", "unchanged-update-a") + if stage != "repeat-add-a": + require(type(group) is str and re.fullmatch(r"op-[0-9a-f]{32}", group) and output.get("operation_id") == group) + if changed: + require(group == installation["operation_group_id"]) + require(type(outcome) is dict and outcome.get("installation_id") == installation["installation_id"] + and outcome.get("requires_confirmation") is False and outcome.get("mutated") is changed) + found = [] + def visit(value): + if type(value) is dict: + for key, item in value.items(): + if key == "mutated": + require(item is changed) + found.append(item) + if key == "no_change": + require(type(item) is bool and (not changed or item is False)) + checks = {"tree_digest": digest, "manifest_digest": installation["package"]["manifest_digest"], "installation_id": installation["installation_id"], + "client_id": "grok", "target": "grok", "scope": "project", "client_binding_id": binding_row["client_binding_id"], + "physical_artifact_id": binding_row["physical_artifact_id"], "target_locator": binding_row["target_locator"]} + if key in checks: + require(same(item, checks[key])) + visit(item) + elif type(value) is list: + for item in value: + visit(item) + visit(data) + require(found) + if verb == "remove": + require(data.get("plugin_data_preserved") is True and data.get("data_retained") is True) + receipt = next(iter(installation["data_receipts"].values())) + require(same(data.get("retained_data"), [{key: receipt[key] for key in ("data_receipt_id", "physical_backend_id", "scope", "state")}])) + return group + + +def complete(public, inspections, proof_rows): + paths, nonce, proof = proofs(proof_rows) + commands = {row["stage"]: row["value"] for row in public} + git, client, installer = commands["git-a"]["argv"][0], commands["version"]["argv"][0], commands["add-a"]["argv"][0] + require(len({canonical(value) for value in (git, client, installer)}) == 3) + elapsed, groups = 0, set() + for stage, row in commands.items(): + out, err = command(row) + scope = "b" if stage.endswith("-b") else "a" + cwd = paths["outside"] if stage in ("version", "git-outside") else paths["project_" + scope] + if stage.startswith("git-"): + argv = [git, "-c", "core.hooksPath=/dev/null", "init", "--quiet"] + require(out == err == b"") + elif stage == "version": + argv = [client, "--version"] + require(out == b"grok 1.0.70 (f50904eaee6c)\n" and err == b"") + else: + verb = "repair" if "repair" in stage else stage.removeprefix("repeat-").removeprefix("unchanged-").split("-")[0] + selected = proof[stage] + name = "uap-grok-cycle-" + scope + selector = str(PurePosixPath(paths["home"]).parent / ("cycle-source-" + scope)) if verb == "add" or stage == "update-a" else load(stream(selected["state"]))["installations"][0]["installation_id"] if verb == "repair" else name + argv = [installer, verb, selector] + (["--no-save"] if verb == "add" else []) + ["--scope", "project", "--project-root", cwd, "--target", "grok", "--format", "json"] + if stage == "repeat-remove-a": + require(out == b"" and err == b'agentplugins: plugin is not installed for target "grok" in project scope; no target was changed\n') + else: + group = public_result(out, stage, verb, selected) + if group: + require(group not in groups) + groups.add(group) + if stage.startswith("add-"): + expected_progress = "Resolving and validating one Agent Plugin package for every selected target...\nInstalling " + name + " for 1 selected clients...\n" + elif stage == "repeat-add-a": + expected_progress = "Resolving and validating one Agent Plugin package for every selected target...\nApplying transactional client package...\n" + elif verb == "update": + expected_progress = "Resolving and validating one updated Agent Plugin package for every selected target...\n" + elif verb == "repair": + expected_progress = "Resolving and validating each unique exact installed package revision once...\n" + else: + expected_progress = "" + require(err == expected_progress.encode()) + require(row["argv"] == argv and row["cwd"] == cwd and row["exit"] == (1 if stage == "repeat-remove-a" else 0) + and row["error"] is False and row["deadline"] is False and row["elapsed_ms"] <= 35000) + elapsed += row["elapsed_ms"] + for capture in inspections: + stage, row = capture["stage"], capture["value"] + scope = "outside" if stage.endswith("outside") else stage[-1] + cwd = paths["outside"] if scope == "outside" else paths["project_" + scope] + require(row["argv"] == [client, "inspect", "--json"] and row["cwd"] == cwd and row["exit"] == 0 and row["error"] is False and row["deadline"] is False) + require(row["elapsed_ms"] <= 35000) + raw, err = command(row) + require(err == b"") + name = "uap-grok-original-user" + user = paths["home"] + "/.grok/plugins/" + name + # Each producer has one held source spelling; raw aliases are not normalized. + expected = [(name, "user", user, "Original user " + nonce, 1, True, user + "/./skills/" + name + "/SKILL.md")] + if scope != "outside": + foreign = "uap-grok-foreign-" + scope + expected.append((foreign, "project", cwd + "/.grok/plugins/" + foreign, "Foreign inert marker", 1, False, "")) + owned = stage in ("installed-a", "installed-b", "stable-a", "updated-a", "updated-b", "repaired-a", "remaining-b") + if owned: + key = "add-b" if scope == "b" else "update-a" if stage in ("updated-a", "damaged-a", "repaired-a") else "add-a" + selected = proof[key] + name, active = "uap-grok-cycle-" + scope, selected["binding"]["target_locator"] + expected.append((name, "project", active, selected["revision"] + " " + nonce, 1, True, active + "/skills/" + name + "/SKILL.md")) + # Empty outside reports the native provisional no-config allow; the + # separately validated durable trust-store recipe still contains A/B only. + inspect(raw, cwd, True, expected) + elapsed += row["elapsed_ms"] + return client, elapsed + + +def members(bodies, source): + try: + return admit(bodies, source) + except (KeyError, TypeError, AttributeError, IndexError, RecursionError, OverflowError, UnicodeError) as error: + raise ValueError("Grok complete skills-cycle structure refused") from error + + +def admit(bodies, source): + require(set(bodies) == {"runtime-attempt.json", *MEMBERS} and all(len(raw) <= 1 << 20 and not private(raw) for raw in bodies.values())) + result = load(bodies["runtime-attempt.json"]) + fields = set("client version runtime_attempt_complete activation vendor_authentication_qualified real_model_qualified qualification source_identity acquisition scanner elapsed_ms test_failed capture_mode attempt_stage cycle_public_stages cycle_inspect_stages cycle_proof_stages cycle_observed_complete".split()) + require(type(result) is dict and set(result) == fields) + require(result["client"] == "grok" and result["version"] == "1.0.70" and result["capture_mode"] == MODE and result["attempt_stage"] == "complete-cycle" + and result["activation"] == "prepared" and result["vendor_authentication_qualified"] is False and result["real_model_qualified"] is False) + complete_flag = result["runtime_attempt_complete"] + require(type(complete_flag) is bool and type(result["test_failed"]) is bool and type(result["cycle_observed_complete"]) is bool + and type(result["elapsed_ms"]) is int and 0 <= result["elapsed_ms"] <= 195000) + require(same(result["source_identity"], {"installer_base_commit": source["source_commit"], "installer_tree": source["source_tree"], "installer_source_state": "committed", "installer_patch_sha256": []})) + require(same(result["qualification"], {key: complete_flag and key in FLAGS[:2] for key in FLAGS})) + rows = [records(bodies[name], order) for name, order in zip(MEMBERS, (PUBLIC, INSPECT, PROOF))] + for key, captured in zip(("cycle_public_stages", "cycle_inspect_stages", "cycle_proof_stages"), rows): + require(same(result[key], [row["stage"] for row in captured])) + for captured in rows[:2]: + for row in captured: + command(row["value"]) + if not complete_flag: + require(result["test_failed"] is True) + return result + require(result["test_failed"] is False and result["cycle_observed_complete"] is True and result["elapsed_ms"] < 150000 + and all(len(captured) == len(order) for captured, order in zip(rows, (PUBLIC, INSPECT, PROOF)))) + client, elapsed = complete(*rows) + require(elapsed <= result["elapsed_ms"]) + acquisition = result["acquisition"] + require(same(acquisition, {"client": "grok", "acquisition": {"sha256": "11d27a30adfb75c4b9b180072ab27412a1916f054ff5497ab70fb9a77d304bcc", "pin_verified": True, + "members": [{"Name": "grok", "Kind": "file", "Bytes": 195700192, "sha256": "22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9"}]}, "node": None, + "runtime_files": [{"path": client, "sha256": "22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9"}]})) + require(same(result["scanner"], {"archive_sha256": "2b3d176db752433b904a4b42375543ff398f4841d22e48f7d4f23ded925b72da", "binary_sha256": "b6723451f155eb49e84acb34d1f06de929bddfcff6238cd4e38cdc6519396fba", "platform": "linux-amd64", "route": "prepopulated supported scanner cache; ordinary CLI security assessment", "source_tag": "v0.1.3", "version": "0.1.3"})) + return result diff --git a/scripts/project_native_runtime.py b/scripts/project_native_runtime.py index 521b3cedd..9e17aed55 100644 --- a/scripts/project_native_runtime.py +++ b/scripts/project_native_runtime.py @@ -49,8 +49,8 @@ "qoder": {"test": "TestAgentpluginsQoderNativeProjectRuntimeAttempt", "file": "repotests/agentplugins_qoder_project_native_attempt_test.go", "optin": "AGENTPLUGINS_QODER_PROJECT_RUNTIME_ATTEMPT", "seconds": 180, "phase": "runtime-attempt"}, - "grok": {"test": "TestAgentpluginsGrokNativeProjectRuntimeAttempt", - "file": "repotests/agentplugins_grok_project_native_attempt_test.go", + "grok": {"test": "TestAgentpluginsGrokNativeProjectCompleteCycle", + "file": "repotests/agentplugins_grok_cycle_lifecycle_test.go", "optin": "AGENTPLUGINS_GROK_PROJECT_RUNTIME_ATTEMPT", "seconds": 180, "phase": "runtime-attempt"}, "kiro": {"test": "TestAgentpluginsKiroNativeProjectRuntimeAttempt", "file": "repotests/agentplugins_kiro_project_native_attempt_test.go", @@ -236,11 +236,421 @@ def kimi_members(bodies, source): return result +def grok_private_credential(body): + if len(body) > 1 << 20 or b"uap-grok-private-" in body: + return True + budget = [4 * len(body), 16384] + + def numeric_refusal(raw, error): + if str(error) != "nonfinite JSON" or len(raw) > budget[0]: + return True + budget[0] -= len(raw) + try: + json.loads(raw.decode("utf-8", errors="strict"), parse_int=lambda _: None, parse_float=lambda _: None, + parse_constant=lambda _: (_ for _ in ()).throw(json.JSONDecodeError("Non-JSON constant", "", 0))) + except json.JSONDecodeError: + return False + except (UnicodeError, RecursionError, ValueError): + return True + return True + + def encoded(raw, depth): + if depth > 64 or len(raw) > budget[0]: + return True + budget[0] -= len(raw) + try: + value = strict_json(raw) + except (UnicodeError, RecursionError): + return True + except ValueError as error: + if not isinstance(error, json.JSONDecodeError) and numeric_refusal(raw, error): + return True + if b"\n" not in raw: + return False + for line in io.BytesIO(raw): + line = line.removesuffix(b"\n") + if len(line) > budget[0]: + return True + budget[0] -= len(line) + try: + value = strict_json(line) + except (UnicodeError, RecursionError): + return True + except ValueError as line_error: + if not isinstance(line_error, json.JSONDecodeError) and numeric_refusal(line, line_error): + return True + continue + if visit(value, depth): + return True + return False + return visit(value, depth) + + def visit(value, depth, structure=0): + budget[1] -= 1 + if depth > 64 or structure > 32 or budget[1] < 0: + return True + if type(value) is str: + try: + raw = value.encode("utf-8", errors="strict") + except UnicodeError: + return True + return b"uap-grok-private-" in raw or encoded(raw, depth + 1) + if type(value) is dict: + for key, item in value.items(): + try: + if len(key.encode("utf-8", errors="strict")) > 4096: + return True + except UnicodeError: + return True + if visit(key, depth + 1, structure + 1) or visit(item, depth + 1, structure + 1): + return True + elif type(value) is list: + return any(visit(item, depth + 1, structure + 1) for item in value) + return False + + return encoded(body, 0) + + +def grok_provider_route_observation(row): + if "route_observation" not in row: + return + observation = row["route_observation"] + flags = {"peer_loopback", "host_matches", "relative_url", "empty_url_host", "empty_raw_path", "content_type_exact"} + fields = flags | {"method", "uri", "content_type_count", "content_type_kind"} + uris = {"/", "/v1/chat/completions", "/chat/completions", "/v1/models", "/models", "/v1/responses", "/responses", "other"} + if (row["failure"] != "route" or not row["started"] or type(observation) is not dict or set(observation) != fields + or any(type(observation[key]) is not bool for key in flags) + or type(observation["method"]) is not str or observation["method"] not in {"POST", "GET", "other"} + or type(observation["uri"]) is not str or observation["uri"] not in uris + or type(observation["content_type_count"]) is not str + or observation["content_type_count"] not in {"none", "one", "multiple"} + or type(observation["content_type_kind"]) is not str + or observation["content_type_kind"] not in {"missing", "json-bare", "json-utf8", "other"}): + raise ValueError("finite Grok route observation differs") + count, kind = observation["content_type_count"], observation["content_type_kind"] + if ((count == "none") != (kind == "missing") or (count == "multiple" and kind != "other") + or (kind in {"json-bare", "json-utf8"} and count != "one") + or (observation["content_type_exact"] and (count != "one" or kind != "json-bare")) + or (observation["method"] == "POST" and observation["uri"] == "/v1/chat/completions" + and all(observation[key] for key in flags))): + raise ValueError("contradictory Grok route observation") + + +def grok_provider_members(bodies, client): + if client == "grok" and any(grok_private_credential(body) for body in bodies.values()): + raise ValueError("unredacted Grok fixture credential refused") + members = {"grok-provider.json", "grok-provider.request.json", "grok-provider.response.sse"} + present = set(bodies) & members + if not present: + return set() + if client != "grok" or "grok-provider.json" not in present: + raise ValueError("Grok-only provider receipt required") + if len(bodies["grok-provider.json"]) > 2048: + raise ValueError("Grok provider receipt bound exceeded") + row = strict_json(bodies["grok-provider.json"]) + fields = {"transport", "model", "started", "requests", "responses", "credential_matched", + "failure", "shutdown_complete", "forced_close"} + failures = {"none", "listen", "serve", "deadline", "route", "credential", "duplicate", + "body", "request-shape", "response-write", "cleanup"} + if (type(row) is not dict or set(row) not in (fields, fields | {"route_observation"}) + or row["transport"] != "scripted loopback OpenAI chat-completions SSE; no real model" + or row["model"] != "uap-grok-fixture" + or type(row["failure"]) is not str or row["failure"] not in failures + or any(type(row[key]) is not bool for key in ("started", "credential_matched", "shutdown_complete", "forced_close")) + or any(type(row[key]) is not int or not 0 <= row[key] <= 1 for key in ("requests", "responses")) + or row["responses"] > row["requests"]): + raise ValueError("finite Grok provider receipt differs") + if ((not row["started"] and (row["failure"] != "listen" or row["requests"] or row["responses"] + or row["credential_matched"] or row["shutdown_complete"] or row["forced_close"])) + or (row["requests"] and (not row["started"] or not row["credential_matched"])) + or (row["failure"] == "none" and (row["requests"] != row["responses"] + or row["forced_close"] or not row["shutdown_complete"]))): + raise ValueError("Grok partial capture or cleanup state differs") + grok_provider_route_observation(row) + required = {"grok-provider.json"} + if row["requests"]: + required.add("grok-provider.request.json") + if row["responses"]: + required.add("grok-provider.response.sse") + if present != required or any(not bodies[name].strip() for name in present): + raise ValueError("Grok provider member counts differ") + if row["requests"]: + request = strict_json(bodies["grok-provider.request.json"]) + if type(request) is not dict: + raise ValueError("Grok opaque captured request must be a JSON object") + if "uap-grok-private-" in json.dumps(request): + raise ValueError("encoded Grok fixture credential refused") + return members + + +GROK_TITLE_SYSTEM = ( + "You are tasked with generating the session title. The user is asking almost always software engineering related questions on their codebase.\n" + "We describe the session title below\n# Session Title\n" + "A short and distinctive 5-10 word descriptive title for the session. Super info dense, no filler.\n\n" + "You will be given the user query below encapsulated in .\n\n" + "Just generate the session_title and nothing else") +EXPECTED_TITLE_REQUEST = { + "model": "uap-grok-title-fixture", "stream": True, "stream_options": {"include_usage": True}, + "temperature": 1.0, "max_tokens": 100, + "messages": [{"role": "system", "content": GROK_TITLE_SYSTEM}, + {"role": "user", "content": "\nDescribe only the installed uap-grok-attempt skill marker. Do not run any tool or change any file.\n"}], + "tools": [{"type": "function", "function": { + "name": "session_title", "description": "Generate the session_title which we use for the user_message", + "parameters": {"type": "object", "required": ["session_title"], "additionalProperties": False, + "properties": {"session_title": {"type": "string", "description": "Final session title, just 5-10 word descriptive title for the session. Super info dense, no filler."}}}}}], + "tool_choice": {"type": "function", "function": {"name": "session_title"}}} + + +def grok_title_provider_members(bodies, client): + names = {"grok-title-provider.json", "grok-title-provider.request.json", "grok-title-provider.response.sse"} + present = set(bodies) & names + if not present: + return set() + if client != "grok" or "grok-provider.json" not in bodies or "grok-title-provider.json" not in present: + raise ValueError("separate Grok title/main receipts required") + if len(bodies["grok-title-provider.json"]) > 2048: + raise ValueError("Grok title receipt bound") + row = strict_json(bodies["grok-title-provider.json"]) + fields = {"role", "transport", "model", "started", "requests", "responses", "credential_matched", + "failure", "shutdown_complete", "forced_close"} + failures = {"none", "listen", "serve", "deadline", "route", "credential", "duplicate", + "body", "request-shape", "privacy", "response-write", "cleanup"} + if (type(row) is not dict or set(row) not in (fields, fields | {"route_observation"}) + or row["role"] != "session-title" + or row["transport"] != "scripted loopback OpenAI session-title SSE; no real model" + or row["model"] != "uap-grok-title-fixture" + or type(row["failure"]) is not str or row["failure"] not in failures + or any(type(row[key]) is not bool for key in ("started", "credential_matched", "shutdown_complete", "forced_close")) + or any(type(row[key]) is not int or not 0 <= row[key] <= 1 for key in ("requests", "responses")) + or row["responses"] > row["requests"]): + raise ValueError("finite Grok title receipt differs") + if ((not row["started"] and (row["failure"] != "listen" or row["requests"] or row["responses"] + or row["credential_matched"] or row["shutdown_complete"] or row["forced_close"])) + or (row["requests"] and (not row["started"] or not row["credential_matched"])) + or (row["failure"] == "none" and (row["requests"] != row["responses"] + or row["forced_close"] or not row["shutdown_complete"])) + or (row["started"] and row["failure"] == "listen") + or (row["failure"] in {"body", "privacy"} and row["requests"]) + or (row["failure"] in {"request-shape", "privacy", "body", "response-write"} and row["responses"])): + raise ValueError("Grok title partial capture or cleanup differs") + grok_provider_route_observation(row) + required = {"grok-title-provider.json"} + if row["requests"]: + required.add("grok-title-provider.request.json") + if row["responses"]: + required.add("grok-title-provider.response.sse") + if present != required or any(not bodies[name].strip() for name in present): + raise ValueError("Grok title member counts differ") + request = None + if row["requests"]: + raw = bodies["grok-title-provider.request.json"] + request = strict_json(raw) + if len(raw) > 1 << 20 or type(request) is not dict or grok_private_credential(raw): + raise ValueError("Grok title safe captured object required") + if row["responses"]: + expected = EXPECTED_TITLE_REQUEST + response = bodies["grok-title-provider.response.sse"] + if (request != expected or type(request["stream"]) is not bool + or request["stream_options"]["include_usage"] is not True + or request["tools"][0]["function"]["parameters"]["additionalProperties"] is not False + or type(request["temperature"]) not in (int, float) or len(response) != 570 + or hashlib.sha256(response).hexdigest() != "eea6c4c389c7e27cf0e6d1cc9e705bc84a4d77ae8c5afddaa846ec8255e6ed68"): + raise ValueError("Grok title fixed request/response differs") + return names + + +def grok_duplicate_object(raw): + """Finite refused JSON data only; never a provider/model request grammar.""" + if type(raw) is not bytes or not 0 < len(raw) <= 1 << 20: + raise ValueError("Grok duplicate raw body bound") + value = strict_json(raw) + if type(value) is not dict: + raise ValueError("Grok duplicate raw object required") + nodes = 0 + def visit(item, depth): + nonlocal nodes + nodes += 1 + if depth > 32 or nodes > 16384: + raise ValueError("Grok duplicate JSON structure bound") + if type(item) is dict: + for key, child in item.items(): + if len(key.encode("utf-8", errors="strict")) > 4096: + raise ValueError("Grok duplicate JSON key bound") + visit(child, depth + 1) + elif type(item) is list: + for child in item: visit(child, depth + 1) + elif type(item) in (int, float): + try: + finite = math.isfinite(float(item)) + except OverflowError: + finite = False + if not finite: + raise ValueError("Grok duplicate nonfinite JSON number") + visit(value, 0) + decoded = json.dumps(value, ensure_ascii=False, separators=(",", ":")).encode("utf-8", errors="strict") + if grok_private_credential(raw) or grok_private_credential(decoded): + raise ValueError("Grok duplicate privacy refusal") + return value + + +def grok_duplicate_members(bodies, client): + names = {"grok-provider.duplicate.json", "grok-provider.duplicate-request.json"} + present = set(bodies) & names + if not present: + return set() + receipt = bodies.get("grok-provider.duplicate.json") + if client != "grok" or type(receipt) is not bytes or not 0 < len(receipt) <= 2048: + raise ValueError("Grok-only duplicate receipt required") + row = strict_json(receipt) + fields = {"schema_version", "role", "attempt_index", "status", "original_failure", "response_issued", "captured_bytes", "captured_sha256"} + if (type(row) is not dict or set(row) != fields or type(row["schema_version"]) is not int or row["schema_version"] != 1 + or row["role"] != "main-duplicate-diagnostic" or type(row["attempt_index"]) is not int or row["attempt_index"] != 2 + or row["original_failure"] != "duplicate" or row["response_issued"] is not False + or type(row["captured_bytes"]) is not int or type(row["captured_sha256"]) is not str + or type(row["status"]) is not str or row["status"] not in {"captured", "body-refused", "json-refused", "privacy-refused", "context-refused"}): + raise ValueError("Grok duplicate finite receipt differs") + # Old main/title validators remain the canonical closed parent schemas. + grok_provider_members(bodies, client) + grok_title_provider_members(bodies, client) + main = strict_json(bodies.get("grok-provider.json", b"null")) + title = strict_json(bodies.get("grok-title-provider.json", b"null")) + if (type(main) is not dict or main.get("requests") != 1 or main.get("responses") != 1 or main.get("failure") != "duplicate" + or main.get("started") is not True or main.get("credential_matched") is not True + or type(title) is not dict or title.get("role") != "session-title" or title.get("started") is not True): + raise ValueError("Grok duplicate completed first main/title join absent") + request = grok_duplicate_object(bodies["grok-provider.request.json"]) + if (request.get("model") != "uap-grok-fixture" or request.get("stream") is not True + or type(request.get("messages")) is not list or not 1 <= len(request["messages"]) <= 128 + or hashlib.sha256(bodies["grok-provider.response.sse"]).hexdigest() != "f2c19ea9dbc0ddbf7cc2facaa5d2d01877897afa0f6fea33cc5651c0ecc9f7b9"): + raise ValueError("Grok duplicate unchanged first exchange differs") + result = strict_json(bodies.get("runtime-attempt.json", b"null")) + if (type(result) is not dict or result.get("client") != "grok" or result.get("runtime_attempt_complete") is not False + or result.get("test_failed") is not True or result.get("vendor_authentication_qualified") is not False + or result.get("real_model_qualified") is not False or type(result.get("qualification")) is not dict + or set(result["qualification"]) != set(shared.QUALIFICATION_CASES) or any(value is not False for value in result["qualification"].values())): + raise ValueError("Grok duplicate remains failed and unqualified") + if row["status"] == "captured": + raw = bodies.get("grok-provider.duplicate-request.json") + if (type(raw) is not bytes or not 0 < len(raw) <= 1 << 20 or row["captured_bytes"] != len(raw) + or row["captured_sha256"] != hashlib.sha256(raw).hexdigest()): + raise ValueError("Grok duplicate raw byte/hash join differs") + grok_duplicate_object(raw) + elif present != {"grok-provider.duplicate.json"} or row["captured_bytes"] != 0 or row["captured_sha256"] != "": + raise ValueError("Grok duplicate refusal retained raw") + return names + + +def grok_selection_identity_keys(row, fields): + return type(row) is dict and all(key == expected or key.casefold() != expected.casefold() + for key in row for expected in fields.split()) + + +def grok_selected_skill(skills, name, path): + if type(skills) is not list: + raise ValueError("Grok selected skills must be an array") + found = False + for row in skills: + if (not grok_selection_identity_keys(row, "name description source userInvocable disabled") + or type(row.get("name")) is not str or not row["name"] + or not grok_selection_identity_keys(row.get("source"), "type plugin_name path")): + raise ValueError("Grok selected skill identity differs") + origin = row["source"] + plugin = origin.get("plugin_name", "") + if row["name"].casefold() != name.casefold() and (type(plugin) is not str or plugin.casefold() != name.casefold()): + continue + if (found or row["name"] != name or row.get("description") != "Harmless installed startup marker" + or row.get("userInvocable") is not True or row.get("disabled", False) is not False + or origin != {"type": "plugin", "plugin_name": name, "path": path}): + raise ValueError("Grok selected skill source is not the owned installation") + found = True + if not found: + raise ValueError("Grok selected loaded skill is absent") + + +def grok_selected_inspection(bodies, result, client): + if client != "grok": + if "fixture_selection" in result: + raise ValueError("Grok fixture selection cannot apply to another client") + return + marker, observation = result.get("fixture_selection"), result.get("native_inspection") + if "fixture_selection" not in result: + if type(observation) is dict and (observation.get("enabled") is True or observation.get("project_trusted") is True): + raise ValueError("Grok selected observation requires its fixture marker") + return + if marker != "owned-project-trust-and-selected-plugin-name": + raise ValueError("unregistered Grok fixture selection") + # Authored intent is not a completed observation. Preserve failed/unknown + # raw inspect attempts without inventing a successful selected skill. + if "native_inspection" not in result: + return + binding = result.get("binding") + if type(binding) is not dict: + raise ValueError("Grok selected inspection requires its project binding") + profile, target, physical = (binding.get(key) for key in ("native_profile_root", "target_locator", "physical_artifact_id")) + if (any(type(value) is not str or not value or not os.path.isabs(value) or os.path.normpath(value) != value for value in (profile, target)) + or type(physical) is not str or re.fullmatch(r"[a-z0-9][a-z0-9.-]{0,63}", physical) is None + or binding.get("client_id") != "grok" or binding.get("scope") != "project" + or binding.get("activation") != "prepared" or binding.get("verification") != "package_validated" + or Path(profile).name != ".grok" or target != str(Path(profile) / "plugins" / physical)): + raise ValueError("Grok selected binding identity differs") + project, name = str(Path(profile).parent), "uap-grok-attempt" + skill = str(Path(target) / "skills" / name / "SKILL.md") + expected = {"name": name, "scope": "project", "path": target, "enabled": True, "project_trusted": True, + "installed_skill": skill, "interpretation": "exact selected plugin skill listed; no skill execution or lifecycle/runtime qualification"} + if (type(observation) is not dict or observation != expected or observation.get("enabled") is not True + or observation.get("project_trusted") is not True or result.get("installed_skill") != skill): + raise ValueError("Grok completed selected observation differs") + raw = bodies.get("native-inspect.stdout.log") + command = strict_json(bodies.get("native-inspect.command.json", b"null")) + version = strict_json(bodies.get("version.command.json", b"null")) + if (type(command) is not dict or type(version) is not dict or type(version.get("argv")) is not list + or len(version["argv"]) != 2 or version["argv"][1] != "--version" + or command.get("argv") != [version["argv"][0], "inspect", "--json"] or command.get("cwd") != project + or command.get("exit") != 0 or command.get("error") is not False or command.get("deadline") is not False + or bodies.get("native-inspect.stderr.log") != b"" or type(raw) is not bytes): + raise ValueError("Grok selected observation lacks its successful exact command") + inspect = strict_json(raw) + if (not grok_selection_identity_keys(inspect, "grokVersion cwd projectRoot projectTrusted skills plugins") + or inspect.get("grokVersion") != "1.0.70" or inspect.get("cwd") != project + or inspect.get("projectRoot") not in (project, project + "/") or inspect.get("projectTrusted") is not True + or type(inspect.get("plugins")) is not list): + raise ValueError("Grok selected raw project inspection differs") + seen, found = set(), False + for plugin in inspect["plugins"]: + if (not grok_selection_identity_keys(plugin, "name scope path enabled") or type(plugin.get("name")) is not str + or not plugin["name"].strip() or plugin["name"].casefold() in seen + or plugin.get("scope") not in ("project", "user") or type(plugin.get("path")) is not str + or not os.path.isabs(plugin["path"]) or os.path.normpath(plugin["path"]) != plugin["path"] + or type(plugin.get("enabled")) is not bool): + raise ValueError("Grok selected raw plugin identity differs") + seen.add(plugin["name"].casefold()) + if plugin["name"].casefold() == name.casefold(): + if plugin["name"] != name or plugin["scope"] != "project" or plugin["path"] != target or plugin["enabled"] is not True: + raise ValueError("Grok selected raw plugin differs") + found = True + if not found: + raise ValueError("Grok selected raw plugin is absent") + grok_selected_skill(inspect.get("skills"), name, skill) + + def cli_attempt_members(bodies, source, client): if client not in CLI_ATTEMPTS: raise ValueError("unknown fixed native CLI attempt") + result = strict_json(bodies.get("runtime-attempt.json", b"null")) + if type(result) is dict and result.get("capture_mode") == "grok-complete-skills-cycle": + if client != "grok": + raise ValueError("Grok complete skills cycle cannot apply to another client") + from project_native_grok_cycle import members + return members(bodies, source) allowed = {"runtime-attempt.json", "probe-events.jsonl"} - commands = CLI_COMMANDS + (("native-catalog",) if client == "qoder" else ()) + allowed |= grok_provider_members(bodies, client) + allowed |= grok_title_provider_members(bodies, client) + allowed |= grok_duplicate_members(bodies, client) + commands = CLI_COMMANDS + if client == "qoder": + commands += ("native-catalog",) + elif client == "grok": + commands += ("native-inspect",) for label in commands: triple = {label + suffix for suffix in (".command.json", ".stdout.log", ".stderr.log")} allowed |= triple @@ -271,6 +681,7 @@ def cli_attempt_members(bodies, source, client): if result.get("source_identity") != {"installer_base_commit": source["source_commit"], "installer_tree": source["source_tree"], "installer_source_state": "committed", "installer_patch_sha256": []}: raise ValueError("native CLI evidence source differs") + grok_selected_inspection(bodies, result, client) return result diff --git a/scripts/test_project_native_grok.py b/scripts/test_project_native_grok.py new file mode 100644 index 000000000..7c7f7a817 --- /dev/null +++ b/scripts/test_project_native_grok.py @@ -0,0 +1,258 @@ +"""Inert Grok pinned help contracts; no vendor download or execution.""" +import importlib.util +from pathlib import Path +import json +import os +import subprocess +import tempfile +import unittest +from unittest.mock import patch +import urllib.request +from urllib.parse import urlsplit + + +LEAF = Path(__file__).with_name("project_native_collectors") / "grok.py" + + +def load_leaf(): + spec = importlib.util.spec_from_file_location("grok_project_help", LEAF) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +grok = load_leaf() + + +def help_body(command): + name = "grok" + (" " + command if command else "") + suffix = " " if command in ("mcp", "plugin") else " [OPTIONS]" + return ("Usage: " + name + suffix + "\nOptions:\n --help\n" + + ("Commands:\n inspect\n mcp\n plugin\n" if not command else "")).encode() + + +class FakeContext: + phase = "help" + + def __init__(self, replacement=None): + self.calls, self.replacement = [], replacement or {} + + def probe(self, label, arguments, project="a", extra_env=None, timeout=30): + self.calls.append((label, arguments, project, extra_env, timeout)) + command = next(command for name, _, command in grok.PROBES if name == label) + # Inert grammar example, not a vendor commit observation. + stdout = b"grok 1.0.70 (0123456789ab)\n" if command is None else help_body(command) + return self.replacement.get(label, ({"exit": 0, "error": None}, stdout, b"")) + + +class ScopeContext(FakeContext): + phase = "observe" + + def __init__(self, root, replacement=None): + super().__init__(replacement) + self.root = Path(root) + self.home = self.root / "home" + self.projects = {scope: self.root / scope for scope in ("a", "b", "user")} + for directory in (self.home, *self.projects.values()): + directory.mkdir() + + def probe(self, label, arguments, project="a", extra_env=None, timeout=30): + if label in {row[0] for row in grok.PROBES}: + return super().probe(label, arguments, project, extra_env, timeout) + self.calls.append((label, arguments, project, extra_env, timeout)) + replacement = self.replacement.get(label) + if callable(replacement): + return replacement() + if replacement is not None: + return replacement + # Opaque inert input deliberately has no guessed vendor source schema. + body = b"{}\n" if arguments == ["inspect", "--json"] else b"[]\n" + return {"exit": 0, "error": None}, body, b"" + + +class GrokCollectorTests(unittest.TestCase): + def test_measured_official_archive_and_executable_pins_and_bounds(self): + self.assertEqual(grok.CLIENT, "grok") + self.assertEqual(grok.VERSION, "1.0.70") + self.assertEqual(grok.AVAILABLE_PHASES, ("acquire", "help", "observe")) + self.assertFalse(getattr(grok, "ACQUISITION_ONLY", False)) + expected = {"url": "https://x.ai/cli/grok-1.0.70-linux-x86_64.gz", + "integrity": "sha256:11d27a30adfb75c4b9b180072ab27412a1916f054ff5497ab70fb9a77d304bcc", + "expected_bytes": 76_760_282, + "binary_sha256": "22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9", + "max_bytes": 96 << 20, "max_expanded_bytes": 256 << 20, + "format": "gzip", "entrypoint": "grok", "runtime": "native"} + self.assertEqual(grok.ACQUISITION, expected) + url = urlsplit(grok.ACQUISITION["url"]) + self.assertFalse(url.query or url.fragment or url.username or url.password) + + def test_import_and_fake_help_have_no_process_network_or_profile_effect(self): + with (patch.object(subprocess, "Popen", side_effect=AssertionError("process execution")) as process, + patch.object(urllib.request, "urlopen", side_effect=AssertionError("network acquisition")) as download): + leaf = load_leaf() + ctx = FakeContext() + result = leaf.collect(ctx) + process.assert_not_called() + download.assert_not_called() + self.assertEqual(ctx.calls, [(label, list(args), "a", {"GROK_DISABLE_AUTOUPDATER": "1"}, 30) for label, args, _ in grok.PROBES]) + self.assertEqual(len(ctx.calls), 9) + self.assertEqual([row["id"] for row in result["cases"]], ["grok.help"]) + self.assertEqual(result["cases"][0]["status"], "passed") + self.assertIn("grok.scope-observation", result["unobserved"]) + self.assertIn("grok.mcp-tools-call", result["unobserved"]) + version = result["observations"]["grok-version"] + self.assertEqual(version["compiled_commit_observed"], "0123456789ab") + self.assertIsNone(version["channel_observed"]) + self.assertIs(version["shipping_source_equivalence_claimed"], False) + + def test_unregistered_phases_cannot_become_scope_support(self): + for phase in (None, "acquire", "runtime-attempt"): + with self.subTest(phase=phase): + ctx = FakeContext() + ctx.phase = phase + with self.assertRaisesRegex(ValueError, "explicit help or observe phase"): + grok.collect(ctx) + self.assertEqual(ctx.calls, []) + + def scope_context(self, replacement=None): + directory = tempfile.TemporaryDirectory() + self.addCleanup(directory.cleanup) + return ScopeContext(directory.name, replacement) + + def test_observe_runs_exact_fifteen_readonly_catalogs_and_keeps_qualification_false(self): + ctx = self.scope_context() + with (patch.object(subprocess, "Popen", side_effect=AssertionError("vendor execution")), + patch.object(urllib.request, "urlopen", side_effect=AssertionError("network"))): + result = grok.collect(ctx) + calls = ctx.calls[len(grok.PROBES):] + expected = [("grok-" + stage + "-" + project + "-" + kind, + list(arguments), project, {"GROK_DISABLE_AUTOUPDATER": "1"}, 30) + for stage, projects in (("empty", ("a", "user")), ("populated", ("a", "b", "user"))) + for project in projects for kind, arguments in grok.CATALOGS] + self.assertEqual(calls, expected) + self.assertTrue(all(call[3] == {"GROK_DISABLE_AUTOUPDATER": "1"} for call in ctx.calls)) + self.assertEqual([row["id"] for row in result["cases"]], ["grok.help", "grok.scope-observation"]) + facts = result["observations"]["opaque-scope-catalogs"] + self.assertEqual(len(facts["catalogs"]), 15) + self.assertEqual(len(facts["protected_files"]), 6) + self.assertFalse(any(value for key, value in facts.items() if key.endswith("_qualified"))) + self.assertNotIn("grok.scope-observation", result["unobserved"]) + for scope, root in (("user", ctx.home), ("a", ctx.projects["a"]), ("b", ctx.projects["b"])): + plugin = root / ".grok/plugins" / grok.NAMES[scope] + names = sorted(str(path.relative_to(plugin)) for path in plugin.rglob("*") if path.is_file()) + self.assertEqual(names, [".grok-plugin/plugin.json", "skills/" + grok.NAMES[scope] + "/SKILL.md"]) + manifest = json.loads((plugin / ".grok-plugin/plugin.json").read_text()) + self.assertEqual(set(manifest), {"name", "version", "description", "skills"}) + + def test_opaque_json_is_whole_bounded_duplicatefree_finite_and_structurally_bounded(self): + self.assertEqual(grok._json(b'{"opaque":[1,true,null,"x"]}'), {"opaque": [1, True, None, "x"]}) + for body in [b"", b"true", b"null", b"0", b"{} {}", b'{"x":1,"x":2}', + b'{"x":{"a":1,"a":2}}', b"[NaN]", b"[Infinity]", b"[1e9999]", + b"[\xff]", b"[" * 34 + b"0" + b"]" * 34, + b"[" + b"0," * 8192 + b"0]", b" " * (1 << 20) + b"[]"]: + with self.subTest(body=body[:80]), self.assertRaises((ValueError, UnicodeError)): + grok._json(body) + + def test_missing_or_failed_catalog_does_not_emit_research_success(self): + for response in [({"exit": 1, "error": None}, b"{}", b""), + ({"exit": 0, "error": "deadline"}, b"{}", b""), + ({"exit": 0, "error": None}, b"{}", b"Authentication required"), + ({"exit": 0, "error": None}, b"not json", b""), + ({"exit": False, "error": None}, b"{}", b"")]: + ctx = self.scope_context({"grok-populated-b-plugins": response}) + with self.subTest(response=response), self.assertRaises(ValueError): + grok.collect(ctx) + self.assertEqual(ctx.calls[-1][0], "grok-populated-b-plugins") + + def test_native_mutation_hardlink_and_parent_alias_fail_preservation(self): + for kind in ("mutation", "hardlink", "symlink-parent"): + ctx = self.scope_context() + def invalidated(): + plugin = ctx.home / ".grok/plugins" / grok.NAMES["user"] + manifest = plugin / ".grok-plugin/plugin.json" + if kind == "mutation": + manifest.write_text("{}\n") + elif kind == "hardlink": + os.link(manifest, ctx.root / "hardlink") + else: + parent = plugin / ".grok-plugin" + target = plugin / "moved" + parent.rename(target) + parent.symlink_to(target, target_is_directory=True) + return {"exit": 0, "error": None}, b"{}", b"" + ctx.replacement["grok-populated-a-inspect"] = invalidated + with self.subTest(kind=kind), self.assertRaises((ValueError, OSError)): + grok.collect(ctx) + self.assertEqual(ctx.calls[-1][0], "grok-populated-a-inspect") + + def test_observe_refuses_original_home_as_project_and_preexisting_alias(self): + ctx = self.scope_context() + ctx.projects["a"] = ctx.home + with self.assertRaisesRegex(ValueError, "original home"): + grok.collect(ctx) + self.assertEqual(len(ctx.calls), len(grok.PROBES)) + ctx = self.scope_context() + (ctx.home / ".grok").symlink_to(ctx.projects["a"], target_is_directory=True) + with self.assertRaises(OSError): + grok.collect(ctx) + self.assertEqual(len(ctx.calls), len(grok.PROBES) + 6) + + def test_source_version_grammar_is_whole_exact_and_does_not_invent_commit(self): + row = grok._version(b"grok 1.0.70 (abcdef0) [stable]\n") + self.assertEqual(row["compiled_commit_observed"], "abcdef0") + self.assertEqual(row["channel_observed"], "stable") + for body in (b"grok 1.0.67 (abcdef0)\n", b"grok 1.0.700 (abcdef0)\n", + b"grok 1.0.70 (abcdef0) [alpha]\n", b"grok 1.0.70 (abcdef0)", + b"grok 1.0.70 (ABCDEF0)\n", b"grok 1.0.70 (abcdef)\n", + b"grok 1.0.70 (" + b"a" * 41 + b")\n", + b"grok 1.0.70 (abcdef0)\nextra\n", b"1.0.70\n"): + with self.subTest(body=body): + ctx = FakeContext({"grok-version": ({"exit": 0, "error": None}, body, b"")}) + with self.assertRaisesRegex(ValueError, "version differs"): + grok.collect(ctx) + self.assertEqual(len(ctx.calls), 1) + + def test_wrong_command_duplicate_usage_and_missing_option_fail(self): + bodies = [help_body("mcp list"), b"Usage: grok mcpish [OPTIONS]\n--help\n", + help_body("mcp") + help_body("mcp"), b"Usage: grok mcp \n", + b"Usage: fake grok mcp \n--help\n", + b"Usage: grok mcp extra\n --help\n", + help_body("mcp").replace(b"--help", b"--helper"), + help_body("mcp").replace(b"--help", b"--helpful"), + b"Usage: grok mcp \n --help\n", "mcp") + grok._help("Usage: grok mcp list [OPTIONS]\n --help\n", "mcp list") + grok._help("Usage: grok inspect\n --help\n", "inspect") + with self.assertRaisesRegex(ValueError, "selected command surface"): + grok._help("Usage: grok [OPTIONS]\n --help\nCommands:\n inspect\n mcp\n", "") + with self.assertRaisesRegex(ValueError, "selected command surface"): + grok._help("Usage: grok [OPTIONS]\n --help\nCommands:\n inspectish\n mcpish\n pluginish\n", "") + grok._help("Usage: grok mcp doctor [OPTIONS] [NAME]\n -h, --help Print help\n", "mcp doctor") + grok._help("Usage: grok inspect ...\n -h, --help Print help\n", "inspect") + + def test_nonzero_missing_error_bool_exit_stderr_and_non_utf8_fail(self): + failures = [({"exit": 1, "error": None}, help_body("doctor"), b""), + ({"exit": 0, "error": "deadline"}, help_body("doctor"), b""), + ({"exit": False, "error": None}, help_body("doctor"), b""), + ({"exit": 0}, help_body("doctor"), b""), + ({"exit": 0, "error": None}, help_body("doctor"), b"Sign in"), + ({"exit": 0, "error": None}, b"\xff", b""), + ({"exit": 0, "error": None}, b"", b"")] + for response in failures: + with self.subTest(response=response): + ctx = FakeContext({"grok-doctor-help": response}) + with self.assertRaises(ValueError): + grok.collect(ctx) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_project_native_grok_cycle.py b/scripts/test_project_native_grok_cycle.py new file mode 100644 index 000000000..e629b77a8 --- /dev/null +++ b/scripts/test_project_native_grok_cycle.py @@ -0,0 +1,399 @@ +"""Authored parser inputs and hostile joins; no native execution/qualification.""" +import base64 +import copy +import json +import unittest + +import project_native_grok_cycle as cycle +import project_native_runtime as runtime + +SOURCE = {"source_commit": "a" * 40, "source_tree": "b" * 40} +ROOT, NONCE = "/inert/grok-cycle", "1" * 32 + + +def raw(value): + return json.dumps(value, sort_keys=True, separators=(",", ":")).encode() + + +def stream(body): + return {"bytes": len(body), "sha256": cycle.sha(body), "body": base64.b64encode(body).decode()} + + +def snap(files, mode=0o600, directories=0o700): + paths = {"."} + for path in files: + paths.add(path) + paths.update(str(parent) for parent in cycle.PurePosixPath(path).parents) + result = {} + for index, path in enumerate(sorted(paths)): + file = path in files + entry = {"kind": "file" if file else "directory", "mode": mode if file else directories, + "uid": 1000, "gid": 1000, "device": 1, "inode": index + 1, "modified_ns": 100 if file else 0, "changed_ns": 100 if file else 0} + if file: + entry["content"] = stream(files[path]) + result[path] = entry + return result + + +def mutation(bound, group, sequence, kind, before="", after=""): + operation = group + "-001" + suffix = cycle.sha(operation.encode())[:16] + row = {"operation_id": operation, "operation_group_id": group, "sequence": sequence, + "mutation_type": kind, "client_binding_id": bound["client_binding_id"], "phase": "committed", + "active_path": bound["target_locator"], "backup_path": str(cycle.PurePosixPath(bound["target_locator"]).parent / (".agentplugins-backup-" + suffix))} + if kind == "directory_swap": row["staging_path"] = bound["native_profile_root"] + "/.agentplugins-staging-" + suffix + if before: row["before_digest"] = before + if after: row["after_digest"] = after + return row + + +def inputs(): + """A complete independent authored container, never genuine hosted proof.""" + paths = {"home": ROOT + "/home", "project_a": ROOT + "/project", "project_b": ROOT + "/project-b", "outside": ROOT + "/outside"} + config = b'[marketplace]\ndefault_skills_installs_purged = true\n[plugins]\nenabled = ["uap-grok-cycle-a", "uap-grok-cycle-b", "uap-grok-original-user"]\ndisabled = ["uap-grok-foreign-a", "uap-grok-foreign-b"]\n[features]\nturn_summary = false\n' + trust = "".join('[folders."' + paths[key] + '"]\ntrusted = true\n' for key in ("project_a", "project_b")).encode() + fixed = {paths["home"] + "/.grok/config.toml": snap({".": config}), paths["home"] + "/.grok/trusted_folders.toml": snap({".": trust})} + for who in ("a", "b", "user"): + name = "uap-grok-original-user" if who == "user" else "uap-grok-foreign-" + who + native = paths["home"] if who == "user" else paths["project_" + who] + desc = "Original user " + NONCE if who == "user" else "Foreign inert marker" + marker = "USER_" + NONCE if who == "user" else "FOREIGN_" + who + "_" + NONCE + fixed[native + "/.grok/plugins/" + name] = snap({".grok-plugin/plugin.json": raw({"name": name, "version": "0.9.0", "skills": "./skills/"}), "skills/" + name + "/SKILL.md": f"---\nname: {name}\ndescription: {desc}\n---\n{marker}\n".encode()}) + if who != "user": + fixed[native + "/.uap/plugin-data/caller-owned"] = snap({"keep.txt": f"CALLER_{who}_{NONCE}\n".encode()}) + proof, current = {}, {} + for stage in cycle.PROOF: + if stage == "fixture": + proof[stage] = {"root": ROOT, **paths, "nonce": NONCE, "fixed": fixed} + continue + if stage.startswith("source-"): + _, who, revision = stage.split("-", 2) + files = cycle.authored(who, revision, NONCE) + proof[stage] = {"scope": who, "source": ROOT + "/cycle-source-" + who, "revision": revision, "digest": cycle.tree_digest(files, True), "files": snap(files)} + continue + if stage == "final": + proof[stage] = {"preserved": fixed, **{"retained_" + who: current[who]["payload"] for who in ("a", "b")}, **{"absent_" + who: current[who]["binding"]["target_locator"] for who in ("a", "b")}} + continue + if stage == "damaged-a": + proof[stage] = {"scope": "a", "removed_path": current["a"]["binding"]["target_locator"], "before": current["a"]["payload"], "absent": True, "preserved": fixed} + continue + who = "b" if stage.endswith("-b") else "a" + if stage in ("repeat-add-a", "unchanged-update-a", "before-source-revision-a", "before-missing-a", "repeat-remove-a"): + proof[stage] = copy.deepcopy(current[who]) + continue + revision = "b-one" if who == "b" else "a-two" if cycle.PROOF.index(stage) >= cycle.PROOF.index("update-a") else "a-one" + name = "uap-grok-cycle-" + who + iid = who * 8 + "-1111-4111-8111-" + who * 12 + physical = name + "-" + cycle.sha(iid.encode())[:12] + active = paths["project_" + who] + "/.grok/plugins/" + physical + bid = "client_" + cycle.sha("\0".join((iid, "grok", "project", active)).encode())[:24] + source = proof["source-" + who + "-" + revision] + manifest = "sha256:" + cycle.sha(cycle.authored(who, revision, NONCE)["plugin.json"]) + stamp = "2026-10-28T00:00:00Z" + b = {"client_binding_id": bid, "client_id": "grok", "scope": "project", "target_locator": active, "physical_artifact_id": physical, + "materialization": "materialized", "activation": "prepared", "authentication": "not_checked", "policy": "allowed", "verification": "package_validated", + "affected_surfaces": ["grok"], "native_profile_root": paths["project_" + who] + "/.grok", "updated_at": stamp, + "package_revision": {"version": "1.0.0", "tree_digest": source["digest"], "manifest_digest": manifest}, + "native_objects": [{"object_id": "package:grok:" + physical, "kind": "managed_package_directory", "logical_name": name, "path": active, "protection_class": "managed", "managed_digest": cycle.tree_digest(cycle.projected(who, revision, NONCE), False)}]} + group = "op-" + cycle.sha(stage.encode())[:32] + removed = stage.startswith("remove-") + previous = current.get(who) + if removed: + b = copy.deepcopy(previous["binding"]) + else: + b["receipts"] = copy.deepcopy(previous["binding"]["receipts"]) if previous else [] + before = previous["binding"]["native_objects"][0]["managed_digest"] if previous and stage != "missing-repair-a" else "" + b["receipts"].append(mutation(b, group, len(b["receipts"]) + 1, "directory_swap", before, b["native_objects"][0]["managed_digest"])) + ins = {"installation_id": iid, "declared_name": name, "origin_mode": "direct", "operation_group_id": group, "created_at": stamp, "updated_at": stamp, + "source": {"source_binding_id": "src_" + cycle.sha((source["source"] + "\0\0").encode())[:24], "requested_source": source["source"], "canonical_source": source["source"], "resolved_revision": "", "tree_digest": source["digest"]}, + "package": {"loader_kind": "agent_plugins", "format_id": "agent-plugins/1.0.0", "schema_uri": cycle.SCHEMA, "declared_name": name, "version": "1.0.0", "manifest_digest": manifest, "inventory": {"mcp_present": False, "mcp_enabled": False, "skills": [name]}}, "clients": {} if removed else {bid: b}} + payload = snap(cycle.projected(who, revision, NONCE), 0o644, 0o755) + if removed: + did = "data_" + cycle.sha("\0".join((iid, physical, "project")).encode())[:24] + path = paths["project_" + who] + "/.uap/plugin-data/" + physical + receipt = {"data_receipt_id": did, "physical_backend_id": physical, "scope": "project", "locator": path, "ownership_digest": "sha256:" + cycle.sha(("agentplugins-plugin-data-v1\0" + did + "\0" + path).encode()), "state": "owned", "created_at": stamp, "updated_at": stamp} + ins.update(data_retained=True, data_receipts={did: receipt}) + payload = snap({".agentplugins-data-owner.json": raw(receipt) + b"\n"}) + state = {"schema_version": 5, "installations": [ins]} + if removed: + state["transaction_receipts"] = [mutation(b, group, len(b["receipts"]) + 1, "directory_remove", b["native_objects"][0]["managed_digest"])] + value = {"scope": who, "revision": revision, "removed": removed, "state": stream(raw(state)), "binding": b, "source_digest": source["digest"], "payload": payload, "preserved": fixed} + proof[stage], current[who] = value, value + return paths, proof + + +def container(): + paths, proof = inputs() + public, inspections = [], [] + def command(argv, cwd, out=b"", err=b"", exit_code=0): + return {"argv": argv, "cwd": cwd, "exit": exit_code, "error": False, "deadline": False, "elapsed_ms": 10, + "cleanup": "full descendant supervision; forced cleanup fails", "stdout": stream(out), "stderr": stream(err)} + for stage in cycle.PUBLIC: + who = "b" if stage.endswith("-b") else "a" + cwd = paths["outside"] if stage in ("git-outside", "version") else paths["project_" + who] + out, err, exit_code = b"", b"", 0 + if stage.startswith("git-"): + argv = ["/held/git", "-c", "core.hooksPath=/dev/null", "init", "--quiet"] + elif stage == "version": + argv, out = ["/held/grok", "--version"], b"grok 1.0.70 (f50904eaee6c)\n" + else: + verb = "repair" if "repair" in stage else stage.removeprefix("repeat-").removeprefix("unchanged-").split("-")[0] + p = proof[stage] + ins = cycle.load(cycle.stream(p["state"]))["installations"][0] + name = "uap-grok-cycle-" + who + selector = ROOT + "/cycle-source-" + who if verb == "add" or stage == "update-a" else ins["installation_id"] if verb == "repair" else name + argv = ["/held/installer", verb, selector] + (["--no-save"] if verb == "add" else []) + ["--scope", "project", "--project-root", cwd, "--target", "grok", "--format", "json"] + if stage == "repeat-remove-a": + exit_code, err = 1, b'agentplugins: plugin is not installed for target "grok" in project scope; no target was changed\n' + else: + group = "op-" + cycle.sha(stage.encode())[:32] + output = {"operation_id": group, "plugin": name, "version": "1.0.0", "source": "local" if verb in ("repair", "remove") else "direct local source", "tree_digest": p["source_digest"], "manifest_digest": ins["package"]["manifest_digest"], "dry_run": False, + "result": {"installation_id": ins["installation_id"], "requires_confirmation": False, "mutated": stage not in ("repeat-add-a", "unchanged-update-a")}} + target = {"target": "grok", "status": "external_completed", "output": output} + if verb == "update": target["selected"] = True + data = {"operation_id": group, "batch": True, "status": "data_retained" if verb == "remove" else "completed", "succeeded": 1, "failed": 0, "plugin": name, "dry_run": False, "targets": [target]} + if stage == "repeat-add-a": + del output["operation_id"] + output["result"]["no_change"] = True + data = output + if verb == "remove": + receipt = next(iter(ins["data_receipts"].values())) + data.update(plugin_data_preserved=True, data_retained=True, retained_data=[{key: receipt[key] for key in ("data_receipt_id", "physical_backend_id", "scope", "state")}]) + out = raw({"schema_version": 1, "command": verb, "result": "success", "data": data}) + if stage.startswith("add-"): err = f"Resolving and validating one Agent Plugin package for every selected target...\nInstalling {name} for 1 selected clients...\n".encode() + elif stage == "repeat-add-a": err = b"Resolving and validating one Agent Plugin package for every selected target...\nApplying transactional client package...\n" + elif verb == "update": err = b"Resolving and validating one updated Agent Plugin package for every selected target...\n" + elif verb == "repair": err = b"Resolving and validating each unique exact installed package revision once...\n" + public.append({"stage": stage, "value": command(argv, cwd, out, err, exit_code)}) + for stage in cycle.INSPECT: + who = "outside" if stage.endswith("outside") else stage[-1] + cwd = paths["outside"] if who == "outside" else paths["project_" + who] + user = paths["home"] + "/.grok/plugins/uap-grok-original-user" + selected = [("uap-grok-original-user", "user", user, "Original user " + NONCE, True, user + "/./skills/uap-grok-original-user/SKILL.md")] + if who != "outside": + name = "uap-grok-foreign-" + who + selected.append((name, "project", cwd + "/.grok/plugins/" + name, "Foreign inert marker", False, "")) + if stage in ("installed-a", "installed-b", "stable-a", "updated-a", "updated-b", "repaired-a", "remaining-b"): + key = "add-b" if who == "b" else "update-a" if stage in ("updated-a", "repaired-a") else "add-a" + p = proof[key] + name, active = "uap-grok-cycle-" + who, p["binding"]["target_locator"] + selected.append((name, "project", active, p["revision"] + " " + NONCE, True, active + "/skills/" + name + "/SKILL.md")) + plugins = [{"name": name, "scope": scope, "path": path, "enabled": True, "provides": {"skills": 1, "agents": 0, "hooks": False, "mcpServers": 0}} for name, scope, path, _, _, _ in selected] + skills = [{"name": name, "description": desc, "source": {"type": "plugin", "plugin_name": name, "path": skill_path}, "userInvocable": True} for name, _, _, desc, enabled, skill_path in selected if enabled] + out = raw({"grokVersion": "1.0.70", "cwd": cwd, "projectRoot": cwd + "/", "projectTrusted": True, "mcpServers": [], "plugins": plugins, "skills": skills}) + inspections.append({"stage": stage, "value": command(["/held/grok", "inspect", "--json"], cwd, out)}) + result = {"client": "grok", "version": "1.0.70", "capture_mode": cycle.MODE, "attempt_stage": "complete-cycle", "runtime_attempt_complete": True, + "activation": "prepared", "vendor_authentication_qualified": False, "real_model_qualified": False, "test_failed": False, "cycle_observed_complete": True, "elapsed_ms": 1000, + "qualification": {key: key in cycle.FLAGS[:2] for key in cycle.FLAGS}, "source_identity": {"installer_base_commit": SOURCE["source_commit"], "installer_tree": SOURCE["source_tree"], "installer_source_state": "committed", "installer_patch_sha256": []}, + "cycle_public_stages": list(cycle.PUBLIC), "cycle_inspect_stages": list(cycle.INSPECT), "cycle_proof_stages": list(cycle.PROOF), + "acquisition": {"client": "grok", "node": None, "acquisition": {"sha256": "11d27a30adfb75c4b9b180072ab27412a1916f054ff5497ab70fb9a77d304bcc", "pin_verified": True, + "members": [{"Name": "grok", "Kind": "file", "Bytes": 195700192, "sha256": "22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9"}]}, "runtime_files": [{"path": "/held/grok", "sha256": "22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9"}]}, + "scanner": {"archive_sha256": "2b3d176db752433b904a4b42375543ff398f4841d22e48f7d4f23ded925b72da", "binary_sha256": "b6723451f155eb49e84acb34d1f06de929bddfcff6238cd4e38cdc6519396fba", "platform": "linux-amd64", "route": "prepopulated supported scanner cache; ordinary CLI security assessment", "source_tag": "v0.1.3", "version": "0.1.3"}} + rows = public, inspections, [{"stage": stage, "value": proof[stage]} for stage in cycle.PROOF] + bodies = {name: b"".join(raw(row) + b"\n" for row in captured) for name, captured in zip(cycle.MEMBERS, rows)} + bodies["runtime-attempt.json"] = raw(result) + return bodies + + +class GrokCycleContracts(unittest.TestCase): + def test_complete_four_member_parser_input_closes_only_two_flags(self): + bodies = container() + result = runtime.cli_attempt_members(bodies, SOURCE, "grok") + self.assertTrue(result["runtime_attempt_complete"]) + self.assertEqual(sum(result["qualification"].values()), 2) + self.assertEqual(len(bodies), 4) + self.assertFalse(result["real_model_qualified"]) + self.assertFalse(result["vendor_authentication_qualified"]) + + def test_empty_outside_requires_exact_provisional_allow_without_durable_grant(self): + paths, proof = inputs() + trust_path = paths["home"] + "/.grok/trusted_folders.toml" + trust = cycle.stream(proof["fixture"]["fixed"][trust_path]["."]["content"]) + expected = "".join('[folders."' + paths[key] + '"]\ntrusted = true\n' for key in ("project_a", "project_b")).encode() + self.assertEqual(trust, expected) + self.assertNotIn(paths["outside"].encode(), trust) + for stage in (stage for stage in cycle.INSPECT if stage.endswith("outside")): + bodies = container() + rows = [cycle.load(line) for line in bodies[cycle.MEMBERS[1]].splitlines()] + row = next(row for row in rows if row["stage"] == stage) + inspection = cycle.load(cycle.stream(row["value"]["stdout"])) + self.assertIs(inspection["projectTrusted"], True) + inspection["projectTrusted"] = False + row["value"]["stdout"] = stream(raw(inspection)) + bodies[cycle.MEMBERS[1]] = b"".join(raw(row) + b"\n" for row in rows) + with self.subTest(stage=stage), self.assertRaises(ValueError): + runtime.cli_attempt_members(bodies, SOURCE, "grok") + + def test_missing_stage_failed_command_forged_qualification_and_foreign_scope_refuse(self): + for fault in ("stage", "command", "cwd", "elapsed", "flag", "source", "extra", "missing-proof", "native-absent", "public-id"): + bodies = container() + result = cycle.load(bodies["runtime-attempt.json"]) + if fault == "flag": result["qualification"]["mcp-tools-call"] = True + elif fault == "source": result["source_identity"]["installer_tree"] = "0" * 40 + elif fault == "extra": bodies["unrecorded.log"] = b"extra" + elif fault == "missing-proof": bodies[cycle.MEMBERS[2]] = b"" + else: + name = cycle.MEMBERS[1] if fault == "native-absent" else cycle.MEMBERS[0] + rows = [cycle.load(line) for line in bodies[name].splitlines()] + if fault == "stage": rows.pop() + elif fault == "command": rows[0]["value"]["error"] = True + elif fault == "cwd": rows[0]["value"]["cwd"] = "/foreign" + elif fault == "elapsed": rows[0]["value"]["elapsed_ms"] = 35001 + elif fault == "native-absent": + native = cycle.load(cycle.stream(rows[-1]["value"]["stdout"])) + native["skills"].append(copy.deepcopy(native["skills"][0])) + rows[-1]["value"]["stdout"] = stream(raw(native)) + else: + output = cycle.load(cycle.stream(rows[4]["value"]["stdout"])) + output["data"]["targets"][0]["output"]["result"]["installation_id"] = "foreign" + rows[4]["value"]["stdout"] = stream(raw(output)) + bodies[name] = b"".join(raw(row) + b"\n" for row in rows) + bodies["runtime-attempt.json"] = raw(result) + with self.subTest(fault=fault), self.assertRaises(ValueError): runtime.cli_attempt_members(bodies, SOURCE, "grok") + with self.assertRaises(ValueError): runtime.cli_attempt_members(container(), SOURCE, "qoder") + + def test_authored_complete_source_state_projection_and_real_retained_shape(self): + paths, proof = inputs() + parsed_paths, nonce, completed = cycle.proofs([{"stage": stage, "value": proof[stage]} for stage in cycle.PROOF]) + self.assertEqual(paths, parsed_paths) + self.assertEqual(nonce, NONCE) + self.assertTrue(completed["remove-a"]["removed"]) + self.assertEqual(completed["missing-repair-a"]["source_digest"], completed["update-a"]["source_digest"]) + self.assertNotEqual(completed["add-a"]["source_digest"], completed["update-a"]["source_digest"]) + + def test_source_state_native_owner_and_original_authority_mutations_refuse(self): + for fault in ("source-digest", "foreign-inode", "foreign-bytes", "binding-path", "scope", "native-digest", "retained-data", "missing-partial", "final-stale", "changed-noop"): + _, p = inputs() + if fault == "source-digest": p["source-a-a-two"]["digest"] = "sha256:" + "0" * 64 + elif fault == "foreign-inode": + key = ROOT + "/home/.grok/plugins/uap-grok-original-user" + p["update-a"]["preserved"] = copy.deepcopy(p["update-a"]["preserved"]) + p["update-a"]["preserved"][key]["."]["inode"] += 1 + elif fault == "foreign-bytes": + key = ROOT + "/project/.uap/plugin-data/caller-owned" + p["fixture"]["fixed"][key]["keep.txt"]["content"] = stream(b"foreign") + elif fault == "binding-path": p["update-a"]["binding"]["target_locator"] = "/foreign" + elif fault == "scope": p["update-a"]["scope"] = "b" + elif fault == "native-digest": p["add-a"]["binding"]["native_objects"][0]["managed_digest"] = "sha256:" + "0" * 64 + elif fault == "retained-data": p["remove-a"]["payload"][".agentplugins-data-owner.json"]["content"] = stream(b"{}") + elif fault == "missing-partial": p["damaged-a"]["removed_path"] += "/skills/uap-grok-cycle-a/SKILL.md" + elif fault == "final-stale": p["final"]["absent_a"] = "/foreign" + else: p["repeat-add-a"]["state"] = stream(cycle.stream(p["repeat-add-a"]["state"]) + b" ") + with self.subTest(fault=fault), self.assertRaises(ValueError): + cycle.proofs([{"stage": stage, "value": p[stage]} for stage in cycle.PROOF]) + + def test_transaction_receipt_types_paths_sequences_history_and_public_groups_refuse(self): + faults = ("type", "staging", "backup", "active", "operation", "group", "sequence-bool", "sequence", "phase", "binding", "before", "after", "extra", "history", "count", "state-receipt", "remove-staging", "remove-after", "remove-backup", "remove-sequence", "latest-group") + for fault in faults: + _, proof = inputs() + stage = "remove-a" if fault.startswith("remove-") or fault == "state-receipt" else "update-a" + value = proof[stage] + state = cycle.load(cycle.stream(value["state"])) + bound = value["binding"] + target = state["transaction_receipts"][0] if stage == "remove-a" else bound["receipts"][-1] + changes = {"type": ("mutation_type", "foreign_write"), "staging": ("staging_path", "/foreign"), "backup": ("backup_path", "/foreign"), "active": ("active_path", "/foreign"), + "operation": ("operation_id", target["operation_group_id"] + "-002"), "group": ("operation_group_id", "op-" + "0" * 32), "sequence-bool": ("sequence", True), "sequence": ("sequence", 1), + "phase": ("phase", "state_committed"), "binding": ("client_binding_id", "foreign"), "before": ("before_digest", "sha256:" + "0" * 64), "after": ("after_digest", "sha256:" + "0" * 64), "extra": ("foreign", True), + "remove-staging": ("staging_path", "/foreign"), "remove-after": ("after_digest", target.get("before_digest")), "remove-backup": ("backup_path", "/foreign"), "remove-sequence": ("sequence", 1)} + if fault in changes: + key, change = changes[fault] + target[key] = change + elif fault == "history": + old = bound["receipts"][0] + bound["receipts"][0] = mutation(bound, "op-" + "0" * 32, 1, "directory_swap", "", old["after_digest"]) + elif fault == "count": bound["receipts"].pop(0) + elif fault == "state-receipt": state["transaction_receipts"] = [] + else: state["installations"][0]["operation_group_id"] = "op-" + "0" * 32 + if stage != "remove-a": state["installations"][0]["clients"][bound["client_binding_id"]] = copy.deepcopy(bound) + value["state"] = stream(raw(state)) + with self.subTest(fault=fault), self.assertRaises(ValueError): + cycle.proofs([{"stage": label, "value": proof[label]} for label in cycle.PROOF]) + for fault in ("missing", "foreign", "output", "reuse"): + bodies = container() + rows = [cycle.load(line) for line in bodies[cycle.MEMBERS[0]].splitlines()] + row = rows[7 if fault == "reuse" else 4]["value"] + document = cycle.load(cycle.stream(row["stdout"])) + if fault == "missing": del document["data"]["operation_id"] + elif fault == "foreign": document["data"]["operation_id"] = "op-" + "0" * 32 + elif fault == "output": document["data"]["targets"][0]["output"]["operation_id"] = "op-" + "0" * 32 + else: + previous = cycle.load(cycle.stream(rows[4]["value"]["stdout"]))["data"]["operation_id"] + document["data"]["operation_id"] = previous + document["data"]["targets"][0]["output"]["operation_id"] = previous + row["stdout"] = stream(raw(document)) + bodies[cycle.MEMBERS[0]] = b"".join(raw(item) + b"\n" for item in rows) + with self.subTest(fault=fault), self.assertRaises(ValueError): cycle.members(bodies, SOURCE) + + def test_public_singleton_and_installed_source_remain_stage_bound(self): + bodies = container() + rows = {row["stage"]: row["value"] for row in map(cycle.load, bodies[cycle.MEMBERS[0]].splitlines())} + _, proof = inputs() + for stage in ("repeat-add-a", "intact-repair-a", "missing-repair-a", "remove-a", "remove-b"): + verb = "add" if stage == "repeat-add-a" else "repair" if "repair" in stage else "remove" + document = cycle.load(cycle.stream(rows[stage]["stdout"])) + cycle.public_result(raw(document), stage, verb, proof[stage]) + for fault in ("source", "digest", "scope", "mutated", "group"): + bad = copy.deepcopy(document) + output = bad["data"] if stage == "repeat-add-a" else bad["data"]["targets"][0]["output"] + if fault == "source": output["source"] = "local" if stage == "repeat-add-a" else "direct local source" + elif fault == "digest": output["tree_digest"] = "sha256:" + "0" * 64 + elif fault == "scope": output["result"]["scope"] = "user" + elif fault == "mutated": output["result"]["mutated"] = stage == "repeat-add-a" + else: output["operation_id"] = "op-" + "0" * 32 + with self.subTest(stage=stage, fault=fault), self.assertRaises(ValueError): + cycle.public_result(raw(bad), stage, verb, proof[stage]) + singleton = cycle.stream(rows["repeat-add-a"]["stdout"]) + with self.assertRaises(ValueError): cycle.public_result(singleton, "add-a", "add", proof["add-a"]) + + def test_failed_prefix_retains_unknown_native_output_without_qualification(self): + bodies = container() + summary = cycle.load(bodies["runtime-attempt.json"]) + summary.update(runtime_attempt_complete=False, cycle_observed_complete=False, test_failed=True, qualification={key: False for key in cycle.FLAGS}) + public = [cycle.load(line) for line in bodies[cycle.MEMBERS[0]].splitlines()][:4] + native = [cycle.load(bodies[cycle.MEMBERS[1]].splitlines()[0])] + native[0]["value"]["stdout"] = stream(b'{"unknownShippingShape":true}') + for name, rows, field in zip(cycle.MEMBERS, (public, native, []), ("cycle_public_stages", "cycle_inspect_stages", "cycle_proof_stages")): + bodies[name] = b"".join(raw(row) + b"\n" for row in rows) + summary[field] = [row["stage"] for row in rows] + bodies["runtime-attempt.json"] = raw(summary) + self.assertEqual(cycle.members(bodies, SOURCE), summary) + self.assertFalse(any(summary["qualification"].values())) + self.assertEqual(cycle.stream(cycle.load(bodies[cycle.MEMBERS[1]].splitlines()[0])["value"]["stdout"]), b'{"unknownShippingShape":true}') + + def test_raw_and_encoded_private_controls_before_base64_and_after_decode(self): + for body in (b"uap-grok-private-inert", b'{"key":"\\u0075ap-grok-private-inert"}', b'{}\n{"key":"\\u0075ap-grok-private-inert"}\n'): + self.assertTrue(runtime.grok_private_credential(body)) + with self.assertRaises(ValueError): cycle.stream(stream(body)) + self.assertEqual(cycle.stream(stream(b"ordinary\n")), b"ordinary\n") + for key, value in (("bytes", True), ("sha256", "0" * 64), ("body", "Zh=="), ("extra", False)): + altered = {**stream(b"f"), key: value} + with self.assertRaises(ValueError): cycle.stream(altered) + + def test_whole_native_catalog_joins_and_disabled_foreign_not_trust_bit(self): + expected = [("user", "user", "/owned/home/plugin", "Original marker", 1, True, "/owned/home/plugin/./skills/user/SKILL.md"), + ("owned", "project", "/owned/A/owned", "Owned marker", 1, True, "/owned/A/owned/skills/owned/SKILL.md"), + ("foreign", "project", "/owned/A/foreign", "Foreign inert marker", 1, False, "")] + row = {"grokVersion": "1.0.70", "cwd": "/owned/A", "projectRoot": "/owned/A/", "projectTrusted": True, "mcpServers": [], + "plugins": [{"name": name, "scope": scope, "path": path, "enabled": True, "provides": {"skills": count, "agents": 0, "hooks": False, "mcpServers": 0}} for name, scope, path, _, count, _, _ in expected], + "skills": [{"name": name, "description": desc, "userInvocable": True, "source": {"type": "plugin", "plugin_name": name, "path": skill_path}} for name, _, _, desc, _, loaded, skill_path in expected if loaded]} + cycle.inspect(raw(row), "/owned/A", True, expected) + for index, paths in enumerate((("/owned/home/plugin/skills/user/SKILL.md", "/owned/home/plugin/../plugin/skills/user/SKILL.md", "/owned/home/plugin/././skills/user/SKILL.md", "/foreign/./skills/user/SKILL.md"), + ("/owned/A/owned/./skills/owned/SKILL.md", "/owned/A/owned/../owned/skills/owned/SKILL.md", "/owned/A/owned/././skills/owned/SKILL.md", "/foreign/skills/owned/SKILL.md"))): + for path in paths: + bad = copy.deepcopy(row) + bad["skills"][index]["source"]["path"] = path + with self.subTest(index=index, path=path), self.assertRaises(ValueError): cycle.inspect(raw(bad), "/owned/A", True, expected) + for fault in ("foreign-loaded", "source", "alias", "duplicate", "count", "trusted", "disabled"): + bad = copy.deepcopy(row) + if fault == "foreign-loaded": bad["skills"].append({**bad["skills"][0], "name": "foreign"}) + elif fault == "source": bad["skills"][0]["source"]["path"] = "/foreign" + elif fault == "alias": bad["ProjectTrusted"] = False + elif fault == "duplicate": bad["plugins"][1] = bad["plugins"][0] + elif fault == "count": bad["plugins"][0]["provides"]["skills"] = True + elif fault == "trusted": bad["projectTrusted"] = False + else: bad["skills"][0]["disabled"] = True + with self.subTest(fault=fault), self.assertRaises(ValueError): cycle.inspect(raw(bad), "/owned/A", True, expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_project_native_runtime.py b/scripts/test_project_native_runtime.py index d2994c490..d511e4e54 100644 --- a/scripts/test_project_native_runtime.py +++ b/scripts/test_project_native_runtime.py @@ -129,6 +129,93 @@ def test_fixed_cli_attempts_cannot_turn_captured_output_into_success(self): with self.assertRaises(ValueError): runtime.cli_attempt_members({"runtime-attempt.json": json.dumps({**result, **change}).encode()}, self.source, client) + def grok_selected_bodies(self): + project, name, target = "/owned/project", "uap-grok-attempt", "/owned/project/.grok/plugins/selected-artifact" + skill = target + "/skills/" + name + "/SKILL.md" + result = {**self.cli_result("grok"), "fixture_selection": "owned-project-trust-and-selected-plugin-name", + "binding": {"client_id": "grok", "scope": "project", "activation": "prepared", "verification": "package_validated", + "native_profile_root": project + "/.grok", "target_locator": target, "physical_artifact_id": "selected-artifact"}, + "installed_skill": skill, "native_inspection": {"name": name, "scope": "project", "path": target, "enabled": True, + "project_trusted": True, "installed_skill": skill, + "interpretation": "exact selected plugin skill listed; no skill execution or lifecycle/runtime qualification"}} + inspect = {"grokVersion": "1.0.70", "cwd": project, "projectRoot": project + "/", "projectTrusted": True, + "plugins": [{"name": name, "scope": "project", "path": target, "enabled": True}], + "skills": [{"name": name, "description": "Harmless installed startup marker", "userInvocable": True, + "source": {"type": "plugin", "plugin_name": name, "path": skill}}]} + bodies = {"runtime-attempt.json": json.dumps(result).encode()} + for label, argv, cwd, output in (("version", ["/held/grok", "--version"], "/owned/outside", b"grok 1.0.70 (f50904eaee6c)\n"), + ("native-inspect", ["/held/grok", "inspect", "--json"], project, json.dumps(inspect).encode())): + bodies.update({label + ".stdout.log": output, label + ".stderr.log": b"", label + ".command.json": json.dumps({ + "argv": argv, "cwd": cwd, "exit": 0, "error": False, "deadline": False, "elapsed_ms": 1, + "cleanup": "full descendant supervision; forced cleanup fails"}).encode()}) + return bodies + + def test_grok_selected_observation_is_joined_and_still_failed_only(self): + bodies = self.grok_selected_bodies() + result = runtime.cli_attempt_members(bodies, self.source, "grok") + self.assertTrue(result["test_failed"]) + self.assertFalse(result["runtime_attempt_complete"]) + self.assertFalse(any(result["qualification"].values())) + for change in ({"runtime_attempt_complete": True}, {"test_failed": False}, {"vendor_authentication_qualified": True}, + {"real_model_qualified": True}, {"fixture_selection": None}, {"fixture_selection": "global-trust"}, + {"native_inspection": None}, {"installed_skill": "/foreign/SKILL.md"}, {"binding": {}}): + with self.subTest(change=change), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "runtime-attempt.json": json.dumps({**result, **change}).encode()}, self.source, "grok") + unmarked = dict(result) + del unmarked["fixture_selection"] + with self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "runtime-attempt.json": json.dumps(unmarked).encode()}, self.source, "grok") + for key, value in (("enabled", 1), ("project_trusted", 1)): + changed = {**result, "native_inspection": {**result["native_inspection"], key: value}} + with self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "runtime-attempt.json": json.dumps(changed).encode()}, self.source, "grok") + + def test_grok_selected_raw_cannot_replace_owned_skill_with_trust_only(self): + for kind in ("no-skill", "duplicate-skill", "foreign-path", "foreign-plugin", "disabled-skill", "not-invocable", "description", "alias", "trust-only", "plugin-disabled", "wrong-command", "wrong-cwd", "failed-command", "stderr", "missing-version"): + bodies = self.grok_selected_bodies() + inspect = json.loads(bodies["native-inspect.stdout.log"]) + skill = inspect["skills"][0] + if kind == "no-skill": inspect["skills"] = [] + elif kind == "duplicate-skill": inspect["skills"].append(skill) + elif kind == "foreign-path": skill["source"]["path"] = "/foreign/SKILL.md" + elif kind == "foreign-plugin": skill["source"]["plugin_name"] = "foreign" + elif kind == "disabled-skill": skill["disabled"] = True + elif kind == "not-invocable": skill["userInvocable"] = False + elif kind == "description": skill["description"] = "foreign" + elif kind == "alias": skill["source"]["plugin_Name"] = skill["name"] + elif kind == "trust-only": inspect["projectTrusted"] = False + elif kind == "plugin-disabled": inspect["plugins"][0]["enabled"] = False + elif kind == "stderr": bodies["native-inspect.stderr.log"] = b"diagnostic" + elif kind == "missing-version": + for suffix in (".command.json", ".stdout.log", ".stderr.log"): del bodies["version" + suffix] + else: + command = json.loads(bodies["native-inspect.command.json"]) + if kind == "wrong-command": command["argv"] = ["/foreign/grok", "inspect", "--json"] + elif kind == "wrong-cwd": command["cwd"] = "/owned/b" + elif kind == "failed-command": command["exit"] = 1 + bodies["native-inspect.command.json"] = json.dumps(command).encode() + bodies["native-inspect.stdout.log"] = json.dumps(inspect).encode() + with self.subTest(kind=kind), self.assertRaises(ValueError): + runtime.cli_attempt_members(bodies, self.source, "grok") + + def test_grok_selection_intent_preserves_unknown_failed_inspection(self): + bodies = self.grok_selected_bodies() + result = json.loads(bodies["runtime-attempt.json"]) + del result["native_inspection"] + bodies["runtime-attempt.json"] = json.dumps(result).encode() + bodies["native-inspect.stdout.log"] = b'{"unknown":"actual failed diagnostic remains opaque"}' + self.assertTrue(runtime.cli_attempt_members(bodies, self.source, "grok")["test_failed"]) + for label in ("native-inspect", "version"): + for suffix in (".command.json", ".stdout.log", ".stderr.log"): del bodies[label + suffix] + self.assertTrue(runtime.cli_attempt_members(bodies, self.source, "grok")["test_failed"]) + for client in ("qoder", "kiro", "windsurf"): + unchanged = {**self.cli_result(client), "native_inspection": {"enabled": True, "project_trusted": True}} + admitted = runtime.cli_attempt_members({"runtime-attempt.json": json.dumps(unchanged).encode()}, self.source, client) + self.assertEqual(admitted, unchanged) + changed = {**self.cli_result(client), "fixture_selection": result["fixture_selection"]} + with self.subTest(client=client), self.assertRaises(ValueError): + runtime.cli_attempt_members({"runtime-attempt.json": json.dumps(changed).encode()}, self.source, client) + def test_cli_member_table_rejects_partial_commands_provider_and_profiles(self): summary = {"runtime-attempt.json": json.dumps(self.cli_result("qoder")).encode()} for extra in ({"git-a.stdout.log": b""}, {"provider-request-1.json": b"{}"}, {"profile.json": b"{}"}): @@ -147,6 +234,387 @@ def test_cli_member_table_rejects_partial_commands_provider_and_profiles(self): def cline_result(self): return {**self.cli_result("qoder"), "client": "cline", "version": "3.0.73"} + def grok_provider_bodies(self): + row = {"transport": "scripted loopback OpenAI chat-completions SSE; no real model", + "model": "uap-grok-fixture", "started": True, "requests": 1, "responses": 1, + "credential_matched": True, "failure": "none", "shutdown_complete": True, "forced_close": False} + return {"runtime-attempt.json": json.dumps(self.cli_result("grok")).encode(), + "grok-provider.json": json.dumps(row).encode(), + "grok-provider.request.json": b'{"model":"uap-grok-fixture","messages":[{"role":"user","content":"inert"}],"stream":true,"opaque":"captured only"}', + "grok-provider.response.sse": b"inert provider response envelope; no native qualification"} + + def test_grok_provider_closed_capture_remains_a_failed_unqualified_attempt(self): + bodies = self.grok_provider_bodies() + result = runtime.cli_attempt_members(bodies, self.source, "grok") + self.assertTrue(result["test_failed"]) + self.assertFalse(result["runtime_attempt_complete"]) + self.assertFalse(any(result["qualification"].values())) + for missing in bodies: + with self.subTest(missing=missing), self.assertRaises(ValueError): + runtime.cli_attempt_members({key: body for key, body in bodies.items() if key != missing}, self.source, "grok") + for client in ("qoder", "kiro", "windsurf"): + with self.subTest(client=client), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "runtime-attempt.json": json.dumps(self.cli_result(client)).encode()}, self.source, client) + for extra in ("profile.json", "grok-provider-2.request.json", "grok-provider.headers.json"): + with self.subTest(extra=extra), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, extra: b"{}"}, self.source, "grok") + + def test_grok_provider_receipt_rejects_forged_counts_fields_and_cleanup(self): + bodies = self.grok_provider_bodies() + original = json.loads(bodies["grok-provider.json"]) + changes = ({"requests": True}, {"responses": 2}, {"requests": 0}, {"responses": 0}, + {"started": False}, {"credential_matched": False}, {"shutdown_complete": 1}, + {"shutdown_complete": False}, {"forced_close": True}, {"failure": "private arbitrary error"}, + {"failure": []}, {"model": "foreign"}, {"transport": "real model"}, {"extra": "private"}) + for change in changes: + with self.subTest(change=change), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "grok-provider.json": json.dumps({**original, **change}).encode()}, self.source, "grok") + for name, body in (("grok-provider.json", b" " * 2049 + bodies["grok-provider.json"]), + ("grok-provider.request.json", b"[]"), ("grok-provider.request.json", b'{"x":1,"x":2}'), + ("grok-provider.response.sse", b"\n")): + with self.subTest(name=name), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, name: body}, self.source, "grok") + + def test_grok_provider_partial_failures_keep_only_their_actual_members(self): + bodies = self.grok_provider_bodies() + original = json.loads(bodies["grok-provider.json"]) + for requests, failure, started in ((0, "listen", False), (0, "credential", True), (1, "request-shape", True), (1, "response-write", True)): + row = {**original, "requests": requests, "responses": 0, "failure": failure, "started": started, + "credential_matched": bool(requests), "shutdown_complete": started} + present = {key: value for key, value in bodies.items() if key not in + ({"grok-provider.response.sse"} | ({"grok-provider.request.json"} if not requests else set()))} + present["grok-provider.json"] = json.dumps(row).encode() + result = runtime.cli_attempt_members(present, self.source, "grok") + self.assertTrue(result["test_failed"]) + self.assertFalse(any(result["qualification"].values())) + with self.assertRaises(ValueError): + runtime.cli_attempt_members({**present, "grok-provider.response.sse": b"unrecorded response"}, self.source, "grok") + + def grok_title_bodies(self): + bodies = self.grok_provider_bodies() + row = {"role": "session-title", "transport": "scripted loopback OpenAI session-title SSE; no real model", + "model": "uap-grok-title-fixture", "started": True, "requests": 1, "responses": 1, + "credential_matched": True, "failure": "none", "shutdown_complete": True, "forced_close": False} + bodies.update({"grok-title-provider.json": json.dumps(row).encode(), + "grok-title-provider.request.json": b'{"model":"uap-grok-title-fixture","messages":[{"role":"system","content":"You are tasked with generating the session title. The user is asking almost always software engineering related questions on their codebase.\\nWe describe the session title below\\n# Session Title\\nA short and distinctive 5-10 word descriptive title for the session. Super info dense, no filler.\\n\\nYou will be given the user query below encapsulated in .\\n\\nJust generate the session_title and nothing else"},{"role":"user","content":"\\nDescribe only the installed uap-grok-attempt skill marker. Do not run any tool or change any file.\\n"}],"temperature":1.0,"max_tokens":100,"tools":[{"type":"function","function":{"name":"session_title","description":"Generate the session_title which we use for the user_message","parameters":{"type":"object","required":["session_title"],"properties":{"session_title":{"type":"string","description":"Final session title, just 5-10 word descriptive title for the session. Super info dense, no filler."}},"additionalProperties":false}}}],"tool_choice":{"type":"function","function":{"name":"session_title"}},"stream":true,"stream_options":{"include_usage":true}}', + "grok-title-provider.response.sse": b'data: {"id":"uap-grok-title-capture","object":"chat.completion.chunk","created":1,"model":"uap-grok-title-fixture","choices":[{"index":0,"delta":{"role":"assistant","tool_calls":[{"index":0,"id":"uap-grok-title-call","type":"function","function":{"name":"session_title","arguments":"{\\"session_title\\":\\"Disposable plugin startup observation\\"}"}}]},"finish_reason":null}]}\n\ndata: {"id":"uap-grok-title-capture","object":"chat.completion.chunk","created":1,"model":"uap-grok-title-fixture","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}\n\ndata: [DONE]\n\n'}) + return bodies + + def test_grok_title_is_separate_fixed_evidence_and_cannot_qualify(self): + bodies = self.grok_title_bodies() + result = runtime.cli_attempt_members(bodies, self.source, "grok") + self.assertTrue(result["test_failed"]) + self.assertFalse(result["runtime_attempt_complete"]) + self.assertFalse(any(result["qualification"].values())) + self.assertEqual(hashlib.sha256(bodies["grok-title-provider.request.json"]).hexdigest(), + "6310901ba3d8138dbf06eb01e447139f815a42424513ed048263f39b1ced658a") + self.assertEqual(hashlib.sha256(bodies["grok-title-provider.response.sse"]).hexdigest(), + "eea6c4c389c7e27cf0e6d1cc9e705bc84a4d77ae8c5afddaa846ec8255e6ed68") + for missing in ("grok-title-provider.json", "grok-title-provider.request.json", "grok-title-provider.response.sse", "grok-provider.json"): + with self.subTest(missing=missing), self.assertRaises(ValueError): + runtime.cli_attempt_members({key: value for key, value in bodies.items() if key != missing}, self.source, "grok") + for extra in ("grok-title-provider.headers.json", "grok-title-provider-2.request.json"): + with self.subTest(extra=extra), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, extra: b"{}"}, self.source, "grok") + + def test_grok_title_receipt_and_fixed_response_refuse_unmeasured_states(self): + bodies = self.grok_title_bodies() + row = json.loads(bodies["grok-title-provider.json"]) + changes = ({"role": "main"}, {"model": "uap-grok-fixture"}, {"transport": "real model"}, + {"requests": True}, {"responses": 2}, {"started": False}, {"credential_matched": False}, + {"forced_close": True}, {"shutdown_complete": False}, {"failure": "private error"}, + {"failure": "privacy"}, {"failure": "body"}, {"failure": "request-shape"}, {"extra": "unknown"}) + for change in changes: + with self.subTest(change=change), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "grok-title-provider.json": json.dumps({**row, **change}).encode()}, self.source, "grok") + for name, raw in (("grok-title-provider.json", b" " * 2049), + ("grok-title-provider.request.json", b"[]"), + ("grok-title-provider.request.json", b'{"x":1,"x":2}'), + ("grok-title-provider.request.json", b" " * ((1 << 20) + 1)), + ("grok-title-provider.response.sse", bodies["grok-title-provider.response.sse"] + b"\n")): + with self.subTest(name=name), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, name: raw}, self.source, "grok") + for kind in ("main-model", "system", "stream-type", "temperature-type", "schema-type", "extra"): + request = json.loads(bodies["grok-title-provider.request.json"]) + if kind == "main-model": request["model"] = "uap-grok-fixture" + elif kind == "system": request["messages"][0]["content"] = "changed" + elif kind == "stream-type": request["stream"] = 1 + elif kind == "temperature-type": request["temperature"] = True + elif kind == "schema-type": request["tools"][0]["function"]["parameters"]["additionalProperties"] = 0 + else: request["extra"] = "unknown" + with self.subTest(kind=kind), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "grok-title-provider.request.json": json.dumps(request).encode()}, self.source, "grok") + + def test_grok_title_safe_shape_refusal_and_prebody_failures_preserve_failed_raw(self): + original = self.grok_title_bodies() + row = json.loads(original["grok-title-provider.json"]) + for count, failure in ((1, "request-shape"), (0, "request-shape"), (0, "privacy"), (0, "body"), (0, "credential")): + bodies = {key: value for key, value in original.items() if key not in + {"grok-title-provider.request.json", "grok-title-provider.response.sse"}} + record = {**row, "requests": count, "responses": 0, "failure": failure, + "credential_matched": failure != "credential"} + bodies["grok-title-provider.json"] = json.dumps(record).encode() + if count: bodies["grok-title-provider.request.json"] = b'{"model":"safe-unmeasured-title"}' + result = runtime.cli_attempt_members(bodies, self.source, "grok") + self.assertTrue(result["test_failed"]) + self.assertFalse(any(result["qualification"].values())) + with self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "grok-title-provider.response.sse": original["grok-title-provider.response.sse"]}, self.source, "grok") + for raw in (b'{"opaque":"uap-grok-private-inert"}', b'{"opaque":"\\u0075ap-grok-private-inert"}'): + changed = {**original, "grok-title-provider.request.json": raw} + with self.assertRaises(ValueError): runtime.cli_attempt_members(changed, self.source, "grok") + + def grok_duplicate_bodies(self): + """Independent refused diagnostic records; no native observation.""" + bodies = self.grok_title_bodies() + main = json.loads(bodies["grok-provider.json"]) + main["failure"] = "duplicate" + bodies["grok-provider.json"] = json.dumps(main).encode() + bodies["grok-provider.response.sse"] = (b'data: {"id":"uap-grok-capture","object":"chat.completion.chunk","created":1,"model":"uap-grok-fixture","choices":[{"index":0,"delta":{"role":"assistant","content":"UAP_GROK_PROVIDER_CAPTURE"},"finish_reason":null}]}\n\n' + b'data: {"id":"uap-grok-capture","object":"chat.completion.chunk","created":1,"model":"uap-grok-fixture","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}\n\ndata: [DONE]\n\n') + raw = b'{"unfamiliar":{"purpose":"ordinary refused data"},"model":"not admitted"}' + bodies["grok-provider.duplicate-request.json"] = raw + bodies["grok-provider.duplicate.json"] = json.dumps({"schema_version": 1, "role": "main-duplicate-diagnostic", "attempt_index": 2, + "status": "captured", "original_failure": "duplicate", "response_issued": False, "captured_bytes": len(raw), "captured_sha256": hashlib.sha256(raw).hexdigest()}).encode() + return bodies + + def test_grok_duplicate_capture_and_refusals_remain_failed_only(self): + for status in ("captured", "body-refused", "json-refused", "privacy-refused", "context-refused"): + with self.subTest(status=status): + bodies = self.grok_duplicate_bodies() + if status != "captured": + row = json.loads(bodies["grok-provider.duplicate.json"]) + row.update(status=status, captured_bytes=0, captured_sha256="") + bodies["grok-provider.duplicate.json"] = json.dumps(row).encode() + del bodies["grok-provider.duplicate-request.json"] + result = runtime.cli_attempt_members(bodies, self.source, "grok") + self.assertTrue(result["test_failed"]) + self.assertFalse(result["runtime_attempt_complete"]) + self.assertFalse(any(result["qualification"].values())) + # Historical absence of the optional pair stays valid and unchanged. + self.assertTrue(runtime.cli_attempt_members(self.grok_title_bodies(), self.source, "grok")["test_failed"]) + + def test_grok_duplicate_receipt_has_exact_types_and_member_joins(self): + changes = ({"schema_version": True}, {"attempt_index": 2.0}, {"role": "session-title"}, {"status": "accepted"}, {"status": []}, + {"original_failure": "none"}, {"response_issued": 0}, {"response_issued": True}, {"captured_bytes": True}, + {"captured_bytes": 1}, {"captured_sha256": "0" * 64}, {"captured_sha256": None}, {"private_error": "forbidden"}, + {"status": "privacy-refused", "captured_bytes": 0, "captured_sha256": ""}) + for change in changes: + with self.subTest(change=change), self.assertRaises(ValueError): + bodies = self.grok_duplicate_bodies() + row = json.loads(bodies["grok-provider.duplicate.json"]) + row.update(change) + bodies["grok-provider.duplicate.json"] = json.dumps(row).encode() + runtime.cli_attempt_members(bodies, self.source, "grok") + for missing in ("grok-provider.duplicate.json", "grok-provider.duplicate-request.json", "grok-provider.json", "grok-provider.request.json", "grok-provider.response.sse", "grok-title-provider.json"): + with self.subTest(missing=missing), self.assertRaises(ValueError): + bodies = self.grok_duplicate_bodies() + del bodies[missing] + runtime.cli_attempt_members(bodies, self.source, "grok") + for extra in ("grok-provider.duplicate-request-2.json", "grok-provider.duplicate.headers.json"): + with self.subTest(extra=extra), self.assertRaises(ValueError): + runtime.cli_attempt_members({**self.grok_duplicate_bodies(), extra: b"{}"}, self.source, "grok") + with self.assertRaises(ValueError): + runtime.cli_attempt_members(self.grok_duplicate_bodies(), self.source, "qoder") + + def test_grok_duplicate_cannot_forge_first_exchange_or_success(self): + for kind in ("main-counter", "main-failure", "main-response", "main-model", "title-identity", "runtime", "test-failed", "authentication", "model", "qualification"): + with self.subTest(kind=kind), self.assertRaises(ValueError): + bodies = self.grok_duplicate_bodies() + if kind in ("main-counter", "main-failure"): + row = json.loads(bodies["grok-provider.json"]) + row.update({"requests": 0} if kind == "main-counter" else {"failure": "none"}) + bodies["grok-provider.json"] = json.dumps(row).encode() + elif kind == "main-response": bodies["grok-provider.response.sse"] += b"\n" + elif kind == "main-model": + row = json.loads(bodies["grok-provider.request.json"]) + row["model"] = "foreign" + bodies["grok-provider.request.json"] = json.dumps(row).encode() + elif kind == "title-identity": + row = json.loads(bodies["grok-title-provider.json"]) + row["model"] = "foreign" + bodies["grok-title-provider.json"] = json.dumps(row).encode() + else: + row = json.loads(bodies["runtime-attempt.json"]) + if kind == "qualification": row["qualification"]["mcp-tools-call"] = True + else: row[{"runtime": "runtime_attempt_complete", "test-failed": "test_failed", "authentication": "vendor_authentication_qualified", "model": "real_model_qualified"}[kind]] = kind != "test-failed" + bodies["runtime-attempt.json"] = json.dumps(row).encode() + runtime.cli_attempt_members(bodies, self.source, "grok") + + def test_grok_duplicate_coherent_hash_cannot_cover_unsafe_or_nonfinite_body(self): + unsafe = (b"", b"[]", b"null", b"42", b'{"unclosed":', b'{"x":1,"x":2}', b"{} {}", b'{"n":1e9999}', + json.dumps({"n": 10 ** 400}).encode(), b'{"n":' + b"[" * 34 + b"0" + b"]" * 34 + b"}", + b'{"nodes":[' + b"0," * 16384 + b"0]}", json.dumps({"é" * 2049: True}).encode(), b"\xff", b" " * ((1 << 20) + 1), + b'{"opaque":"uap-grok-private-inert"}', b'{"opaque":"\\u0075ap-grok-private-inert"}', + json.dumps({"opaque": '{"\\u0075ap-grok-private-inert":"ordinary"}'}).encode()) + for raw in unsafe: + with self.subTest(raw_bytes=len(raw)), self.assertRaises(ValueError): + bodies = self.grok_duplicate_bodies() + row = json.loads(bodies["grok-provider.duplicate.json"]) + row.update(captured_bytes=len(raw), captured_sha256=hashlib.sha256(raw).hexdigest()) + bodies["grok-provider.duplicate.json"] = json.dumps(row).encode() + bodies["grok-provider.duplicate-request.json"] = raw + runtime.cli_attempt_members(bodies, self.source, "grok") + bodies = self.grok_duplicate_bodies() + raw = json.dumps({"é" * 2048: 10 ** 308}, ensure_ascii=False).encode() + row = json.loads(bodies["grok-provider.duplicate.json"]) + row.update(captured_bytes=len(raw), captured_sha256=hashlib.sha256(raw).hexdigest()) + bodies["grok-provider.duplicate.json"], bodies["grok-provider.duplicate-request.json"] = json.dumps(row).encode(), raw + self.assertTrue(runtime.cli_attempt_members(bodies, self.source, "grok")["test_failed"]) + + def test_grok_duplicate_unicode_scalar_policy_is_new_and_raw_preserving(self): + refused = (b'{"opaque":"\\ud800"}', b'{"opaque":"\\udc00"}', b'{"opaque":"\\ud800\\u0041"}', b'{"\\ud800":"ordinary"}', + json.dumps({"opaque": '{"opaque":"\\ud800"}'}).encode(), json.dumps({'{"opaque":"\\ud800"}': "ordinary"}).encode(), + json.dumps({"opaque": '"\\ud800"'}).encode(), json.dumps({"opaque": '{}\n{"opaque":"\\ud800"}'}).encode()) + accepted = (b'{"opaque":"\\ud83d\\ude00"}', b'{"opaque":"\\ufffd"}', '{"opaque":"�"}'.encode(), '{"opaque":"é😀"}'.encode(), + b'{"opaque":"\\\\ud800"}', b'{"opaque":"\\/path"}', b'{"opaque":"\\\"\\\\ud800"}', b'{"\\ud83d\\ude00":"ordinary"}') + for raw, valid in [(raw, False) for raw in refused] + [(raw, True) for raw in accepted]: + with self.subTest(raw=raw, valid=valid): + bodies = self.grok_duplicate_bodies() + row = json.loads(bodies["grok-provider.duplicate.json"]) + row.update(captured_bytes=len(raw), captured_sha256=hashlib.sha256(raw).hexdigest()) + bodies["grok-provider.duplicate.json"], bodies["grok-provider.duplicate-request.json"] = json.dumps(row).encode(), raw + if valid: + self.assertTrue(runtime.cli_attempt_members(bodies, self.source, "grok")["test_failed"]) + self.assertEqual(bodies["grok-provider.duplicate-request.json"], raw) + else: + with self.assertRaises(ValueError): + runtime.cli_attempt_members(bodies, self.source, "grok") + + def grok_route_bodies(self): + bodies = self.grok_provider_bodies() + row = json.loads(bodies["grok-provider.json"]) + row.update(requests=0, responses=0, credential_matched=False, failure="route") + row["route_observation"] = { + "method": "POST", "uri": "/v1/chat/completions", "peer_loopback": True, "host_matches": True, + "relative_url": True, "empty_url_host": True, "empty_raw_path": True, "content_type_exact": False, + "content_type_count": "one", "content_type_kind": "json-utf8"} + del bodies["grok-provider.request.json"] + del bodies["grok-provider.response.sse"] + return bodies, row + + def test_grok_route_observation_is_optional_closed_and_never_qualifies(self): + bodies, row = self.grok_route_bodies() + for change in ({}, {"method": "GET", "content_type_exact": True, "content_type_kind": "json-bare"}, + {"uri": "/models", "content_type_exact": True, "content_type_kind": "json-bare"}, + {"content_type_count": "none", "content_type_kind": "missing"}, + {"content_type_count": "multiple", "content_type_kind": "other"}): + updated = {**row, "route_observation": {**row["route_observation"], **change}} + result = runtime.cli_attempt_members({**bodies, "grok-provider.json": json.dumps(updated).encode()}, + self.source, "grok") + self.assertFalse(any(result["qualification"].values())) + del row["route_observation"] + runtime.cli_attempt_members({**bodies, "grok-provider.json": json.dumps(row).encode()}, self.source, "grok") + + def test_grok_route_observation_refuses_forged_types_enums_fields_and_contradictions(self): + bodies, row = self.grok_route_bodies() + observation = row["route_observation"] + for change in ({"method": []}, {"method": "PUT"}, {"uri": "/private-query?token=private"}, + {"peer_loopback": 1}, {"content_type_count": 1}, {"content_type_kind": None}, + {"extra": "private-value"}, {"content_type_count": "none"}, + {"content_type_kind": "missing"}, {"content_type_count": "multiple"}, + {"content_type_exact": True}, {"content_type_exact": True, "content_type_kind": "json-bare"}): + updated = {**row, "route_observation": {**observation, **change}} + with self.subTest(change=change), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "grok-provider.json": json.dumps(updated).encode()}, + self.source, "grok") + for key in observation: + incomplete = {name: value for name, value in observation.items() if name != key} + with self.subTest(missing=key), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "grok-provider.json": json.dumps({**row, "route_observation": incomplete}).encode()}, + self.source, "grok") + for change in ({"failure": "serve"}, {"route_observation": None}, {"route_observation": []}, + {"started": False, "failure": "listen", "shutdown_complete": False}): + with self.subTest(row=change), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "grok-provider.json": json.dumps({**row, **change}).encode()}, + self.source, "grok") + + def test_grok_origin_route_is_only_failed_evidence_and_never_normalizes_uris(self): + bodies, row = self.grok_route_bodies() + observation = {**row["route_observation"], "method": "GET", "uri": "/", + "content_type_count": "none", "content_type_kind": "missing"} + row["route_observation"] = observation + result = runtime.cli_attempt_members({**bodies, "grok-provider.json": json.dumps(row).encode()}, + self.source, "grok") + self.assertTrue(result["test_failed"]) + self.assertFalse(result["runtime_attempt_complete"]) + self.assertFalse(any(result["qualification"].values())) + for uri in ("/?credential=private-query", "//", "/v1/", "/%2F", "http://private-host/", "/private-root"): + rejected = {**row, "route_observation": {**observation, "uri": uri}} + with self.subTest(uri=uri), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "grok-provider.json": json.dumps(rejected).encode()}, + self.source, "grok") + + def test_grok_provider_private_canary_is_refused_in_every_capture_surface(self): + bodies = self.grok_provider_bodies() + canary = b"uap-grok-private-" + b"a" * 32 + for name in bodies: + with self.subTest(member=name), self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, name: bodies[name] + canary}, self.source, "grok") + encoded = b'{"opaque":"\\u0075ap-grok-private-' + b"a" * 32 + b'"}' + for stream in (b'{"event":"start"}\n' + encoded, encoded + b'\n{"event":"end"}\n'): + self.assertNotIn(b"uap-grok-private-", stream) + self.assertTrue(runtime.grok_private_credential(stream)) + for present in (bodies, {"runtime-attempt.json": bodies["runtime-attempt.json"]}): + with self.subTest(provider="grok-provider.json" in present), self.assertRaises(ValueError): + runtime.cli_attempt_members({**present, "probe-events.jsonl": stream}, self.source, "grok") + self.assertFalse(runtime.grok_private_credential(b'{"event":"start"}\n{"event":"end"}\n')) + self.assertTrue(runtime.grok_private_credential(b" " * ((1 << 20) + 1))) + with self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "grok-provider.request.json": encoded}, self.source, "grok") + with self.assertRaises(ValueError): + runtime.cli_attempt_members({"runtime-attempt.json": json.dumps(self.cli_result("grok")).encode(), + "probe-events.jsonl": canary}, self.source, "grok") + + def test_grok_nested_semantic_json_strings_are_refused_before_admission(self): + bodies = self.grok_provider_bodies() + for seed in (b'{"key":"\\u0075ap-grok-private-inert"}', + b'{"\\u0075ap-grok-private-inert":"ordinary"}', b'["\\u0075ap-grok-private-inert"]'): + nested = seed + for _ in range(8): + nested = json.dumps({"content": nested.decode()}).encode() + for raw in (nested, b'{"first":true}\n' + nested + b"\n"): + self.assertNotIn(b"uap-grok-private-", raw) + self.assertTrue(runtime.grok_private_credential(raw)) + with self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "probe-events.jsonl": raw}, self.source, "grok") + benign = json.dumps({"content": '{"key":"ordinary"}\n{"tail":true}'}).encode() + self.assertFalse(runtime.grok_private_credential(benign)) + self.assertFalse(runtime.grok_private_credential(b"ordinary non-JSON diagnostic")) + + def test_grok_privacy_budget_is_shared_across_lines_siblings_and_nested_text(self): + for raw in (b"\xff", b'{"same":1,"same":2}', b"[" * 33 + b"0" + b"]" * 33, + b"[" + b"0," * 17000 + b"0]", json.dumps({"k" * 4097: True}).encode()): + self.assertTrue(runtime.grok_private_credential(raw)) + self.assertTrue(runtime.grok_private_credential( + b'{"n":' + b"9" * 5000 + b',"key":"\\u0075ap-grok-private-inert"}')) + nested = json.dumps("x" * 4096).encode() + for _ in range(2): + nested = json.dumps({"content": nested.decode()}).encode() + self.assertFalse(runtime.grok_private_credential(nested)) + self.assertTrue(runtime.grok_private_credential(nested + b"\n" + nested + b"\n")) + for keys in (("first", "last"), ("last", "first")): + siblings = json.dumps({key: nested.decode() for key in keys}).encode() + self.assertTrue(runtime.grok_private_credential(siblings)) + self.assertFalse(runtime.grok_private_credential(b" " * (1 << 20))) + + def test_grok_numeric_privacy_refusal_requires_complete_json_syntax(self): + text = b"1e999" + b"a" * 59 + for raw in (text, b"-1e999ordinary", b"1e999 []", b"1e999 1e999", b"NaN", b"Infinity", + json.dumps({"metadata": text.decode()}).encode(), + json.dumps({text.decode(): "ordinary"}).encode(), + text + b'\n{"event":"ordinary"}\n'): + self.assertFalse(runtime.grok_private_credential(raw)) + for raw in (b"1e999", b'{"n":1e999}', b"[1e999]", json.dumps("1e999").encode(), + text + b'\n{"opaque":"\\u0075ap-grok-private-inert"}\n', + b"[1e999," + b"[" * 10050 + b"]" * 10050 + b"]"): + self.assertTrue(runtime.grok_private_credential(raw)) + bodies = self.grok_provider_bodies() + runtime.cli_attempt_members({**bodies, "probe-events.jsonl": json.dumps({"metadata": text.decode()}).encode()}, + self.source, "grok") + with self.assertRaises(ValueError): + runtime.cli_attempt_members({**bodies, "probe-events.jsonl": text + + b'\n{"opaque":"\\u0075ap-grok-private-inert"}\n'}, self.source, "grok") def cline_cycle_bodies(self, complete=True): result = {**self.cline_result(), "capture_mode": "cline-complete-cycle-first-capture", "cycle_capture_review_required": True, "cycle_observed_complete": complete, @@ -356,6 +824,33 @@ def test_catalog_triple_is_admitted_only_for_qoder_and_never_qualifies_it(self): with self.assertRaises(ValueError): runtime.cli_attempt_members({**summary, **triple}, self.source, client) + def test_inspect_triple_is_admitted_only_for_grok_without_qualification(self): + command = {"argv": ["/owned/grok", "inspect", "--json"], "cwd": "/owned/A", "exit": 0, + "error": False, "deadline": False, "elapsed_ms": 1, + "cleanup": "full descendant supervision; forced cleanup fails"} + triple = {"native-inspect.command.json": json.dumps(command).encode(), + "native-inspect.stdout.log": b'{"grokVersion":"1.0.70"}\n', + "native-inspect.stderr.log": b""} + for client in runtime.CLI_ATTEMPTS: + summary = {"runtime-attempt.json": json.dumps(self.cli_result(client)).encode()} + if client != "grok": + with self.subTest(client=client), self.assertRaises(ValueError): + runtime.cli_attempt_members({**summary, **triple}, self.source, client) + continue + with tempfile.TemporaryDirectory() as temporary: + path = Path(temporary) / "research.zip" + self.envelope(path, list({**summary, **triple}.items())) + bodies, result = runtime.admit_runtime(path, client, self.source) + self.assertEqual(set(bodies), set(summary) | set(triple)) + self.assertTrue(result["test_failed"]) + self.assertFalse(result["runtime_attempt_complete"]) + self.assertFalse(any(result["qualification"].values())) + for missing in triple: + with self.subTest(missing=missing), self.assertRaises(ValueError): + runtime.cli_attempt_members({**summary, **{key: value for key, value in triple.items() if key != missing}}, self.source, client) + with self.assertRaises(ValueError): + runtime.cli_attempt_members({**summary, **triple, "native-inspect.extra.log": b""}, self.source, client) + def test_cline_capture_is_unqualified_and_provider_key_is_never_admitted(self): result = self.cline_result() base = {"runtime-attempt.json": json.dumps(result).encode()} @@ -898,6 +1393,7 @@ def load_tests(loader, tests, pattern): # Broad research discovery already selects the new client-specific module. if pattern in (None, "test_project_native_runtime.py"): tests.addTests(loader.loadTestsFromName("test_project_native_cline_cycle.CompleteCycleTests")) + tests.addTests(loader.loadTestsFromName("test_project_native_grok_cycle.GrokCycleContracts")) return tests