Add project attention contract + assess_attention (schema v20)#83
Conversation
Setlist becomes the canonical store for project priority and expected attention, so a downstream assistant (Hermes) can distinguish genuine neglect from intentional quiet without keeping its own priority list. - Schema v20: nullable attention-contract columns on projects (priority_tier, attention_mode, attention_cadence_days, next_review_at, priority_reason). Existing projects migrate as not_configured — a gap, never a false warning. Structural no-op migration via ensureColumns. - Core: new AttentionAssessor (attention.ts) computing a deterministic attention debt = days-since-meaningful-touch ÷ cadence × priority weight. States: nourished / due_soon / undernourished / due_for_review / suppressed / not_configured. Meaningful touch is conservative: git commit in a registered path, substantive retained memory (decision/outcome/correction/learning/pattern), or material registry update — passive reads, health checks, and exports never count, and attention-contract writes deliberately do not bump updated_at (D-016). - waiting/paused projects suppress until next_review_at arrives, then flip to due_for_review. Archived/inactive projects are excluded from portfolio results. - MCP: new assess_attention tool (63 tools total); update_project accepts the five contract fields with validation; portfolio_brief gains a bounded, urgency-ordered attention section (≤5 entries per list). - Context export v2 (setlist-project-context.v2): snapshots carry the contract + live-agreeing assessment, manifest entries carry attention_state/debt/tier, INDEX.md leads with attention concerns. Atomic manifest-last write order preserved. - Desktop: attention contract editable in the project edit form. - assess_health semantics untouched; registry health and attention are separate assessments. Tests: 1062 passing (new attention.test.ts covers the brief's acceptance scenarios), typecheck clean, verify:mcp-abi OK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Found six blocking correctness/security issues and one contract-documentation issue. Runtime validation was environment-limited: the sandbox prevented build output, and dependencies/Vitest were unavailable. Findings (7)
|
- P1: context export sanitizes project names into safe basenames (registry names are not character-validated) with a hash suffix on sanitized names to prevent collisions, plus a resolved-path containment guard — a name like ../../escape can no longer write outside the managed directory. - P2: updateCore validates all attention-contract fields BEFORE any UPDATE executes and runs the core+attention writes in one transaction, so a rejected combined update never leaves a partial write. - P2: next_review_at now enforces a strict ISO 8601 grammar with real calendar-date validation (rejects locale strings, bare numbers, and impossible dates like 2026-02-30 that Date() silently normalizes). - P2: CLI `context` flags moved to the strict shared parser (`--dir --yes` is now a usage error, `--interval 60junk` rejected). - P2: CLI `update` moved to the strict parser and extended with the five attention-contract flags (`none` clears a field). - P2: INDEX.md attention concerns and inline flags now require status=active, matching live portfolio attention exclusion of inactive projects — no false offline alerts. - P3: INVARIANTS.md schema invariant reconciled to v20. Regression tests pin each fix; suite 1066 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Three blocking correctness issues remain. The review was source-based; typecheck could not run because the read-only workspace prevented TypeScript from writing build artifacts. Findings (3)
|
- P2: next_review_at validator now rejects everything JS Date cannot parse — leap seconds (:60) and out-of-range zone offsets (+24:00, +02:60) — with a final finite-epoch belt, so a stored review date can never suppress a waiting project forever via NaN comparisons. - P2: updateCore is now fully atomic across ALL fields: area and parent_project are prevalidated (existence + cycle walk, via the extracted resolveParentIdOrThrow shared with setParentProject) before any write and applied inside the same transaction as core + attention fields — a combined save with an invalid area or a parent cycle persists nothing. - P2: export basenames are collision-proof per generation: computeProjectContextBasenames disambiguates case-insensitive and sanitized-vs-literal collisions with per-name hash suffixes and refuses the export on any residual collision instead of silently overwriting one project's files with another's. Regression tests pin all three; suite 1068 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Four blocking correctness issues remain. The new attention feature is broadly wired, but payload bounds, CLI discovery, calendar-date handling, and scheduler replacement safety are broken. Tests could not be rerun because this checkout lacks dependencies and network access was unavailable. Findings (4)
|
- P2: portfolio_brief bounds not_configured to the same 5-entry cap as the other attention lists (a freshly-migrated registry has EVERY project unconfigured) and adds not_configured_count for honesty. - P2: the authoritative CLI command registry (commands.ts) now carries the five attention flags on `update` (and --json on `context`), so help output and self-registered capability inputs match the runtime. - P2: a bare next_review_at date is interpreted as LOCAL midnight, not UTC — pausing "until 2026-09-01" no longer resurfaces the project the prior evening in western timezones (reviewEpoch in attention.ts). - P2: context-schedule install rolls back on launchctl bootstrap failure — restores and reloads the previous plist (or removes the new one on first install) instead of stranding a stopped schedule; launchctl is injectable for tests. Regression tests pin all four; suite 1072 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against The attention model is internally coherent, but the default export path breaks the end-to-end offline fallback contract. Focused runtime tests could not run because this isolated worktree has no dependencies; the dependency-free CLI introspection tests passed. Findings (2)
|
- P2 (default export directory): resolved by documenting the deliberate decision rather than changing the default. The XDG default is intentional — automated jobs must never write into cloud-synced folders by default (filesystem doctrine); a synced fallback location such as ~/Areas/reference/System/project-context/ is an explicit --dir opt-in, which is exactly how this machine's launchd job is configured. Encoded in context-export.ts and README so the divergence between product default and one deployment's documented path can't read as an accident. - P3: `setlist context` now honors --json on install/uninstall/status (plan/applied envelopes and a status object), not just export. Suite 1072 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Four blocking correctness and safety issues remain. Diff hygiene passed, but runtime tests and typechecks could not be rerun because this review worktree has no installed dependencies. Findings (4)
|
- P2: the attention ratio and debt use exact elapsed time instead of floored whole days — a 1-day-cadence project touched 47 hours ago now reads undernourished instead of hiding up to 24h of overdue-ness. days_since_meaningful_touch stays a whole number for readability. - P2: update_project is atomic including paths — updateCore accepts the replace-semantics paths set, validates it up front, and applies it in the same transaction as core/attention/area/parent; the MCP handler no longer makes a second setProjectPaths call that could fail after the fields committed. - P2: context-schedule uninstall no longer treats every bootout failure as "already unloaded": if launchctl print shows the job still loaded, the uninstall aborts without removing the plist (no false success leaving an unmanaged running job). - P2: the exporter refuses symlinked managed directories (export root or projects/) — the lexical containment check can't see links, so a swapped-in symlink could otherwise redirect writes outside the target. Regression tests pin all four; suite 1077 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Two blocking correctness issues remain. Automated tests could not run because dependencies are absent and the review workspace is read-only. Findings (2)
|
- P2: no-op writes no longer count as meaningful touches. updateCore drops sets that echo the stored value (same status/description/goals/ display_name/email_account, same area/parent, identical paths replacement) so updated_at only moves on genuine persisted change; updateFields bumps updated_at only when writeFields actually wrote rows (a fully producer-skipped call is not project activity; writeFields now returns the written count). - (The round's second finding — uninstall bootout swallow — was already fixed in e78f79d; that review ran against the prior head.) Regression test pins the echo/skip cases; suite 1078 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Two blocking correctness defects can hide neglected projects or publish internally inconsistent offline context. The authoritative surface map also omits the new export and scheduler contracts. Findings (3)
|
- P2: writeFields counts only rows whose stored value actually changed (upsert WHERE now also requires field_value IS NOT excluded value), so periodic reconciliation re-writing identical values no longer bumps updated_at and cannot make an untouched project read as nourished. - P2: the export generation reads from one immutable VACUUM INTO database snapshot — briefs and attention assessments can no longer interleave with a concurrent writer and publish a project file whose brief and attention result describe different states. Side benefit: generation reads never touch the live interactions log. - P3: SURFACES.md gains the offline-context-export ripple edge (brief/attention shape ↔ versioned export contract ↔ launchd scheduler). Suite 1078 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against The attention model is broadly wired across all required surfaces, but a no-op enrichment call can falsely mark a neglected project as recently nourished. Findings (1)
|
- P2: enrichProject only writes (and only bumps updated_at) when the merged profile genuinely differs from the stored one — an empty enrich_project call or a reconciliation re-sending existing values no longer counts as a meaningful touch for assess_attention. Suite 1078 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against One blocking correctness issue remains in the central attention signal. Static review covered all 38 changed files and authoritative contracts; test execution was unavailable because this isolated checkout lacks dependencies. Findings (1)
|
setProjectArea, setParentProject, and setProjectPaths no longer bump updated_at when reapplying the current value — the last three public writers where an idempotent reconciliation could fake a meaningful touch for assess_attention (D-016). Regression coverage extends the echo test to the standalone setters. Suite 1078 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Three blocking correctness gaps remain in the new attention contract. Focused tests could not run because dependencies are absent and network access failed; git diff validation passed. Findings (3)
|
- P2: the five attention field names join RESERVED_FIELD_NAMES —
write_fields({priority_tier: ...}) now errors with a redirect to
update_project instead of storing an inert project_fields row that
assess_attention never reads.
- P2: batchUpdate is change-aware per project — a batch re-applying
stored values (e.g. status_filter='active' + status='active') no
longer bumps updated_at across the whole portfolio.
- P2: setProjectType skips the write and timestamp bump when reapplying
the current type.
Regression tests extend the no-op sweep; suite 1079 green, typecheck
clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Three blocking correctness problems remain in Git activity detection and the offline export's atomicity guarantees. Findings (3)
|
- P2: markdown project files carry generation_id in frontmatter and INDEX.md names its generation, so readers can perform the documented generation check against manifest.json on every artifact, not just the JSON twins. - P2: exports into the same directory are serialized by an atomic mkdir lock (.setlist-context-export-lock) — a concurrent second export gets CONTEXT_EXPORT_BUSY instead of interleaving files and committing a mismatched manifest. Stale locks (crashed exporter) are broken after 10 minutes; assertManagedDirectory ignores the lock. - P2: latestCommitInPath uses `git -C <path> log -1 -- .` (containing worktree + folder-scoped) instead of requiring <path>/.git, so a project registered at a monorepo subfolder reports its git activity in both health and attention assessments. Suite 1081 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Two P2 correctness issues remain: the export mutex can admit concurrent writers, and attention assessment misses recognized legacy project-memory aliases. Automated tests could not run because dependencies are absent ( Findings (2)
|
- P2: the export lock carries an ownership token — release removes the lock only while it still holds this exporter's token (a slow run can never delete a successor's lock), and stale-lock takeover goes through an atomic rename so exactly one of two racing takeover attempts wins. assertManagedDirectory ignores takeover remnants. - P2: the substantive-memory touch query is alias-aware (D-014): legacy rows stored under a case variant of the canonical name or under the project's display name now count, with each fuzzy alias included only when the canonical resolver maps it unambiguously back to this project (a shared display name or case-twin never attributes another project's memories). Regression tests pin both; suite 1082 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Two blocking correctness issues remain: attention assessments can borrow memories from a different case-variant project, and schema v20 omits constraints explicitly required by the standing model decisions. Focused tests could not run because dependencies are absent and downloading them while executing untrusted PR tests was denied. Findings (2)
|
- P2: the case-variant memory alias is gated on an actual case-twin count instead of resolver probes — with projects casey/CASEY, the resolver's exact-name tier "resolved" each spelling to itself and let a LOWER() match union both projects' memories; now any case twin disables the fuzzy alias entirely. - P2: v19→v20 lands the two constraints D-014 explicitly deferred to the next schema bump: 'revived' joins the memory_versions.change_type CHECK (shape-guarded, crash-re-entrant table rebuild following the v8→v9 pattern) and memory_edges gains its natural key as a real UNIQUE(source_id, target_id, relationship_type) index, created after collapsing existing duplicates to the earliest row. The index lives in the migration step, not SCHEMA_SQL, because SCHEMA_SQL runs on every open before migrations and would fail over still-duplicated legacy rows. MemoryChangeType, CLAUDE.md, and INVARIANTS.md reconciled. Migration tests cover revived acceptance, duplicate-edge rejection, and dedupe-on-upgrade; suite 1085 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Two blocking contract defects remain: revival history is mislabeled, and offline exports can falsely report stale digests as fresh. The test suite could not be rerun because dependencies are unavailable in this read-only environment; diff whitespace validation passed. Findings (3)
|
- P2: revive-on-re-retain now records change_type 'revived' (the v20 enum value this PR added) instead of 'updated' — history consumers can distinguish a revival from an ordinary edit; the D-014 revive test asserts it end-to-end. - P2: exported digest_stale uses getProjectDigest's honest path-based staleness (null when unknowable) instead of the brief's inline value that defaulted to false without a spec_version field — offline agents are no longer told an obsolete digest is confidently fresh (D-011). - P3: the symlinked-root check runs before the managed-directory check and the lock, so a symlinked --dir is refused before anything (lock included) mutates the link target; covered by a symlinked-root test. Suite 1086 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Two blocking correctness issues can produce false attention and digest signals. The migration also preserves an arbitrary duplicate rather than the documented earliest one. Findings (3)
|
- P2: the substantive-memory touch orders by per-row MAX(created_at, updated_at) — a reinforced or revived decision counts as today's activity instead of dating to its original creation. - P2: brief.digest.stale is now honest at the source — loadProjectDigest (feeding get_project_brief and the export's embedded brief) reuses getProjectDigest's path-based staleness with the null-when-unknowable tri-state, so the export's top-level digest_stale and the embedded brief can never disagree. ProjectBriefDigest.stale widened to boolean | null (renderer reads are truthiness-based). - P3: the v19→v20 edge dedupe keeps the earliest row by created_at with an id tiebreaker instead of MIN(id) over random UUIDs. Suite 1087 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against The new context-export work is substantial, but one contract regression remains: unknown digest freshness is exposed as fresh/current in the main human-facing surfaces. I also found one non-blocking mismatch between the migration invariant and the implemented deduplication rule. Automated tests could not run because this read-only checkout has no installed Vitest dependencies. Findings (2)
|
- P2: the digest tri-state propagates through every consumer — CLI brief prints "freshness unknown" (not "fresh") for null, the desktop Overview tag reads "Digest unverified" with a warning (not "current"), and the renderer api + core ProjectDigest types declare stale: boolean | null. - P3: INVARIANTS.md dedupe wording reconciled to the implemented rule (earliest created_at, id tiebreak). Suite 1087 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Two blocking correctness issues remain in digest freshness reporting and launchd schedule removal. Findings (2)
|
- P2: loadProjectDigest no longer passes the stored project_fields.spec_version as a caller override — that bypassed the path-based tri-state check and let a pathless project report stale:false. Briefs now match getProjectDigest exactly; the two project-brief tests that encoded the old field-compare semantics are rewritten to the honest tri-state contract, and the unused loadCurrentSpecVersion helper is removed. - P2: schedule uninstall and status probe launchd by label even when the plist file is absent — a bootstrapped job whose plist was hand-deleted is booted out (uninstall) and reported as loaded (status), never a false not_installed while the exporter keeps running. Suite 1088 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against Two blocking gaps can silently provide incomplete or misleading attention data. Automated tests and type-checking could not run because this checkout lacks the required vitest and TypeScript dependencies. Findings (2)
|
- P2: the latest meaningful touch is part of the assessment for every non-archived state — suppressed, due_for_review, and not_configured results now carry last_meaningful_touch and days_since, so an assistant (or the offline export) never reports "none recorded" for real activity. - P2: reserved-field validation runs on every writeFields entry point — register() (and registerExistingWorkspace via it) now rejects attention-contract names and 'paths' in registration fields with the same redirect updateFields gives, via a shared assertNoReservedFieldNames helper. Suite 1089 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — REQUEST_CHANGESLocal Codex review (gpt-5.5) of this PR's changes against A failed registration can leave a partially created project in the registry. Independent tests could not run because this checkout has no installed Vitest dependency. Findings (1)
|
- P2: reserved-field validation in register() runs before any INSERT — a rejected registration no longer strands a half-registered project whose retry fails as a duplicate. Test asserts the clean-retry path. Suite 1090 green, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
…missing) The clean-retry register IS the stranded-row proof — it would raise DuplicateProjectError if the rejected attempt had inserted anything. Suite 1089 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
🤖 Codex review — APPROVELocal Codex review (gpt-5.5) of this PR's changes against No blocking correctness, security, migration, or invariant violations found. The implementation is consistent across the main runtime surfaces, and Findings (1)
|
…d mem-export (#81) Steward marker advanced dcaf634 → 6197802 with plain-language summary, registry-sync and cross-project-routing attestations. SURFACES CLI row gains the #80 mem-export completeness note. .fctry/.gitignore backfill from the canonical-patterns hook (kept out of the feature PR per loop norms). Issue #82 closed as resolved by #83's update_project paths support; #84 remains the only follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi
Implements
docs/setlist-project-attention-brief.md— Setlist becomes the canonical source for project priority and expected attention, computing an attention-nourishment signal separate from registry health.Data model & migration
projects—priority_tier(critical/important/normal/someday),attention_mode(active/maintenance/waiting/paused),attention_cadence_days(positive int),next_review_at(ISO),priority_reason. Added viaensureColumns(structural no-op migration step); existing projects migrate with an unconfigured contract and surface asnot_configured, never a false warning. Validation (enums, positive integer, ISO timestamp) lives at the write boundary inRegistry.updateCore.Attention formula (deterministic, tested)
weights critical 2.0 / important 1.5 / normal 1.0 / someday 0.5;
due_soonabove a 0.75 ratio,undernourishedpast 1.0. Cadence decides overdue; priority decides ordering.Meaningful touch (conservative, source-visible): git commit in a registered path, substantive retained memory (decision/outcome/correction/learning/pattern — not observation), or material registry update (
updated_at). The interactions log is deliberately not a source; passive reads, health checks, briefs, and exports never count. Attention-contract writes do not bumpupdated_at(see MODEL_DECISIONS D-016) so configuring a neglected project can't mask its neglect.Intentional quiet: waiting/paused →
suppressedwhilenext_review_atis future (or unset), →due_for_reviewonce it arrives. Archived/inactive projects excluded from portfolio results.Changed contracts
assess_attention(name → single assessment; no name → portfolio ordered by debt) — 63 tools;update_projectaccepts the five contract fields (null clears);portfolio_briefgains a boundedattentionsection (≤5 undernourished/due-soon, ≤5 due-for-review, gap names, per-state summary).AttentionAssessor, contract onProjectRecord/get_project/ProjectBrief.project.attention; capability introspection updated.setlist-project-context.v2— snapshot carries contract + assessment agreeing with live results at export time; manifest entries carryattention_state/attention_debt/priority_tier; INDEX.md leads with an "Attention concerns" section. Manifest-last atomic write order preserved. Existing launchd schedule unchanged (no new daemon).assess_healthuntouched — existing consumers keep current semantics.How Hermes reads/writes
Read:
assess_attention(live),portfolio_brief.attention(session start), or the v2 export when offline. Write back natural-language corrections viaupdate_project(e.g. "pause until September" →attention_mode: waiting,next_review_at). No scheduling/notification logic lives in Setlist.Acceptance scenarios
All 11 brief scenarios covered in
packages/core/tests/attention.test.ts,context-export.test.ts(export agreement), andcross-query.test.ts(bounded brief section).Validation
npm test— 57 files, 1062 passednpm run typecheck— clean (main/preload/renderer)npm run build— cleannpm run verify:mcp-abi— OK🤖 Generated with Claude Code
https://claude.ai/code/session_01Rcn8wbbJG5NELgFEskFdhi