Skip to content

Port NPA psychographic profiling into IronClaw#321

Closed
jayzalowitz wants to merge 1 commit intonearai:stagingfrom
jayzalowitz:jayzalowitz/chat-onboarding-and-routine-advisor
Closed

Port NPA psychographic profiling into IronClaw#321
jayzalowitz wants to merge 1 commit intonearai:stagingfrom
jayzalowitz:jayzalowitz/chat-onboarding-and-routine-advisor

Conversation

@jayzalowitz
Copy link
Contributor

@jayzalowitz jayzalowitz commented Feb 23, 2026

Summary

Ports the NPA psychographic profiling system into IronClaw, enabling personalized assistant interactions based on user communication style, personality traits, and behavioral patterns.

What changed

  • Profile schema (src/profile.rs): 9-dimension psychographic profile with PsychographicProfile, CommunicationStyle, PersonalityTraits, BehaviorPatterns, InteractionPreferences, FriendshipProfile, ContextProfile, AssistancePreferences, CohortInfo, and AnalysisMetadata. Shared PROFILE_JSON_SCHEMA constant prevents schema drift between onboarding and workspace. Custom deserializers handle backward compatibility (v1 bare cohort strings, Vec qualities) and LLM resilience (trait scores accept floats/negatives/out-of-range via f64 deserialization with clamping to 0-100, defaulting to 50 on non-numeric input).

  • First-interaction onboarding (src/workspace/mod.rs): Instead of a wizard step, onboarding happens conversationally during the user's first interaction with the running assistant. The First Contact system prompt block injects when no profile document exists (has_profile_doc is false) and instructs the LLM to naturally learn about the user using a one-step-removed questioning technique, then write the profile via memory_write. The block self-disables once any valid profile JSON is written, regardless of which fields are populated.

  • Three-tier prompt augmentation (src/workspace/mod.rs):

    • Tier 1 (always-on): single-line interaction style summary — requires is_populated() (preferred_name, profession, or goals present)
    • Tier 2 (gated on confidence > 0.6 + 7-day recency): full communication details, goals, pain points
    • Suppresses default/unknown values to keep the prompt clean
  • Onboarding engine (src/setup/onboarding_chat.rs): Conversational engine with 6 intent tracking, kept for potential future ironclaw onboard --personal CLI command. Generates confidence-scored profiles from conversation transcripts.

  • Profile evolution (src/setup/profile_evolution.rs): Weekly re-analysis prompt with confidence gating (only update fields when confidence > 0.6), gradual personality trait shifts (max ±10 per update), and schema-version-aware updates. Version field is schema version (1=original, 2=enriched), not a revision counter.

  • Skills: delegation/SKILL.md and routine-advisor/SKILL.md for delegated task management and routine setup guidance.

  • Wizard: Unchanged at 8 steps. Personal onboarding moved to first assistant interaction per maintainer feedback.

Design decisions

  • Strings over enums for profile fields: LLM-generated JSON may produce slight variations; accepting String with validation at the display layer is more resilient than strict enum deserialization.
  • Top-level confidence field: Quick-access for Tier 2 gating without traversing into analysis_metadata. Intentional duplication.
  • has_profile_doc vs is_populated(): First Contact gates on document existence (any valid profile written → stop onboarding), while Tier 1/2 augmentation gates on is_populated() (meaningful content present). This prevents onboarding from looping if a profile was written with minimal fields.
  • profile_onboarding_completed kept in Settings: Durable flag distinguishing "never onboarded" from "onboarded but profile was reset", separate from the content-based is_populated() check. TODO to wire into agent loop.
  • Trait score deserializer: Accepts f64 to handle LLM outputs >255 or negative; clamps 0-100, rounds, defaults to 50 on non-finite/non-numeric. Protects against whole-profile deserialization failure from a single bad trait score.
  • Confidence formula guard: max(message_count, 1) prevents division by zero in all three confidence calculation sites.

Test plan

  • cargo fmt — no formatting issues
  • cargo clippy --all --benches --tests --examples --all-features — zero warnings
  • cargo test --lib profile — 22 profile-specific tests pass
  • Trait score clamping: values >100, >255, floats, negatives all handled correctly
  • is_populated() tests: default profile is false, profile with name is true
  • Backward compat: old profile JSON (v1, bare cohort string, Vec qualities) still deserializes
  • Manual: verify First Contact block injects on first interaction and stops after profile write

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings February 23, 2026 02:48
@github-actions github-actions bot added scope: workspace Persistent memory / workspace scope: config Configuration scope: setup Onboarding / setup scope: docs Documentation size: XL 500+ changed lines risk: high Safety, secrets, auth, or critical infrastructure contributor: new First-time contributor labels Feb 23, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jayzalowitz, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the agent's ability to understand and interact with users by porting a sophisticated psychographic profiling system. It introduces a conversational onboarding experience to build rich user profiles, which then dynamically inform the agent's communication style, proactivity, and assistance preferences. This foundational change aims to create a more personalized and effective user experience from the very first interaction.

Highlights

  • Psychographic Profiling Integration: Integrated Cami2's comprehensive psychographic profiling system into IronClaw, enabling deep user understanding.
  • Chat-Based Onboarding Flow: Introduced a new conversational onboarding experience to gather user preferences and build initial psychographic profiles.
  • 9-Dimension Analysis Framework: Implemented a detailed 9-dimension analysis framework for psychographic assessment, including communication style, personality traits, and behavioral patterns.
  • Profile Evolution and Confidence Gating: Enabled weekly profile evolution with confidence gating (>0.6 threshold) to refine user profiles based on ongoing interactions.
  • Dynamic System Prompt Augmentation: Updated system prompt logic to dynamically inject user profile data (Tier 1 summary, Tier 2 full context) based on confidence and recency, and provided first-contact guidance for new users.
  • New Skills for Task Management: Added delegation and routine-advisor skills to assist users with task management, scheduling, and automation.
  • Onboarding Bypass Configuration: Provided a SKIP_WIZARD environment variable to allow bypassing the personal onboarding process for hosted platforms.
Changelog
  • skills/delegation/SKILL.md
    • Added a new skill definition for task delegation, including activation keywords, patterns, and a detailed process for clarifying, breaking down, tracking, executing, and reporting tasks.
  • skills/routine-advisor/SKILL.md
    • Added a new skill definition for suggesting and managing user routines, outlining when and how to suggest routines, pacing guidelines, and parameters for routine creation.
  • src/lib.rs
    • Added a new public module profile to the crate.
  • src/main.rs
    • Implemented a check for the SKIP_WIZARD environment variable to allow bypassing the onboarding process.
  • src/profile.rs
    • Added a new file defining the PsychographicProfile struct and its sub-components (e.g., PersonalityTraits, CommunicationPreferences, CohortClassification, FriendshipQualities, RelationshipValues, InteractionPreferences, AnalysisMetadata), including a constant ANALYSIS_FRAMEWORK and backward-compatible deserializers for older profile formats.
  • src/setup/mod.rs
    • Updated the setup wizard's step count to include personal onboarding and exposed new onboarding_chat and profile_evolution modules.
  • src/setup/onboarding_chat.rs
    • Added a new module implementing the conversational onboarding logic, including intent tracking, LLM interaction, and psychographic profile generation from conversation transcripts.
  • src/setup/profile_evolution.rs
    • Added a new module containing functions and constants for generating LLM prompts for weekly psychographic profile evolution based on recent user interactions.
  • src/setup/wizard.rs
    • Modified the SetupWizard to incorporate a new "Personal onboarding" step, which uses the OnboardingChat to generate and store a user's psychographic profile, and added helper functions for building the LLM provider and storing the profile.
  • src/workspace/document.rs
    • Defined new constants PROFILE and ASSISTANT_DIRECTIVES for standard paths to the user's psychographic profile and derived assistant directives.
  • src/workspace/mod.rs
    • Enhanced the build_system_prompt function to dynamically inject user profile information (Tier 1 summary, Tier 2 detailed context based on confidence and recency) and first-contact guidance for new users.
    • Updated the SOUL.md content with new sections on autonomy and profile building guidelines.
Activity
  • The pull request includes 1441 passing tests, indicating comprehensive automated testing.
  • Zero Clippy warnings confirm adherence to Rust linting standards.
  • Both backend configurations (libsql and default) compile successfully.
  • Manual verification steps are outlined for old profile JSON deserialization, first-contact guidance, and Tier 2 personalization activation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@jayzalowitz jayzalowitz changed the title Port cami2 psychographic profiling into IronClaw Port NPA psychographic profiling into IronClaw Feb 23, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR ports the complete psychographic profiling system from cami2 into IronClaw, enabling personalized assistant behavior through a conversational onboarding flow and ongoing profile evolution. The system introduces a 9-dimension analysis framework that captures personality traits, communication preferences, behavioral patterns, and relationship dynamics.

Changes:

  • Adds a 987-line profile module with 5 new data structures (CohortClassification, FriendshipQualities, RelationshipValues, InteractionPreferences, AnalysisMetadata) and backward-compatible schema migration from v1 to v2
  • Implements conversational onboarding engine with 6-intent tracking, one-step-removed questioning technique, and LLM-driven profile generation with retry logic
  • Introduces three-tier system prompt augmentation: Tier 1 summary always shown, Tier 2 full personalization gated on confidence > 0.6 and 7-day recency check
  • Adds two new skills (delegation, routine-advisor) for passive profiling and proactive automation suggestions
  • Configures SKIP_WIZARD environment variable to bypass setup on hosted platforms

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/profile.rs Core psychographic profile types with 9-dimension framework, custom deserializers for backward compatibility, and rendering methods for USER.md, assistant-directives.md, and HEARTBEAT.md
src/setup/onboarding_chat.rs Conversational onboarding engine with intent tracking, JSON parsing with retry logic, and profile generation from conversation transcript
src/setup/profile_evolution.rs Weekly profile update prompt generation with confidence gating and evolution routine template
src/setup/wizard.rs Step 9 integration: personal onboarding flow with LLM provider initialization and multi-backend profile storage
src/workspace/mod.rs Three-tier system prompt injection with confidence and recency checks, plus first-contact guidance for new users
src/workspace/document.rs New path constants for profile.json and assistant-directives.md
src/main.rs SKIP_WIZARD environment variable check to bypass wizard on hosted platforms
src/settings.rs personal_onboarding_completed flag tracking
src/lib.rs Public profile module export
src/setup/mod.rs Module exports for onboarding_chat, profile_evolution, and SetupError
skills/delegation/SKILL.md Task delegation skill with profile-aware communication and autonomy levels
skills/routine-advisor/SKILL.md Routine suggestion skill with pacing guidelines and user-type-specific ideas

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from 8e091fb to 04886e1 Compare February 23, 2026 02:53
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request successfully ports the psychographic profiling system from cami2 into IronClaw. It introduces a comprehensive 9-dimension analysis framework, conversational onboarding, and automated profile evolution. The implementation is robust, including backward compatibility for older profile schemas and integration with the existing workspace and setup wizard. I have identified a few areas for improvement, primarily around type safety by using enums instead of strings for fixed categories, refining the JSON parsing logic to be more resilient, and ensuring explicit error handling for parsing failures, aligning with best practices for clear error messages.

Copilot AI review requested due to automatic review settings February 23, 2026 03:02
@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from 04886e1 to bacfe08 Compare February 23, 2026 03:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from 4d13201 to b5f5e32 Compare February 23, 2026 03:23
@ilblackdragon
Copy link
Member

Instead of doing this during wizard - let's move it into first interaction with the assistant.

Onboarding after asking questions can educate user what is possible to do and help setup channels like telegram/whatsapp/etc based on question like "what is your preferred personal/work communication".

Copilot AI review requested due to automatic review settings February 23, 2026 15:09
@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from cdc3c8b to 5209483 Compare February 23, 2026 15:09
@jayzalowitz
Copy link
Contributor Author

Done — moved personal onboarding from wizard Step 9 to the first assistant interaction.

What changed:

  • Removed step_personal_onboarding(), build_onboarding_llm(), and store_profile() from the wizard; total steps back to 8
  • Rewrote the ## First Contact system prompt block in workspace/mod.rs to instruct the LLM to conduct a natural onboarding conversation over the first 3-5 turns
  • The LLM uses the one-step-removed technique, learns the user's name/preferences, asks about preferred communication channels (Telegram, Slack, etc.), and writes the psychographic profile via memory_write when it has enough signal
  • Once the profile is written, has_rich_profile flips to true and the First Contact block stops injecting — no flag needed

The onboarding chat engine (onboarding_chat.rs) is kept for potential future ironclaw onboard --personal CLI use, but it's no longer wired into the wizard.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from 5209483 to c28be23 Compare February 23, 2026 15:30
Copilot AI review requested due to automatic review settings February 23, 2026 15:34
@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from c28be23 to 4cd6cd2 Compare February 23, 2026 15:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from 4cd6cd2 to e98dadd Compare February 23, 2026 15:52
Copilot AI review requested due to automatic review settings February 23, 2026 16:21
@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from e98dadd to 6c1081a Compare February 23, 2026 16:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings February 23, 2026 23:54
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from b3e727a to ef9b0dd Compare March 1, 2026 05:01
Copilot AI review requested due to automatic review settings March 1, 2026 05:23
@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from ef9b0dd to c6369b4 Compare March 1, 2026 05:23
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from c6369b4 to 4d57fa3 Compare March 1, 2026 05:46
Port the complete psychographic profiling system from NPA into IronClaw,
including enriched profile schema, conversational onboarding, profile
evolution, and three-tier prompt augmentation.

Personal onboarding moved from wizard Step 9 to first assistant
interaction per maintainer feedback — the First Contact system prompt
block now instructs the LLM to conduct a natural onboarding conversation
that builds the psychographic profile via memory_write.

Changes:
- Enrich profile.rs with 5 new structs, 9-dimension analysis framework,
  custom deserializers for backward compatibility, and rendering methods
- Add conversational onboarding engine with one-step-removed questioning
  technique, personality framework, and confidence-scored profile generation
- Add profile evolution with confidence gating, analysis metadata tracking,
  and weekly update routine
- Replace thin interaction style injection with three-tier system gated on
  confidence > 0.6 and profile recency
- Replace wizard Step 9 with First Contact system prompt block that drives
  conversational onboarding during the user's first interaction
- Add autonomy progression to SOUL.md seed and personality framework to
  AGENTS.md seed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 1, 2026 06:09
@jayzalowitz jayzalowitz force-pushed the jayzalowitz/chat-onboarding-and-routine-advisor branch from 4d57fa3 to d22bdef Compare March 1, 2026 06:09
@github-actions github-actions bot added the scope: tool/builtin Built-in tools label Mar 1, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +270 to +277
// Sync derived identity documents when the profile is written.
let mut synced_docs: Vec<&str> = Vec::new();
if path == paths::PROFILE {
match self.workspace.sync_profile_documents().await {
Ok(true) => {
tracing::info!("profile write: synced USER.md + assistant-directives.md");
synced_docs.extend_from_slice(&[paths::USER, paths::ASSISTANT_DIRECTIVES]);

Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

context/assistant-directives.md is now injected into the system prompt (workspace/mod.rs), but it is not included in PROTECTED_IDENTITY_FILES. This means an attacker (or prompt-injected model) can overwrite it directly via memory_write, persisting malicious system instructions. Add paths::ASSISTANT_DIRECTIVES to the protected list, and keep updates to this file restricted to the trusted sync_profile_documents path.

Copilot uses AI. Check for mistakes.
Comment on lines +272 to +294
if path == paths::PROFILE {
match self.workspace.sync_profile_documents().await {
Ok(true) => {
tracing::info!("profile write: synced USER.md + assistant-directives.md");
synced_docs.extend_from_slice(&[paths::USER, paths::ASSISTANT_DIRECTIVES]);

// Persist the onboarding-completed flag.
let toml_path = crate::settings::Settings::default_toml_path();
if let Ok(Some(mut settings)) = crate::settings::Settings::load_toml(&toml_path)
&& !settings.profile_onboarding_completed
{
settings.profile_onboarding_completed = true;
if let Err(e) = settings.save_toml(&toml_path) {
tracing::warn!("failed to persist profile_onboarding_completed: {e}");
}
}
}
Ok(false) => {
tracing::debug!("profile not populated, skipping document sync");
}
Err(e) => {
tracing::warn!("profile document sync failed: {e}");
}
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sync_profile_documents() writes USER.md (a protected identity file) as a side-effect of a memory_write call to the profile. This effectively reintroduces a tool-driven path to modify system-prompt identity content. Consider sanitizing/escaping profile-derived strings before emitting markdown, and/or ensuring the sync happens only after validating the profile JSON parses as PsychographicProfile and was written with append=false (to prevent partial/poisoned writes from generating identity docs).

Suggested change
if path == paths::PROFILE {
match self.workspace.sync_profile_documents().await {
Ok(true) => {
tracing::info!("profile write: synced USER.md + assistant-directives.md");
synced_docs.extend_from_slice(&[paths::USER, paths::ASSISTANT_DIRECTIVES]);
// Persist the onboarding-completed flag.
let toml_path = crate::settings::Settings::default_toml_path();
if let Ok(Some(mut settings)) = crate::settings::Settings::load_toml(&toml_path)
&& !settings.profile_onboarding_completed
{
settings.profile_onboarding_completed = true;
if let Err(e) = settings.save_toml(&toml_path) {
tracing::warn!("failed to persist profile_onboarding_completed: {e}");
}
}
}
Ok(false) => {
tracing::debug!("profile not populated, skipping document sync");
}
Err(e) => {
tracing::warn!("profile document sync failed: {e}");
}
if path == paths::PROFILE && !append {
// Only sync derived identity docs when we have a non-append write
// and the new profile content parses as valid JSON. This helps
// prevent partial/poisoned writes from generating identity docs.
if let Err(e) = serde_json::from_str::<serde_json::Value>(&content) {
tracing::warn!(
"skipping profile document sync: profile content is not valid JSON: {e}"
);
} else {
match self.workspace.sync_profile_documents().await {
Ok(true) => {
tracing::info!(
"profile write: synced USER.md + assistant-directives.md"
);
synced_docs
.extend_from_slice(&[paths::USER, paths::ASSISTANT_DIRECTIVES]);
// Persist the onboarding-completed flag.
let toml_path = crate::settings::Settings::default_toml_path();
if let Ok(Some(mut settings)) =
crate::settings::Settings::load_toml(&toml_path)
&& !settings.profile_onboarding_completed
{
settings.profile_onboarding_completed = true;
if let Err(e) = settings.save_toml(&toml_path) {
tracing::warn!(
"failed to persist profile_onboarding_completed: {e}"
);
}
}
}
Ok(false) => {
tracing::debug!("profile not populated, skipping document sync");
}
Err(e) => {
tracing::warn!("profile document sync failed: {e}");
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +93
/// Reserved flag for whether the conversational profile onboarding has
/// been completed.
///
/// Intended semantics (not yet wired into the agent loop): this will be
/// set during the user's first interaction with the running assistant
/// (not during the setup wizard), after the agent builds a psychographic
/// profile via `memory_write`.
///
// TODO: Wire into agent loop — currently the system uses `is_populated()`
// (content check) to gate First Contact. This flag will eventually
// distinguish "never onboarded" from "onboarded but profile was reset".
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment says profile_onboarding_completed is "not yet wired into the agent loop", but this PR now sets it on profile writes in MemoryWriteTool. Update the comment to reflect current behavior (set when context/profile.json is written successfully / profile is populated), and clarify which storage layer (TOML vs DB settings) is considered the source of truth for this flag.

Suggested change
/// Reserved flag for whether the conversational profile onboarding has
/// been completed.
///
/// Intended semantics (not yet wired into the agent loop): this will be
/// set during the user's first interaction with the running assistant
/// (not during the setup wizard), after the agent builds a psychographic
/// profile via `memory_write`.
///
// TODO: Wire into agent loop — currently the system uses `is_populated()`
// (content check) to gate First Contact. This flag will eventually
// distinguish "never onboarded" from "onboarded but profile was reset".
/// Flag for whether the conversational profile onboarding has been
/// completed.
///
/// Current semantics: this is set once the assistant has successfully
/// created/populated the user's conversational profile (e.g., after
/// `MemoryWriteTool` writes a non-empty `context/profile.json` during
/// the user's first real interaction with the running assistant, not
/// during the setup wizard).
///
/// Source of truth: this flag is owned by the runtime settings layer
/// represented by this struct (persisted via `~/.ironclaw/settings.json`
/// and any backing DB-based settings), not by static TOML configuration.
/// TOML may provide a default/seed value, but the persisted settings
/// storage is authoritative for this mutable flag.

Copilot uses AI. Check for mistakes.
Comment on lines +228 to +229
// persisting. First Contact will still fire on next turn
// because has_rich_profile checks profile content, not existence.
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is now outdated: First Contact injection in workspace/mod.rs gates on has_profile_doc (any valid profile JSON exists), not on has_rich_profile/is_populated(). Update the note so future readers don’t assume onboarding will continue after a minimal-but-valid profile write.

Suggested change
// persisting. First Contact will still fire on next turn
// because has_rich_profile checks profile content, not existence.
// persisting it if they want it saved to disk. First Contact
// injection in `workspace/mod.rs` currently gates on `has_profile_doc`
// (i.e., the existence of any valid profile JSON), not on a "rich" profile.

Copilot uses AI. Check for mistakes.
Comment on lines +310 to +311
schema below. Also use `memory_write` to update `USER.md` with a human-readable markdown \
summary of what you've learned.
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIRST_CONTACT_INTRO instructs the agent to update USER.md via memory_write, but memory_write explicitly blocks writes to USER.md (protected identity file). This will cause onboarding tool calls to fail. Since USER.md is now derived from context/profile.json via sync_profile_documents, update the prompt to stop instructing memory_write to write USER.md (or introduce a dedicated safe tool for syncing).

Suggested change
schema below. Also use `memory_write` to update `USER.md` with a human-readable markdown \
summary of what you've learned.
schema below. `USER.md` will be generated from this JSON (e.g., via `sync_profile_documents`); \
do not attempt to write `USER.md` directly with `memory_write`.

Copilot uses AI. Check for mistakes.
Comment on lines +1001 to +1002
When you learn something notable, silently update `context/profile.json` and \
`USER.md` using `memory_write`. Merge new data — don't replace the whole file.\n\
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The agent instructions say to "silently update context/profile.json and USER.md using memory_write" and "Merge new data — don't replace the whole file." As written, this conflicts with memory_write protections (cannot write USER.md) and can easily lead to invalid JSON if the model tries to “merge” by appending. Adjust this guidance to: update only context/profile.json (read-modify-write with append=false), and rely on the post-write sync to regenerate derived docs.

Suggested change
When you learn something notable, silently update `context/profile.json` and \
`USER.md` using `memory_write`. Merge new data — don't replace the whole file.\n\
When you learn something notable, use `memory_read` + `memory_write` to update \
`context/profile.json` only (read-modify-write with `append=false`). Rely on the \
post-write sync to regenerate any derived docs.\n\

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

let profile: crate::profile::PsychographicProfile = match serde_json::from_str(&doc.content)
{
Ok(p) => p,
Err(_) => return Ok(false),
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sync_profile_documents treats any JSON parse failure as Ok(false) and drops the error detail. This makes it hard to debug malformed context/profile.json (it will silently skip syncing USER.md/directives). Consider logging the parse error (or returning a distinct error) so operators can see why syncing was skipped.

Suggested change
Err(_) => return Ok(false),
Err(e) => {
eprintln!(
"sync_profile_documents: failed to parse {} as JSON: {e}",
paths::PROFILE
);
return Ok(false);
}

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +77
5. If updates are needed, write the updated profile to `context/profile.json` using `memory_write`.
6. Also update `USER.md` with a refreshed markdown summary if the profile changed.
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The weekly evolution routine prompt tells the agent to memory_write context/profile.json (and USER.md) but doesn’t mention append=false. Since memory_write defaults to appending, this can corrupt the JSON profile on subsequent updates; and USER.md writes are blocked by the tool. Update the routine instructions to overwrite context/profile.json with append=false and rely on the profile-write sync to update USER.md/assistant-directives.

Suggested change
5. If updates are needed, write the updated profile to `context/profile.json` using `memory_write`.
6. Also update `USER.md` with a refreshed markdown summary if the profile changed.
5. If updates are needed, write the updated profile to `context/profile.json` using the `memory_write` tool with `append=false` so the file is fully replaced with the new JSON profile.
6. Do NOT write directly to `USER.md`. The system will automatically sync `USER.md` and assistant directives from `context/profile.json` after a successful profile write.

Copilot uses AI. Check for mistakes.
Comment on lines +806 to +808
if self.read(paths::HEARTBEAT).await.is_err() {
self.write(paths::HEARTBEAT, &profile.to_heartbeat_md())
.await?;
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seeds HEARTBEAT.md when read(paths::HEARTBEAT) returns any error. That can mask real DB/storage errors by treating them as “file missing” and attempting a write. Consider only seeding when the error is specifically WorkspaceError::DocumentNotFound (and propagate other errors).

Suggested change
if self.read(paths::HEARTBEAT).await.is_err() {
self.write(paths::HEARTBEAT, &profile.to_heartbeat_md())
.await?;
match self.read(paths::HEARTBEAT).await {
Ok(_) => {
// HEARTBEAT.md already exists; don't overwrite.
}
Err(WorkspaceError::DocumentNotFound) => {
self.write(paths::HEARTBEAT, &profile.to_heartbeat_md())
.await?;
}
Err(e) => {
// Propagate unexpected storage/DB errors.
return Err(e);
}

Copilot uses AI. Check for mistakes.
Comment on lines +1059 to +1064
if std::env::var("SKIP_WIZARD")
.map(|v| !v.is_empty() && v != "0" && v != "false")
.unwrap_or(false)
{
return None;
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SKIP_WIZARD parsing is case-sensitive and treats any non-empty value other than "0"/"false" as true, so values like "False" or "FALSE" will unexpectedly skip onboarding. The repo already has a shared parse_bool_env helper that normalizes case and validates values. Consider reusing that helper here or at least lowercasing the env var before comparison for consistent behavior.

Copilot uses AI. Check for mistakes.
- Weekly reflection prompt (what went well, what to improve)
- Periodic task/reminder check-in
- Regular cleanup of stale tasks or notes
- Weekly profile evolution (if the user has a profile in `context/profile.json`, suggest a Monday routine that reads the profile via `memory_read`, searches recent conversations for new patterns with `memory_search`, and updates the profile via `memory_write` if any fields should change with confidence > 0.6 — be conservative, only update with clear evidence)
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This skill suggests updating context/profile.json via memory_write, but doesn’t specify using append=false. Since the tool defaults to appending, following this guidance can corrupt the JSON profile after the initial write. Consider explicitly noting that JSON files like context/profile.json should be written with append=false (overwrite) after reading/merging.

Suggested change
- Weekly profile evolution (if the user has a profile in `context/profile.json`, suggest a Monday routine that reads the profile via `memory_read`, searches recent conversations for new patterns with `memory_search`, and updates the profile via `memory_write` if any fields should change with confidence > 0.6 — be conservative, only update with clear evidence)
- Weekly profile evolution (if the user has a profile in `context/profile.json`, suggest a Monday routine that reads the profile via `memory_read`, searches recent conversations for new patterns with `memory_search`, merges any changes into an in-memory profile object, and then updates the profile via `memory_write` with `append=false` (overwrite) if any fields should change with confidence > 0.6 — be conservative, only update with clear evidence)

Copilot uses AI. Check for mistakes.
@ilblackdragon
Copy link
Member

Closing in favor of #927 which builds on this work and takes it to completion. The NPA psychographic profiling system has been ported and refined — interactive onboarding replaced with a simpler bootstrap greeting flow, workspace seed files added, and routine advisor integrated.

Thank you @jayzalowitz for the original contribution and design work on the profiling schema, analysis framework, and onboarding approach! 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: new First-time contributor risk: high Safety, secrets, auth, or critical infrastructure scope: config Configuration scope: docs Documentation scope: setup Onboarding / setup scope: tool/builtin Built-in tools scope: workspace Persistent memory / workspace size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants