ci: consolidate platform pipelines and test prerequisites on macOS support
PR and Push Build/Test / windows-build-and-test (push) Failing after 38m25s
PR and Push Build/Test / portable-build-and-test (push) Successful in 8m9s
PR and Push Build/Test / macos-native-full (push) Skipped

This commit is contained in:
dh committed 2026-10-06 09:02:19 +02:00
1 parent 1b19b08f2e
commit 728e66dd72
76 files changed
+7477 -239

No files matched your search

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-10-03
@@ -0,0 +1,36 @@
## Context
The change started on codex/macos-support at bd35ebc. Verification includes the later fast-forward to 1164c26846686c1912fd1816cd06de80352e9504, which adds existing macOS CI and shutdown improvements. Existing OpenSpec requirements and local TDD policy own this work; no additional tracker or AGENTS scaffolding is needed. The user approved native notifications plus a menu-bar pending-request list. The interview exit audit found no remaining material product decision; packaging and transport are implementation choices.
## Goals / Non-Goals
Goals: restore bidirectional macOS user decisions for existing approvals, inactivity and calendar prompts, preserve pending decisions when OS notifications are hidden, and start independent approved workflow conversations.
Non-goals: microphone enumeration/recovery, new audio backends, unrelated chat rendering, remote notifications, changes to Windows behavior, production deployment or OpenSpec archival.
## Decisions
1. A small managed DesktopUserRequestStore owns process-local pending requests, allowed action IDs, deadlines and cancellation. Producers provide callbacks; the store has no recording/agent business dependencies. Approvals await decisions, while calendar/inactivity prompts enqueue and return immediately. Windows adapters remain unchanged.
2. GET /desktop/requests lists pending requests; POST /desktop/requests/{id}/respond submits an action. Native buttons and menu actions use the same endpoint. Response IDs are consumed once; invalid, canceled and expired responses cannot trigger work. Accepted callbacks use the originating lifetime rather than HTTP cancellation. Shutdown removes all requests. No answer never grants approval.
3. Package the existing AppKit desktop helper as Native/MeetingAssistantDesktopControls.app with stable identifier cloud.schweigert.meeting-assistant.desktop-controls. The launcher retains service supervision. The helper polls requests, schedules notifications once per ID, removes stale notifications and always offers pending-menu actions. Notification dismissal alone leaves the central request pending. Notification authorization is requested on first needed delivery; denied authorization still permits the menu fallback.
4. A managed macOS session store preserves the default interactive session and creates independent sessions for approved workflow prompts. GET /desktop/agent-windows announces requested native windows; the helper opens each session independently once. The detailed prompt starts once server-side. Session-aware chat snapshots and sends never restart the original prompt on page reload.
5. The helper decodes the existing numeric recording enum; the HTTP contract stays unchanged. Native self-tests exercise decoding and request/action handling without touching the live instance.
6. Keep current deadlines: approvals ten minutes, inactivity notifications one minute, calendar notifications five minutes capped at appointment end. Requests do not persist across application shutdown. Reopening a notification after shutdown cannot recreate an approval.
7. Resolve workflow sessions lazily when an approved prompt opens a window. Eagerly resolving sessions from the window service creates a dependency cycle through the agent pipeline, meeting coordinator and summary workflow service. The default window does not need to resolve this graph; application-owned session execution still observes shutdown.
## Risks / Trade-offs
- OS notifications can be hidden by Focus or screen sharing -> independent menu list retains actionable requests until their original deadline.
- Multiple callbacks and polling race -> atomic action validation/removal and idempotent stale-response rejection; cancellation registrations are disposed outside store locks.
- Awaiting a user decision could stall calendar or inactivity processing -> nonblocking prompt adapters with tests at their existing interfaces.
- Native app identity/direct launch may affect notification delivery -> build/signature/native checks and isolated operational verification; do not claim delivery without observing it.
- Background sessions and UI polling race -> immutable published snapshots and per-session send serialization.
- Existing active macOS specs describe unavailable automatic approvals -> update that scenario to refer to a genuinely unavailable communication channel, while the new capability defines menu fallback behavior.
## Migration Plan
Build and verify the portable target, complete behavioral tests and OpenSpec validation, then build the Windows target to check isolation. Any production replacement remains a separate delivery action and must occur only while the application is idle. Rollback uses the previous complete signed application bundle.
## Open Questions
No unresolved product decisions. Actual notification permission, delivery and response routing must be checked on macOS; unavailable permission is an operational verification limit, not a reason to remove menu access.
@@ -0,0 +1,63 @@
# Implementation evidence
Implementation completed on 2026-10-03; no production replacement, push, commit or archive was performed by this task. Full Windows resource-build verification and native interaction acceptance remain open below.
Branch: `codex/macos-support`. Starting baseline: `bd35ebc`. Verification HEAD: `1164c26846686c1912fd1816cd06de80352e9504`; the branch was fast-forwarded during this work to its existing macOS CI/shutdown change. The final portable suite ran after that commit. The 20 changed/new production and test files are hashed in `/private/tmp/mac-user-channel-evidence/code-identity.txt`; that manifest has SHA-256 `6cf3f10a72315c782cf17467be2dd7aa56fa279f191359780628ae818237d575`.
The approved implementation provides one managed pending-request lifecycle, macOS approval/inactivity/calendar adapters, native notifications and pending-menu actions, and independent approved workflow conversations. Managed logic uses C#/.NET. The existing Swift AppKit/UserNotifications host and embedded WebKit JavaScript retain their platform bindings. Windows notification registrations remain selected for the Windows target.
## Behavioral implementation and regression evidence
- Managed lifecycle: listing, one-time valid decisions, invalid-action preservation, deadlines, originating cancellation, withdrawal, shutdown and callback failure. Compiled/discovered red and green slices are retained, including `01-store-red.log`, `03-invalid-action-red.log`, `05-cancellation-red.log`, `07-expiry-red.log`, `09-withdraw-shutdown-red-and-sessions.log`, `11-failed-callback-red-and-sessions.log` and `19-expired-list-red.log`.
- Prompt adapters: explicit Yes/No approval with decline on timeout/cancellation; nonblocking inactivity Stop/Continue/Pause; nonblocking calendar Record/Skip/AttachMetadata using the original appointment and callback. Adapter reds include `13-approval-red.log`, `15-inactivity-red-and-workflow.log` and `17-calendar-red.log`.
- HTTP composition: list/respond and session-aware snapshot/send endpoints, invalid actions 400, consumed/stale requests 410, unknown sessions 404, and actual macOS service registrations. Executed reds are `root-http-list-red.log`, `root-http-response-red.log` and `root-composition-red.log`. Final focused managed/channel verification: **27 passed, zero failed/skipped**, `root-managed-final-green.log`.
- Workflow sessions: full prompt starts once, histories remain independent, snapshots/reloads do not replay work, response disconnection does not cancel accepted work, shutdown does cancel it. `sessions-01-red.log` records the original missing behavior. The first complete suite exposed an eager dependency-resolution cycle (14 failing startup tests), retained in `full-portable-suite-before-di-fix.log`; a deferred session factory fixed it. The startup/channel subset then passed **17/17**, `startup-and-channel-green.log`.
- Native host: compiled red/green checks cover numeric recording status, .NET timestamps, request reconciliation/menu fallback, allowed once-only actions, stale polling after acknowledgement, native actionable content and independent window IDs. All **seven native groups pass**, including during the integrated Release build and publish.
Temporary logs are under `/private/tmp/mac-user-channel-evidence`. They may be removed by normal temporary-directory cleanup.
## Complete builds and tests
The final portable suite passed **602/602 tests, zero failed/skipped**:
```zsh
dotnet test MeetingAssistant.slnx -f net10.0 -c Release -p:EnableWindowsTargeting=true --logger 'trx;LogFileName=macos-user-channel-full.trx'
```
Evidence: `full-portable-suite.log` and generated `MeetingAssistant.Tests/TestResults/macos-user-channel-full.trx`.
Strict OpenSpec validation passed for both `add-macos-user-communication` and the adjusted `add-macos-desktop-controls` change (`openspec-user-communication.log`, `openspec-desktop-controls.log`). `git diff --check` was clean. All 20 source-file hashes still matched the recorded manifest after documentation updates.
The standard Windows build reached C# compilation but failed at resource expansion because `MakePri.exe` cannot execute on macOS (`Exec format error`, `windows-build.log`). Windows C# compilation succeeded with only that resource-generation step disabled:
```zsh
dotnet build MeetingAssistant/MeetingAssistant.csproj -f net10.0-windows10.0.19041.0 -c Release -p:EnableWindowsTargeting=true -p:AppxGeneratePriEnabled=false
```
Evidence: `windows-cross-compile.log`. This checks compilation/isolation, not complete Windows resource/package generation or Windows runtime behavior. A Windows or configured Wine CI environment must complete task 4.2; no Wine runtime is installed on this Mac.
The macOS publish succeeded, compiling the native helpers and executing their self-tests:
```zsh
dotnet publish MeetingAssistant/MeetingAssistant.csproj -f net10.0 -c Release --self-contained false -p:EnableWindowsTargeting=true -o /private/tmp/mac-user-channel-evidence/publish
```
Evidence: `macos-publish.log`. The outer `MeetingAssistant.app` and nested `Native/MeetingAssistantDesktopControls.app` passed `codesign --verify --deep --strict`; the nested plist passed `plutil -lint`. The published desktop helper passed all seven self-test groups. Its stable identifier is `cloud.schweigert.meeting-assistant.desktop-controls`; no-argument cold activation exited without recreating a request.
## Isolated operational verification
The C# verification host (`/private/tmp/mac-user-channel-fixture/Program.cs`, `Fixture.csproj`, README) ran the production request store, endpoint module and workflow sessions at `127.0.0.1:5198`. Only the external agent pipeline was replaced with a harmless no-tool response. Its native manifest had no recording profiles or global hotkeys. It registered no live vault, calendar, audio, recording or LLM services.
- The actual signed desktop host reported notification authorization and scheduling. A workflow session opened a native AppKit/WebKit window; its accessibility tree and screenshot displayed the full verification prompt once and the expected harmless reply.
- A live request rejected an unoffered action with 400 and stayed pending; No returned 200; duplicate No and an expired request returned 410. In-memory callbacks recorded exactly one No per answered request.
- A temporary read-only Swift probe used the same stable app identifier and Apple's `UNUserNotificationCenter` API. It requested no permissions and scheduled/removed nothing. For request `09ba4dc708784aaf98b1ecd85f44aa8a`, `notification-delivery.json` reports `complete=true`, `delivered=true`, `pending=false`, registered category actions `yes/no`, authorized notification access and enabled alert/Notification Center/sound settings. This proves Notification Center delivery, not visible banner presentation or a physical button click.
- After that request received No (HTTP 200), `notification-after-response.json` reports `delivered=false` and `pending=false`. `operational-decisions.json` contains exactly the two independently answered No decisions, proving cleanup after the response and no duplicate callback.
- The isolated native host and verification web host were stopped after verification. The installed live Meeting Assistant was neither stopped nor replaced.
Probe entry: `/private/tmp/mac-user-channel-evidence/NotificationReadOnlyProbe.app/Contents/MacOS/macos-desktop-controls <request-id>`. Dependencies: the existing signed macOS app identity and UserNotifications framework. Effects: read-only API calls and one bounded JSON result, with no unrelated notification content printed.
## Remaining acceptance checks and limits
The native/browser UI bridge repeatedly timed out while selecting windowless menu/notification surfaces and interacting with the isolated browser page. No claim is made for actual notification/menu clicks, real notification dismissal retaining its menu item, permission-denied/Focus-hidden fallback, or the visible default draft alongside two simultaneous workflow windows. The implementation and native state tests cover those paths; manual/native interaction acceptance remains in task 4.4.
No live recording/hotkey regression exercise was performed in this isolated fixture. Existing numeric-status regression and the full portable suite passed. Full Windows resource/package generation remains the other verification limit. OpenSpec stays active pending acceptance and those checks; production delivery is separate.
@@ -0,0 +1,24 @@
## Why
macOS can record and process meetings, but several user-decision flows are disabled or block the calendar scheduler. The agreed solution is native actionable notifications plus a menu-bar list of pending requests, so meeting decisions remain accessible when notifications are hidden.
## What Changes
- Add pending user requests with allowed actions, deadlines, cancellation and one-time responses.
- Add macOS notification delivery and an actionable pending-request menu, sharing the same decisions.
- Enable project/workflow approvals and inactivity prompts on macOS; move calendar prompts onto the same non-blocking channel, including attachment to an active meeting.
- Open a separate interactive agent conversation after an approved workflow-change request without replacing the existing conversation or draft.
- Package the notification/menu host with a stable application identity and decode the existing numeric recording status correctly.
- Preserve Windows notification behavior. Microphone device monitoring, audio recovery, sample playback and unrelated agent rendering changes are outside this change.
## Capabilities
### New Capabilities
- `desktop-user-requests`: Actionable native notifications and pending-menu decisions, host adapters, lifecycle rules and approved macOS workflow conversations.
### Modified Capabilities
None. Existing approval and calendar behaviors gain a macOS implementation; existing recording-status requirements are repaired without changing the HTTP format.
## Impact
.NET request lifecycle and existing prompt registrations, read/respond loopback endpoints, Swift AppKit/UserNotifications desktop host, macOS packaging, agent session routing, behavioral tests and operational documentation. Requests and conversations remain process-local; shutdown cancels pending decisions. No remote notification service, new credentials or production deployment is required by this implementation change.
@@ -0,0 +1,96 @@
## ADDED Requirements
### Requirement: Desktop user requests have one managed lifecycle
Meeting Assistant SHALL expose pending user requests with an unpredictable ID, kind, title, message, creation time, expiration time and explicitly allowed action IDs and labels. The managed request lifecycle SHALL be independent of native notification presentation. Responses SHALL consume a request at most once. Invalid actions SHALL leave a valid request pending. Expiration, originating cancellation, withdrawal and application shutdown SHALL remove the request and prevent later responses from performing work. Absence of a response SHALL NOT grant approval. Accepted callbacks SHALL use the originating request lifetime, not the lifetime of the responding HTTP connection.
#### Scenario: User answers an offered action once
- **WHEN** a user submits an allowed action for a pending request
- **THEN** that decision is delivered once and the request disappears
- **AND** repeating the response performs no work
#### Scenario: Invalid action preserves the request
- **WHEN** a response names an action that the request does not offer
- **THEN** the response is rejected and the request remains pending
#### Scenario: Expired or canceled request cannot perform work
- **WHEN** a request expires, is canceled, is withdrawn or its application shuts down
- **THEN** the pending list excludes it
- **AND** a later response cannot invoke its action
### Requirement: macOS offers native notifications and pending-menu actions
Meeting Assistant SHALL announce pending user requests through native actionable macOS notifications and SHALL also show their count and offered actions in the menu bar. Both presentations SHALL submit the same managed decision. Denied or hidden notifications SHALL NOT prevent menu decisions. Dismissing a notification SHALL NOT approve the request or remove the independent menu decision. Answered or expired requests SHALL be removed from native delivered and scheduled notifications. Delivery SHALL be asynchronous and SHALL NOT block recording, calendar checks or other requests.
The native notification/menu host SHALL be packaged as a signed application bundle with a stable identifier and SHALL preserve the existing configured controls and numeric recording-status HTTP contract. Cold activation without a valid service/manifest SHALL NOT reconstruct an old request or approve it.
#### Scenario: Native action and menu action have the same effect
- **WHEN** the user chooses an offered action from either its notification or pending-menu entry
- **THEN** the application submits the same request ID and action
- **AND** the other presentation cannot execute the action a second time
#### Scenario: Notification permission is denied
- **WHEN** notification authorization is denied or a notification is hidden
- **THEN** the request remains selectable from the menu until its original deadline
#### Scenario: Existing recording status is decoded
- **WHEN** the service returns recording state as the existing numeric enum
- **THEN** the macOS helper shows the corresponding recording state and existing stop/abort controls
### Requirement: macOS approvals wait for explicit user decisions
On macOS, existing project-association and workflow-change approval interfaces SHALL use the desktop request channel. Yes SHALL approve; No, cancellation or ten-minute expiration SHALL decline. The summarizer SHALL receive new project access only after the existing approved-association operation succeeds. Workflow requests SHALL remain nonblocking for the summary and SHALL open their detailed prompt only after valid approval.
#### Scenario: macOS project association is approved
- **WHEN** a pending project association receives Yes before its deadline
- **THEN** the existing association operation can proceed and scope expands only through that operation
#### Scenario: macOS approval is declined or expires
- **WHEN** the user chooses No or an approval expires
- **THEN** no new project access or workflow conversation is granted
### Requirement: macOS inactivity and calendar prompts are nonblocking
The macOS inactivity prompt SHALL offer Stop, Continue and Pause through the pending request channel, expire after one minute and use the originating recording cancellation. DismissAllAsync SHALL remove inactivity requests without removing other kinds. The prompt method SHALL return without waiting for a user decision so existing inactivity checks and automatic stop remain operational.
The macOS calendar prompt SHALL offer Record and Skip and SHALL additionally offer Attach metadata to current meeting when the existing request allows it. It SHALL preserve the exact appointment metadata in the existing callback and expire after five minutes or at appointment end, whichever comes first. Its prompt method SHALL return without waiting, so other appointments and calendar synchronization continue.
#### Scenario: Inactivity prompt does not block automatic safeguards
- **WHEN** the application enqueues an inactivity warning
- **THEN** the prompt method returns while the request remains unanswered
- **AND** a valid offered response invokes the existing meeting callback
#### Scenario: An inactive recording withdraws its request
- **WHEN** the originating recording cancels its inactivity prompt lifetime
- **THEN** that request disappears and a late action cannot affect a later recording
#### Scenario: Calendar prompt allows attaching exact appointment metadata
- **WHEN** a calendar request permits attachment and the user selects Attach metadata
- **THEN** the existing callback receives AttachMetadataToCurrentMeeting for that appointment
- **AND** recording is not stopped and restarted by that action
#### Scenario: Unanswered calendar prompt does not block other work
- **WHEN** a calendar prompt remains unanswered
- **THEN** its show method has already returned
- **AND** subsequent calendar checks can run
### Requirement: Approved workflow prompts use independent macOS conversations
After a valid workflow approval, macOS SHALL create a new independent interactive agent conversation and request its own native window. The full detailed prompt SHALL start once server-side. The existing conversation, history and draft SHALL remain intact. Window loading, snapshot polling and page reload SHALL NOT resubmit that prompt. Unknown session IDs SHALL be rejected instead of using another conversation. Background work SHALL observe application shutdown and SHALL NOT be canceled merely because a page load or response connection closes.
#### Scenario: Approved workflow starts once without replacing the current chat
- **WHEN** a valid workflow request is approved while the normal agent conversation exists
- **THEN** a separate session receives the full prompt once
- **AND** the normal session and draft remain intact
- **AND** requesting snapshots or reloading its page does not start the prompt again
#### Scenario: Two workflow requests remain independent
- **WHEN** two workflow requests receive valid approval
- **THEN** each receives its own conversation and window request
#### Scenario: Unknown session is rejected
- **WHEN** a client sends a chat message for an unknown session ID
- **THEN** the service returns not found without changing any conversation
### Requirement: Windows user decisions preserve their existing implementation
Windows builds SHALL retain existing notification services and user-decision semantics. macOS notification helper packaging SHALL NOT be required to build or run the Windows target.
#### Scenario: Windows composition stays isolated
- **WHEN** the Windows target is built and services are registered
- **THEN** existing Windows prompt implementations remain selected
- **AND** no macOS helper is invoked
@@ -0,0 +1,28 @@
## 1. Managed requests and existing prompt adapters
- [x] 1.1 Add one behavioral red/green slice for listing and accepting a pending request.
- [x] 1.2 Add red/green coverage for invalid/duplicate actions, deadlines, cancellation, withdrawal and shutdown.
- [x] 1.3 Add macOS approval, inactivity and calendar adapters through their existing public interfaces; keep calendar/inactivity nonblocking.
- [x] 1.4 Register macOS adapters and expose request list/response endpoints with HTTP behavior tests.
## 2. Independent approved workflow conversations
- [x] 2.1 Add red/green session tests for one full prompt per independent workflow conversation, keeping default history/draft.
- [x] 2.2 Expose session-aware snapshot/send and native window requests; reject unknown sessions and preserve application cancellation.
- [x] 2.3 Replace obsolete unavailable-approval tests/scenarios with the new communication behavior.
## 3. Native notification and pending-menu host
- [x] 3.1 Package/sign the AppKit helper as a stable nested application bundle and preserve publish layout.
- [x] 3.2 Add native request decoding/action self-tests and numeric status regression coverage before implementation.
- [x] 3.3 Deliver actionable notifications, retain menu fallback, route decisions once and remove stale notifications.
- [x] 3.4 Open independently requested agent windows without changing the normal agent window; handle safe cold activation.
## 4. Documentation and verification
- [x] 4.1 Update README/configuration/workflow documentation and record implementation evidence.
- [ ] 4.2 Pass focused tests, the complete portable suite, Windows target build, native self-tests/signature checks and strict OpenSpec validation.
- [x] 4.3 Verify the request channel through isolated application endpoints and native UI where available; record any notification authorization/delivery limitation explicitly.
- [ ] 4.4 Complete native UI acceptance: choose notification/menu actions, dismiss a notification while retaining its menu request, verify permission-denied fallback, and preserve the default draft alongside two workflow windows.
Verification notes: 602/602 portable tests, focused tests, seven native self-test groups, macOS publish/signature checks and strict specification checks passed. Task 4.2 remains open only for the full Windows resource build: the Windows C# target compiles with `AppxGeneratePriEnabled=false`, while its standard resource build needs Windows `MakePri.exe`. Task 4.3 observed an independent native conversation and proved OS notification delivery, registered actions, one-time HTTP decisions and removal after a response. Task 4.4 requires a working UI-control surface; the available native/browser bridge timed out during these interactions. See implementation-evidence.md for exact evidence and limitations.