Commit Graph
1780 Commits
Author SHA1 Message Date
affaan-m 503e2ef331 fix(mcp): reconcile reviewed chrome-devtools pin with current main
Preserve the contributor pin and current main. Keep both default connector declarations at the exact official 1.10.1 release and retain user-managed configuration behavior.

Source-PR: https://github.com/affaan-m/ECC/pull/3233
Source-Head: 62bf2b2910
Integration-Main: d3b8a3e908
2026-09-27 21:41:47 -04:00
affaan-m 506f25fb54 fix(plan-canvas): confine artifact assets and sandbox SVG documents
Preserve the contributor security repair and current main. Apply the artifact sandbox to SVG document responses and report unavailable symlink checks separately from passing assertions.

Source-PR: https://github.com/affaan-m/ECC/pull/3241
Source-Head: d237e1972f
Integration-Main: d3b8a3e908
2026-09-27 21:39:24 -04:00
affaan-m f5d0fcc7e1 fix(install): reconcile UTF-8 BOM support with current main
Preserve the contributor commits and current main changes.

Source-PR: https://github.com/affaan-m/ECC/pull/3210
Source-Head: 94ca2de35e
Integration-Main: d3b8a3e908
2026-09-27 21:35:30 -04:00
affaan-m 6dc706fe85 fix(install): distinguish user plugins from ECC aliases
Attribute unrecorded OpenCode aliases only to exact trusted ECC source or build bytes while keeping planned and recorded destinations strict. Pass the trusted source root through repair plans and preserve unrelated user plugin files.
2026-09-27 21:23:11 -04:00
affaan-m 9f6c16fe02 fix(tests): make guarded parent replacement portable to Windows 2026-09-27 21:04:21 -04:00
affaan-m aff9c64560 fix(control-pane): preserve snapshot integrity during failed refreshes 2026-09-27 21:03:37 -04:00
affaan-m 5ffe239ee5 fix(tests): reconcile launcher coverage with current main 2026-09-27 21:03:37 -04:00
affaan-m 610495474a Merge current main while preserving reviewed OpenCode consent guards 2026-09-27 20:32:22 -04:00
affaan-m 923019d99f fix(opencode): preserve consent ownership through guarded install and repair 2026-09-27 20:32:22 -04:00
affaan-m 80b9339727 Merge current main into PR #3121, preserving reviewed contribution 2026-09-27 20:26:25 -04:00
affaan-m f8c604d37e Merge current main into PR #3060, preserving reviewed contribution 2026-09-27 20:24:52 -04:00
Tushar PanchalandClaude Sonnet 5 042924e1f4 fix(install): support invocation through POSIX sh (#3014)
* fix(install): make install.sh robust to sh/dash invocation

Two related portability fixes so `sh install.sh` behaves correctly
even though the script is written for bash:

- The cygpath detection used the bash-only `&>/dev/null` redirection.
  dash misparses `&>`, so `command -v cygpath &>/dev/null` always took
  the true branch and tried to run the nonexistent `cygpath` binary,
  failing with "cygpath: not found". Switched to the POSIX-portable
  `>/dev/null 2>&1` form.

- Some dash builds don't support `set -o pipefail`, so `sh install.sh`
  can fail immediately at that line before even reaching the cygpath
  check (or the `[[ ... ]]` symlink-resolution logic further down).
  Added a guard that re-execs the script under bash when the current
  shell lacks bash capabilities, so the rest of the bash-only syntax
  always runs under a real bash regardless of the invoking shell. The
  guard probes for the `[[` compound command directly (via
  `eval '[[ 1 == 1 ]]'`) rather than trusting the $BASH_VERSION
  environment variable, since that variable could be inherited or
  spoofed under a non-bash shell and cause the guard to be skipped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XBaBRjaZgwhrZtYoqcQfp

* test(install): cover the sh install.sh re-exec path

Greptile flagged that the bash re-exec guard added in install.sh has
no automated coverage, since the existing test helper only invoked
the wrapper via `bash`. Adds two regression tests:

- "delegates to the Node installer when invoked via a POSIX sh
  wrapper" — runs the script via `sh` and asserts the args/cwd still
  reach the Node installer correctly.
- "re-execs into bash under sh even when BASH_VERSION is spoofed in
  the environment" — exercises the eval '[[ 1 == 1 ]]' capability
  probe directly, guarding against a regression back to trusting the
  (spoofable) $BASH_VERSION variable.

CodeRabbit then pointed out that the second test used generic `sh`,
which could trivially pass without exercising the re-exec branch at
all if `sh` ever resolves to bash on some system. Added a
findPosixOnlyShell() helper that prefers `dash` (falling back to
checking `sh`, and skipping with an explicit message if neither
genuinely lacks bash's `[[`), so the test reliably exercises the
branch it claims to cover instead of passing vacuously.

CodeRabbit then flagged that the skip path itself was miscounted as
a pass (the callback returned normally, so `test()` reported success
and `passed` was incremented even though nothing executed). Moved
the findPosixOnlyShell() check outside the test() registration, so
the test is only registered — and only counted — when a genuinely
POSIX-only shell is actually available; otherwise it's excluded from
both the passed and failed counts with an explicit skip line.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XBaBRjaZgwhrZtYoqcQfp

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 19:04:10 -05:00
haelyra befde0d2cd fix(profiles): bind routing receipts and harden review paths 2026-09-27 18:01:40 -04:00
haelyra 874883c728 fix(profiles): make evaluator permissions and Windows checks portable 2026-09-27 17:03:28 -04:00
haelyra 0765f7a2ca feat(profiles): add opt-in Lean/Full and hybrid Auto selection 2026-09-27 16:46:07 -04:00
pasmudandCommandCodeBot d42d918bd3 fix(control-pane): stop a stale poll lowering the staleness mark
The settled-token guard correctly ignored an older poll, but that poll
still set settledPoll to its own token on the way out. With three polls
settling in the order 3, 1, 2, the ignored poll 1 pulled the mark back
to 1, so the even older poll 2 then passed the guard and replaced poll
3's counts, or marked a healthy view offline.

The mark now only moves forward. A poll that settles late is itself
stale, so it has no business moving the mark at all.

Covered by a new runtime case that settles three polls out of order as
3, 1, 2, once with an older success and once with an older failure. The
success case catches the counts regressing, the failure case catches a
healthy view being marked offline. Reverting the fix trips each of them
independently, and the unmutated file passes all 21.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-27 14:03:13 +00:00
vam 3ade64aa43 Merge origin/main into feat/2885-agent-security-hardening 2026-09-27 20:43:42 +08:00
vam c196dc7d51 fix(skills): reject Windows-unsafe components in workspace_path example
Reject reserved device names (including with extensions and the
superscript COM/LPT variants), trailing dots or spaces, and colon
stream syntax on every platform, and execute the documented snippet
in a regression test.
2026-09-27 20:34:49 +08:00
Seekers2001 2b35e299ea test: cover launcher error handling 2026-09-27 17:32:44 +08:00
pasmudandCommandCodeBot cee58ba39e fix(control-pane): settle the poll guard against the last settled request
The staleness guard compared a poll against the newest poll STARTED, and
only the failure path used it. Two orderings were wrong.

An older success settling after a newer one had no guard at all, so it
overwrote the newer counts, the canvas label and the announcement with
older data. Both paths now check the token.

An older failure settling while a newer poll was still in flight was
discarded, because a newer poll had merely started. The page then kept
the last successful counts and its no-steering message as if they were
current. Anchoring the guard to the newest poll that has SETTLED fixes
that: the older failure is still the newest thing to have settled, so it
lands, and the pending poll restores the view when it answers.

That single change covers both orderings, so the guard no longer needs
to know which path it is on. Each poll records itself as settled when it
finishes, whatever the outcome.

A poll also had no timeout, so a request that never answered left the
last steering guidance on screen indefinitely. Each poll now aborts
after ten seconds through an AbortController, and that abort is what
wakes the poll, so a timeout is reported as an outage instead of
silently leaving stale guidance.

The test harness can now hold a request open indefinitely and fire
pending timers on demand, so the timeout is exercised without waiting.
New assertions cover an older success losing to a newer one, an older
failure landing while a newer poll is in flight and then being
recovered by it, and a hung poll timing out into the offline state.
Removing the success guard, re-anchoring the guard to the last started
poll, and removing the timeout each fail one of those assertions, and
the unmutated file passes all of them.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-27 08:11:44 +00:00
Matt Van HornandClaude Opus 5.5 6e15d45467 test(ci): pin that identifiers containing CLAUDE_PROJECT_DIR still pass
Add the mirror fixture for the fence scan: $MY_CLAUDE_PROJECT_DIR,
${MY_CLAUDE_PROJECT_DIR}, $CLAUDE_PROJECT_DIR_TMP and
${CLAUDE_PROJECT_DIR_TMP} in a bash fence must not fail. The scan
already requires the `$` to be attached and ends on a word boundary, so
this pins the pass side as firmly as the fail side.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EzFUaXW84Y6LHzKysc5mTc
2026-09-26 22:34:06 -07:00
Seekers2001 9d1e05a06e fix: normalize markdown link path separators 2026-09-27 11:05:25 +08:00
Seekers2001 5b65751bd5 fix: restore plan canvas process launcher 2026-09-27 11:02:31 +08:00
pasmudandCommandCodeBot e97e333040 fix(control-pane): keep the canvas label and live region consistent
Two ways the outage state could mislead an operator reading either
channel.

The failure branch cleared the live region but left the canvas
aria-label holding the counts from the last successful poll, so landing
on the canvas during an outage reported stale counts. It now sets the
same unavailable wording on the canvas. apply() already restores the
count label on the next valid poll, and there is now a test for that.

Polls run every 5000ms and are not sequenced, so a slow older request
can settle after a newer one has succeeded. Its catch block then
announced an outage while the canvas showed the newer counts, and the
two channels contradicted each other. Each poll now takes a monotonic
token and only the newest one may touch the view, so a superseded
failure is dropped.

The runtime test can park a response, which lets it settle an older
failure after a newer success. New assertions cover the canvas label
after an outage, the restored label after recovery, and the
superseded-failure case. Removing the canvas reset, the token guard, or
the restore in apply() each fail an assertion, and the unmutated view
passes all of them.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-27 02:44:18 +00:00
pasmudandCommandCodeBot 9e0996c6d6 fix(control-pane): replace stale live-region guidance when a poll fails
The live region announced the advisory and steering state on every
change, but a failed or invalid poll only set the visible status to
offline. The live region kept the last known message, so during an
outage a screen-reader user could still hear "No steering is required"
from a poll that had already failed. That is worse than silence,
because stale guidance reads as current.

Route announcements through a single helper and have the failure path
replace the guidance with a message saying the data is unavailable and
the counts are unknown. The helper still suppresses a repeat of the
same message, so a continuing outage stays quiet and recovery speaks
again.

Cover the success then failure and failure then recovery sequences at
runtime. Reverting just the failure-path announcement fails the new
assertion, and the unmutated view passes all of them.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-27 02:01:44 +00:00
pasmudandCommandCodeBot 6744a01748 fix(control-pane): announce polled advisories through a live region
The control-plane view polls every few seconds and rewrites the canvas
aria-label with the new counts, but neither the canvas nor #status is a
live region, so a screen-reader user working elsewhere never hears an
advisory appear.

Add a visually hidden polite live region that speaks only when the
advisory and steering counts actually move. Re-announcing an unchanged
summary every five seconds would talk over the operator without saying
anything new, so the last announced pair is remembered and a no-change
poll writes nothing. The canvas keeps its own label as the on-demand
description.

Also give the runtime test a recording canvas context and a populated
view, so the rendered risk words, the marker shapes, and the live region
are asserted at runtime rather than only against template source. The
risk-level assertion now reads the rendered risk cells instead of the
whole panel, where a level word could be satisfied by a task id.

The populated assertions were mutation checked: collapsing the marker
shapes, dropping the risk words, freezing the canvas label, announcing
on every poll, emptying the live region, and removing the steering cue
each fail at least one assertion.

Fixes the no-regex-spaces lint error on the riskColor match, which used
two literal spaces in a row.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-27 00:46:39 +00:00
pasmudandCommandCodeBot 18e78da390 test(control-pane): derive the a11y palette from the rendered view
CodeRabbit correctly noted that hard-coding #2ea043 and #ff7b72 in the
test decoupled the contrast assertions from the colors the view
actually renders, so a palette change could leave them passing.

The test now reads the legend glyphs, the legend colors, the
riskColor() returns, and the body background back out of the rendered
HTML, and asserts that the legend and canvas palettes agree before
checking any ratio. Verified both directions: a mismatched canvas
color fails the palette check, and a full revert to #3fb950 fails the
luminance check at 1.01:1.

Extends the coverage to the traffic level and the body background,
which were previously unchecked.

Co-Authored-By: CommandCodeBot <noreply@commandcode.ai>
2026-09-26 22:59:22 +00:00
pasmudandCommandCodeBot 3052732f28 fix(control-pane): add non-color risk cues to the control-plane view
The control-plane projection encoded risk with hue alone. The clear
green #3fb950 and the resolution red #ff7b72 differ by only 1.01:1 in
relative luminance, so a red-green color-blind operator saw the same
grey for "clear" and "steer now".

Risk now carries a second, non-color channel, matching the pattern
merged for the sibling airspace view in #3193:

- markers are drawn as a circle, square, or triangle per risk level
- the legend shows the matching glyph instead of color-only dots
- the Lanes panel prints the level name next to the percentage
- the canvas gets role="img" and a live aria-label describing counts

The clear green also moves to #2ea043 so it stays separable from the
resolution red by luminance (1.34:1) while keeping 5.73:1 against the
#0b0e14 background.

Adds tests/lib/control-plane-view-ui-a11y.test.js covering the roles,
the shapes, the legend, and the contrast ratios. The existing test
harness needed a setAttribute stub for the new aria-label update.

Co-Authored-By: CommandCodeBot <noreply@commandcode.ai>
2026-09-26 22:52:34 +00:00
WRG d237e1972f fix(scripts): sandbox HTML siblings, link-name MIME, portable fixtures
Review follow-ups: serve text/html sibling assets under the artifact sandbox CSP (direct navigation no longer bypasses it); resolve MIME from the request name first with target extension as fallback; make security fixtures cross-platform (no angle brackets in filenames, skip when symlink creation is denied). Residual check-then-read TOCTOU documented as accepted for the loopback-local threat model. Tests 33/33 GREEN, ESLint clean.
2026-09-26 02:39:42 +10:00
WRG 742395bca7 fix(scripts): confine plan-canvas artifact assets and escape 404 paths
Serve artifact pages with a sandbox-only CSP mirroring the viewer iframe, fail-closed realpath confinement for sibling assets, and escape the artifact path in the missing-artifact 404. TDD: 4 RED failures (CSP header, 404 escaping, symlink escape) before the fix, 31/31 GREEN after; markdown/sessions/hook suites and ESLint clean.
2026-09-26 02:14:24 +10:00
Matt Van Horn da5757b6ff fix(ci): flag CLAUDE_PROJECT_DIR in skill fences with Codex rewrite guidance 2026-09-25 06:44:12 -07:00
Tomelias 8f6b4d6133 test(skills): add MCP dependency review regression coverage 2026-09-25 13:08:55 +03:00
Samuel Bushi 980e637c3c fix: normalize viewport evidence labels 2026-09-25 09:13:36 +02:00
L4XB 217f303713 fix(control-pane): fail a load whose snapshot cannot be shown, and run the shown query
render() ran after load()'s ordered failure path and after shownLoad
had moved on. A snapshot the board could not show (a session whose
detectedHarnesses is stored as an object) threw past the ordering, and
an older valid snapshot could no longer take the board. render() now
runs inside that path, and shownLoad moves only once it returns.

Run posted state.query, the query typed last. When an older load
succeeds after a newer one failed, the board shows the older query's
results and recall command while Run acted on the newer query. Each
load now keeps its query, render() records it for the board, and Run
posts that.

The test harness assigns new arrays instead of pushing onto
page.pending, and records requests the same way.
2026-09-25 06:45:11 +02:00
L4XB a82d587e3f fix(control-pane): order data and outcomes of every load separately
Two cases the finished-load rule got wrong. When the first snapshot was
still loading and a live refresh failed, the first snapshot arrived
late and was dropped, leaving the pane empty. And manual loads, the
Refresh button and a query, reported their failures outside the
ordering, so a late manual failure could cover newer data and an older
success could clear a newer manual failure.

The board now shows the newest data any load brought, and the error
box the outcome of the newest load that has finished. load() reports
its own fetch failures, as a live-refresh failure when the interval
started it and as a plain error otherwise, so every load is ordered the
same way. Data that arrives after a newer failure still shows, and the
failure stays, dated by that data.
2026-09-25 04:59:07 +02:00
L4XB 36460824ee test(control-pane): pin that a late response keeps its hands off newer data
Two held refreshes complete in reverse order with different data. The
older response arrives last and must not replace what the newer one
put on the board. It did before the previous commit.
2026-09-25 03:13:18 +02:00
L4XB 928a295247 fix(control-pane): let the newest finished load decide the board
A newer refresh that failed was shown, and then an older one that
succeeded cleared the message, so the board looked live although no
load that started after the failed one had succeeded.

The board now follows the newest load that has finished, with its data
or with its failure. A load that finishes after a newer one leaves the
board alone: its data is not rendered and its failure is not reported.
This also keeps a late response from replacing newer data.
2026-09-25 03:12:40 +02:00
L4XB 029f850bd0 test(control-pane): pin which load decides a failed refresh
With three refreshes in flight, the second succeeds and the first
fails while the third still runs. The board shows data from a load that
started after the failed one, so the failure stays off it. Comparing
against every load started so far would report it.
2026-09-25 02:56:33 +02:00
L4XB 96516aa247 test(control-pane): give the page a fixed clock
The refresh-failure test looked for the current Gregorian year in a time
the page formats with toLocaleString. A locale with another calendar
shows another year: under th_TH the page prints 2569 and the test
failed. The page now runs on a fixed instant, and the test compares
against toLocaleString of that instant.
2026-09-25 02:55:35 +02:00
L4XB 9848380309 fix(control-pane): judge a failed refresh by the loads that started after it
A failed refresh was dropped whenever any load finished while it ran.
When two interval refreshes overlapped and the older one succeeded, the
newer one's failure was dropped too, so the board looked live although
its latest refresh had failed.

Loads are now numbered as they start. A failure is dropped only when
the board shows data from a load that started after it.
2026-09-25 02:55:35 +02:00
L4XB 10969f587b fix(control-pane): keep a superseded refresh failure off the board
A live refresh that fails after a newer load has already replaced the
board no longer describes what is shown, so it is only reported while
the data it was meant to replace is still on screen. The time of that
data now includes its date, for a pane left open overnight, and it is
kept in its own binding instead of on the shared state object.
2026-09-25 00:07:08 +02:00
L4XB 0ae2fa06a4 fix(control-pane): report a failed live refresh instead of discarding it
The 15-second refresh caught every error with `load().catch(() => {})`,
so a control pane that had lost its server kept showing the last
snapshot as if it were current. A failed refresh now shows the error box
with "Live refresh failed. The data below is from <time>." and the
error, and the next successful refresh clears it, as load() already
clears the box.

The new test runs the served page script in a vm with a stand-in for the
browser APIs it uses, fails a refresh, and checks both transitions.

Fixes #3176
2026-09-24 23:25:13 +02:00
ritms42andClaude Opus 5.5 62bf2b2910 fix(mcp): pin chrome-devtools-mcp to 1.10.1 instead of @latest
The default chrome-devtools connector is launched with `npx -y` and
`@latest`, so every session start can silently install whatever version
was most recently published to npm. Pinning makes the default connector
reproducible and removes the unpinned-npx finding that /security-scan
(AgentShield) reports against ECC's own .mcp.json.

The same spec is pinned in the Codex merge script so both harnesses
stay in sync, and the Codex merge test is updated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 21:48:57 +02:00
Geronimo 2a9b56bf36 fix: replace blanket ECC_TEST_ALLOW_FAILURES with targeted ECC_TEST_SKIP
Per chenzh01 review: the blanket ECC_TEST_ALLOW_FAILURES=1 env var masks all failures. Replace with ECC_TEST_SKIP listing only the known-broken test files. Any new failure will break CI. Added skip-pattern warning in test output for visibility.
2026-09-25 01:04:04 +05:30
Samuel Bushi c770b08eef fix: require rendered evidence for every target viewport 2026-09-24 12:37:05 +02:00
Samuel Bushi 90481d5e4a fix: consolidate UI evidence gate into frontend design direction 2026-09-24 11:29:32 +02:00
Yann Roberto 6d9c1eb3bc fix(skill-stocktake): explicitly replace full-run caches 2026-09-23 13:17:34 +08:00
Yann Roberto dddbe52cb7 fix(skill-stocktake): exclude trash and mark unmeasured usage 2026-09-23 11:41:37 +08:00
nguyenphivn 1aea78011b chore(codex): route sample roles to GPT-6 Sol and Luna 2026-09-23 09:28:37 +07:00
Dante 306c217729 fix: scan piped foreach grouping blocks 2026-09-23 01:37:51 +08:00