Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3abc53f
docs(wip): design for learning-state config-store migration (#1438)
josephfung Jul 17, 2026
bff35d2
docs(wip): implementation plan for learning-state config-store migrat…
josephfung Jul 17, 2026
fb55b55
docs(wip): consolidate skill version bumps into one PR-level task (#1…
josephfung Jul 17, 2026
7178bba
feat(learning-state): config-store JSON accessors for learning machin…
josephfung Jul 17, 2026
16b0cf3
refactor(voice-learn): store voice proposal in config-store, not pend…
josephfung Jul 17, 2026
e7d9db4
refactor(sent-observe): completion queue + asked-guard in config-stor…
josephfung Jul 17, 2026
a54b7d6
test(task-completion): assert candidate survives a completeTask failu…
josephfung Jul 17, 2026
39ea917
refactor(learning-digest): completion digest in config-store, resolve…
josephfung Jul 17, 2026
075a18f
refactor(sent-observe): matched-draft guard in config-store, excludes…
josephfung Jul 17, 2026
d2f462d
refactor(learning): delete dead markdown parse/format machinery (#1438)
josephfung Jul 17, 2026
76b6646
docs(adr-029): machine state moved to config-store JSON; specs 04/13/…
josephfung Jul 17, 2026
d61ebac
chore: changelog + skill version bumps for learning-state migration (…
josephfung Jul 17, 2026
c34790c
refactor(task-completion): digest-first write ordering + confirm/dism…
josephfung Jul 17, 2026
b2c3bbe
fix(learning-state): surface config-store soft-reject so writes hold …
josephfung Jul 17, 2026
bf664bd
fix(learning-state): address review — guard/resolve/completion soft-r…
josephfung Jul 17, 2026
79eccfb
fix(learning-state): drop malformed map entries at the read boundary,…
josephfung Jul 18, 2026
eb03351
fix(learning-state): validate each id-set member is a string, drop no…
josephfung Jul 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ bus event types) are noted explicitly even in the `0.x` range.
- **`task-completion-from-sent`** — re-check tasks are open and CEO-owned; fail closed on subtask-lookup errors. (#1419)
- **Voice-learning capture** — a body-only draft edit refreshes the snapshot in place, preserving its original `created_at`. (#1419)
- **`voice-learn`** — voice-guide approval loop no longer breaks after one cycle; honors dismiss cooldown. (#1419)
- **Learning-subsystem config writes** — soft-rejected `ConfigStore` writes now hold state instead of silently advancing. (#1438)

### Added

Expand Down Expand Up @@ -70,6 +71,7 @@ bus event types) are noted explicitly even in the `0.x` range.
- **Install docs** — README points to `releases/latest/download/install.sh` and documents `CURIA_VERSION`.
- **`00-overview.md` Spec Index** — dropped the `Status` column and "N of M Done" counts. (#1282)
- **Spec audit fixes** — corrected stale claims: Node 24, console UI, secrets vault, setup story, fallback-provider prose. (#1282)
- **Learning subsystem** — queue/status/guard state moved from OKF doc bodies to config-store JSON. (#1438)

### Removed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ Concrete mapping:
| Learned voice | free-form `WritingVoice.guide`, maintained by a weekly batched LLM pass and approved via the digest |
| Todo completion | `tasks` + `task-complete` / digest confirm |
| Capability evidence | Pre-scored rows in `autonomy_action_log` |
| Cadence / watermarks / guard markers | Scheduler crons + `config-store` |
| Cadence / watermarks | Scheduler crons + `config-store` (`ceo_inbox`) |
| Queue / status / guard state (completion candidates, digest items, voice proposal, matched-draft & asked-task guards) | `config-store` JSON (`ceo_inbox`), one whole-rewritten key per store |

Machine-state keys use whole-object last-write-wins (no per-doc version check); acceptable because each key has effectively a single writer per cron tick and the queue key is keyed by task id so a held-watermark retry re-adds idempotently. Prose evidence (`pending-diffs.md`), snapshots, and shadow docs remain OKF.

Rationale for rejecting new stores (options 1 and 3): the destinations already exist and are versioned/auditable; a parallel schema would duplicate versioning, digest surfacing, and autonomy gating. The KG is the wrong home for raw draft prose and edit diffs (spec 21: "the workspace is the scratchpad, the KG is the record").

Expand Down
2 changes: 1 addition & 1 deletion docs/specs/04-channels.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Interactive terminal for local dev and testing. Reads from stdin, writes to stdo
- **CEO inbox drafts (v0.35):** the CEO inbox can now find, read, and edit unsent drafts. `ceo-inbox-list` / `ceo-inbox-search` query Nylas's `/drafts` resource (the DRAFTS folder) when scoped with `folder: 'DRAFTS'`; `ceo-inbox-read` accepts a `draft_id` to return a draft's full body; and the new `ceo-inbox-draft-edit` skill updates a draft's recipients/subject/body. Draft results key on `drafts` (not `messages`) so a genuine "none" is distinguishable from a silent zero, and the inbox poll watermark is not applied to drafts.
- **Email accounts (v0.37):** multiple agent-owned mailboxes are managed from the console under **Settings → Channels → Email → Email accounts**. Each account is a row in the `email_accounts` table (migration `064_create_email_accounts.sql`) with its Nylas grant stored in the vault at a per-account key (`channel.email.<name>.nylas_grant_id`). The shared app-level `NYLAS_API_KEY` remains an env-bootstrapped vault secret. The legacy `channel_accounts.email` YAML path and `CEO_PRIMARY_EMAIL` env var are retired.
- **Vault credential wiring (v0.37):** at startup `applyChannelVaultSecrets()` overlays channel vault credentials (`channel.<name>.<field>`) onto the runtime config before adapters are constructed, so credentials saved via the Channels console take effect on the next restart without any env-var or YAML change.
- **Sent-mail observation (v0.42, ADR-029):** a scheduled `ceo-inbox-sent-observe` skill polls the CEO's **Sent** folder through the existing `ceo-inbox` Nylas grant (no new credential). Scope is **all sent mail**. Each run is watermarked (`config-store` under the `ceo_inbox` namespace, same high-water pattern as the inbound email poll) and is idempotent on re-run. For each new sent message it (1) correlates to Curia-authored draft snapshots (below) by `thread_id` + recipients + send time and appends `(draft, sent)` pairs to a rolling OKF evidence doc, and (2) correlates to open `owner='ceo'` tasks for completion candidates ([spec 19](19-tasks-and-backlog.md)). It emits a `ceo.sent_observed` audit event and advances the watermark. Observe runs as a **separate daily cron** on `agents/ceo-inbox.yaml` with its own Operating Modes task text — never folded into the 15-min triage loop.
- **Sent-mail observation (v0.42, ADR-029):** a scheduled `ceo-inbox-sent-observe` skill polls the CEO's **Sent** folder through the existing `ceo-inbox` Nylas grant (no new credential). Scope is **all sent mail**. Each run is watermarked (`config-store` under the `ceo_inbox` namespace, same high-water pattern as the inbound email poll) and is idempotent on re-run. For each new sent message it (1) correlates to Curia-authored draft snapshots (below) by `thread_id` + recipients + send time and appends `(draft, sent)` pairs to a rolling OKF evidence doc, and (2) correlates to open `owner='ceo'` tasks for completion candidates, queued in `config-store` (`ceo_inbox`, `sent_observe.completion_candidates`) rather than an OKF doc ([spec 19](19-tasks-and-backlog.md)). It emits a `ceo.sent_observed` audit event and advances the watermark. Observe runs as a **separate daily cron** on `agents/ceo-inbox.yaml` with its own Operating Modes task text — never folded into the 15-min triage loop.
- **Draft capture for voice learning (v0.42):** the three CEO-inbox draft skills (`ceo-inbox-draft-reply`, `ceo-inbox-draft-compose`, `ceo-inbox-draft-edit`) snapshot what Curia proposed into an OKF scratch doc at `/scratch/voice-learning/<draft_id>.md` (frontmatter: `draft_id`, `thread_id`, recipients, subject, `created_at`, `linked_task_ids[]`, `agent_version`; body = drafted markdown). Capture failure logs and **must not** block draft creation. Matched `(draft, sent)` diffs accumulate in `/scratch/voice-learning/pending-diffs.md` for the weekly `voice-learn` job ([spec 13](13-office-identity.md)). That job makes a single batched LLM extraction pass over the accumulated diffs to produce an updated `WritingVoice.guide`, proposed in the digest for CEO approval.
- **OKF evidence retention:** raw draft snapshots and pending-diff evidence are scratch-path docs. Once a pair has been folded into the writing-voice profile (or dismissed), the learning job deletes or TTL-expires the consumed evidence — default scratch TTL applies to unmatched snapshots; consumed evidence must not be retained indefinitely. Shadow drafts for punted mail follow the same scratch path conventions and never surface or send ([spec 14](14-autonomy-engine.md)).
- Nylas abstracts away provider differences (Gmail, Outlook, IMAP) and handles OAuth
Expand Down
10 changes: 6 additions & 4 deletions docs/specs/13-office-identity.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,11 +373,13 @@ A weekly `voice-learn` job on `ceo-inbox` (cron `0 8 * * 1`) reads accumulated
(`ctx.infraLlm.extract()`) over that accumulated evidence to produce an updated
free-form `WritingVoice.guide`.

The job never writes the profile directly. It queues a "## Guide Proposal"
block (`status: pending`) in the digest for CEO review. The CEO approves via
the `resolve-learning-digest` skill's `approve_voice` action, which calls
The job never writes the profile directly. It records a single pending proposal
object in `config-store` (`voice_learn.proposal`), surfaced in the digest for
CEO review. The CEO approves via the `resolve-learning-digest` skill's
`approve_voice` action, which calls
`executiveProfileService.update({ writingVoice: { ...current, guide } })`.
Dismissed proposals get a guard marker and cooldown before re-propose.
Dismissed proposals set the `voice_learn.dismissed` config cooldown key before
re-propose.

The learned guide renders into agent prompts through `compileWritingVoiceBlock`,
under a "How the executive actually writes (learned from their edits)" heading.
Expand Down
6 changes: 3 additions & 3 deletions docs/specs/19-tasks-and-backlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,9 @@ for these learning surfaces are explicitly deferred.
| High-confidence match | Auto-`task-complete` with reversible digest undo note | Confirm in digest |
| Low-confidence match | Confirm in digest | Confirm in digest |

High-risk tasks (e.g. "Plan AGM") never auto-complete. Fuzzy candidates get a guard marker
(`completion_asked: {date}`) so they are not re-surfaced every run. Auto-completes remain
reversible via reopen from the digest undo note.
High-risk tasks (e.g. "Plan AGM") never auto-complete. Fuzzy candidates are recorded in the
`sent_observe.asked_task_ids` guard set (config-store, `ceo_inbox`) so they are not
re-surfaced every run. Auto-completes remain reversible via reopen from the digest undo note.
- **Autonomy** ([spec 14](14-autonomy-engine.md)). The "execute the safe stuff, escalate
the rest" behavior is the existing autonomy engine — `action_risk` per skill against the
live score. Drafting (low) just happens; outbound (medium) is gated; spending money
Expand Down
Loading