Add Linear solo-operator control plane - #72
chitcommit wants to merge 3 commits into
Conversation
📝 WalkthroughWalkthroughAdds the ChangesLinear solo operator capability
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CLAUDE.md`:
- Line 9: Update the capability overlay entry in CLAUDE.md to use the refreshed
date 2026-07-22, or explicitly identify 2026-05-11 as the original baseline
while indicating the refreshed overlay date.
In `@docs/audits/2026-07-22-linear-solo-operations-review.md`:
- Line 74: Update the remaining-drift statement in the audit so its
uncataloged-skill count reconciles with the reported 32 canonical skills and 19
cataloged artifacts, changing 14 to 13 unless the inventories intentionally use
different scopes; if they do, document those scope differences next to the
count.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7bc3e69b-60cb-4c29-8b78-21178716a1d1
⛔ Files ignored due to path filters (1)
capabilities.generated.jsonis excluded by!**/*.generated.*
📒 Files selected for processing (12)
.claude-plugin/marketplace.jsonCHARTER.mdCHITTY.mdCLAUDE.mdREADME.mdcanonical/.dispatch-state/skills/linear-solo-operator.jsoncanonical/skills/linear-solo-operator.mddocs/audits/2026-07-22-linear-solo-operations-review.mdmarketplace.jsonplugins/chittyos-core/.claude-plugin/plugin.jsonplugins/chittyos-core/codex-skills/linear-solo-operator/SKILL.mdplugins/chittyos-core/skills/linear-solo-operator/SKILL.md
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b517e5fad3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@canonical/mcp/chittyos.md`:
- Around line 17-18: Update the MCP launch command in the chitty-mcp
configuration to prevent CF_ACCESS_CLIENT_SECRET from being expanded into
process arguments. Use mcp-remote’s supported placeholder configuration or a
wrapper that reads the secret at runtime while preserving the existing
authentication behavior and command flow.
In `@plugins/chittymarket-manager/skills/market/market.sh`:
- Around line 54-69: Update resolve_artifact_path so an empty raw artifact path
remains empty instead of being joined with MANIFEST_DIR. Preserve existing
tilde, absolute, and relative path resolution for non-empty inputs, ensuring
toggle_skill and toggle_agent do not receive an unintended directory when
standalone.path is missing.
- Around line 434-445: Update cmd_sync to interpolate the expanded MANIFEST_REAL
or MANIFEST_DIR values into its Python block instead of the raw MANIFEST value,
ensuring realpath and open use the correctly expanded manifest location.
Preserve resolve_artifact_path’s existing behavior for relative artifact paths.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8d710235-d544-4b1a-8029-70f1a8ce8f3e
📒 Files selected for processing (7)
CLAUDE.mdcanonical/.dispatch-state/mcp/chittyos.jsoncanonical/mcp/chittyos.mddocs/audits/2026-07-22-linear-solo-operations-review.mdplugins/chittyagent-dispatch/scripts/lib/audit.pyplugins/chittymarket-manager/skills/market/market.shscripts/test-plugins.sh
🚧 Files skipped from review as they are similar to previous changes (2)
- docs/audits/2026-07-22-linear-solo-operations-review.md
- CLAUDE.md
| - >- | ||
| TOKEN="$(chitty-mcp-token chittymcp 2>/dev/null || true)"; if [ -n "$TOKEN" ]; then export MCP_AUTH_HEADER="Bearer $TOKEN"; fi; exec npx -y mcp-remote https://mcp.chitty.cc/mcp --header "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" --header "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file excerpt =="
if [ -f canonical/mcp/chittyos.md ]; then
nl -ba canonical/mcp/chittyos.md | sed -n '1,80p'
else
echo "canonical/mcp/chittyos.md not found"
fi
echo
echo "== references to mcp-remote and headers =="
rg -n "mcp-remote|--header|CF_ACCESS_CLIENT_SECRET|CF_ACCESS_CLIENT_ID" . \
--glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' || true
echo
echo "== package/readme files mentioning mcp-remote =="
rg -n '"?mcp-remote"?|`@modelcontextprotocol`|mcp-remote|MCP_AUTH_HEADER|--header' README.md docs canonical .github package*.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null || trueRepository: chittyos/chittymarket
Length of output: 218
🌐 Web query:
mcp-remote environment variable CF_ACCESS_CLIENT_SECRET process arguments header documentation
💡 Result:
In the context of mcp-remote, CF_ACCESS_CLIENT_SECRET is typically used as a environment variable to store the secret for Cloudflare Access Service Tokens [1][2][3][4]. While mcp-remote does not natively process CF_ACCESS_CLIENT_SECRET as a built-in configuration parameter, it enables you to pass this value to the remote server via custom HTTP headers [5][6][7]. To use CF_ACCESS_CLIENT_SECRET with mcp-remote, you must define it within the env section of your MCP configuration (e.g., in claude_desktop_config.json) and inject it into the headers using the --header command-line argument [5][7]. Recommended Configuration Pattern: In your MCP configuration file (such as your Claude Desktop config), configure the environment variable and reference it in the header argument [5][7]: { "mcpServers": { "your-server-name": { "command": "npx", "args": [ "mcp-remote", "https://your-remote-mcp-server-url", "--header", "CF-Access-Client-Id:${CF_ACCESS_CLIENT_ID}", "--header", "CF-Access-Client-Secret:${CF_ACCESS_CLIENT_SECRET}" ], "env": { "CF_ACCESS_CLIENT_ID": "your-client-id.access", "CF_ACCESS_CLIENT_SECRET": "your-client-secret" } } } } Key Documentation Points: 1. Header Substitution: mcp-remote supports environment variable substitution in --header arguments using the ${VAR_NAME} syntax [6][7]. 2. Best Practices: When defining headers, it is recommended to avoid spaces around the colon (e.g., Authorization:${TOKEN} instead of Authorization: ${TOKEN}) to prevent issues with how some editors or CLI environments parse arguments [7]. 3. Cloudflare Access: These headers (CF-Access-Client-Id and CF-Access-Client-Secret) are required when your MCP server is protected by a Cloudflare Access policy, as they prevent 403 Forbidden errors at the edge [1][2]. 4. Alternatives: If you are using pre-registered OAuth clients instead of Service Tokens, mcp-remote also provides the --static-oauth-client-info flag to handle client metadata [5][8][6].
Citations:
- 1: https://github.com/caelaxie/inoreader-mcp
- 2: feat: add Cloudflare Zero Trust (Access Service Token) support TrackLine/mcp-remnawave#4
- 3: https://medium.com/@rnwqyzxnn/build-and-host-remote-mcp-server-to-cloudflare-e7508a63ad9c
- 4: https://zenn.dev/oymk/articles/6b4a923713e69c?locale=en
- 5: https://github.com/geelen/mcp-remote?tab=readme-ov-file
- 6: https://deepwiki.com/geelen/mcp-remote/2.3-command-line-reference
- 7: https://github.com/geelen/mcp-remote/blob/ce68351d/README.md
- 8: https://registry.npmjs.org/mcp-remote
🌐 Web query:
github modelcontextprotocol mcp-remote source header environment variable
💡 Result:
The mcp-remote tool supports environment variable substitution within custom headers passed via the --header CLI argument using the ${VAR_NAME} syntax [1][2]. When you define headers in your MCP client configuration (such as claude_desktop_config.json), you can reference environment variables defined in the env block of the same configuration entry [3][4]. For example: { "mcpServers": { "remote-example": { "command": "npx", "args": [ "mcp-remote", "https://remote.mcp.server/sse", "--header", "Authorization:${AUTH_HEADER}" ], "env": { "AUTH_HEADER": "Bearer " } } } } The mcp-remote tool parses these --header arguments and replaces the ${VAR_NAME} placeholder with the corresponding value from the process's environment variables [1][2]. Important considerations: - Syntax: Use ${VAR_NAME} exactly [1][2]. - Troubleshooting: If you experience issues with values not being replaced (e.g., resulting in an empty header value), ensure there are no unintended spaces around the colon in the header definition [3][5][4]. A known issue in some MCP clients causes command-line arguments containing spaces to be mangled when invoking npx; using the format HeaderName:${VAR_NAME} (without spaces) is a recommended workaround [3][4]. - Scope: MCP servers do not automatically inherit all environment variables from your shell [6][7][8]. You must explicitly define required variables in the env block of your client configuration [6][7].
Citations:
- 1: https://github.com/geelen/mcp-remote/blob/ce68351d/src/lib/utils.ts
- 2: https://deepwiki.com/geelen/mcp-remote/2.3-command-line-reference
- 3: https://github.com/geelen/mcp-remote?tab=readme-ov-file
- 4: https://github.com/geelen/mcp-remote/blob/ce68351d/README.md
- 5: Utilize Env Vars in arguments modelcontextprotocol/inspector#539
- 6: https://github.com/modelcontextprotocol/docs/blob/main/docs/tools/debugging.mdx
- 7: https://techdocs.broadcom.com/us/en/vmware-tanzu/spring/application-advisor/1-5/app-advisor/model-context-protocol-server.html
- 8: https://apxml.com/courses/getting-started-model-context-protocol/chapter-4-debugging-and-client-integration/managing-environment-variables
🌐 Web query:
geelen mcp-remote npx mcp-remote --static-oauth-client-info CF-Access-Client-Secret
💡 Result:
The command line argument --static-oauth-client-info is a feature of the mcp-remote tool, created by Glen Maddern (geelen), used to provide pre-registered OAuth client information for remote MCP servers that do not support dynamic client registration [1][2]. When using this flag, you can provide the information as either a JSON string or a file path (prefixed with @) [2]. For example: npx mcp-remote https://remote.mcp.server/sse --static-oauth-client-info '{"client_id": "your-id", "client_secret": "your-secret"}' Alternatively, if you have the configuration stored in a file (e.g., config.json), you can use: npx mcp-remote https://remote.mcp.server/sse --static-oauth-client-info @config.json This tool serves as a proxy to allow MCP clients that only support local (stdio) transport to connect to remote MCP servers over HTTP/SSE, with support for authentication [3][4].
Citations:
- 1: https://registry.npmjs.org/mcp-remote
- 2: https://deepwiki.com/geelen/mcp-remote/2.3-command-line-reference
- 3: https://github.com/geelen/mcp-remote?tab=readme-ov-file
- 4: https://deepwiki.com/geelen/mcp-remote/2-getting-started
🌐 Web query:
site:github.com/geelen/mcp-remote --header "${CF_ACCESS_CLIENT_SECRET}" package.json
💡 Result:
The string ${CF_ACCESS_CLIENT_SECRET} is a placeholder for a sensitive environment variable often used when configuring custom HTTP headers for Model Context Protocol (MCP) servers, specifically when using tools like mcp-remote [1][2]. In contexts such as the mcp-remote configuration within a package.json or MCP configuration file, users often need to pass authentication credentials—such as a Cloudflare Access Client Secret—to a remote server [1][3]. Because package.json files are often committed to version control, it is considered a security best practice to use environment variables (like ${CF_ACCESS_CLIENT_SECRET}) rather than hardcoding sensitive values directly in the configuration [1][3]. Key points regarding this usage: - Configuration: You typically define the command and args in your MCP configuration, using the --header flag to inject authentication tokens [1][2]. - Environment Variables: By using ${VAR_NAME} syntax, the configuration relies on the environment where the MCP host (like Claude Desktop or Cursor) is running to provide the actual secret at runtime [1][2]. - Security: Storing secrets in cleartext within configuration files poses a risk of accidental exposure. To further mitigate this, some tools have introduced features like --headerFile to load headers from external, non-committed files, keeping sensitive data out of process arguments and configuration files entirely [3]. When configuring this, ensure your host environment is properly configured to export the CF_ACCESS_CLIENT_SECRET variable before launching the MCP client [1][2].
Citations:
- 1: https://github.com/geelen/mcp-remote/
- 2: https://github.com/geelen/mcp-remote/blob/ce68351d/README.md
- 3: Add --headerFile option for loading headers from a file punkpeye/mcp-remote#80
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file excerpt =="
if [ -f canonical/mcp/chittyos.md ]; then
awk '{printf "%5d\t%s\n", NR, $0}' canonical/mcp/chittyos.md | sed -n '1,80p'
else
echo "canonical/mcp/chittyos.md not found"
fi
echo
echo "== references to mcp-remote and headers =="
rg -n "mcp-remote|--header|CF_ACCESS_CLIENT_SECRET|CF_ACCESS_CLIENT_ID" . \
--glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' || true
echo
echo "== references in known package/readme/config files =="
rg -n '"?mcp-remote"?|`@modelcontextprotocol`|mcp-remote|MCP_AUTH_HEADER|--header' README.md docs canonical .github package*.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null || trueRepository: chittyos/chittymarket
Length of output: 1936
Keep the Cloudflare secret out of process arguments.
CF_ACCESS_CLIENT_SECRET is expanded directly into the mcp-remote command line, where it can be exposed via process listings or diagnostics. Use mcp-remote’s configured placeholder syntax or a wrapper that reads the secret without placing it on the argv.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@canonical/mcp/chittyos.md` around lines 17 - 18, Update the MCP launch
command in the chitty-mcp configuration to prevent CF_ACCESS_CLIENT_SECRET from
being expanded into process arguments. Use mcp-remote’s supported placeholder
configuration or a wrapper that reads the secret at runtime while preserving the
existing authentication behavior and command flow.
| # Resolve artifact filesystem paths consistently. Tilde paths are installed | ||
| # locations; absolute paths remain absolute; repo-relative paths are anchored | ||
| # to the real marketplace manifest rather than the caller's working directory. | ||
| resolve_artifact_path() { | ||
| python3 - "$MANIFEST_DIR" "$1" <<'PY' | ||
| import os | ||
| import sys | ||
|
|
||
| manifest_dir, raw = sys.argv[1:] | ||
| expanded = os.path.expanduser(raw) | ||
| if not os.path.isabs(expanded): | ||
| expanded = os.path.join(manifest_dir, expanded) | ||
| print(os.path.normpath(expanded)) | ||
| PY | ||
| } | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Keep empty artifact paths empty.
resolve_artifact_path "" currently resolves to MANIFEST_DIR because os.path.join(manifest_dir, "") returns the directory. This differs from the sync helper and can make toggle_skill or toggle_agent target an unintended path when standalone.path is missing. Preserve empty paths or reject them before invoking the resolver.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/chittymarket-manager/skills/market/market.sh` around lines 54 - 69,
Update resolve_artifact_path so an empty raw artifact path remains empty instead
of being joined with MANIFEST_DIR. Preserve existing tilde, absolute, and
relative path resolution for non-empty inputs, ensuring toggle_skill and
toggle_agent do not receive an unintended directory when standalone.path is
missing.
| manifest_dir = os.path.dirname(os.path.realpath(manifest_path)) | ||
| ch1tty_path = '$CH1TTY_SERVERS' | ||
| settings_path = '$SETTINGS' | ||
| blocklist_path = '$BLOCKLIST' | ||
| home = os.path.expanduser('~') | ||
|
|
||
| def resolve_artifact_path(path): | ||
| if not path: | ||
| return '' | ||
| path = os.path.expanduser(path) | ||
| if not os.path.isabs(path): | ||
| path = os.path.join(manifest_dir, path) | ||
| return os.path.normpath(path) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -a 'market.sh' . | sed 's#^\./##' | head -20
file="$(fd 'market.sh' . | grep 'plugins/chittymarket-manager/skills/market/market.sh' | head -1 || true)"
if [ -z "$file" ]; then
echo "file not found"
exit 0
fi
echo "== line count =="
wc -l "$file"
echo "== outline =="
ast-grep outline "$file" --view compact | sed -n '1,160p' || true
echo "== relevant lines 1-90 =="
sed -n '1,90p' "$file" | nl -ba
echo "== relevant lines 400-460 =="
sed -n '400,460p' "$file" | nl -ba -v400
echo "== MANIFEST_REAL occurrences =="
rg -n "MANIFEST_REAL|manifest_path|realpath|expanduser|settings_path|blocklist_path|MANIFEST" "$file"Repository: chittyos/chittymarket
Length of output: 587
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="./plugins/chittymarket-manager/skills/market/market.sh"
echo "== relevant lines 1-90 from $file =="
awk '{printf "%4d %s\n", NR, $0}' "$file" | sed -n '1,90p'
echo "== relevant lines 400-460 from $file =="
awk '{printf "%4d %s\n", NR, $0}' "$file" | sed -n '400,460p'
echo "== occurrences =="
grep -nE "MANIFEST_REAL|manifest_path|realpath|expanduser|settings_path|blocklist_path|MANIFEST|cmd_sync" "$file" || trueRepository: chittyos/chittymarket
Length of output: 7119
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import os, sys
paths = [
"~/test/file.json",
"$HOME/test/file.json",
"relative/to/manifest.json",
os.path.expanduser("~/test/file.json"),
]
tmp = os.path.join(os.path.expanduser("~"), "tmp")
try:
os.makedirs(tmp, exist_ok=True)
for p in paths:
try:
r = os.path.realpath(p)
print(p, "=>", r, "open_ok=", os.path.isfile(r))
except Exception as e:
print(p, "=>", type(e).__name__, str(e)[:120])
finally:
try:
os.rmdir(tmp)
except OSError:
pass
PYRepository: chittyos/chittymarket
Length of output: 476
Use the expanded manifest path in cmd_sync.
MANIFEST_REAL already expands ~, but cmd_sync interpolates the raw MANIFEST value and then passes it to os.path.realpath() and open(). A tilde path like ~/... resolves as a literal ~ component and can fail to open or use the wrong base directory; pass MANIFEST_REAL/MANIFEST_DIR into the Python block instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/chittymarket-manager/skills/market/market.sh` around lines 434 - 445,
Update cmd_sync to interpolate the expanded MANIFEST_REAL or MANIFEST_DIR values
into its Python block instead of the raw MANIFEST value, ensuring realpath and
open use the correctly expanded manifest location. Preserve
resolve_artifact_path’s existing behavior for relative artifact paths.
There was a problem hiding this comment.
💡 Codex Review
This diff changes market.sh from 100755 to 100644, so installed users following the documented direct invocations like ~/.claude/skills/market/market.sh list now get Permission denied unless they happen to wrap it with bash. Since this file is the /market actuator and advertises direct script usage, keep the executable bit (or update every documented/invoked path to call it through Bash).
chittymarket/scripts/test-plugins.sh
Line 1 in 720d87b
This diff also changes scripts/test-plugins.sh from 100755 to 100644, so anyone running the repository's documented test script directly as scripts/test-plugins.sh now gets Permission denied despite the shebang; CI happens to use bash scripts/test-plugins.sh, but local validation workflows and the commit's own validation command format rely on the script remaining executable like the other shell scripts in scripts/.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Summary
linear-solo-operatorskill for AI-led, human-in-the-loop execution by a one-person human teamchittyos-coreand brings capability-overlay coverage to 106/106Review remediation
Commit
720d87bresolves every review thread and the required CI blocker:/market enable|disable|syncresolve repo-relative artifact paths from the real manifest locationValidation
scripts/lint-plugins.sh: passscripts/test-plugins.sh: 78/78 passgit diff --check: passControl-plane record
Architecture, execution order, platform boundary, and human-visible role-routing requirements are tracked in CFDXN-78.