Files
ironclaw/skills/commitment-triage/SKILL.md
firat.sertgoz ab38a0b234 feat(bridge): workspace-backed project registration + adapter improvements (#2533)
* feat(projects): workspace-backed project registration; migrate commitments into projects/commitments/

[cherry-pick-target: feat/projects-workspace-backed]

Replace the parallel `.system/engine/projects/*.json` schema with
workspace-backed project registration. Writing any file under
`projects/<slug>/` is now the declaration that the project exists —
the engine auto-registers it on `memory_write`, and `mission_create`
can reference it by slug. The model reasons about projects through
normal workspace APIs instead of a hidden sidecar schema.

Engine + bridge

- `ProjectId::from_slug(user_id, slug)` derives a stable v5 UUID;
  `Project::new` routes through it so constructing the same project
  twice returns the same ID (no duplicates).
- `slugify_simple` in `ironclaw_engine::types` — pure slug, no UUID
  suffix, reverses cleanly from a `projects/<slug>/` directory name.
- Project metadata moves from `.system/engine/projects/{slug}--{id8}/
  project.json` to user-facing `projects/<slug>/.project.json`.
  One-shot startup migration copies legacy files over, idempotent.
- `HybridStore::load_projects_from_workspace` scans `projects/*/` and
  synthesizes a stub `Project` for bare directories, so a write under
  `projects/foo/` surfaces immediately on restart.
- `EffectBridgeAdapter::ensure_project_for_memory_write` hook runs
  after a successful `memory_write`: if the target is under
  `projects/<slug>/...`, finds-or-creates the project and splices
  `project_id` into the tool output (enables
  `{{call-N.project_id}}` template refs).
- Extract `resolve_project_ref` helper from the inline block in
  `handle_mission_call` — now used by both `mission_create`'s
  `project_id` param and future project-aware tools.

Skills (13 files)

- Mechanical `commitments/` → `projects/commitments/` across the nine
  commitment-domain skills (commitment-setup, -triage, -digest,
  decision-capture, delegation-tracker, idea-parking,
  tech-debt-tracker, product-prioritization, security-review).
- Four persona setup skills (ceo-setup, developer-setup,
  trader-setup, content-creator-setup) gain an explicit "declare the
  project" step (write `projects/commitments/AGENTS.md` with
  persona-specific operating principles) and pass
  `project_id: "commitments"` on every `mission_create`. Setup
  markers move to `projects/commitments/.<persona>-setup-complete`.
- `ceo-setup` gets a v0.4.0 rewrite that also installs two dashboard
  widgets under `projects/commitments/.system/widgets/`:
  `commitments-this-week` (overdue / due / completed counts) and
  `delegations-waiting` (delegation list with stale-at-2-days flag).
  Both poll `projects/commitments/widgets/state.json`, refreshed by
  the triage mission each run.

Tests

- Three new unit tests in `bridge::effect_adapter::tests`:
  `extract_project_slug_recognizes_project_paths`,
  `extract_project_slug_rejects_degenerate_targets`,
  `project_new_is_deterministic_from_user_and_slug`.
- Update `tests/e2e_live_personas.rs` path assertions
  (`workspace_paths`, `read_under`, `verify_setup_landed`,
  `DEV_SETUP_CHECKS` needles, two workflow turn messages) to the new
  `projects/commitments/` prefix.
- Add a diagnostic dump in `run_turn` when a persona workflow turn
  times out with no response, so live-test hangs surface the
  captured status events instead of an opaque panic.

No backcompat for the old flat `commitments/` layout — pre-production
deployment, nothing in the wild depends on it.

* fix: adapt cherry-picked project registration to staging API surface

Add missing struct fields (engine_store, skill_registry) and setter
methods to EffectBridgeAdapter, expose MissionManager::store() accessor,
add sync_v1_skill_to_store to skill_migration, and remove references
to fields/methods not yet on staging (Project::goals/metrics,
LiveTestHarnessBuilder::with_skills_dir, V2SkillMetadata::bundle_path).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(bridge): address review — drop slug-prefix fallback, harden tests (#2533)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(bridge): address PR #2533 review — slug-consistency, migration hardening, caller-level tests

- synth_bare_project now normalizes the raw dir name via slugify_simple
  before ProjectId::from_slug, matching Project::new. Returns Option so
  unsluggable dirs (`---`, `!!!`) don't produce phantom projects.
- migrate_legacy_project_jsons upgraded to warn! and moves unparseable
  legacy project.json aside as project.broken.json so the user can
  recover instead of the engine masking the loss on every boot.
- Document project_slug's engine-internal (mission-path, UUID-suffixed)
  scope vs project_dir's user-facing (no-UUID) scope so the two slug
  schemes aren't conflated in future edits.
- Drop unused ProjectId param from project_dir / project_path.
- Trim Project::new docstring per CLAUDE.md style.

Tests added (19):
- types::project: slug variant collapse, unicode, empty-slug stability,
  run/edge normalization
- store_adapter unit: project_slug_for_name contract, project_dir/path,
  synth_bare_project↔Project::new ID equivalence across 12 weird names,
  unsluggable-dir rejection, cross-user isolation
- store_adapter migration_tests (libsql): bare-dir load, metadata over
  synth, non-canonical skip, weird-slug collapse, user-edit preservation,
  broken-JSON move-aside
- effect_adapter caller-level: drives execute_action("memory_write")
  for canonical / idempotent / non-projects / nested / weird-slug /
  cross-user / pathological targets per .claude/rules/testing.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Illia Polosukhin <ilblackdragon@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 23:23:15 +09:00

8.8 KiB

name, version, description, activation
name version description activation
commitment-triage 0.2.0 Recognize obligations in conversation, extract signals with immediacy and expiration, create and manage commitments in the workspace.
keywords patterns exclude_keywords tags max_context_tokens
need to
have to
must do
promised
committed to
deadline
by friday
by tomorrow
follow up
get back to
remind me
track this
mark done
commitment
obligation
overdue
slack message from
asked me to review
can you review
this week
(?i)I (need|have|should|must|ought) to
(?i)(remind me|don't let me forget|make sure I)
(?i)(by|before|until) (monday|tuesday|wednesday|thursday|friday|saturday|sunday|tomorrow|tonight|end of)
(?i)(promised|committed|agreed) (to|that)
(?i)(slack|email|dm|text) message from .+: .+
setup commitments
install commitments
commitments
task-management
personal-assistant
2000

Commitment Triage

You have a commitments tracking system in the workspace under projects/commitments/. Read projects/commitments/README.md for the full schema if you need field details.

Mode A: Passive signal detection

When the user says something that implies an obligation, promise, or deadline — but is NOT explicitly asking you to track it — silently extract a signal.

Triggers: "I need to...", "I promised Sarah...", "I should get back to...", "The report is due Friday", "They asked me to review..."

Treat these as especially strong passive-signal cases even if the user does not say "track this":

  • "The strategy team asked me to review the international expansion reforecast this week."
  • "Legal asked me to comment on the draft by Thursday."
  • "Slack message from Priya: can you review the OAuth callback edge case this week?"
  • "I need to review Sarah's deck before Friday."

These should usually become review, reply, or follow-up signals with immediacy: prompt when the request is time-sensitive or comes from a named person/team.

Treat inbound-message phrasings like these as strong passive signals too:

  • "Slack message from strategy: can you review the international expansion reforecast this week?"
  • "Email from legal: can you comment on the draft by Thursday?"

Action:

  1. Check for duplicates: memory_search for key phrases within projects/commitments/
  2. If no duplicate, call memory_write with:
    • target: projects/commitments/signals/pending/<slug>.md
    • append: false
    • Content: signal frontmatter + description
  3. Only after the memory_write succeeds, at a natural pause briefly note: "I've tracked a commitment about [topic]."

Do not merely acknowledge or summarize an obligation. This mode is successful only if a signal is actually written to projects/commitments/signals/pending/.

Do NOT interrupt the conversation flow. Signal extraction is a side-effect. For commitment tracking, use memory_tree, memory_read, and memory_write. Do not use CodeAct, shell commands, or creative-generation tools unless the user explicitly asked for execution rather than tracking.

Signal template:

---
type: signal
source_channel: <current channel>
source_message: "<brief quote>"
detected_at: <today YYYY-MM-DD>
immediacy: <realtime|prompt|batch — see rules below>
expires_at: <YYYY-MM-DD or null>
confidence: <high if explicit obligation, medium if implied, low if ambiguous>
obligation_type: <reply|deliver|attend|review|decide|follow-up|informational>
mentions: [<people mentioned>]
destination: null
promoted_to: null
---
<1-2 sentence description of the detected obligation.>

Immediacy rules:

  • realtime: production incidents, security alerts, stop-loss triggers, anything marked urgent by the user. If you detect a realtime signal, send a message immediately — do not wait for the next triage run.
  • prompt: urgent DMs from key people, trending topics (for creators), time-sensitive requests, or named-person/team asks like "the strategy team asked me to review..."
  • batch: most obligations — meeting action items, reports to read, tasks with multi-day deadlines

Signal destinations (set during triage, not initial extraction):

  • commitment: actionable, tracked → promote to projects/commitments/open/
  • parked_idea: interesting but not now → write to projects/commitments/parked-ideas/
  • intelligence: informational, shapes future decisions → write a durable MemoryDoc via memory_write to a non-commitments path (e.g. context/intel/<slug>.md)
  • dismissed: not relevant

Mode B: Explicit capture

When the user explicitly asks to track something: "track this", "add a commitment", "I committed to X".

If the user says "track this separately", "track this too", or otherwise introduces a second distinct obligation, create a new commitment file for that new item. Do not overwrite or silently reuse the previous commitment unless it is clearly the same obligation.

Phrasings like "track this request from Slack", "track this request from email", or "track this review request" are explicit capture requests. They should go through Mode B and produce a persisted commitment or signal write, not just a summary response.

Example:

  • User: "Track this: Sarah is going to deliver the Q2 budget proposal by Friday."
  • User later: "Track this separately: Bob is drafting the acquisition term sheet by Tuesday next week."
  • Required behavior: create a second distinct memory_write for Bob's term sheet. Do not only confirm it in prose, and do not reuse Sarah's file.

Action:

  1. Skip the signal stage — write directly to projects/commitments/open/<slug>.md
  2. Ask for missing details ONLY if truly ambiguous. Infer reasonable defaults.
  3. Confirm briefly only after the write succeeds: "Tracked: [description], due [date], urgency [level]."

This mode is only successful if a commitment file is actually written. For explicit capture, prefer direct memory_write updates to the workspace. Do not switch to CodeAct or shell execution for simple tracking tasks.

Commitment template:

---
type: commitment
status: open
urgency: <critical|high|medium|low>
due: <YYYY-MM-DD or null>
created_at: <today>
stale_after: <14 days from now, or sooner for urgent items>
owner: <user|agent>
delegated_to: null
resolution_path: <agent_can_handle|needs_reply|needs_decision|note_only>
source_signal: null
resolved_by: null
tags: [<inferred tags>]
---
# <Title>
<Description.>

## Resolution path
- [ ] <Step 1>
- [ ] <Step 2>

Urgency rules:

  • critical: due today or overdue
  • high: due within 3 days
  • medium: due within 2 weeks or soon but no hard deadline
  • low: no deadline, whenever

Resolution path inference:

  • Agent can research, draft, review code, summarize → agent_can_handle
  • User must reply to a person → needs_reply
  • User must choose between options → needs_decision
  • Just tracking awareness → note_only

For agent_can_handle, note in the commitment body what the agent would do. The agent must NOT act autonomously without user approval — add a note: "I can handle this. Want me to proceed?"

Mode C: Resolution

When the user says they finished something: "done with X", "finished the review", "sent the reply to Sarah".

Action:

  1. memory_tree("projects/commitments/open/", depth=1) to find the matching commitment
  2. memory_read the likely match to confirm
  3. Write the updated file (status: resolved, resolved_by: user) to projects/commitments/resolved/<same-slug>.md
  4. Overwrite the original with empty content: memory_write(target="projects/commitments/open/<slug>.md", content="", append=false)
  5. Confirm: "Resolved: [title]."

Mode D: Signal promotion (used by triage mission)

When reviewing pending signals (manually via "review signals" or during a triage mission run):

  1. memory_tree("projects/commitments/signals/pending/", depth=1) to list signals
  2. For each, memory_read and route to destination:
    • Actionable → create commitment in projects/commitments/open/, set signal destination: commitment
    • Interesting but not now → write to projects/commitments/parked-ideas/, set destination: parked_idea
    • Informational → write a MemoryDoc to context/intel/, set destination: intelligence
    • Not relevant → move to signals/expired/, set destination: dismissed
  3. Update the signal's promoted_to field for commitment destinations

Filename conventions

Slugify: lowercase, hyphens, no special chars, max 50 chars. Examples:

  • "Review Sarah's deck" → review-sarah-deck.md
  • "Submit Q1 tax filing" → submit-q1-tax-filing.md