Skip to content

Release v0.12.0 (dev to main, prod promotion) - #1089

Merged
jozer-rami merged 1041 commits into
mainfrom
dev
Oct 26, 2026
Merged

jozer-rami merged 1041 commits into
mainfrom
dev

Conversation

@jozer-rami

@jozer-rami jozer-rami commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Promotes dev → main for the v0.12.0 prod deploy and tag (merge commit, not squash).

One merge serves both the prod promotion and the release. The release-prep commit (#1171) lands on dev first, so the images prod promotes already report releaseId: v0.12.0. The v0.11.0 cycle promoted first (#1063) and bumped after (#1067), and needed two extra prod applies to fix the reported version.

Dev HEAD 88479f5 (#1171) is the pin set dev deployed and verified (infra pin-status.yaml: verified: true, infra commit 825da97, pipeline run 40287842008 attempt 2). Dev's external-rest-api runs sha-88479f5 and reports releaseId: v0.12.0.

Attempt 1 of that dev deploy failed with the Codex/JWT guard's inputs_changed (phase: migration, verdict: UNAVAILABLE). The guard started 2 seconds after the inter-service token step restarted the Deployments it reads. The same failure happened on Oct 19 (infra run 39257220008). A rerun passed.

Release prep

Included PRs

Deploy notes

🤖 Generated with Claude Code

@alfredolopez80

Copy link
Copy Markdown
Contributor

@jozer-rami — exact-head review of this promotion PR (base cfa72530f, head b5688c494) is complete. Verdict: YES — no merge blockers found in the reviewed diff scope.

Findings

  1. Low (introduced, docs-only) — docs/agents/CLERUM_WORKFLOW_RECIPE_GUIDE.md:499: the new prose says an omitted/empty allowedTools.include offers every clerum__* tool, but clerum__gfs_* tools still require explicit inclusion plus runtime scope/access (mcp-host/src/workflow/workflowService.ts:923, :1112). A one-line doc fix is recommended; no production change is indicated.
  2. Medium (pre-existing, not introduced here) — workflow-recipes/src/reconciler/workflowRecipeReconciler.ts:2395: a triggered-run child assigns child-scoped physical names to resources[] Secrets, then skips creating them under verified parent inheritance; the snippet preflight resolves the child name and reports "not found" even when the parent's Secret exists. The path is byte-identical in base, and the new test supplies the instance map manually while explicitly not covering child-map construction. Worth a separate follow-up.

Verified clean

  • Allowlist enforcement gates both internal and MCP dispatch (no bypass found via unregistered names, GFS opt-in, cancellation ordering, or router reuse); rejected calls cannot satisfy toolChoice: required; absent/empty include preserves base behavior.
  • The snippet Secret parent scope is used only after controller ownerReference, live parent UID, and exact DB parent/child binding checks; invalid bindings fall back to the child name and transient DB failures requeue. Shared Secrets remain accessible; unowned, other-owner, and child-owner Secrets fail closed.
  • Retention fixtures stay inside the retention windows and occurred_at <= ingested_at + 5 minutes; version bumps match lockfiles and intervening history; all five new pinned Vitest names exist at head.

Evidence and limits

CI was green at the fetched head (mcp-host, workflow-recipes, and control-api suites; Real Postgres 16; CodeQL; image gates). Review lanes did not run local tests or runtime/T2/E2E; those remain separate evidence lanes. This comment is the only PR mutation performed; the full sanitized review record is local.

zach88 and others added 29 commits October 15, 2026 19:36
Ownership of the native sandbox-ui view moves from refs and effect ordering
in the renderer to an explicit (state, event) -> { state, effects } reducer
keyed by launch id. Every event/condition pair is decided by a total table;
unlisted pairs are a no-op by reference. Level rules run after each
transition in a fixed order: sweep non-owner records (drop pre-IPC ones,
close the rest by CAS), advance the owner once its tab has no predecessor in
main, release handoffs whose views are gone, and settle every launch that
left the pending set exactly once.

Not wired into the app yet.

The table suite has one case per row, a totality sweep, one case per level
rule and fast-check properties for settle-once and close-after-open; every
row and rule was mutated and turns the suite red (two equivalent/unreachable
guards kept as hardening).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gainst the real main process

EmbedOwnershipMachine is an external store around the ownership reducer:
synchronous dispatch, events raised while effects run are queued, and open
and close IPCs are issued synchronously in effect order so a close can never
reach main ahead of its own open. A launch that never reaches main (invalid
app reference, open throwing synchronously) settles as failed ahead of
already-queued events. whenSettled keeps its outcome after settlement so a
launch that settles inside the same dispatch can still be awaited.

The sandbox-ui Electron fakes move to a shared fixture without behaviour
change (lifecycle suite 62/62 before and after). A property suite drives the
reducer and interpreter through the real ipc.ts handlers, AppService and
driver under fc.scheduler interleavings (mints, measurements, navigation,
crashes, renderer reloads, session changes, throwing cleanups, parent window
close, deep-link handoff continuations) and checks the ownership invariants
on every transition and at rest. The previous renderer-level ownership
scenarios are ported as named cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… seam

Strip select and close, keyboard shortcuts, grid and deep-link app launches
and composer focus now activate tabs through activateAndLaunch, which commits
the store update and the launch state together under flushSync. The legacy
deactivation effect therefore still sees the incoming launch in the same
commit, and behaviour is unchanged (the existing ownership suite passes
untouched).

Every caller passes an updater instead of a value computed from the last
committed tabs, so a pending store update absorbed by flushSync (identity
reset, embed title, saved route) is no longer silently undone. No other
writer of the tab store can activate an app tab; a runtime guard in the new
seam suite fails on any app-tab activation outside the seam, on flushSync
warnings from a lifecycle, and on a second flushSync call site.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… app view

Wire the EmbedOwnershipMachine into App and SandboxUiPage so the renderer
no longer emulates main's atomic read-persist-close with refs and effect
ordering.

- The activation seam dispatches intent/relaunch after its flushSync, with
  the launch built from the committed tab (reveal gestures) or the new tab
  (grid, deep link); closing a tab launches only when the commit changed the
  active app tab.
- `projected` comes from a single layout effect; activeSandboxUiApp and the
  mounted flag are derived from the machine.
- Removed the four ownership refs, the deactivation effect,
  readOutgoingRouteDecision, the handoff copy, the opening/closed/openFailed
  handlers, and the page-side launch bookkeeping (launchRef, launchSeqRef,
  release, onClosed, openApp, sandboxUi.reload).
- Refresh, Reopen (after a crash) and Retry (session banner) relaunch
  through the seam; re-clicking a live app tab is a no-op.
- A crash while the owner is still opening now shows the crash failure and
  settles the launch as failed instead of leaving a blank tab.
- Deep links await their launch outcome: mounted acks, failed defers,
  abandoned acks and restores the team.
- capturePreview races a 500 ms timeout before hiding the view under an
  overlay.
- App integration tests now cover wiring only; ownership stays covered by
  the property tests against the real main process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The renderer now learns about every view's end through sandboxUi:released,
so the channels that emulated ownership from the renderer side go away:

- sandboxUi:close requires a launchId; closing whatever is live stays
  available only to main's own callers (session change, renderer replaced).
- sandboxUi:getLocation and AppService.getSandboxUiLocation are removed;
  the final location travels in released.
- The sandboxUi:closed event to the renderer is removed; the driver's
  internal unsolicited-close hook still cancels the session refresh and
  unpins plugin surfaces.
- sandboxUi:reload and AppService.reloadSandboxUi are removed (Refresh now
  relaunches). The internal reload on a top-level 401 stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on intent

A pending same-chat selection advances the selection intent revision but
leaves the active switch request in place. When its catalog load fails, the
selection effect returns without a switch, and the superseded click returns
on its stale revision without confirming or withdrawing the mark. Send and
load-older then matched the leftover mark and were refused silently on a
blank view. The guards now also require the mark's revision to be current.

Row 15 of the provisional-selection suite covers it for both Host kinds;
without the revision check both cases fail (send dispatched 0 times).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3-H1)

The split-layout signature accepted any state/state.db*.pre-*.bak name, so a
user file such as state/state.dbX.pre-1.bak next to a real workspace/ made a
stateful Host run the merge and flatten both folders into the PVC root. The
base init writes only state.db.pre-<ts>.bak, state.db-wal.pre-<ts>.bak and
state.db-shm.pre-<ts>.bak, so the signature and the database-entry checks now
name exactly those three.

Class-check: globs in the merge script that decide which names the base init
wrote -> 2 sites (signature loop, database-entry refusal loop) -> both narrowed
to the three exact .pre-*.bak names; the mcp-host agent-tool prefix rule
(protectedPaths.ts, shell.ts) stays wide on purpose, because protection must
cover every state.db* name.

Repro-test: the no-op case "user state/ folder with .pre-*.bak names the base
init never writes" fails at d983af8 (tree
changed); restoring the old signature glob alone turns it red again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tted

Case 16 opens a chat on another Host from the sidebar while the click's
write is pending, then returns to the first chat while its catalog load
fails. The superseded click must not leave a provisional mark that refuses
send. Runs for chatllm and chatllm-stateless; removing the selection intent
revision check turns it red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The boot retention sweep sent one sweep_closed_sessions operation that
ran a single unchunked DELETE with its message cascade and FTS triggers.
With a large backlog that one statement outlasts the worker progress
bound (CLERUM_DB_WORKER_STALL_TIMEOUT_MS), so the queue latched
"db worker made no progress", the Host exited 1, the transaction rolled
back, and every restart repeated the same sweep.

The operation now takes a required limit and deletes at most that many
rows: expired sessions' messages first, then expired sessions once none
of their messages remain. sweepRetention repeats it with 250 rows per
batch until a batch deletes nothing, so the worker acknowledges every
batch. PersistQueue and its progress bound are unchanged. Between
batches an expired session can show part of its history until a later
batch removes it.

Class-check: worker operations whose single ACK covers work that grows
with retained history -> 1 op with a live caller (sweep_closed_sessions,
from main.ts boot) -> converted; the processing and expired-approval
reaps grow with in-flight sessions and pending approvals, not history
(27 ms per 500-session chunk measured), and are unchanged.

Repro-test: sweepRetention.realWorker.test.ts seeds 300k expired
messages and runs the sweep on the real worker with a 1000 ms bound; at
3158019 it fails with "db worker made
no progress for 1000ms". sessionSearch.test.ts #13 fails there with one
operation. Ignoring the limit in the dispatcher, an unbounded batch size
or dropping the caller loop each turn both tests red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
persistFailureExit.test.ts runs the real factory, queue and handler, but
re-implements main.ts's shutdown flag and final process.exit, so nothing
executed main.ts's own wiring: process.exit(exitCode) -> process.exit(0)
or () => isShuttingDown -> () => false stayed green and type-checked.

The new test compiles the real main.ts and dbWorker.ts and boots the
Host in dev mode with the SQLite store. A test-side --require preload
replaces worker_threads.Worker so the DB worker is still the real one
and only controls one delivery: it withholds the shutdown message
(graceful shutdown whose store close stalls) or terminates the worker on
SIGUSR2 (worker exit while serving). No production code changes.

Class-check: main.ts lines that decide the process exit code on a
persist failure -> 3 (the onPersistFailure option, the shutdown-flag
getter, the final process.exit) -> each pinned by one scenario.

Repro-test: the wiring is correct at
3158019, so red-first is by mutation,
each of which type-checks: process.exit(0) turns the shutdown scenario
red (exit 0), () => false turns it red (the in-service handler logs),
removing onPersistFailure turns the serving scenario red (no handler
line). Unmutated: 5/5 green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sidebar now paints the local cache before the Host catalogs arrive, so a
chat hydrated from the server showed its placeholder title ("New Chat") until
the Host answered, which for a suspended stateless Host means the whole wake.
The server title only lived in the in-memory catalog merge.

The existing freshness write (chat:reconcileSessions -> upsertServerSessions)
now carries the server's own title, and the store writes it into entries that
are already in the index without moving updatedAt. Server-only sessions and
deleted chats are still never created. A chat with a pending local rename
does not send a title, so the optimistic title is not overwritten. The
cross-Host publication writes its Host's titles too, so a Host the user never
selects keeps them across restarts. The next catalog page overwrites the
stored value, so it follows server-side renames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A chat that exists only on the server enters the local index when the
user opens it, through ChatStore.createChat, which stored 'New Chat'.
The catalog page that carried its server title had already been
reconciled before the chat was in the index, so the title reached the
index only on the next launch's catalog load, and that launch still
painted 'New Chat' from the cache first.

upsertServerSessions now keeps the titles of server-only sessions in
memory, and createChat uses (and drops) the remembered title for a new
entry. A session reported without a title (a pending local rename)
forgets the remembered one, so an outdated server title is never
written. No entry is created for a server-only session, and a locally
deleted chat is still refused by createChat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Installations upgrading from 0.10.x/0.11.x rely on
CONTEXT_MAPPER_WORKSPACE_LAYOUT_MERGE staying at its default to merge split
stateless PVC trees; a shipped overlay that set "false" would start those
Hosts on an empty database with no error.

scripts/tests/test-hcc-layout-merge-flag-render.sh reads the four rendered
minikube overlays and requires the HCC container to exist with
CONTEXT_MAPPER_HOST_IMAGE (liveness) and the flag to be unset or "true".
A negative control injects "false" into one render and must be rejected,
and every expected overlay must be checked. deploy-manifest-check renders
the two ghcr overlays as well and runs it; no cluster is involved.

Refs #825

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Installations upgrading from 0.10.x/0.11.x already have stateless Host
Deployments with the split template: workspace-layout-init, workspace and
state subPath mounts, CLERUM_SESSION_DB_DIR=/var/lib/clerum/state and a
RollingUpdate strategy. Nothing tested that this HCC replaces them.

The fixtures are the Deployments v0.11.0's HostReconciler renders for a
stateless active and a suspended Host with the lifecycle test mocks; they
differ from this head's render only in the storage fields. The test
persists each one as the live Deployment, reconciles once and requires one
replacement with workspace-layout-merge, no subPath, the root DB dir,
Recreate and the replicas kept (1 and 0). A second reconcile must not
replace it again.

Refs #825

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
deploy/scripts/host-layout-merge.sh tells an operator, on any cluster,
what the workspace-layout-merge init will do to every Host PVC before an
upgrade (inventory) and whether CONTEXT_MAPPER_WORKSPACE_LAYOUT_MERGE can
be turned off (verify).

Each PVC is classified by a short-lived read-only Pod that runs the merge
script itself with the moves removed: deploy/scripts/workspace-layout-merge.sh
is the exact output of buildWorkspaceLayoutMergeScript(), and check-script
rewrites it line by line with counted asserts, so a changed merge script
stops the check instead of drifting from it. Writability is read from mode
bits because the check mount is read-only.

- inventory reports WILL_MERGE, WILL_REFUSE_F1..F4 (with the init's own
  line), NO_SPLIT_SIGNATURE or UNREADABLE per PVC, PRE_825, the desktop
  export warning, UPGRADE_BLOCKERS, orphan PVCs (reported, not blocking)
  and FLAG_FALSE_WITH_SPLIT_TREES.
- verify reads the merge init status of running Host Pods and checks every
  other PVC; it passes only when every Host Deployment renders the merge
  init and no tree still carries the split-layout signature.
- Every kubectl call names --context; check Pods run as uid/gid 1001 with
  no service-account token and are deleted after their log is read.

Tests: deploy/scripts/host-layout-merge.test.mjs (fake kubectl, 17 tests,
wired into the ci-public #825 helpers step). Every tree in the HCC merge
test file is also run through the check and must agree with the real
merge, and the committed copy must equal the builder output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S5 seeds the inventoried split tree on a suspended Host: before the wake,
deploy/scripts/host-layout-merge.sh must read that PVC as WILL_MERGE
(inventory) and SPLIT (verify); after the merge, verify must read it as
POD_OK.

S6 seeds the T5 shape (root database, state/state.db, workspace/): before
the wake, inventory must predict WILL_REFUSE_F2 with the init's own line,
and its check Pod runs between the two tree hashes, so the existing
"tree unchanged" assertion also covers it; while the Host is in
Init:Error, verify must report MERGE_REFUSED with the F2 line.

The assertions read only that PVC's row and the exit code; the fleet
verdict depends on the other Hosts of the profile. Exit 2 always fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a chat with an empty local cache whose history starts at turn 1
replaced its title with the first user message, even when the index
already held the server's title. The first message now names the chat
only while it has no title yet ('New Chat'); the title stays ephemeral.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Installations that run v0.10.x or v0.11.x have stateless Hosts with split
PVC trees. The new note explains how to run host-layout-merge.sh before and
after the upgrade, what each result means, how to repair a Host the merge
refuses (including a Host switched from stateless to stateful on those
releases), when the flag can be turned off in the installation's own
overlay, and the rollback rule.

host-storage-layout.md: criterion 4 of the turn-off is now "no Host PVC
still carries the split-layout signature", checked by the script; the F2
case names the pre-upgrade origin; the Tests section lists the script tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…T-002)

ensureFontsReady marked itself ready before loading the bundled Roboto
faces, so one failed load left the process on system faces for good. A
failed load is now tried again after 30 seconds, 5 minutes and 30
minutes, registering only the faces still missing; after the last try
the system faces are used for the life of the process and that is
logged once. The system faces register once, as before.

Refs #1118
…-004)

A sparkline Chart.js refused was removed with its container, so the KPI
lost its trend without a word. It is now replaced by a short fixed note,
'Trend could not be drawn', set as text and without the error's details;
a sparkline that draws is unchanged.

Refs #1116
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	mcp-host/package-lock.json
#	mcp-host/package.json
# Conflicts:
#	docs/surfaces/desktop-app.md
Adds scripts/e2e/e2e-hcc-layout-upgrade.sh and the Makefile target
test-e2e-hcc-layout-upgrade, an opt-in lane on its own fresh branch
profile (plan A7.40, commit 2b).

main installs v0.11.0 (published images; this tree's manifests and CRDs,
checked equal to the tag), seeds one Host per row (T1 stateful, T2
stateless active, T3 stateless suspended, T5 stateless switched to
stateful by a CommunicationChannel, T6 stateless -> stateful ->
stateless), checks host-layout-merge.sh inventory, upgrades to this head
with the database kept, checks verify, wakes T3, repairs T5 with the
upgrade-note procedure, and checks the history and seed file of every
row.

control upgrades with CONTEXT_MAPPER_WORKSPACE_LAYOUT_MERGE=false and
must see inventory report FLAG_FALSE_WITH_SPLIT_TREES and the history
check fail; E2E_UPGRADE_RESULT=PASS needs the main log of the same HEAD.

The helpers shared with e2e-hcc-storage-layout-switch.sh move to
scripts/e2e/e2e-825-lib.sh. Unit tests cover the main-evidence check and
the mode and context refusals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alfredolopez80 and others added 20 commits October 22, 2026 21:26
…e guard

Rework scripts/validate-nginx-images.sh after the consolidated review of
#1177 (C-01..C-05, C-07..C-11, C-13 in part):

- One scanner: grep lists the files that mention nginx, then the tagged,
  untagged, shell-array and same-line image: rules run on that list. The
  rg branch, the awk YAML reader and the unchecked find are gone; every
  grep listing goes through scan_or_fail, which fails on status > 1.
- Kustomizations are found by filename (kustomization.yaml, .yml and
  Kustomization) and their images: entries are read with yq, so flow
  lists, flow maps and JSON bodies are checked. The NGINX rule applies to
  the resulting image (newName, else name).
- Untagged NGINX names are rejected in an image field, a Dockerfile FROM
  (any case, with flags) and shell arrays; an image: key with its value on
  the next line is rejected. A trailing '@' is an error.
- Registry-qualified references keep their registry in
  --list-digest-pins.
- The release floors, the tag grammar and the NGINX name test move to
  scripts/nginx-image-policy.sh, shared with the digest verifier.
  FIXED_NGINX_IMAGE_DIGEST is removed: the Dockerfile must have exactly
  one FROM, FIXED_NGINX_IMAGE pinned by a well-formed digest, and the
  verifier proves the digest is that release.
- Scan scope: surfaces that are deployed, pulled or copied by users.
  Unit-test files that never pull an image leave the list;
  tests/e2e/playwright/control-ui/recipe-scope-copy.spec.ts joins it.

The contract test copies the policy file, compares the scan list with its
own, and covers each new rule with a negative case and a positive one
(208 cases). nginx-egress-proxy/README.md no longer repeats the base
version and documents the bump procedure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rror

scripts/verify-nginx-image-digests.sh now sources the shared NGINX policy,
rejects a reference with a policy problem or without a digest before any
registry call, and inspects each pin once under the name it is written
with, so a registry-qualified pin is read from its own registry (C-09).
With NGINX_REGISTRY_MIRROR set, Docker Hub names are inspected on that
mirror at the same digest; a failed inspect is an error, with no retry
and no second registry (C-06).

New offline contract scripts/tests/test-verify-nginx-image-digests.sh
(C-12): a fake docker records every call and answers with inline image
configs. 15 cases cover a match, mismatches on one or both platforms, a
missing platform, an inspect failure, rejections with zero registry
calls, an empty reference list, the mirror rewrite and a
registry-qualified pin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Docker Hub throttles anonymous pulls, and the rest of ci-public.yml
already pulls Docker Hub images through mirror.gcr.io (C-06):

- Validate platform NGINX images: timeout-minutes 10, a step for the
  offline verifier contract, and NGINX_REGISTRY_MIRROR=mirror.gcr.io for
  the registry check.
- Egress proxy TLS: timeout-minutes 20 and the same variable.

With the variable set, test-nginx-egress-proxy-tls.sh builds the proxy
with --build-context mapping the Dockerfile FROM to the mirror reference
at the same digest, fails unless the build log names that reference (a
non-matching key is ignored by BuildKit, which then pulls Docker Hub),
and starts the upstream from the mirror reference. Unset, the test is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rship

refactor(desktop-app): own the embedded app view in a state machine
…under entra-v2 (BR2-M1)

ENTRA_BLOCKED_RESOURCE_APPS missed several high-impact Microsoft first-party
APIs, so their GUID-form scopes classified as an unknown 'guid': reported as
unverified at discovery, confirmable at install and accepted at runtime. A
GUID names no host, so a confirmed one would let Entra mint a token for that
API and hand it to whatever MCP host the CR points at. Add Dynamics CRM,
Azure SQL Database, Log Analytics API, Skype and Teams Tenant Admin API and
Microsoft Teams Services (16 entries). The classifier logic is unchanged;
only the closed table grows.

Only the GUID form is blocked. A first-party MCP server whose audience lives
on its own host (e.g. Dataverse at <org>.crm.dynamics.com/api/mcp) is still
connected with that host's URL-form scope, which the host rule accepts. The
map comment and the how-to doc no longer claim these APIs never host an MCP
server.

Deliberately not blocked, because their URL-form audience lives on a host
other than their MCP server, so the confirmed GUID form is the only way to
connect them:
- Azure DevOps (499b84ac-1321-427f-aa17-267ca6975798), MCP server at
  mcp.dev.azure.com;
- Power BI Service (00000009-0000-0000-c000-000000000000), MCP server at
  api.fabric.microsoft.com, URL audience on analysis.windows.net (which
  classifies as 'foreign').
Both stay unknown GUIDs that need confirmation; the map comment, the doc and
a test pin that decision.

Collateral (intended):
- discovery now reports these scopes as a violation instead of unverified;
- install returns 400 scope_audience_mismatch for them even when the
  operator confirms the scope;
- an existing CR carrying one of these GUIDs stops resolving at runtime
  (409 remote_oauth_spec_incoherent, reason scope_audience_mismatch) on
  authorize-url, user-token, the callbacks and the refresh reader; revoke
  and sweep still get the grant coordinate.

Class-check: `ENTRA_BLOCKED_RESOURCE_APPS` across evenfire (src, test, ui,
docs) -> 1 reader (classifyEntraScope, asProfiles.ts) plus 3 literal mirrors
(BLOCKED_LITERAL and blockedGuids in test/oauth.asProfiles.test.ts, the
"Blocked APIs" paragraph in docs/how-to/connect-oauth-connector.md) -> all
3 mirrors updated; no other reader exists (control-ui only renders
control-api verdicts). No pattern rule added by design.
Repro-test: control-api/test/oauth.asProfiles.test.ts:450 and
control-api/test/oauth.remoteSpecCoherence.test.ts:309 (fail at b001d28:
profileScopeViolation returns null instead of the scope for all 10 new
GUID scopes, the 16-entry table equality fails, and the Azure SQL runtime
case resolves instead of throwing remote_oauth_spec_incoherent; 30
assertion failures, 0 setup errors; the Azure DevOps / Power BI pins pass
there by design)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
T0 runs npm test in the nearest package of every changed file and skips
only tests/e2e/**. workflow-recipes/tests/e2e is a live-cluster suite
whose context defaults to clerum-test; T0 runs before any profile is
bootstrapped, so a diff touching it stops the T2 lane with
context "clerum-test" does not exist (19/19 files failed). Skip
*/tests/e2e/** as well. In the current tree that adds only
workflow-recipes/tests/e2e.

Refs #1184

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts limited to control-api and control-ui package.json/package-lock.json
version fields: kept the branch's higher version; dev's Node engine bump
(>=24.22.1) merged cleanly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat: remote OAuth lane for Microsoft Entra ID (entra-v2 profile)
Resolve the control-api and control-ui version conflicts to the dev
version plus one patch (0.1.815, 0.1.744), so both packages stay bumped
against the base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ubuntu-latest now resolves to ubuntu-26.04, where Playwright 1.58 cannot
install Chromium ("Playwright does not support chromium on
ubuntu26.04-x64"), so the desktop-app and external-rest-api legs fail in
`npx playwright install --with-deps chromium`. Pin the job until
Playwright supports 26.04.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…de properties

- A '*' tag is no longer skipped as a policy glob. No scanned file holds
  one, and `nginx:${NGINX_TAG:-1.30.*}` in a shell script passed.
- A Kustomize newName that carries its own tag or digest is classified by
  its repository part, so `newName: "nginx:1.30.5-alpine"` is checked.
- `COPY --from=nginx` and `RUN --mount=...,from=nginx` count as untagged
  image positions.
- An untagged name after a YAML anchor or tag (`&a`, `!!str`) is rejected.

Each rule has negative cases in the contract test and a recorded vacuity
mutation. A name reached through a YAML alias or a shell variable is
still not resolved; the header says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The verifier joined platform and version into text lines and compared
the second whitespace field, so `NGINX_VERSION=1.30.6 extra` passed and a
value with an embedded newline could stand in for a missing arm64
manifest. Each required platform is now looked up in jq and its value is
compared whole, still JSON-encoded; an absent platform is null.

The contract test reads the release from FIXED_NGINX_IMAGE instead of a
literal, so a bump or a floor raise needs no edit there, and adds the
two cases above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tyContext

The TLS test hand-copied the docker flags for the egress-proxy container.
It now builds the Deployment with the HCC reconciler, maps the
egress-proxy securityContext to docker flags, and stops on any field or
value it does not map. After start it checks the NGINX master's
/proc/1/status: generated uid/gid, empty CapEff and CapBnd, NoNewPrivs 1
and Seccomp 2 (RuntimeDefault is the engine default profile).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…est in the bump steps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rity-upgrade

fix(nginx): upgrade platform NGINX images to 1.30.6 for October 2026 advisories
# Conflicts:
#	external-rest-api/src/releaseManifest.ts
feat(control-api): return secret identity in the mcp-secrets list
Brings dev up to 49ed0f1 (1537 commits). Three files conflicted; each
resolution keeps BOTH sides where the changes are disjoint, and follows
dev's newer model where the two genuinely overlap.

useComposerAttachments.ts
  - dev replaced the image-only mirror with `composerImagesRef` /
    `composerReferencesRef` and the `commitComposerImages` /
    `commitComposerFiles` helpers, and added the document-attachment
    model. This branch's STORY-38 restore path had added its own
    `composerImageAttachmentsRef` / `composerReferenceAttachmentsRef`
    mirrors with the same purpose (written inside every state updater,
    so they reflect committed transitions), and its own exported merge
    functions. The restore path now uses dev's mirrors and commits
    through `commitComposerImages`; a matching `commitComposerReferences`
    helper keeps the reference mirror live (dev's own reference handlers
    did not write theirs inside the updater).
  - dev renamed `COMPOSER_MAX_IMAGE_ATTACHMENTS` to
    `COMPOSER_MAX_ATTACHMENTS` and made images and documents SHARE one
    per-message count. The image merge now takes the documents already
    attached as occupied slots, so a restore cannot push the composer
    past the shared ceiling; the cancel-restore toast names the shared
    "20-attachment limit" instead of the old image-only limit.
  - dev removed `clearComposerDraftAfterSend` and the
    `clearComposerAfterSend` wrapper, and returns `clearComposerDraft`
    from the hook instead. This branch used that wrapper only through
    dev's own send-path cleanup, which dev has since rewritten, so the
    wrapper is gone with it.
  - The reference merge keeps `FILE_REFERENCE_MAX_COUNT`: dev still caps
    the Global Files a message may reference at that number.

useAgentChatController.ts
  - the parked-restoration queue (R2-M1 / R3-M4) and dev's
    `readyComposerFiles` memo are disjoint, so both are kept.
  - the cancel path keeps this branch's
    `restoreComposerAttachmentsAfterCancel` call and dev's note that a
    snapshot holding documents the Host never received stays for
    Recover files.
  - dev's own send-path cleanup (origin-scoped draft clearing,
    `sendStillAuthorized`, `resetComposerAttachments`) is taken as-is:
    this branch never changed it, so nothing of this branch's is lost.

ComposerPanel.test.tsx
  - the type import list is the union of both sides.

Also: dev raised the Node floor to 24.22.1 (.nvmrc), so the local
toolchain was moved to it, and mcp-host's dependencies were installed
because dev's new native-canvas tests need @napi-rs/canvas.
@jozer-rami jozer-rami changed the title Promote dev to main (v0.11.0 release manifest to prod) Release v0.12.0 (dev to main, prod promotion) Oct 23, 2026
miguelo-999 and others added 8 commits October 23, 2026 10:23
…on bumps)

The dev merge's pre-commit hook reformatted 11 of dev's files: the
repo's LOCKED prettier (3.8.2, per package-lock.json) breaks long
unions onto one member per line, while dev's tree is formatted with
prettier 3.9.9, which keeps them inline. The reformat is cosmetic,
but it made this PR differ from dev in four backend services — and
`validate-release-version-bumps` reads that as "external-rest-api code
changed but its version stayed at 0.1.136", which reddened CI.

This restores dev's exact bytes for all 11 files, so the PR's diff
against dev is again only the Desktop lane's own work, and realigns the
lane's own files to the prettier dev's tree actually uses.

The pre-commit hook bumps the version of every service whose files are
staged, so restoring these files bumps control-api, control-ui,
external-rest-api, host-context-controller and mcp-host. Their CODE is
byte-identical to dev again, so those bumps are reverted in the next
commit: nothing in this PR changes those services.
The previous commit restored dev's exact bytes for the eleven files the
dev merge's pre-commit hook had reformatted. The hook bumps the version
of every service whose files are staged, so that restoration bumped
control-api, control-ui, external-rest-api, host-context-controller and
mcp-host — even though their code is byte-identical to dev again.

This returns those five versions, their lockfiles and the release
manifest to dev's values, so this PR changes no service at all: the
only difference between this branch and dev is the Desktop lane's own
work.

`validate-release-version-bumps` now finds no changed service code
against dev, and the release manifest matches the package versions.
fix(desktop): keep attachments on cancel, folder zip download, chat pill pointer, centered gear
@jozer-rami
jozer-rami merged commit 304766a into main Oct 26, 2026
153 of 154 checks passed
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.

7 participants