This file is the shared, authoritative agent guide for CodeIndex. It is used by Codex, Claude Code, and any other coding agent working in this repository.
AGENTS.md and CLAUDE.md are thin entry points only; they just redirect here. Any new rule, policy, workflow pointer, or contract note must be added to this file (or to a .codex/workflows/*.md workflow), not to AGENTS.md or CLAUDE.md. Tool-specific guidance goes under Tool-Specific Notes in this file. When this guide and an entry-point file disagree, this guide wins.
For implementation tasks:
- Read the agent entry point for your tool, if one was loaded automatically (
AGENTS.mdfor Codex,CLAUDE.mdfor Claude Code). Those files are thin entry points and only point here. - Read this file.
- Read the relevant workflow in
.codex/workflows/(see the Workflow Index below). - Read project-specific files referenced by that workflow, such as
SELF_IMPROVEMENT.md,DEVELOPER_GUIDE.md, orTESTING_GUIDE.md. - Read only the additional source files needed for the task.
Task-specific procedures live in .codex/workflows/. The directory is a shared workflow library for all coding agents, not only Codex.
See .codex/workflows/README.md for the workflow directory map and rule-placement guidance.
- issue fixing:
.codex/workflows/issue-fix.md - changelog fragments:
.codex/workflows/changelog-fragment.md - release changelog:
.codex/workflows/release-changelog.md - adversarial review:
.codex/workflows/adversarial-review.md - commit checks:
.codex/workflows/precommit.md - PR finalization and CI checks:
.codex/workflows/pr-finalize.md - related/new issue scope control:
.codex/workflows/issue-scope.md
For CodeIndex work, dogfood the project-built CodeIndex binary.
Do not use grep, rg, ripgrep, ag, ack, find, fd, locate, git grep, Python scripts, or a globally installed cdidx for code search or repository discovery. Use the locally built CodeIndex binary from this repository:
dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dllExamples:
dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll search SymbolExtractordotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll symbols --lang csharpdotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll inspect src/CodeIndex/Indexer/SymbolExtractor.cs
Before implementation, first check whether the local index already matches the current workspace:
dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll status --check --jsonIf the command exits 0 and reports index_matches_workspace: true, you may trust the existing .cdidx/codeindex.db without rebuilding it. If it exits with stale-index status or reports mismatched workspace_check counts, refresh the local index as documented by the project guidance. If the exact index-refresh command is documented elsewhere, use that documented command instead of inventing a new one.
This rule applies to code search and repository understanding. It does not forbid Git commands, build tools, test runners, package managers, or small shell checks that are not being used to search implementation code. Enforcement of forbidden tools is provided separately by the Claude and Codex guard hooks.
Command-search enforcement is tool-specific and adapter-driven:
- Codex uses
.codex/hooks.json, which invokes.codex/hooks/bash_guard.pyand.codex/hooks/permission_request_guard.py. - Claude Code uses
.claude/settings.json, which invokes.claude/hooks/bash-guard.py. - Both Bash guard adapters delegate shared command policy to
.agent_harness/command_guard_core.py; update the shared core for common command policy and review both adapters only when tool-specific behavior changes. - Codex uses the
codeindex_workspacepermission profile for workspace writes plus limited GitHub CLI network access togithub.comandapi.github.com. - Codex may use normal development GitHub CLI commands including
gh issue list/view/create/edit/comment,gh pr list/view/create/edit/comment/ready/close,gh repo view, andgh status. - Keep
gh auth,gh api,gh secret,gh release,gh repo create,gh repo fork,gh repo delete, andgh pr mergeblocked.gh apiis blocked because arbitrary REST/GraphQL calls can bypass subcommand-level policy intent;gh pr mergeis blocked because it mutates remote PR state in a high-risk way.
- Follow the repository-tracked
.claude/settings.jsonand.claude/hooks/bash-guard.pypolicy files when running in Claude Code. - Do not edit those policy files during ordinary implementation work unless the task is explicitly about Claude Code guard behavior.
- For shell search and navigation, prefer the built-in Grep / Glob tools or the locally built
cdidxbinary described above.
Keep changes focused on the requested issue or task.
Do not expand scope merely because an improvement is interesting.
Related issues may be included only when the relationship is clear and the same change naturally resolves them.
Use .codex/workflows/issue-scope.md for detailed scope rules.
Before editing, create a short plan. For normal issue work, the plan must be at most 10 lines and include only:
- files or areas likely to change;
- validation commands;
- main risks.
Do not spend excessive output tokens on planning.
Prefer the smallest correct fix. Add or update tests when behavior changes. Before adding a test method, check whether the scenario can share the closest existing method's setup and execution without obscuring a distinct contract. Prefer one cohesive method with a shared fixture for related read-only variants; keep separate methods when isolation, discovery identity, mutable state, or failure diagnosis materially benefits. Avoid unrelated refactors. Preserve public behavior unless the issue explicitly requires a change. When editing changelog content, verify that both English and Japanese entries are updated where the repository convention requires both.
- Treat documentation as part of the feature contract, not as optional cleanup.
- If a change affects user-visible behavior, CLI/MCP output, flags, error messages, install/release behavior, or contributor/agent workflow, update the matching docs in the same change. For CodeIndex this usually means
README.md,DEVELOPER_GUIDE.md,TESTING_GUIDE.md,SELF_IMPROVEMENT.md,INTEGRATION_POLICY.md,AGENT_GUIDE.md, or the relevant.codex/workflows/*.mdfile. Do not put agent workflow or policy content inAGENTS.md/CLAUDE.md; they are thin entry points. - Do not open or merge a PR with a user-visible change unless the required docs and changelog updates are present, or the PR body explicitly explains why no docs/changelog change is needed.
- Changelog entries are required for user-visible or behavior-changing work. For ordinary implementation PRs, write the changelog entry as a bilingual fragment under
changelog.d/unreleased/; do not updateCHANGELOG.mddirectly as the default path. - Use issue-based fragment names and
issues:front matter only when the work is actually tied to GitHub issues. For non-issue work, use a+<slug>.<category>.mdfragment and omitissuesentirely. Never writeissues: nullorissues: []. - Ordinary implementation PRs must not edit
CHANGELOG.md. Reserve directCHANGELOG.mdedits for release-preparation PRs that aggregate fragments into a release note. IfCHANGELOG.mdis edited, update both English and Japanese sections in the same commit, and only after confirming the work is a release-preparation change.
- Changelog entries are required for user-visible or behavior-changing work. For ordinary implementation PRs, write the changelog entry as a bilingual fragment under
- Follow
DEVELOPER_GUIDE.mdfor architecture and dependency policy. Production/runtime dependencies stay limited toMicrosoft.Data.Sqlite. - Follow
TESTING_GUIDE.mdfor test conventions, helpers, and parallelism rules. - Follow
SELF_IMPROVEMENT.mdwhen the task is about improvingcdidxitself. - If a change is user-facing, keep the matching tests, docs, and changelog entry in the same commit.
- Preserve cross-platform behavior when touching filesystem behavior, process execution, console output, or SQLite lifetime.
- Ask before implementing breaking, destructive, or user-workflow-changing changes.
Before each commit, follow .codex/workflows/precommit.md.
When an AI agent creates a commit, use git commit --no-gpg-sign so local signing-key passphrases do not block agent-driven commits.
Commit messages must be in English and include relevant issue numbers.
Prefer PR body Fixes #123 lines as the primary auto-close mechanism.
For adversarial review, follow .codex/workflows/adversarial-review.md.
Reviews must focus on blocking/actionable issues, not nitpicks.
Follow .codex/workflows/pr-finalize.md.
CI watching must be bounded. Do not loop indefinitely.
status --jsonand related JSON/MCP payloads currently expose the trust fields documented inREADME.mdandDEVELOPER_GUIDE.md, includingfold_ready,fold_ready_reason,graph_table_available,graph_data_current,index_complete,index_incomplete_reasons,issues_table_available,file_issues_data_current,migration_in_progress,sql_graph_contract_ready,sql_graph_contract_degraded_reason,hotspot_family_ready,hotspot_family_degraded_reason,language_readiness,csharp_symbol_name_ready,csharp_metadata_target_ready,csharp_metadata_target_degraded_reason,indexed_head_commit,worktree_head_changed,indexed_head_sha,indexed_head_branch,indexed_head_timestamp,commits_ahead_of_indexed_head,head_freshness,index_writer_version,index_newer_than_reader,index_newer_than_reader_reason,unknown_extension_file_count,unknown_extension_files,unknown_extension_files_truncated,unknown_extension_file_path_limit,unknown_extension_extension_counts,unknown_extension_category_counts,unknown_extension_groups,extractors,git_executable,path_case_sensitive,data_dir,data_dir_source,data_dir_mode,db_file_mode,database_permission_policy,database_permission_diagnostics,mac_profile,mac_profile_diagnostics,db_size_bytes,wal_size_bytes,db_pragma_settings(journal_mode,synchronous,wal_autocheckpoint,busy_timeout_ms,page_count,freelist_count,page_size,auto_vacuum),prepared_command_cache(count,capacity,hit_count,miss_count,eviction_count),maintenance_guidance, WAL checkpoint diagnostics (read_only_fallback,wal_checkpoint_attempted,wal_checkpoint_succeeded,wal_checkpoint_skipped_reason,wal_checkpoint_failure_reason,wal_checkpoint_busy,wal_checkpoint_log_page_count,wal_checkpoint_checkpointed_page_count,wal_checkpoint_remaining_page_count,read_only_immutable_fallback,wal_stale_snapshot_risk,wal_stale_snapshot_reason),symbol_kinds,symbols_by_language, status kind cap metadata (symbol_kind_limit,symbol_kind_name_limit,symbol_kind_total_count,symbol_kind_omitted_count,symbol_kind_names_truncated,symbols_by_language_kind_total_counts,symbols_by_language_kind_omitted_counts,symbols_by_language_kind_names_truncated),process,last_index_run,last_failed_or_partial_index_run,last_failed_or_partial_index_run.progress_persisted,last_failed_or_partial_index_run.recovery_hint,last_failed_or_partial_index_run.file_errors,last_workspace_freshened_at,hooks,hook_diagnostics,trust_overrides, MCP-onlymcp_session,mcp.rate_limit.bucket_limit,mcp.rate_limit.bucket_limit_rejection_count, and thestatus --check-onlystale_after_seconds/index_age_secondsthreshold audit fields andrepair_commands.status --checkrepair actions are structured byname,action,args,mutation_class,safety_class, andsafety_notes. Preserve compatibilityreasonas the first trigger and orderedreasonsas the complete trigger set. Deduplicate only exact structured identities; different targets, options, actions, mutation classes, or safety semantics must remain distinct. JSON and human output must use the same deduplicated order. Human output must preserve platform-aware shell quoting, visibly escape control characters to keep each repair action on one diagnostic line, and leave structured JSONargsunchanged.maintenance_guidance.fts_optimizationis the shared, read-only recommendation contract for status, explain, optimize preview, and optimize execution. Keeprecommended,action,reason,threshold_writes,observed_writes, andstatesynchronized; stale or unavailable snapshots must not recommend mutation.- A valid CLI
status --stale-after <duration>implies the workspace check. Check-mode JSON includesquery_context.check_mode(explicitorimplied_by_stale_after) andquery_context.stale_after_seconds; ordinary status JSON omitsquery_context. status --explainderives accepted top-level keys from the same source-generatedStatusResultserializer metadata asstatus --json, excludes ignored properties, and supports bounded dot-separated member paths without reading runtime values. Major readiness, trust, extension, maintenance, and cap-hit sections return structured meaning, source, dependencies, interpretation, and repair guidance; unknown input is sanitized and returns bounded valid candidates. Bounded status explain envelopes also omit database paths, timings, indexed HEADs, and stable-at timestamps.database_size_attributionis part of the synchronized status contract. Preserve its read-only main/WAL/SHM separation; exact logical reconciliation across object, freelist, and unexplained-residual bytes; table/index and page-type subtotals; 20-object/128-character sanitized bounds; and explicitavailable=false/ stableunavailable_reasonbehavior without zero-valued unavailable object metrics.- Explicit WAL truncate-checkpoint diagnostics must preserve SQLite's
(busy, log, checkpointed)result, treat non-zerobusyor positive remaining pages as unsuccessful with bounded machine reasons, accept(0, -1, -1)as the successful non-WAL no-op, and never expose raw exception text or paths. - When any readiness field is degraded, the CLI adds
degraded_root_cause,degraded_reason,recommended_action,alternative_action, andreadiness_degradations[].degraded_root_causeis the primary stable machine code;readiness_degradations[]lists every degraded field withroot_cause, human reason, and remediation strings. hotspot_family_degraded_reasoncurrently useshotspot_family_support_not_indexed,hotspot_family_metadata_stale,hotspot_family_disabled_at_index_time,partial_family_key_population, andhotspot_family_marker_fingerprint_incomplete; the incomplete marker fingerprint code means marker traversal hit safety caps and should stay synchronized with README / developer-guide recovery notes.issues_table_availablereports physicalfile_issuestable presence only.file_issues_data_currentreports whether the table is also stamped current for the active index generation.graph_table_availablereports a queryable persisted reference generation, whilegraph_data_current,reference_graph_complete, andindex_completereport current-generation coverage. Reference extraction is bounded at 50,000 lookup symbols, 20,000 lookup lines, 512 names per line, and 20,000 container candidates;reference_extraction_limits,reference_graph_incomplete_reasons, andreference_extraction_cap_hitspublish cap state, andlast_index_run.reference_extraction_cap_hitssnapshots it per run. Cap hits persist per file and propagate degraded, non-authoritative absence semantics to callers, callees, deps, and impact. Indexed Crystal, Groovy, Tcl, Prolog, orambiguous_plrows with a missing or stale extractor stamp adddynamic_reference_graph_contract_staletoreference_graph_incomplete_reasonsand keep graph readiness false until a normal index refresh rewrites them. A per-file extraction failure keeps successful graph rows queryable, stamps completeness false with boundedlast_failed_or_partial_index_run.file_errors, and returns exit11unlessindex --allow-partialexplicitly opts into exit0. While such file failures remain unresolved, a later scoped update automatically uses the normal incremental full-scan path so unrelated targets cannot clear the failure and successful recovery can restore every workspace-wide readiness contract without--rebuild.- Successful CLI full/update indexing, immediate status/workspace status, and MCP indexing/status must derive
index_complete,index_incomplete_reasons,reference_graph_complete, andreference_graph_incomplete_reasonsfrom the same persisted-readiness snapshot. Symbols-only runs and persisted file-size, symbol-count, reference-count, extractor-failure, or reference-cap evidence make the generation incomplete. Legacy databases keep the complete compatibility default only when persisted rows do not prove an omission. index_writer_versionrecords thecdidxversion that last wrote to the DB (stamped intocodeindex_metaascdidx_writer_versionon every full scan, update, and MCP index).index_newer_than_readerflips totruewhenever any persisted numeric contract stamp incodeindex_meta(or unknownPRAGMA user_versionreadiness bits) exceeds the current binary's compiled maximum, so an older CLI re-opening a DB written by a newer CLI degrades loudly with an audit trail instead of silently dropping back to text-search fallbacks.index_newer_than_reader_reasonenumerates the specific newer-than-reader stamps.statusalso surfaces indexed-HEAD freshness viaindexed_head_sha,indexed_head_branch,indexed_head_timestamp,commits_ahead_of_indexed_head, and the compacthead_freshnesssummary. They are stamped bycdidx indexon every successful run (full scan AND partial update, distinct fromindexed_head_commitwhich is full-scan only) on a best-effort basis (never blocks an otherwise-successful index) and omitted on non-git workspaces, detached HEAD (branch only), or legacy DBs created before this contract.worktree_head_changedcompares runtime HEAD with this latest stamp when available and falls back toindexed_head_commitonly for legacy DBs.head_freshness.state=freshrequiresstatus --checkto match the workspace,fresh_but_incompletekeeps matching-workspace freshness distinct from incomplete extraction coverage, andstate=head_currentonly means the runtime HEAD matches theindexed_headselected byindexed_head_source.statusalso surfaces unknown-extension scan coverage viaunknown_extension_file_count, stamped by successful full-repository index runs (cdidx index <projectPath>and MCPindex_project) as the number of non-indexed files with non-empty extensions that do not map to a known language. Current scans also stampunknown_extension_filesas a path sample bounded byunknown_extension_file_path_limititems and the string-list decoded-character budget,unknown_extension_files_truncatedwhen more paths existed than were emitted for either bound, andunknown_extension_file_path_limitas the item cap rather than a guarantee that that many paths are returned. Newer scans also exposeunknown_extension_extension_counts,unknown_extension_category_counts, andunknown_extension_groups; groups classify common non-code buckets such as repository metadata, licenses, binary assets, configuration, structural metadata, and language-support candidates, and includerecommended_actionvalues ofignore_configuration,first_class_structural_extraction, orlanguage_support. These fields are omitted on legacy DBs or before a current full scan has stamped them.statusalso surfaces extractor plugin and pattern-config runtime diagnostics viaextractors, including loaded counts, the zero parentretained_load_context_count, the isolated-workerload_context_lifecycle, skipped file counts, and a bounded diagnostics list for incompatible or malformed plugin/pattern files.extractors.pattern_configs[]reports accepted sidecars with sanitizedpath,source(workspaceoruser), normalizedlanguage, andrule_count; workspace discovery stops at its explicit root and path identity follows the live filesystem case policy. Pattern rules and their 128-rule budget live in immutable workspace snapshots; reindex replaces the owning snapshot, and timeout cooldowns/diagnostics remain workspace-scoped.extractors.snapshot_scopeand highest-firstregistration_precedenceexpose snapshot selection and the fixedbuilt_in > user_plugin > user_pattern > workspace_plugin > workspace_patternresolution order; replacing one workspace snapshot never mutates another. Diagnostic paths, categories, and messages are sanitized before output;diagnostics[].categoryis the stable machine-readable failure code.statussurfaces worker-discovered post-extraction hook manifests and callback budgets throughhooks[], including stable assembly-qualifiedhooks[].id,hooks[].callback_budget_ms, and the worker-onlyhooks[].load_context_lifecycle. Assembly loading, module initialization, type inspection, and constructor validation occur only in deadline-, memory-, and output-bounded discovery workers, which are terminated after returning a manifest.hook_diagnostics[]reports sanitized discovery and callback diagnostics such as candidate-limit truncation, assembly load failure, constructor failure, callback failure, and timeout;hook_diagnostics[].categoryis the stable machine-readable failure code andhook_diagnostics[].hook_idlinks a concrete-hook failure to the corresponding manifest. Index runs enforceCDIDX_HOOK_CALLBACK_BUDGET_MS(default: 5000 ms) on scratch copies, discard timed-out mutations, and disable only the timed-out assembly-qualified hook ID for the remainder of the current run.statusalso surfaces accepted trust-boundary environment overrides throughtrust_overrides[]. Entries includekind,environment_variable, sanitizedvalue, optional sanitizedpath, andmessage; current entries coverCDIDX_TRUST_WORKSPACE_PLUGINSworkspace plugin discovery,CDIDX_HOOKS_DIRhook directory overrides, and the absoluteCDIDX_GIT_EXECUTABLEexecutable override.git_executablereports the selected source, acceptance, stable reason, sanitized path, owner-only-write result, Unix mode, owner category, owner/ancestor trust (including POSIX sticky-ancestor and Windows ACL policy), and boundedgit --versionexecution-probe result even when an explicit Git override is rejected. Buildgit_executableand its matchingtrust_overrides[]entry from the same resolution snapshot. Keep this visible runtime field registered instatus --explainand in both README status-field tables.statusalso surfaces.cdidxdata-directory permissions viadata_dir_modeon POSIX filesystems. New.cdidxdata directories are forced to0700; the field is omitted on Windows, URI DBs, or when the directory mode cannot be inspected.statusalso surfaces database Unix-mode hardening throughdb_file_mode,database_permission_policy, and optionaldatabase_permission_diagnostics[]. The defaultbest_effortpolicy keeps SQLite-capable FUSE/network mounts usable while emitting the stabledatabase_permission_hardening_failedwarning and support-safe operation/target/reason/remediation entries forIOException,UnauthorizedAccessException, andNotSupportedException.CDIDX_DB_PERMISSION_POLICY=strictmakes every applicable database/WAL/SHM mode operation mandatory and fails with the same stable error code plus remediation. Windows and explicit SQLite file URIs skip this POSIX-only enforcement.statusalso surfaces filesystem case-sensitivity viapath_case_sensitive, stamped on every successfulcdidx indexrun (full scan AND partial update, plus MCP-driven indexes) fromcore.ignorecase+ a live filesystem probe.truemeans the volume is case-sensitive (Foo.csandfoo.csare distinct);falsemeans case-insensitive. Omitted on legacy DBs that predate the stamp. Use it to audit path-equality decisions on case-sensitive APFS, WSL NTFS / dev-drive, and ReFS mounts where the prior OS-keyed heuristic could mis-classify the workspace (#1546).statusalso surfaces Linux mandatory-access-control context viamac_profilewhen/proc/self/attr/currentor/proc/self/attr/execindicates an AppArmor or SELinux profile. If proc attribute reads fail on Linux,mac_profile_diagnostics[]reports boundedpath,category, andmessageentries so users can distinguish "no profile" from "profile detection failed" (#1768, #3480).statusalso surfaces DB/WAL size, per-language symbol-kind histograms, current process heap/GC/working-set metrics, and the last successful index run metadata.processis captured at status-call time;last_index_runis persisted at the end of successful CLI and MCP index runs and can include a peak-memory summary when CLI--memory-tracewas used.last_index_run.bytes_read_skipped_file_countandbytes_read_incompletereport whether unreadable files were omitted from thebytes_readtotal.last_index_run.diagnostics,diagnostic_count, anddiagnostics_truncatedcarry bounded warnings for best-effort index metadata writes that failed after the index data itself was successfully written.last_workspace_freshened_atis the latest successful index/update timestamp and can be newer thanindexed_atwhen a partial or no-op update confirms freshness without rewriting indexed file rows.- MCP
statusalso surfaces session diagnostics viamcp_sessionand rate limiter bucket cap diagnostics viamcp.rate_limit.bucket_limit/mcp.rate_limit.bucket_limit_rejection_count.mcp_sessionis not persisted DB state; it includes the currentlog_level, bounded capturedroots, optionalclient_info, bounded optionalclient_capabilities, and an always-presentmetricsobject.mcp_session.metrics.enabled=falseexplicitly identifies an unconfigured sink. An enabled sink reportspath,max_bytes,bytes_written,disposed,degraded,queue_capacity,queue_depth,queued_event_count,written_event_count,dropped_event_count,queue_full_drop_count,serialization_failure_count,write_failure_count,rotation_failure_count,batch_flush_count,consecutive_failure_count,recovery_count, and bounded optionalnext_retry_at,last_recovery_at, andlast_failure. Metrics degradation is telemetry state and does not by itself change MCP ping/HTTP liveness status. When audit logging is configured,mcp_session.audit_logand ping'saudit_logalso reportqueued_record_countandwritten_record_countalongside the existing queue, drop, write, and rotation diagnostics. Shutdown-only abandoned counts and timeout state remain in the final sink shutdown result and bounded stderr diagnostic because the server is no longer available to answer status or ping after shutdown. When advertised roots are capped,roots_truncated,root_count,root_limit, androot_uri_length_limitdescribe the truncation. When client capabilities are capped,client_capabilities_truncated,client_capabilities_truncation_reason,client_capabilities_serialized_bytes,client_capabilities_byte_limit, andclient_capabilities_depth_limitdescribe the retained diagnostic subset.mcp.rate_limit.bucket_limitis the process-local cap across normalized(partition, caller)buckets: every direct call uses one fixed caller-wide coarse partition, canonical known tools additionally use secondary per-tool partitions, and unknownbatch_queryslots share one fixed invalid-slot partition per caller.mcp.rate_limit.bucket_limit_rejection_countcounts calls denied because creating a new bucket would exceed that cap. After an immediate expired-bucket prune,retry_after_msreports the earliest point when every charged token and required capacity constraint can admit the retry (#4547). - Keep
README.md,DEVELOPER_GUIDE.md, and this file synchronized if this contract changes.
- Dockerfile multi-stage builds now emit
call-kind reference edges forFROM <stage> AS <new>andCOPY --from=<stage>when the source name matches a named stage in the same file, socallersandimpactcan follow stage dependencies instead of treating intermediate stages as unused. - Rust macro invocations (
name!(...)/name![...]/name!{...}) now emitcall-kind reference edges, while themacro_rules!declaration keyword remains suppressed so macro definitions do not double-count as calls.
If an operation cannot be completed in the current environment, report:
- what failed;
- what you tried;
- the exact command or manual action needed;
- why it is needed.
If the user requested yellow text for handoff actions, use ANSI yellow when the terminal supports it.