Skip to content

release: 0.22.0 (beta → main) - #357

Open
eFAILution wants to merge 53 commits into
mainfrom
beta
Open

eFAILution wants to merge 53 commits into
mainfrom
beta

Conversation

@eFAILution

@eFAILution eFAILution commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Release PR for the next stable version. Merging to main cuts a stable GitHub Release straight away. The Marketplace publish is a separate manual workflow_dispatch.

What is on beta

PR
#355 fix(completion): use the referenced version's inputs
#362 fix(hover): only trust the detach command in hover markdown
#364 feat(mcp): MCP server for AI agents to check component inputs and versions
#363 refactor(validation): move validation into a host-neutral core, and harden local includes, git and YAML handling
#356, #358, #359, #360, #361 refactor(core): host ports for config, secrets, storage, logging and the token prompt, so the core runs without VS Code
#365 docs(readme): how to turn on and use the MCP server (the Marketplace page)
#367 chore(workflow): issue-keyed branches, PRs and decision records, with a CI check (#366)

User-facing changes

New: MCP server for AI agents (#364). Copilot Chat agent mode, and other MCP clients, can call validate_ci_file, check_component_versions, get_component_spec, list_component_versions and list_component_usages. They find unknown or missing inputs and outdated versions, look up the exact inputs a component version takes, and get suggested edits to apply. The server is read-only and never writes files. It ships inside the VSIX and is off by default. Turn on gitlabComponentHelper.mcp.enabled (application scope, so a workspace can't flip it). Guide: docs/mcp.md.

Fixes:

Behavior changes in the shared HTTP client:

  • httpTimeout is clamped to 1–60s, and 0 means 60s.
  • retryAttempts is clamped to 0–5, and 0 now really means no retries.
  • A whole request must finish within 6 socket timeouts (60s at the default).
  • Responses over 10 MB are aborted.
  • Request dedup keys hash the token.

Tokens: they're trimmed when saved, and tokens with control characters are refused. Diagnostics, quick fixes, hovers and the token prompt are otherwise unchanged. The validation refactor was checked line by line, and unit tests pin every message.

Contributor workflow (#367)

No change for users. Work now starts from an issue. Branches are named <type>/<issue>-<desc>, PRs say Closes #<issue>, and new decisions go in .ai/decisions/ADR-<issue>.yaml. The new Decision Records workflow checks this on every PR and push to beta and main. AGENTS.md holds the rules, and CLAUDE.md just points at it.

Security

Two review rounds on the MCP server. The first found a critical issue, fixed before merge: a repo's settings could get a token sent to a lookalike host through the lossy GCH_TOKEN_<HOST> mapping.

  • Token hand-off: tokens now go from SecretStorage to the server as an exact host-to-token map in a one-time 0600 file, so nothing secret sits in the process environment.
  • Trust: token hosts and the network allowlist come only from user settings and stored tokens, never from repo files or workspace settings.
  • Network: HTTPS only. Link-local, metadata, unspecified and multicast addresses are refused, including IPv4 embedded in IPv6.
  • Files: containment is checked before the filesystem is touched, and symlinks are vetted.
  • Output: spec text is sanitized for prompt injection.
  • Limits: size, time and concurrency are capped.

npm audit --omit=dev reports 0 vulnerabilities. The full audit reports 23 (21 high, 1 critical, 1 moderate), all in dev tooling. The one new devDependency, jsonc-parser 3.3.1 (pinned), is bundled into the MCP server and has no dependencies.

Soak

Not yet seen working in VS Code: the Copilot trust prompt and tools picker, protocol negotiation with VS Code's MCP client, and the server restarting after a token change.

Versioning

beta is at 0.21.7. On merge, .release-it.json's requireEvenMinor rolls this to 0.22.0 for the stable channel.

Verification on beta tip

  • CI (unit, extension-host) and Security Hardening pass on 8e83286, the last code change on beta
  • Every PR passed CI, including the extension-host suite, before merging. 778 unit tests.

Before merging

  • Test drive 0.21.7 with Copilot Chat (docs/mcp.md has prompts to try)
  • Restrict deletions on beta with a ruleset. The last release merge deleted beta and auto-closed open PRs targeting it.
  • Make Decision Records a required check on beta and main.
  • Add a branch-name ruleset: a type prefix (feat/, fix/, chore/, ...), with dependabot/, copilot/, claude/ and beta allowed.

X-Guardian and others added 9 commits October 22, 2026 18:48
Co-authored-by: Simon Heather <simon.heather@yulife.com>
The barrel re-exported handler.ts, which imports vscode at module load,
so anything importing ../errors could not run under plain Node. That
blocks the standalone MCP server and forced pure modules to import
errors/types directly.

Drop the handler re-export and build the HTTP timeout error as a plain
NetworkError with status 408 (same as handler.createHttpError did).

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
The outdated-version check and the cache lookups behind validation
live in ValidationProvider, mixed with vscode diagnostics and the
cache manager instance.

Add src/core/validation/versionFindings.ts. It computes outdated refs
from text plus a reference resolver and a version fetcher, reads
versionCheck.enabled and severity through the config port, and turns
each ref into a finding. Each finding also carries a suggested edit
that bumps the pinned ref, which is the edit the extension's quick fix
makes today.

Add src/core/validation/componentLookup.ts with the pure parts of
findComponentInCache, parseComponentIdentity, and the version lookup
entry, taking the cache contents as input.
ValidationProvider now runs the core include validator and version
check and turns each finding into a vscode.Diagnostic through the new
findingToDiagnostic mapper. What stays in the provider is the VS Code
part: document events, diagnostic collections, code actions, the input
quick pick, and the cache-backed component lookup it hands to the
core. The gitlab.example.com mock component stays in the provider's
lookup, so the core never sees it.

The metadata types move to src/core/validation/metadata.ts.
validationMetadata.ts keeps the attach and read helpers and
re-exports the types.

The provider drops from 1504 to 940 lines. Diagnostic messages, codes,
ranges, severities, and metadata are unchanged; the unit tests added
with the core validator pin them.
createReferenceResolver lived in componentDetector and read the VS
Code Git extension, the workspace folders, and the git CLI directly,
so the MCP server couldn't expand $CI_SERVER_FQDN and friends.

Add src/core/referenceResolver.ts, which expands GitLab variables
over a RepoContextPort (the file's GitLab remote, and a non-GitLab
remote check) and the runtime config port for the component source
fallback. The remote URL parsers move to src/core/gitRemote.ts.

The raw git spawn fallback moves to src/adapters/node/repoContext.ts.
That file also provides a git CLI RepoContextPort for the MCP server.
componentDetector keeps createReferenceResolver(uri) as a thin
wrapper over a VS Code port with the same lookup order as before, so
callers are unchanged.
Local include resolution picked the root and read files through VS
Code APIs inside localComponentResolver, so the MCP server couldn't
validate local: includes.

src/core/localInclude.ts now resolves a local: path over a
WorkspacePort (roots, the resolution root for a file, and a text
reader) and parses the target. The VS Code side moves to
src/adapters/vscode/workspace.ts. That covers the Git extension repo
root, the workspace folder fallbacks, and the open-buffer fast path.
localComponentResolver keeps its exports as thin wrappers, so callers
are unchanged.

Add a done-check unit test. It loads every src/core module under
plain Node, then runs the core validator over the local-include and
enum-options fixtures through a filesystem workspace port and a fake
component lookup.
…gh them (#358)

The MCP server needs to reuse the fetch, cache, and validation code
from plain Node, but those modules read settings, log, and store state
straight through the vscode API.

Add small ports for config, secrets, storage, and logging, a runtime
that holds them, and an Emitter that can stand in for
vscode.EventEmitter later. The default runtime uses the package.json
setting defaults, no secrets, in-memory storage, and stderr, so unit
tests need no setup. The extension installs VS Code adapters first
thing in activate.

Logger and HttpClient now read settings through the config port, and
Logger writes through the log sink. The logLevel watcher and the dev
mode check move to the VS Code logger adapter. Logger.time/timeEnd
used console.time on stdout, which the MCP server needs for protocol
messages, so they now log at DEBUG through the sink.

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

🛡️ Security Hardening Pipeline Results

Branch: beta
Commit: a82d215

Workflow Run: 626
Branch: beta
Commit: a82d215

Scan Status

Scanner Status
bandit ⏭️ skipped
checkov ⏭️ skipped
clamav ⏭️ skipped
codeql ✅ PASS
container ⏭️ skipped
dependency-review ✅ PASS
gitleaks ✅ PASS
grype ⏭️ skipped
lint ⏭️ skipped
opengrep ⏭️ skipped
osv ✅ PASS
sbom ⏭️ skipped
supply-chain ⏭️ skipped
trivy-container ⏭️ skipped
trivy-iac ⏭️ skipped
zap ⏭️ skipped

✅ All enabled scanners completed successfully.

Summaries Collected: 4

Scanner Results

🔬 CodeQL SAST (Javascript)

Status: Completed

Findings Summary

Critical High Medium Low Total
0 0 0 0 0

No security findings detected for Javascript.

Artifacts: CodeQL Reports (Javascript)

🔗 Dependency Review

Status: ✅ No issues found

No vulnerable or license-violating dependencies detected in this PR.
📋 View full report

🔑 Gitleaks (Secrets)

No 🔑 Gitleaks (Secrets) findings summary was produced.

📦 OSV (Dependencies)

No 📦 OSV (Dependencies) findings summary was produced.


Generated by Argus


Generated by Argus

eFAILution and others added 10 commits October 22, 2026 14:27
…ort (#359)

* refactor(core): add host ports and route logger and HTTP config through them

The MCP server needs to reuse the fetch, cache, and validation code
from plain Node, but those modules read settings, log, and store state
straight through the vscode API.

Add small ports for config, secrets, storage, and logging, a runtime
that holds them, and an Emitter that can stand in for
vscode.EventEmitter later. The default runtime uses the package.json
setting defaults, no secrets, in-memory storage, and stderr, so unit
tests need no setup. The extension installs VS Code adapters first
thing in activate.

Logger and HttpClient now read settings through the config port, and
Logger writes through the log sink. The logLevel watcher and the dev
mode check move to the VS Code logger adapter. Logger.time/timeEnd
used console.time on stdout, which the MCP server needs for protocol
messages, so they now log at DEBUG through the sink.

* refactor(core): read the remaining core settings through the config port

The cache manager, component fetcher, discovery config, CI file
matcher, and reference resolver still read settings straight from
vscode.workspace. Route those reads through the runtime config port so
the MCP server can supply its own settings. Same keys, same defaults.

Move the componentSources watcher out of ComponentCacheManager into
activate. It calls the same forceRefresh and logs the same lines, but
the cache manager no longer has to subscribe to VS Code config events.

discoveryConfig no longer needs its lazy vscode require, and
gitlabCiFileMatcher only uses vscode for types now, so both load under
plain Node.

---------

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
Fixes from the security review of the validation core:

- Validation no longer JSON-stringifies each include for a debug log.
  The argument was built even with debug off, and a 500-byte YAML
  alias bomb blocked for seconds before throwing. Documents (and local
  include targets) with more than 100 aliases are skipped.
- isUnsupportedLocalPath treats \ as a separator and rejects drive
  letters, UNC paths, and NUL bytes. The core resolver also checks
  that the joined path stays inside its root.
- Local include targets read from disk must be regular files of at
  most 1 MB, in the Node reader and the VS Code adapter.
- git runs with fixed args, no shell, stdin and stderr ignored, a 5s
  timeout, a 64 KB output cap, and GIT_DIR, GIT_WORK_TREE, and
  GIT_CONFIG* stripped with GIT_TERMINAL_PROMPT=0. It resolves on
  close so stdout isn't truncated.
- The local: line regex no longer backtracks quadratically on long
  whitespace lines.
- parseGitLabRemoteUrl ignores remotes whose host isn't a plain
  hostname, like git@gitlab.com@evil.example:g/p.git.
… into an adapter (#360)

TokenManager, ComponentFetcher, and ComponentService imported vscode
for SecretStorage, EventEmitter, and the token input box, so the MCP
server could not load them.

TokenManager now reads and stores tokens through the runtime secrets
port and fires changes through the core Emitter. When the port can't
store, setTokenForProject throws the same "SecretStorage not
available" error as before. It also keeps a gitlab-token-instances
list in storage, because secret stores can't be listed and the MCP
launcher will need to know which instances have tokens.

The token prompt moves to the VS Code auth prompt adapter behind a new
AuthPromptPort. Hosts without a UI return undefined, which means public
access. The unused ExtensionContext params and setSecretStorage are
gone.

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
Hover markdown embeds component and input descriptions from remote
specs and was marked isTrusted = true, so a command: link in a
description could run any VS Code command when clicked. Enable only
gitlab-component-helper.detachHover.

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
…361)

* refactor(core): persist the component cache through the storage port

ComponentCacheManager and UnifiedCache read and wrote VS Code
globalState through an ExtensionContext and used vscode.EventEmitter,
so the cache could not load outside the extension.

Both now go through the runtime storage port, and the cache manager
fires through the core Emitter. The ExtensionContext params and
setContext are gone. A new persistent flag on the storage port drives
the cache status location text, which used to key off whether a
context was set.

Add an eagerRefresh option (default true, as today). A host that
passes false still loads the stored cache but skips the startup fetch
of every source, so the MCP server makes no network calls until a tool
needs components.

Drop the commands re-export from the component services barrel so the
cache modules that import the barrel stay free of vscode. Everything
under src/services except commands.ts now loads under plain Node.

* fix(core): harden events, token index, and cache logs found in review

- Emitter logs and skips a throwing listener, like vscode.EventEmitter,
  so one bad listener can't fail a token save or a cache update.
- The token instance list is best effort. A failed write is logged and
  the token still counts as saved. A malformed list is logged, and an
  instance string that embeds credentials is never recorded.
- Request dedup keys hash the auth token so the raw token never sits in
  getDeduplicationStats().
- Cache logs report the real storage location and warn when storage
  isn't persistent.
- A failed refresh after a componentSources change logs at warn.

---------

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
ValidationProvider builds vscode.Diagnostic objects inline while it
walks the include list, so the MCP server can't reuse the checks.

Add src/core/validation with validateIncludes, which returns plain
findings (code, severity, message, file, 0-based range, metadata) for
component and local includes. Callers supply the reference resolver,
the component lookup, and the local include reader, so nothing in
core touches vscode. Messages, codes, ranges, and metadata are the
ones the provider publishes today. The provider switches over in the
next change.

The local include text parser moves to src/core/localInclude.ts, and
the reference result types move to src/core/references.ts. The
provider modules re-export both, so callers are unchanged.

New unit tests pin the findings for the extension-host fixtures
(local-include, duplicate-include, flow-inputs, merge-key,
revalidate-on-edit, enum-options) and for component includes with a
fake lookup.
@eFAILution
eFAILution deployed to publish-beta October 22, 2026 19:19 — with GitHub Actions Active
Add a standalone MCP server so agents (Copilot Chat, Claude Code, and
others) can query the same component data the extension uses.

The protocol is hand-rolled: newline-delimited JSON-RPC 2.0 over
stdio, with initialize, ping, tools/list, and tools/call. It
negotiates protocol versions 2025-11-25, 2025-06-18, 2025-03-26, and
2024-11-05, and only sends outputSchema and structuredContent when the
negotiated version has them. Tool arguments are validated against each
tool's schema. Results are capped in size, and unexpected errors don't
leak details. The transport is the only writer to stdout. A stray
console.log is redirected to stderr so it can't corrupt the stream.

The first tool, get_component_spec, returns a component version's
inputs, versions, and latest stable version. Spec text is cleaned of
control and bidi characters and length-capped. GitLab error bodies
are never passed through.

esbuild builds the server to out/mcp/server.js, and a plugin fails the
build on any vscode import. The bundle ships in the VSIX. Test output
and local agent state are now excluded from the package.
…network

Give the MCP server its own host ports and install them once at
startup. After that the runtime is locked against replacement.

- Config: --config > GCH_CONFIG_JSON > GCH_LOG_LEVEL > the root's
  .gitlab-component-helper.json > its .vscode/settings.json (JSONC, via
  jsonc-parser) > defaults. Unknown or wrongly-typed keys are dropped,
  and gitlabToken is refused.
- Tokens: GCH_TOKEN_<HOST> and GITLAB_TOKEN (for GITLAB_HOST) are read
  once and deleted from the environment. The variable name is lossy,
  so a token is only bound to a host named by the launcher's settings,
  GCH_TOKEN_HOSTS, or GITLAB_HOST, never by a workspace file. Two
  trusted hosts that share a variable name get neither token.
- Storage: one JSON file per key in a 0700 cache dir, written
  atomically as 0600.
- Files: only regular files of at most 1 MB under the realpath'd
  roots. Traversal, symlinks out, and missing files all get the same
  answer.
- Network: HTTPS only, to gitlab.com, componentSources instances, and
  hosts with a token. Private, loopback, and link-local addresses are
  refused, including after DNS resolution. A refused host gets a
  message saying how to allow it.
- git: an absolute path found on PATH, only for files under the
  roots, and GIT_CEILING_DIRECTORIES stops discovery above them. Child
  processes no longer inherit GCH_* or GITLAB_TOKEN.
Add the tools an agent needs to find and fix CI component problems:

- validate_ci_file runs the core validator and version check over one
  file, a directory, or every CI file in the workspace (same globs as
  the extension, 500 files max, node_modules and .git skipped).
- check_component_versions returns only outdated-version findings.
- list_component_usages lists every component and local include with
  its line, version, and input names.
- list_component_versions returns tags, branches, and the latest
  stable version.

The server never writes files. Findings carry suggestedEdits for
mechanical fixes (version bumps, and renaming an unknown input that is
one typo away from a declared one). The initialize instructions tell
agents to validate first, read the spec for the exact version before
changing inputs, and apply the edits themselves.

Output is built for untrusted content. Messages are fixed text.
Names, descriptions, and defaults from YAML or specs go in cleaned,
length-capped data fields. Provided input values never appear, since
they can be alias-bomb trees. Findings are capped. Hosts the allowlist
refused come back as blockedHosts with a hint on how to allow them.
Contribute an MCP server definition provider, so Copilot Chat in
agent mode (and other in-editor MCP clients) can start the server with
no setup.

The definition runs out/mcp/server.js with the editor's own Node
(process.execPath with ELECTRON_RUN_AS_NODE=1) and passes:
- a --root per workspace folder
- the effective settings minus gitlabToken as GCH_CONFIG_JSON
- a cache dir under global storage

Tokens are only added in resolveMcpServerDefinition, when VS Code
actually starts the server. That covers every instance in the token
index, plus every componentSources instance with a stored secret, so
tokens saved before the index existed still work. GCH_TOKEN_HOSTS names
those hosts so the server can bind each token to exactly one. Tokens
are never logged.

The definition refreshes on token, settings, and workspace folder
changes. Registration is skipped on editors without the API and can't
fail activation.
Add docs/mcp.md: the tools, using the server from Copilot Chat agent
mode, prompts to try, running it from other MCP clients, and its
options, setting precedence, token variables, host allowlist, file
limits, and protocol versions. Link it from the README.

Record the MCP design in AICaC: the core, adapters, and mcp
components, and the MCP_STDIO_HANDROLLED,
MCP_SERVER_NEVER_WRITES_FILES, MCP_DISTRIBUTED_AS_RELEASE_ASSET, and
MCP_TOKENS_VIA_ENV_FROM_EXTENSION decisions.

Fix the stale VS Code engine version (1.120) in AGENTS.md and
.ai/context.yaml, and drop the removed outputChannel.ts from the
architecture file.
The network policy refused every private and loopback address, which
blocks self-managed GitLab on internal networks even when the user
listed it in componentSources. The host allowlist already limits who
we talk to, so only link-local (cloud metadata), unspecified, and
multicast addresses stay blocked.
takeEnvTokens deleted GCH_TOKEN_HOSTS before bootstrap read it, so
token binding fell back to GCH_CONFIG_JSON. That includes workspace
settings, and GCH_TOKEN_<HOST> names are lossy, so a repository that
set componentSources to gitlab-example.com got the gitlab.example.com
token.

- The extension now passes tokens as one GCH_TOKENS_JSON map of exact
  host to token. The server reads it and deletes it first, and binds
  each token only to its key.
- GCH_TOKEN_<HOST> (for terminal use) binds only to a host named in
  GCH_TOKEN_HOSTS, which is now read before it's deleted, or in
  --config.
- The extension picks token hosts from the token index plus the
  user's global componentSources, never workspace or folder values.
- Only launcher-trusted hosts widen the network allowlist: --config,
  GCH_TRUSTED_HOSTS (the user's global sources), token hosts, and
  gitlab.com. A host named only by a repo file or workspace settings is
  blocked. The hint tells the agent to use user settings, never repo
  files.
- Workspace config files may only set catalog and checking behavior,
  not network, cache, or logging settings.
- Settings are parsed into null-prototype objects and looked up with
  own-property checks, so __proto__ can't smuggle values.
- The allowlist matches url.host, with the default port normalized.
…e lock

- Workspace paths must sit inside a root as written, before any
  realpath or fs call. UNC and \\?\ paths are refused up front.
- readBoundedTextFile stats first and opens with O_NONBLOCK, so a FIFO
  or device is never read. Repo config files are read through the
  workspace guard, so a symlink out of the root is ignored.
- Output text also drops Unicode tag characters, zero-width and
  direction marks, word joiners, BOM, the Arabic letter mark, and
  line and paragraph separators. File paths are cleaned and capped
  everywhere. documentationUrl is kept only when it's https.
- Addresses are parsed to bytes, so IPv4 carried in IPv6 (mapped,
  compatible, NAT64, 6to4) gets the same checks. 100.100.100.200 and
  fd00:ec2::254 (cloud metadata) are blocked.
- lockRuntime freezes the ports and the network policy. HttpClient
  refuses to send when the runtime is locked and no policy is
  installed.
HttpClient is shared by the extension and the MCP server:
- It aborts a response body over 10 MB.
- Each request has an overall deadline (three socket timeouts, at most
  3 minutes), so a server that trickles bytes can't hold it open.
- httpTimeout is clamped to 1-60s and retryAttempts to 0-5 wherever
  they're read.
- A request the network policy refuses (RequestRefusedError, which
  HostNotAllowedError now extends) fails at once, with no retries.
- GitLabComponentError keeps its cause, so wrapped refusals are still
  recognized.

The MCP server stops a tool call after 60s with an isError result.
It also turns away calls beyond 4 in flight, instead of queuing them
without bound.
eFAILution and others added 8 commits October 22, 2026 16:41
…nabled

docs/mcp.md now matches the hardened server:
- GCH_TOKENS_JSON with exact hosts. GCH_TOKEN_<HOST> binds only to
  gitlab.com, GCH_TOKEN_HOSTS, or --config.
- Only launcher-trusted hosts are contacted. Repository files may only
  set catalog and checking settings.
- The response, deadline, tool-call, and walk limits.
- Path rules and the suggestedEdits coordinates.
- What the server gets when an in-editor AI client starts it, and the
  application-scoped gitlabComponentHelper.mcp.enabled setting.

The README settings table lists mcp.enabled, and the
MCP_TOKENS_VIA_ENV_FROM_EXTENSION decision records the new hand-off
and mitigations.
A request that throws while being built (e.g. a token with a trailing
newline in a header) left its deadline timer running, and the timer
later threw a ReferenceError on the uninitialized request. Clear it in
the catch and guard the destroy.

Also give the whole-request deadline 6 socket timeouts (60s at the
default) so slow, steady downloads finish, map httpTimeout 0 to the
longest allowed timeout, clamp pagination and parallel fetches the same
way, and treat retryAttempts 0 as zero retries.
…mlinks

- Tokens are trimmed (a pasted token often ends in a newline) and
  refused if they're empty or contain control characters. That
  applies both when the extension stores one and when the MCP server
  reads GCH_TOKENS_JSON, GCH_TOKEN_<HOST>, or GITLAB_TOKEN. The error
  and warnings never include the token.
- GCH_CONFIG_JSON from the extension now uses the user (global) value
  for every key a workspace may not set, so the repo's
  .vscode/settings.json can't reach the server's trusted layer that
  way. The workspace-settable keys move to core as
  WORKSPACE_SETTABLE_KEYS, shared by both sides.
- lockRuntime deep-freezes every port, not just the top-level object.
- Before realpath, the workspace follows symlinks one component at a
  time. A link with an absolute target (which could be /net autofs or
  a UNC share) or one that lands outside the roots is refused, so
  realpath never touches the network.
…ules

Recommend GCH_TOKENS_JSON for terminal setups, since it binds each
token to exactly one host. Note that tokens are trimmed and ones with
control characters are ignored. Note that network, cache, and logging
settings come from user scope only, and that symlinks must be relative
and stay inside the roots.
AI clients only get the user's GitLab tokens after they opt in with
gitlabComponentHelper.mcp.enabled.
A process's starting environment stays readable to the same OS user
(ps eww) even after the server deletes the variable, so an agent with
terminal access could read tokens passed in env. The extension now
writes them to a random 0600 file in its private storage and passes
only the path. The server refuses a symlinked, oversized, shared, or
foreign-owned file, reads it once, and deletes it. The extension
deletes its own unread files after 30s and on dispose, and purges stale
ones at activation.
The server now reads and deletes a hand-off file only if its name
matches what the extension writes and it sits in a private mcp-handoff
folder we own, so a launcher typo can't make it delete an unrelated
private file. A failed delete after a good read is a warning, not lost
tokens.
eFAILution and others added 2 commits October 22, 2026 17:47
The Marketplace page is the README, so it now covers the opt-in
setting, the Copilot setup steps, example prompts, the tools, how
tokens are protected, and troubleshooting when Copilot doesn't see the
server.

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
@eFAILution
eFAILution deployed to publish-beta October 22, 2026 21:54 — with GitHub Actions Active
eFAILution and others added 6 commits October 22, 2026 18:06
scripts/ci/decisionRecords.ts checks, lists and scaffolds decision
records. New decisions go in .ai/decisions/ADR-<issue>.yaml, one AICaC v2
document per file with a single key that matches the file name.
.ai/decisions.yaml keeps its twenty entries and is closed to new keys.

check fails on duplicate keys, new legacy keys, badly named or shaped
record files and dangling supersedes. With --branch and --base it also
fails on a record whose number is not the branch's issue. list builds the
index from both stores at read time. new writes a skeleton record.

ADR-366 records the decision.

Refs #366
The Decision Records workflow runs on every PR and push to main and
beta, with no paths filter so it can be a required check. Release
commits are skipped by a job-level if. On PRs it passes the head branch
through an environment variable and diffs against HEAD^1; on pushes it
runs a plain check. Actions are SHA-pinned and the token is read-only.

Refs #366
AGENTS.md gets a quick reference and an "Issues, branches, and records"
section. It says to start from an issue, name the branch
<type>/<issue>-<desc>, check nobody has the issue, open a draft PR
against beta, and record decisions as .ai/decisions/ADR-<issue>.yaml.
It also covers what each check failure means and how to move to a
correctly named branch without closing the PR.

The PR template opens with "Closes #" and adds checklist items for the
branch name and the record file. README, workflows.yaml, context.yaml
and architecture.yaml point at the new rules and files.

Refs #366
eFAILution and others added 2 commits October 23, 2026 09:29
* feat(mcp): report why a component fetch failed

Classify fetch failures (TLS, DNS, connection, timeout, 404, rate limit, server) from Node error codes and HTTP statuses and return a fixed-text hint. validate_ci_file adds reason, errorCode and hint to component-fetch-failed findings and groups failures per host in summary.fetchFailures. A path outside the workspace now names the roots.

Refs #370

* fix(mcp): look past our own error codes when classifying a fetch failure

NetworkError carries its own code (NETWORK_ERROR), and the classifier
stopped at it, so a TLS, DNS or connection cause was never reached and
every network failure came back as unknown. Skip our wrapper codes when
walking the cause chain.

Also drops a non-null assertion that failed lint.

Refs #370

* fix(mcp): give expired and mismatched certificates their own hint

The trust-store advice (NODE_EXTRA_CA_CERTS) only helps when Node does
not know the issuer. An expired certificate needs a renewal or a clock
fix, and a hostname mismatch usually means the wrong instance address.

Refs #370

---------

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
eFAILution and others added 2 commits October 23, 2026 09:42
The beta -> main release PR adds every record that landed on beta, and
its head branch has no issue number, so the check always failed it. Each
of those records already passed the rule on its own PR. Fork branches
named beta still get the rule.

Closes #368

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
@eFAILution
eFAILution deployed to publish-beta October 23, 2026 13:48 — with GitHub Actions Active
eFAILution and others added 3 commits October 23, 2026 12:20
…#373)

* feat(mcp): cache component specs and start from the extension's cache

The server fetched a spec from GitLab on every tool call. It now keeps
its own spec cache in its cache directory and checks it before GitLab.
A spec pinned to a tag is reused while cached, as in the extension;
any other ref only for cacheTime. Failed fetches are never stored.

When VS Code starts the server, the extension writes the spec fields of
its component cache to a private file in its storage folder and passes
the path in GCH_CACHE_SEED. The server reads it bounded and
shape-checked, and uses it when its own cache has nothing fresh.

Refs #372

* docs(mcp): describe spec caching and the cache seed

Refs #372

* feat(mcp): keep a running server in step with the extension's cache

The seed was only written when VS Code started the server, so specs the
extension cached later, and cache resets, never reached a running one.
Now the extension rewrites the seed shortly after its cache changes,
and the server re-reads it when the file's mtime changes, checking at
most once a second. A reset is carried as clearedAt, and the server
drops what it fetched before then.

Refs #372

* docs(ai): fix the indentation of an ADR-372 consequence

Refs #372

---------

Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
Co-authored-by: Simon Heather <simon.heather@yulife.com>
@eFAILution
eFAILution deployed to publish-beta October 23, 2026 16:23 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
publish-beta — 1bf3ab54 Deployed Oct 23, 2026 by eFAILution via publish #37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants