The @requires_approval decorator and request_approval() call executed the
approval gate correctly but never wrote the outcome to the activity log.
EU AI Act Article 14 requires documented evidence that HITL measures were
exercised — the missing log_event() calls meant GET /workspaces/:id/activity
could not surface HITL gate outcomes.
Add log_event() at both resolution points in the requires_approval wrapper:
- Denial: event_type="hitl", action="approve", outcome="denied", actor=decided_by
- Grant: event_type="hitl", action="approve", outcome="granted", actor=decided_by
Both calls follow the existing try/except pattern used for audit calls elsewhere
in hitl.py so a missing audit module never blocks the approval flow.
Tests: TestRequiresApproval.test_logs_hitl_denied_event and
test_logs_hitl_approved_event verify log_event is called with the correct
outcome on each resolution path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full retrospective of the 2026-04-16/17 SaaS buildout session:
- What was done (infra migration, 40+ PRs, 5 issues, 4 docs, 1 new repo)
- What should NOT have been changed (wildcard DNS churn, AdminAuth shortcut)
- Security concerns (8 items, 2 CRITICAL)
- Workflow gaps (registration, boot time, CI)
- Tests needed (automated + manual + security)
Phase 35 in PLAN.md covers production hardening follow-ups.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full retrospective of the 2026-04-16/17 SaaS buildout session:
- What was done (infra migration, 40+ PRs, 5 issues, 4 docs, 1 new repo)
- What should NOT have been changed (wildcard DNS churn, AdminAuth shortcut)
- Security concerns (8 items, 2 CRITICAL)
- Workflow gaps (registration, boot time, CI)
- Tests needed (automated + manual + security)
Phase 35 in PLAN.md covers production hardening follow-ups.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add comment to .env.example explaining ANTHROPIC_API_KEY must be set
as a *global* secret (not just workspace-level) so SDK-direct workspaces
(e.g. molecule-hitl, hermes) receive it without 401 errors
- Add ANTHROPIC_API_KEY to saas-secrets.md secret map with context on
why global propagation matters
- Add full rotation procedure section (generate → PUT /settings/secrets
→ verify restart → revoke old key) with blast-radius note
Closes#894
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add comment to .env.example explaining ANTHROPIC_API_KEY must be set
as a *global* secret (not just workspace-level) so SDK-direct workspaces
(e.g. molecule-hitl, hermes) receive it without 401 errors
- Add ANTHROPIC_API_KEY to saas-secrets.md secret map with context on
why global propagation matters
- Add full rotation procedure section (generate → PUT /settings/secrets
→ verify restart → revoke old key) with blast-radius note
Closes#894
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BroadcastToWorkspaceChannels now filters channel_type='slack'.
Telegram is CEO-only — explicit escalations via agent's outbound call,
never auto-posted from cron output. PM's routine pulses and agent
errors were spamming the CEO's Telegram.
PM's Telegram channel stays enabled for POLLING (inbound CEO messages)
but BroadcastToWorkspaceChannels skips it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BroadcastToWorkspaceChannels now filters channel_type='slack'.
Telegram is CEO-only — explicit escalations via agent's outbound call,
never auto-posted from cron output. PM's routine pulses and agent
errors were spamming the CEO's Telegram.
PM's Telegram channel stays enabled for POLLING (inbound CEO messages)
but BroadcastToWorkspaceChannels skips it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Conflicts arose because PR #892 base commits (MemoryInspectorPanel creation,
A2A overlay) had already landed on main via a different merge path, and
last-tick merges (#876, #888) had modified Toolbar, SidePanel, and test
fixtures.
Resolution strategy:
- Toolbar.tsx, SidePanel.tsx, Canvas.a11y.test.tsx, Canvas.pan-to-node.test.tsx,
MemoryInspectorPanel.test.tsx: take main (strictly newer, already contains
the branch's A2A overlay content plus subsequent a11y/UX fixes)
- MemoryInspectorPanel.tsx: take main (543 lines with semantic search) + apply
sanitizeId() helper from #904 + update bodyId prefix to mem-body-
- DetailsTab.tsx: take main (has #875 Field/useId + #878 deleteButtonRef/focus)
+ apply alertdialog structure from #905 while preserving focus management
Mechanical conflict resolution by triage-agent; no logic changes beyond the
four a11y fixes already in the branch (#902-#905).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Conflicts arose because PR #892 base commits (MemoryInspectorPanel creation,
A2A overlay) had already landed on main via a different merge path, and
last-tick merges (#876, #888) had modified Toolbar, SidePanel, and test
fixtures.
Resolution strategy:
- Toolbar.tsx, SidePanel.tsx, Canvas.a11y.test.tsx, Canvas.pan-to-node.test.tsx,
MemoryInspectorPanel.test.tsx: take main (strictly newer, already contains
the branch's A2A overlay content plus subsequent a11y/UX fixes)
- MemoryInspectorPanel.tsx: take main (543 lines with semantic search) + apply
sanitizeId() helper from #904 + update bodyId prefix to mem-body-
- DetailsTab.tsx: take main (has #875 Field/useId + #878 deleteButtonRef/focus)
+ apply alertdialog structure from #905 while preserving focus management
Mechanical conflict resolution by triage-agent; no logic changes beyond the
four a11y fixes already in the branch (#902-#905).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents agentskills.io v0.8.0 raw-drop hermes compatibility and
the before/after runtimes table for the five SKILL.md-only plugins.
Includes links to the companion draft PRs in each plugin repo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents agentskills.io v0.8.0 raw-drop hermes compatibility and
the before/after runtimes table for the five SKILL.md-only plugins.
Includes links to the companion draft PRs in each plugin repo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PR #878 landed before this branch and added useRef + deleteButtonRef focus-
management to DetailsTab.tsx. This commit combines that import with the
useId/cloneElement import added here, and preserves the Field component
htmlFor/id wiring from this PR unchanged.
Mechanical conflict resolution by triage-agent; no logic changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PR #878 landed before this branch and added useRef + deleteButtonRef focus-
management to DetailsTab.tsx. This commit combines that import with the
useId/cloneElement import added here, and preserves the Field component
htmlFor/id wiring from this PR unchanged.
Mechanical conflict resolution by triage-agent; no logic changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
role="alert" is for passive announcements. A delete confirmation with
Confirm/Cancel action buttons requires a user response, which is the
semantics of role="alertdialog" (interactive dialog requiring response).
- Replace role="alert" with role="alertdialog" + aria-modal="true"
- Add aria-labelledby="delete-confirm-title" for an accessible name
- Add <h3 id="delete-confirm-title"> as the labelling element
("Confirm deletion") so AT announces the dialog purpose on focus
Closes#905
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>