[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71DZHT0Q9YYC3Z3MN9VDP2N":3,"skill-same:01M71DZHT0Q9YYC3Z3MN9VDP2N":73},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":6,"ownerHtmlUrl":49,"ownerBlogUrl":50,"ownerDescription":51,"updatedAt":52,"install":53,"resources":66},"01M71DZHT0Q9YYC3Z3MN9VDP2N","learn",null,"Turn reusable findings into scoped policies or insight files.","indigoai-us\u002Fhq-core","indigoai-us","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F122242446?v=4","01M71DTQ5NGD10GT1Z7E6XKWC5","HQ","01M71DTQ6BKSYYBNC9RAKT3YDK",94,"MIT","https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Flearn\u002FSKILL.md",[],[5,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"files","insight","scoped","frontmatter","stat","rm","mkdir","ls","scripts","reusable","qmd","edit","write","read","grep","Bash","git","core","policy","2026-11-10 10:12:30",false,"---\nname: learn\ndescription: Turn reusable findings into scoped policies or insight files.\nallowed-tools: Read, Write, Edit, Grep, Glob, Bash(qmd:*), Bash(grep:*), Bash(mkdir:*), Bash(date:*), Bash(ls:*), Bash(bash:*), Bash(git:*), Bash(rm:*), Bash(stat:*), Bash, Bash(bash core\u002Fscripts\u002Fread-policy-frontmatter.sh:*)\n---\n\n# Learn - Automated Learning Pipeline\n\nCapture a learning, classify it, and inject the rule directly into the file it governs (or the insight into the insight tree).\n\nCalled programmatically by `\u002Fexecute-task` and `\u002Frun-project` after task completion or failure. Also callable manually with `--hard` flag for hard-enforcement rules (formerly `\u002Flearn --hard`). `\u002Fhandoff` and `\u002Fcheckpoint` call this for session insights.\n\n**Input:** the user's argument — structured JSON event data, free text description, or empty\u002F\"auto\" for hook-triggered mode.\n\n## Core Principle\n\n**Two output types, one pipeline.** Content is classified (Step 1.5) and routed to the appropriate format:\n\n### Rules → Policy files (operational directives)\n\n| Scope | Target directory | Format |\n|-------|-----------------|--------|\n| Company | `companies\u002F{co}\u002Fpolicies\u002F{slug}.md` | Policy file (YAML frontmatter + Rule + Rationale) |\n| Repo | `repos\u002F{pub\\|priv}\u002F{repo}\u002F.claude\u002Fpolicies\u002F{slug}.md` | Policy file |\n| Command (operator) | `personal\u002Fpolicies\u002F{slug}.md` (scope: command) | Policy file |\n| Global \u002F personal (**default**) | `personal\u002Fpolicies\u002F{slug}.md` (scope: global) | Policy file |\n| Worker (legacy) | `core\u002Fworkers\u002F*\u002F{id}\u002Fworker.yaml` | Instructions block `## Learnings` |\n\n> **`\u002Flearn` never writes to `core\u002Fpolicies\u002F`.** `core\u002F` is release-shipped scaffold, replaced wholesale by `\u002Fupdate-hq` — anything written there is lost on the next upgrade (hard policy `hq-customizations-live-in-personal-or-company`). Operator-universal learnings go to `personal\u002Fpolicies\u002F`, which the policy trigger hook (`inject-policy-on-trigger.sh`) reads directly — no `core\u002Fpolicies\u002F` mirror — so they still load as global at SessionStart while living in an upgrade-safe location. A genuine product-level core policy (one that should ship to *every* HQ install) is **not** captured via `\u002Flearn`: author it locally, then publish through the hq-core staging repo + `\u002Fpromote-hq-core` (see `staging-promotion-required`). If a learning is judged truly core, `\u002Flearn` stops and points you at that pipeline rather than writing into `core\u002F`.\n\n### Insights → Insight files (educational understanding)\n\n| Scope | Target directory | Format |\n|-------|-----------------|--------|\n| Global \u002F repo | `workspace\u002Finsights\u002Fglobal\u002F{slug}.md` | Insight file (YAML frontmatter + Insight + Context) |\n| Company | `companies\u002F{co}\u002Fknowledge\u002Finsights\u002F{slug}.md` | Insight file in the company's plain synced knowledge directory |\n| Tool | `workspace\u002Finsights\u002Ftools\u002F{slug}.md` | Insight file |\n| Conceptual | `workspace\u002Finsights\u002Fconcepts\u002F{slug}.md` | Insight file |\n\nSee `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Finsights-spec.md` for the insight file format.\n\n**Before creating:** always scan existing policies\u002Finsights for updates (Step 4.5). Update > duplicate.\n\n## Step 0: Load Policies (frontmatter-only gate)\n\nBefore processing, load applicable policies with minimal context burn:\n\n1. Collect eligible paths in the global policy set except `example-policy.md`, keep their order, and split the stable file order into consecutive chunks of at most 40 files. For each chunk, run `bash core\u002Fscripts\u002Fread-policy-frontmatter.sh {file1} {file2} ...` once with each path as a separate argument. Keep each call under 30 KB of output. The first output block belongs to the chunk’s first file argument; each later block belongs to the path named by its preceding `# --- policy-file: \u003Cpath> ---` separator.\n2. Note `enforcement: hard` titles. Only Read the `## Rule` section of hard-enforcement policies if one looks relevant to the current learning\n\nSkip full policy body loads — the frontmatter contains enough metadata for the learn pipeline.\n\n## Step 1: Parse Input\n\nThree input modes:\n\n### Mode 1: Hook-Triggered (auto\u002Fempty input)\n\nIf the argument is empty or \"auto\":\n\n1. Check for `workspace\u002Flearnings\u002F.observe-patterns-latest.json`\n2. If file exists, read it and extract the `observations` array\n3. For each observation in the array:\n   - Extract `pattern_type`, `confidence`, `description`, `severity`, `evidence`\n   - Generate a structured learning event with `source: \"hook-observation\"`, `scope: \"global\"` (or inferred from pattern type)\n   - Process through Steps 2–9 (extract rules, classify scope, dedup, inject, etc.)\n4. Delete the file after processing all observations\n5. Report each learning processed\n\nExample `.observe-patterns-latest.json`:\n```json\n{\n  \"metadata\": {\n    \"created_at\": \"2026-03-07T21:35:00Z\",\n    \"session_end_timestamp\": \"20260307-213500\",\n    \"git_branch\": \"main\",\n    \"git_commit\": \"abc1234\",\n    \"project_context\": \"hq\"\n  },\n  \"observations\": [\n    {\n      \"pattern_type\": \"back-pressure-retry\",\n      \"confidence\": 0.8,\n      \"description\": \"Git log shows fixup\u002Famend commits\",\n      \"severity\": \"high\",\n      \"evidence\": \"fixup commits in recent history\",\n      \"recommendation\": \"Extract pattern about what caused retry\"\n    }\n  ]\n}\n```\n\nNote: in Codex runtime, the observe-patterns hook is not wired up, so this mode typically returns empty — fall through to manual modes.\n\n### Mode 2: Structured JSON (from \u002Fexecute-task, \u002Frun-project, \u002Fhandoff, \u002Fcheckpoint)\n\nIf input is a JSON object:\n```json\n{\n  \"task_id\": \"TASK-001\",\n  \"project\": \"my-project\",\n  \"source\": \"back-pressure-failure|user-correction|success-pattern|task-completion|build-activity|hook-observation|session-insight\",\n  \"severity\": \"critical|high|medium|low\",\n  \"scope\": \"global|worker:{id}|command:{name}|knowledge:{path}|project:{slug}\",\n  \"workers_used\": [\"backend-dev\"],\n  \"back_pressure_failures\": [{\"worker\": \"frontend-dev\", \"check\": \"lint\", \"error\": \"...\"}],\n  \"retries\": 0,\n  \"key_decisions\": [\"...\"],\n  \"issues_encountered\": [\"...\"],\n  \"patterns_discovered\": [\"...\"]\n}\n```\n\nParse it and proceed to Step 1.5.\n\n### Mode 3: Free Text (manual invocation or \u002Flearn --hard delegation)\n\nParse for keywords to determine scope. Generate rule statement from the description. Proceed to Step 1.5.\n\n### Mode 4: Batch Input (from \u002Fhandoff or any caller passing an array)\n\nIf the input starts with `[` (JSON array), enter batch mode:\n\n**Batch input format:**\n```json\n[\n  {\"type\": \"rule|insight\", \"content\": \"...\", \"scope\": \"global|company:{co}|...\", \"source\": \"...\"},\n  ...\n]\n```\n\n**Batch processing rules:**\n\n1. **Detect batch input:** if the input is a JSON array (starts with `[`), enter batch mode\n2. **Run qmd search dedup ONCE for all items:** concatenate all item `content` fields as a single query string for a single `qmd search` call (BM25; never `qmd vsearch` in-turn — first use downloads a ~300MB–2GB GGUF model and has stalled Windows prompts for hours), then match results against each item individually\n3. **Process each item through Steps 2–6 normally:** extract rules, classify scope, create\u002Fupdate policy files — one item at a time using the shared dedup results\n4. **Write a single event log entry** covering all items processed in the batch\n5. **Backward compatibility:** all existing modes (1, 2, 3) work exactly as before — batch is a new detection branch only\n\n## Step 1.5: Classify Content Type\n\nDetermine if input is an operational rule or an educational insight:\n\n| Signal | Type | Route to |\n|--------|------|----------|\n| NEVER\u002FALWAYS\u002Fcondition→action | rule | Policy file (Steps 2–6) |\n| \"why X works\", \"pattern behind\", conceptual explanation | insight | `workspace\u002Finsights\u002F` or `companies\u002F{co}\u002Fknowledge\u002Finsights\u002F` (Step 5b) |\n| User correction (`\u002Flearn --hard`) | rule | Policy file (always) |\n| `back_pressure_failures` | rule | Policy file (always) |\n| `patterns_discovered` (educational) | insight | Insight file (Step 5b) |\n| `source: \"session-insight\"` | insight | Insight file (always — from `\u002Fhandoff` or `\u002Fcheckpoint` step 0c) |\n\n**Default:** if ambiguous, route as policy (existing behavior). Insights are opt-in.\n\nIf content type is **insight**, skip Steps 2 and 5 (rule extraction and policy creation). Proceed to Step 3 for scope classification, Step 4 for dedup, then Step 5b for insight file creation.\n\n## Step 2: Extract Rules\n\nFrom structured input, generate rules:\n\n- `back_pressure_failures` → `NEVER: {anti-pattern that caused failure}` (scope: worker:{id})\n- `retries > 0` → Rule about what caused retry and how to avoid it\n- `key_decisions` → `ALWAYS: {pattern}` if broadly applicable\n- `issues_encountered` → Scoped rule to prevent recurrence\n- `patterns_discovered` → `ALWAYS: {pattern}` for success patterns\n\nFrom free text:\n- Extract the core rule in NEVER\u002FALWAYS\u002Fcondition→action format\n\nIf no meaningful rules can be extracted (task completed cleanly, no failures, no notable patterns), skip injection — log to event log only.\n\n## Step 3: Classify Scope & Resolve Target\n\nFor each extracted rule, determine scope (most specific wins):\n\n| Signal | Scope | Policy directory |\n|--------|-------|------------------|\n| Related to specific company | `company` | `companies\u002F{co}\u002Fpolicies\u002F` |\n| Related to specific repo | `repo` | `repos\u002F{pub\\|priv}\u002F{repo}\u002F.claude\u002Fpolicies\u002F` |\n| Error in specific command | `command` | `personal\u002Fpolicies\u002F` (with `scope: command`) |\n| Failure in specific worker | `worker` | `core\u002Fworkers\u002F*\u002F{id}\u002Fworker.yaml` instructions block (legacy, still supported) |\n| Active session company (when no explicit repo, command, or global scope) | `company` | `companies\u002F{co}\u002Fpolicies\u002F` |\n| Universal pattern (**default** when no company\u002Frepo\u002Fsession context) | `global` | `personal\u002Fpolicies\u002F` |\n| User correction via \u002Flearn --hard | From context; default to active session company, then global | Detected scope directory |\n\n**`\u002Flearn` does not target `core\u002Fpolicies\u002F`.** The global\u002Fcommand rows route to `personal\u002Fpolicies\u002F` — operator-owned and upgrade-safe — and the policy trigger hook reads `personal\u002Fpolicies\u002F` directly (no `core\u002Fpolicies\u002F` mirror), so each entry still loads as a global\u002Fcommand policy. Reserve `core\u002Fpolicies\u002F` for release-shipped policies authored through the staging → `\u002Fpromote-hq-core` pipeline. If a rule is genuinely product-core (must ship to every HQ install), stop and direct the user to that pipeline; never Write it into `core\u002F`.\n\n**Primary output = policy files.** The canonical format for persistent rules is structured policy files (per `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`). Worker.yaml injection is still supported for worker-specific learnings.\n\n**Resolve company\u002Frepo context** (strongest signal wins):\n- An explicit scope in the input or user request wins. Honor explicit `repo`, `command`, or `global` scope rather than the session default; explicit global scope targets `personal\u002Fpolicies\u002F`. Verify an explicit company slug against the manifest.\n- From the current working directory — the **leaf** `companies\u002F\u003Cslug>\u002F` segment (the last one in the path, not the first)\n- From `prd.json` metadata if in project context\n- From `companies\u002Fmanifest.yaml` repo lookup if in repo context\n- From worker path if worker-scoped (`companies\u002F{co}\u002Fworkers\u002F` → `{co}`)\n- From the active session: `bash core\u002Fscripts\u002Fhq-session.sh get company_slug`. For free-text and `\u002Flearn --hard` input with no explicit repo, command, or global scope, default to this active company and target `companies\u002F{co}\u002Fpolicies\u002F`.\n\n**Verify every resolved slug, including the active session `company_slug`, exists in `companies\u002Fmanifest.yaml`** before targeting `companies\u002F{co}\u002Fpolicies\u002F`. If the session value is absent or invalid, do not treat it as company context.\n\n**Never silently fall back to a global target for a company-specific learning.** A global target (→ `personal\u002Fpolicies\u002F`) is correct ONLY when the rule is genuinely universal to this operator's HQ. If a rule is clearly company-specific but the company can't be resolved unambiguously, **stop and ask which company** — do not default the write into `personal\u002Fpolicies\u002F` (and never into `core\u002Fpolicies\u002F`). A misrouted company policy syncs into the wrong tenant vault on the next `hq-sync` (HQ-Pro), a category-1 cross-company bug. See `core\u002Fpolicies\u002Fhq-company-scoped-writes-verify-company.md`.\n\n## Step 4: Dedup Check\n\n**Primary (if qmd available):**\n```bash\nqmd search \"{rule text}\" --json -n 5\n```\n\nIn-turn dedup is BM25 (`qmd search`) only. **Never run `qmd vsearch`, `qmd query`, `qmd embed`, or `qmd pull` during `\u002Flearn`.** Those subcommands auto-download local GGUF models (~300MB–2GB) on first use and have stalled a single Windows HQ prompt for more than two hours. If embeddings are already cached under `~\u002F.cache\u002Fqmd\u002Fmodels\u002F` you may still prefer `qmd search` here — keyword overlap is enough to catch duplicate rules. Kick off `hq index background` out of band if semantic search is wanted later.\n\nCheck results for similarity to the new rule:\n- Similarity > 0.85 → **Skip** (already captured somewhere)\n- Similarity 0.6–0.85 → **Merge** (update existing rule to be more precise)\n- Similarity \u003C 0.6 → **Add new**\n\n**Fallback (if qmd unavailable):**\nUse Grep to search for key terms from the rule across the policy directories:\n- Pattern: key terms from the rule (2-3 significant words)\n- Files: `*.md` in `companies\u002F*\u002Fpolicies\u002F`, `personal\u002Fpolicies\u002F`, `core\u002Fpolicies\u002F` (the release-shipped set), and any repo policy dirs\n- If matching content found → review and decide whether to merge or skip\n\nRecord the dedup action for the Step 9 report. Do not print it to the user in the default HQ style.\n\n## Step 4.5: Scan Existing Policies\n\nBefore creating a new rule, check if an existing policy file already covers this topic:\n\n1. **Resolve policy directories** based on scope:\n   - Company scope → scan `companies\u002F{co}\u002Fpolicies\u002F` (skip `example-policy.md`)\n   - Repo scope → scan `{repoPath}\u002F.claude\u002Fpolicies\u002F`\n   - Global\u002Fcommand scope → scan `personal\u002Fpolicies\u002F` (the write target) and `core\u002Fpolicies\u002F` (the release-shipped set)\n\n2. **Search for matching policies:**\n   ```bash\n   # Grep policy titles and rules for keyword overlap\n   grep -rl \"{key terms from rule}\" {policy_dir}\u002F*.md 2>\u002Fdev\u002Fnull\n   ```\n   Also check `qmd search` results from Step 4 for hits in policy files.\n\n3. **If matching policy found:**\n   - Read the policy file\n   - **Update** the existing policy: append to `## Rule` section, bump `version`, update `updated` date\n   - If new learning contradicts existing policy, flag for user review instead of auto-merging\n   - Set `dedup_action: \"merged-into-policy\"` in event log\n\n4. **If no matching policy found:**\n   - Proceed to Step 5 (create new rule)\n   - For company\u002Frepo\u002Fglobal scoped rules, prefer creating a **policy file** (per `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`) over injecting into worker.yaml. Policy files are the canonical format for persistent rules. **Never** write a learned rule into `.claude\u002FCLAUDE.md` \u002F `AGENTS.md` — the charter is release-shipped scaffold, not a learning store (policy `learned-rules-never-in-claude-md`)\n\n## Step 5: Create or Update Policy File (rule content type)\n\n### Primary: Policy File (company\u002Frepo\u002Fglobal\u002Fcommand scopes)\n\nIf Step 4.5 found a matching policy → update was already handled. Otherwise, create a new policy file.\n\n**Target directory:**\n- Company scope → `companies\u002F{co}\u002Fpolicies\u002F{slug}.md`\n- Repo scope → `repos\u002F{pub|priv}\u002F{repo}\u002F.claude\u002Fpolicies\u002F{slug}.md`\n- Command scope (operator-authored) → `personal\u002Fpolicies\u002F{slug}.md` (scope: command)\n- Global scope → `personal\u002Fpolicies\u002F{slug}.md` (scope: global) — **default for non-company\u002Fnon-repo rules**\n\n> Never Write a `.md` into `core\u002Fpolicies\u002F` from `\u002Flearn` — it is lost on `\u002Fupdate-hq` and is now mechanically blocked by `protect-core.sh` (the block message points back here). `personal\u002Fpolicies\u002F` entries are read directly by the policy trigger hook (no `core\u002Fpolicies\u002F` mirror), so they load identically as global\u002Fcommand policies. Genuine product-core policies ship via the staging → `\u002Fpromote-hq-core` pipeline only.\n\n**Create the directory if needed:**\n```bash\nmkdir -p {target_directory}\n```\n\n**Policy file format** (per `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`):\n\n```markdown\n---\nid: {scope-prefix}-{slug}\ntitle: {Rule title}\nwhen: {boolean trigger expr over context words, or `always`}\non: {[PreToolUse, PostToolUse, UserPromptSubmit, AssistantIntent] | [SessionStart]}\nenforcement: {hard|soft}\n# company: \u003Cslug>   # optional; see Company field below. Uncomment only with one real manifest slug\npublic: {true|false — see Lifecycle stamping below}\nstatus: active\nversion: 1\ncreated: {YYYY-MM-DD}\nupdated: {YYYY-MM-DD}\nsource: {back-pressure-failure|user-correction|success-pattern|task-completion|hook-observation}\nnotes: {only when a global or command policy gets public: false — the portability reason}\nretire_when: {workaround rules only — the fix condition in plain words}\nlast_confirmed: {workaround rules only — today's date, YYYY-MM-DD}\n---\n\n## Rule\n\n{Rule in imperative form}\n\n## Rationale\n\n{Mechanism and trade-offs only: why the rule works and what it costs}\n\n## Provenance\n\n{Company slugs, customer or person names, ticket or PR numbers, and specific dates from the source learning. Omit the section when there are none.}\n```\n\n**Lifecycle stamping (set at authoring time, no confirmation prompt):** `\u002Flearn`\nfills these fields itself and never asks the user about them\n(`hq-learn-auto-no-confirmation`).\n\n- `status: active` on every policy `\u002Flearn` writes.\n- `public:`\n  - Company- and repo-scoped policies: always `public: false`.\n  - Global and command-scoped policies: `public: true` only when the `## Rule`\n    body passes the portability check in `hq-public-policy-rule-body-generic`:\n    no HQ-internal paths (HQ top-level directories such as the company, personal,\n    workspace, repos, or core trees, the `.claude` directory, home-relative or\n    absolute paths), no company, worker, or repo slugs, and no internal infra\n    names (account IDs, bucket, stack, or host names). Otherwise\n    `public: false` with the failing check in `notes:`, for example\n    `notes: public false because the Rule names an HQ-internal path`.\n- Workaround rules: classify the learning as a workaround when it works around\n  a named tool, a specific version, or a known defect. Signals are a tool or\n  package name with a version number, or the words bug, defect, regression,\n  workaround, upstream issue, or until a named tool fixes something. For these,\n  write `retire_when:` with the fix condition in plain words (for example\n  `retire_when: the CLI ships the native fix for the stale session cache`; no\n  quotes or regex symbols, per `policies-spec.md`) and `last_confirmed:` with\n  today's date. Rules that are not workarounds omit both fields.\n- Provenance routing: when the source learning mentions a company slug, a\n  customer or person name, a ticket or PR number, or a specific date, write that\n  text under `## Provenance`. `## Rationale` keeps only the mechanism and the\n  trade-offs. `## Provenance` is archival: it is not injected and does not count\n  toward the hard-body size limit.\n\n**Enforcement mapping:**\n- `source: user-correction` → `enforcement: hard`\n- `severity: critical` → `enforcement: hard`\n- Everything else → `enforcement: soft`\n\n**`when:` \u002F `on:` trigger (just-in-time injection):**\n- `when:` is a boolean expression over an OPEN token set — any word that appears in the relevant command\u002Fprompt\u002FAI-message text is a live token (`git && push`, `refactor`, `supabase`, `.tsx`, `\u002Fdeep-plan`, `secret || credential`). Pick the word(s) that naturally show up when the rule is relevant. Operators: `&& || ! ( )`. Full grammar + fact derivation: `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`.\n- **Every token needs an operator between it and the next one.** `when: merge || pull request` is not \"merge, or a pull request\" — nothing joins `pull` and `request`, so the expression is rejected outright. Write `merge || (pull && request)`, or pick the single word you meant. Quoted phrases, flags, and globs are outside the charset and are rejected the same way. `validate-policy-frontmatter.sh` blocks this at write time; `bash core\u002Fscripts\u002Flint-policy-triggers.sh` finds any that predate the check.\n- `on:` is where it is evaluated. Default for a real trigger: `[PreToolUse, PostToolUse, UserPromptSubmit, AssistantIntent]` (the `when:` does the filtering). `[SessionStart]` with `when: always` is a last resort for a hard rule with no nameable signal, not the home for \"baseline\" or \"ambient\" rules: those have a moment when they apply (writing a message, reading a file, choosing between options), and the keyword for that moment is the trigger. HP-4 of hq-hook-perf moved every core `when: always` policy to keywords for this reason.\n- **`enforcement: hard` + an unconditional `when:` on a reactive event is blocked.** `when: always` (or an OR-chain containing `always`) is TRUE for every command and every prompt, so on `PreToolUse`\u002F`UserPromptSubmit`\u002F`PostToolUse`\u002F`AssistantIntent` it takes a session cap slot from a policy that actually matched. Either move it to `on: [SessionStart]`, or name the real signal — usually the OR-chain is already there and `always` is redundant noise in front of it.\n- If you genuinely can't name a signal and the rule is `enforcement: hard`, `when: always` + `on: [SessionStart]` is allowed; a soft rule without a signal is not worth a policy file.\n- You may omit both fields when `tags:` or `trigger:` supplies a derivable signal: the SessionStart `migrate-policy-triggers.sh` hook backfills them on the next session. If no signal is derivable, only an `enforcement: hard` policy receives the `when: always` + `on: [SessionStart]` fallback; soft or unset policies remain untriggered. Setting the fields at authoring time is better — the migrator never overwrites an existing `when:`.\n\n**Keep an `enforcement: hard` body short.** A hard policy is quoted verbatim into\nevery session it fires in, so its length is a recurring context cost, and the\nwrite hook blocks a binding body over `HQ_POLICY_HARD_RULE_MAX_BYTES` (default\n6144). Only the text above the first archival heading counts and is injected:\nput the rule and its exceptions under `## Rule`, and move worked examples,\nincident write-ups, and API detail under `## Rationale`, `## Examples`,\n`## Reference`, or `## Related`, and who\u002Fwhen\u002Fwhich-ticket detail under\n`## Provenance`. Nothing is lost — the agent gets\nthe file path and can read the rest when it needs the reasoning.\n\n### Company field (client-scoped rules outside a company policies directory)\n\nStep 3 still decides where a rule goes: a company-specific rule goes to `companies\u002F{co}\u002Fpolicies\u002F` by default, and that directory already loads only for the session's own companies, so a file there gets no field. The field is only for a rule that Step 3 sends elsewhere because the user gave an explicit `repo`, `command`, or `global` scope, while the rule itself is about one client. Such a file in `personal\u002Fpolicies\u002F` or a repo's `.claude\u002Fpolicies\u002F` would otherwise load in every session. Write `company: {slug}` in its frontmatter (`companies: [a, b]` only when the user named several). The policy reminder (`inject-policy-on-trigger.sh`) then skips it, if soft or unset, in sessions locked to other companies (see the `company:` field in `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`).\n\n- The slug must be one the user named, or the active or locked session company (`bash core\u002Fscripts\u002Fhq-session.sh get company_slug`), and the rule must be about that company. A rule merely learned while a company session is active, but true everywhere, gets no field. Never use this field to move a company rule out of `companies\u002F{co}\u002Fpolicies\u002F`.\n- Never infer a company from the policy name, slug, or wording. A word in the rule or in `when:` that matches a company folder name is not a company signal; company folder names are often ordinary words.\n- Verify each slug exists in `companies\u002Fmanifest.yaml` before writing it. If the company is unclear, ask; do not guess.\n- Write a real slug only. The template shows the line commented out (`# company: \u003Cslug>`); never copy a placeholder or explanation into the value. `validate-policy-frontmatter.sh` blocks a value that is not a slug in `companies\u002Fmanifest.yaml`, and the reminder ignores names with no company folder.\n- Never write a `company:` line unless Step 3 resolved a company scope (a manifest-verified slug) for this rule. If no scope resolved, leave the line out entirely; never write a placeholder, an empty value, or a guess.\n- Omit the field for a general rule. A missing field means the rule loads for every company, as before.\n- Do not add it to a file in a company policies directory (`companies\u002F{co}\u002Fpolicies\u002F`).\n- The field never hides a binding rule: the reminder never skips a `hard` or `gate` rule on this path, so a hard rule tagged with a company still loads in every session. Keep it that way; do not lower a rule's enforcement to make the company skip apply.\n\n**Policy frontmatter validation:** `when:` and `on:` are required and automatically checked by the `validate-policy-frontmatter.sh` write\u002Fedit hook. For stack-specific rules, express the service token in `when:` (for example, `when: vercel`); do not add retired applicability metadata. See `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md` for the complete schema.\n\n**Slug generation:** lowercase, hyphens, from rule keywords. Prefix: `{co}-` for company, `{repo}-` for repo, `hq-cmd-{name}-` for command, `hq-` for global.\n\n### Fallback: Worker.yaml (worker-scoped learnings)\n\nFor worker-specific learnings, still inject into `core\u002Fworkers\u002F*\u002F{id}\u002Fworker.yaml` instructions block:\n\n```yaml\ninstructions: |\n  ...existing instructions...\n\n  ## Learnings\n  - NEVER: {new rule}\n```\n\n\n## Step 5b: Create Insight File (insight content type only)\n\nIf Step 1.5 classified content as **insight**, skip Step 5 (policy creation) and create an insight file instead.\n\n**Target directory by scope:**\n- Global \u002F repo-scoped → `workspace\u002Finsights\u002Fglobal\u002F{slug}.md`\n- Company-scoped → `companies\u002F{co}\u002Fknowledge\u002Finsights\u002F{slug}.md` (create `insights\u002F` subdir if needed)\n- Tool-specific → `workspace\u002Finsights\u002Ftools\u002F{slug}.md`\n- Conceptual\u002Ftheoretical → `workspace\u002Finsights\u002Fconcepts\u002F{slug}.md`\n\n**Insight file format** (per `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Finsights-spec.md`):\n\n```markdown\n---\ntype: insight\ndomain: [engineering]\ntags: [topic-tags]\nscope: global | company:{co} | repo:{repo} | tool:{tool}\nsource_session: T-{timestamp}-{slug}\ncreated: {YYYY-MM-DD}\nconfidence: high | medium\nrelates_to: []\n---\n\n# {Title}\n\n## Insight\n\n{Core conceptual understanding, 2-4 paragraphs. Educational, not directive.}\n\n## Context\n\n{When\u002Fwhy this matters. What situation makes this knowledge valuable.}\n\n## Example\n\n{Optional. Concrete example showing the insight in practice.}\n```\n\n**Slug generation:** kebab-case from title keywords, max 60 chars. No scope prefix (subdirectory provides scope).\n\n**Confidence mapping:**\n- Validated through execution\u002Ftesting → `confidence: high`\n- Observed but not extensively tested → `confidence: medium`\n\n**After writing:** proceed to Step 7 (event logging).\n\n## Step 6: Evaluate Global Promotion\n\nGlobal promotion means **raising a rule's enforcement to hard, in a policy file** — never injecting it into the charter. Learned rules never go in `.claude\u002FCLAUDE.md` \u002F `AGENTS.md` (policy `learned-rules-never-in-claude-md`); they already surface for every session through the policy trigger hook (`inject-policy-on-trigger.sh`), so a hard-enforcement policy file *is* the global path.\n\nIf a rule meets ANY of:\n- `severity == critical`\n- `source == user-correction` (explicit `\u002Flearn --hard` invocation)\n- Rule triggered 3+ times (check event log)\n\nthen ensure it lives as a **hard-enforcement policy file** at global scope:\n- Personal\u002Fowner learnings → `personal\u002Fpolicies\u002F{slug}.md` (read directly by the policy trigger hook — no `core\u002Fpolicies\u002F` mirror — so it rides global scope and survives `\u002Fupdate-hq`).\n- Release-shipped, all-users learnings → `core\u002Fpolicies\u002F{slug}.md` with the public marker (promoted via `hq-pack-admin`).\n\nSet `enforcement: hard` in the policy frontmatter and ensure the file carries `when:`\u002F`on:` frontmatter so the SessionStart trigger hook surfaces it (Step 8). Do **not** touch `CLAUDE.md`.\n\n## Step 6.5: Capture entity candidates (opt-in companies only)\n\nIf the bound company has `ontology_capture` on\n(`core\u002Fscripts\u002Fknowledge-prefs.sh get {co} ontology_capture`), follow\n`.claude\u002Fskills\u002F_shared\u002Fsession-close-capture.md`: for each person, project,\ncompany, or concept a new or merged rule names, write one entity candidate\n(max 5 per run) with `core\u002Fscripts\u002Fontology-candidate.sh --kind entity`, source\nref `learn:{event_id}`. Audience is `company` when the rule's scope is\n`company:{co}` and the session user otherwise. Off: skip silently. Fail-soft.\n\n## Step 7: Log Event\n\n```bash\nmkdir -p workspace\u002Flearnings\n```\n\nWrite `workspace\u002Flearnings\u002Flearn-{YYYYMMDD-HHMMSS}.json`:\n```json\n{\n  \"event_id\": \"learn-{timestamp}\",\n  \"content_type\": \"rule|insight\",\n  \"rules\": [\n    {\n      \"rule\": \"NEVER: ...\",\n      \"scope\": \"worker:frontend-dev\",\n      \"target_file\": \"core\u002Fworkers\u002Fpublic\u002Fdev-team\u002Ffrontend-dev\u002Fworker.yaml\",\n      \"severity\": \"high\"\n    }\n  ],\n  \"source\": \"back-pressure-failure|session-insight\",\n  \"task_id\": \"TASK-001\",\n  \"project\": \"my-project\",\n  \"dedup_action\": \"new|merged|skipped\",\n  \"promoted_to_global\": true,\n  \"created_at\": \"{ISO8601}\"\n}\n```\n\nThe event log write is mandatory for every invocation (even skipped\u002Ftrivial ones) — downstream tooling relies on it.\n\n## Step 8: Reindex\n\n```bash\nqmd update 2>\u002Fdev\u002Fnull || true\n```\n\n`qmd update` is FTS-only and does not download GGUF models. Do **not** follow it with `qmd embed` or `qmd vsearch` in this turn. If the index should grow embeddings, run `hq index background` (or `qmd embed` with `run_in_background: true`) after the report.\n\nNo manual digest rebuild is needed: policies surface automatically via the SessionStart trigger hook (`inject-policy-on-trigger.sh`) and the `migrate-policy-triggers.sh` backfill. Ensure any new policy file carries `when:`\u002F`on:` frontmatter so it gets injected. (Personal entries under `personal\u002Fpolicies\u002F` are read directly by the trigger hook — there is no mirror step, so a new file loads on the next qualifying event without any reindex.)\n\nInsight-only runs (content_type: insight) write no policy file, so there is nothing for the trigger hook to surface.\n\n**Batch mode note:** In batch mode (Mode 4), there is no per-item or end-of-batch digest step — each policy file written during the batch surfaces on its own via its `when:`\u002F`on:` frontmatter.\n\n## Step 9: Report\n\nChat report follows the active output style (`core\u002Fpolicies\u002Fhq-audience-mode.md`). Files this skill writes stay full prose. The templates below are chat-only.\n\nFor any `scope: company` rule, **surface the resolved company slug and the full target path and confirm them before writing** — this is the visible checkpoint that catches a misroute before it reaches a tenant vault (`hq-company-scoped-writes-verify-company`). After the write, the chat report still follows the audience below.\n\n**Default (`HQ`, and any style that is not `hq-operator`):** one short plain sentence. Do not print Scope, Dedup, Action, file paths, thread IDs, or PIDs. Do not paste the operator template.\n\nWhen trusted command-report details context is present, lead with a short plain-language summary. Put useful, non-sensitive diagnostics in a collapsed `Technical details` section. Without that context, keep the summary-only format.\n\nWhen trusted context is absent (for example, a routed natural-language or programmatic invocation), check the flag at report time with `HQ_CLI_BIN=\"$(command -v hq 2>\u002Fdev\u002Fnull || true)\" node .claude\u002Fhooks\u002Fcommand-report-details-flag.cjs`. Use expanded details only when it prints `true`; otherwise keep the summary-only format. This gate never overrides `hq-operator` style.\n\n- New or updated rule\u002Finsight: `Saved. I'll remember that next time.`\n- Multiple items: `Saved {n} lessons so I'll remember them next time.`\n- Skipped as duplicate: stay silent, or `Already had that. No change.`\n- Stopped because company could not be resolved: keep the one clarifying question (that is a decision, not a report).\n\n**Operator (`\u002Foutput-style hq-operator`):**\n\n**For rules (content_type: rule):**\n```\nLearning captured:\n  Scope: {company:\u003Cslug> | repo:\u003Cname> | command | global}\n  Rule: {rule}\n  Target: {policy file path | worker.yaml path}\n  Action: {created-policy | updated-policy | merged-into-policy | worker-yaml-injection}\n  Global: {promoted|not promoted}\n  Dedup: {new|merged|skipped}\n  Event: workspace\u002Flearnings\u002Flearn-{timestamp}.json\n```\n\n**For insights (content_type: insight):**\n```\nInsight captured:\n  Title: {insight title}\n  Target: {insight file path}\n  Action: {created-insight | updated-insight | merged-into-insight}\n  Dedup: {new|merged|skipped}\n  Event: workspace\u002Flearnings\u002Flearn-{timestamp}.json\n```\n\nIf multiple rules\u002Finsights were extracted, report each.\n\n## Rules\n\n- **Policy files first** — always create structured policy files for company\u002Frepo\u002Fglobal\u002Fcommand scoped rules. Worker.yaml injection only for worker-specific learnings\n- **Scan before create** — always check existing policies for updates before creating new files (Step 4.5)\n- **Never inject empty\u002Ftrivial rules** — \"task completed successfully\" is not a learning\n- **Dedup is mandatory** — always check before injecting (qmd first, Grep fallback)\n- **Learned rules never touch the charter** — never write a learned rule into `.claude\u002FCLAUDE.md` \u002F `AGENTS.md`; global promotion = a hard-enforcement policy file (policy `learned-rules-never-in-claude-md`)\n- **Reindex after every injection** — keeps qmd search current\n- **New policies need `when:`\u002F`on:` frontmatter** — that is what the SessionStart trigger hook (`inject-policy-on-trigger.sh`) uses to surface a policy; no manual digest rebuild exists anymore\n- **Event log is always written** — `workspace\u002Flearnings\u002Flearn-{timestamp}.json` is non-optional\n- **Preserve existing rules** — append only, never overwrite existing rules\n- **User corrections always promote** — \u002Flearn --hard delegations go to a hard-enforcement policy file (never CLAUDE.md)\n- **Match existing style** — use the same rule format as existing rules in the target file\n","# Learn - Automated Learning Pipeline\n\nCapture a learning, classify it, and inject the rule directly into the file it governs (or the insight into the insight tree).\n\nCalled programmatically by `\u002Fexecute-task` and `\u002Frun-project` after task completion or failure. Also callable manually with `--hard` flag for hard-enforcement rules (formerly `\u002Flearn --hard`). `\u002Fhandoff` and `\u002Fcheckpoint` call this for session insights.\n\n**Input:** the user's argument — structured JSON event data, free text description, or empty\u002F\"auto\" for hook-triggered mode.\n\n## Core Principle\n\n**Two output types, one pipeline.** Content is classified (Step 1.5) and routed to the appropriate format:\n\n### Rules → Policy files (operational directives)\n\n| Scope | Target directory | Format |\n|-------|-----------------|--------|\n| Company | `companies\u002F{co}\u002Fpolicies\u002F{slug}.md` | Policy file (YAML frontmatter + Rule + Rationale) |\n| Repo | `repos\u002F{pub\\|priv}\u002F{repo}\u002F.claude\u002Fpolicies\u002F{slug}.md` | Policy file |\n| Command (operator) | `personal\u002Fpolicies\u002F{slug}.md` (scope: command) | Policy file |\n| Global \u002F personal (**default**) | `personal\u002Fpolicies\u002F{slug}.md` (scope: global) | Policy file |\n| Worker (legacy) | `core\u002Fworkers\u002F*\u002F{id}\u002Fworker.yaml` | Instructions block `## Learnings` |\n\n> **`\u002Flearn` never writes to `core\u002Fpolicies\u002F`.** `core\u002F` is release-shipped scaffold, replaced wholesale by `\u002Fupdate-hq` — anything written there is lost on the next upgrade (hard policy `hq-customizations-live-in-personal-or-company`). Operator-universal learnings go to `personal\u002Fpolicies\u002F`, which the policy trigger hook (`inject-policy-on-trigger.sh`) reads directly — no `core\u002Fpolicies\u002F` mirror — so they still load as global at SessionStart while living in an upgrade-safe location. A genuine product-level core policy (one that should ship to *every* HQ install) is **not** captured via `\u002Flearn`: author it locally, then publish through the hq-core staging repo + `\u002Fpromote-hq-core` (see `staging-promotion-required`). If a learning is judged truly core, `\u002Flearn` stops and points you at that pipeline rather than writing into `core\u002F`.\n\n### Insights → Insight files (educational understanding)\n\n| Scope | Target directory | Format |\n|-------|-----------------|--------|\n| Global \u002F repo | `workspace\u002Finsights\u002Fglobal\u002F{slug}.md` | Insight file (YAML frontmatter + Insight + Context) |\n| Company | `companies\u002F{co}\u002Fknowledge\u002Finsights\u002F{slug}.md` | Insight file in the company's plain synced knowledge directory |\n| Tool | `workspace\u002Finsights\u002Ftools\u002F{slug}.md` | Insight file |\n| Conceptual | `workspace\u002Finsights\u002Fconcepts\u002F{slug}.md` | Insight file |\n\nSee `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Finsights-spec.md` for the insight file format.\n\n**Before creating:** always scan existing policies\u002Finsights for updates (Step 4.5). Update > duplicate.\n\n## Step 0: Load Policies (frontmatter-only gate)\n\nBefore processing, load applicable policies with minimal context burn:\n\n1. Collect eligible paths in the global policy set except `example-policy.md`, keep their order, and split the stable file order into consecutive chunks of at most 40 files. For each chunk, run `bash core\u002Fscripts\u002Fread-policy-frontmatter.sh {file1} {file2} ...` once with each path as a separate argument. Keep each call under 30 KB of output. The first output block belongs to the chunk’s first file argument; each later block belongs to the path named by its preceding `# --- policy-file: \u003Cpath> ---` separator.\n2. Note `enforcement: hard` titles. Only Read the `## Rule` section of hard-enforcement policies if one looks relevant to the current learning\n\nSkip full policy body loads — the frontmatter contains enough metadata for the learn pipeline.\n\n## Step 1: Parse Input\n\nThree input modes:\n\n### Mode 1: Hook-Triggered (auto\u002Fempty input)\n\nIf the argument is empty or \"auto\":\n\n1. Check for `workspace\u002Flearnings\u002F.observe-patterns-latest.json`\n2. If file exists, read it and extract the `observations` array\n3. For each observation in the array:\n   - Extract `pattern_type`, `confidence`, `description`, `severity`, `evidence`\n   - Generate a structured learning event with `source: \"hook-observation\"`, `scope: \"global\"` (or inferred from pattern type)\n   - Process through Steps 2–9 (extract rules, classify scope, dedup, inject, etc.)\n4. Delete the file after processing all observations\n5. Report each learning processed\n\nExample `.observe-patterns-latest.json`:\n```json\n{\n  \"metadata\": {\n    \"created_at\": \"2026-03-07T21:35:00Z\",\n    \"session_end_timestamp\": \"20260307-213500\",\n    \"git_branch\": \"main\",\n    \"git_commit\": \"abc1234\",\n    \"project_context\": \"hq\"\n  },\n  \"observations\": [\n    {\n      \"pattern_type\": \"back-pressure-retry\",\n      \"confidence\": 0.8,\n      \"description\": \"Git log shows fixup\u002Famend commits\",\n      \"severity\": \"high\",\n      \"evidence\": \"fixup commits in recent history\",\n      \"recommendation\": \"Extract pattern about what caused retry\"\n    }\n  ]\n}\n```\n\nNote: in Codex runtime, the observe-patterns hook is not wired up, so this mode typically returns empty — fall through to manual modes.\n\n### Mode 2: Structured JSON (from \u002Fexecute-task, \u002Frun-project, \u002Fhandoff, \u002Fcheckpoint)\n\nIf input is a JSON object:\n```json\n{\n  \"task_id\": \"TASK-001\",\n  \"project\": \"my-project\",\n  \"source\": \"back-pressure-failure|user-correction|success-pattern|task-completion|build-activity|hook-observation|session-insight\",\n  \"severity\": \"critical|high|medium|low\",\n  \"scope\": \"global|worker:{id}|command:{name}|knowledge:{path}|project:{slug}\",\n  \"workers_used\": [\"backend-dev\"],\n  \"back_pressure_failures\": [{\"worker\": \"frontend-dev\", \"check\": \"lint\", \"error\": \"...\"}],\n  \"retries\": 0,\n  \"key_decisions\": [\"...\"],\n  \"issues_encountered\": [\"...\"],\n  \"patterns_discovered\": [\"...\"]\n}\n```\n\nParse it and proceed to Step 1.5.\n\n### Mode 3: Free Text (manual invocation or \u002Flearn --hard delegation)\n\nParse for keywords to determine scope. Generate rule statement from the description. Proceed to Step 1.5.\n\n### Mode 4: Batch Input (from \u002Fhandoff or any caller passing an array)\n\nIf the input starts with `[` (JSON array), enter batch mode:\n\n**Batch input format:**\n```json\n[\n  {\"type\": \"rule|insight\", \"content\": \"...\", \"scope\": \"global|company:{co}|...\", \"source\": \"...\"},\n  ...\n]\n```\n\n**Batch processing rules:**\n\n1. **Detect batch input:** if the input is a JSON array (starts with `[`), enter batch mode\n2. **Run qmd search dedup ONCE for all items:** concatenate all item `content` fields as a single query string for a single `qmd search` call (BM25; never `qmd vsearch` in-turn — first use downloads a ~300MB–2GB GGUF model and has stalled Windows prompts for hours), then match results against each item individually\n3. **Process each item through Steps 2–6 normally:** extract rules, classify scope, create\u002Fupdate policy files — one item at a time using the shared dedup results\n4. **Write a single event log entry** covering all items processed in the batch\n5. **Backward compatibility:** all existing modes (1, 2, 3) work exactly as before — batch is a new detection branch only\n\n## Step 1.5: Classify Content Type\n\nDetermine if input is an operational rule or an educational insight:\n\n| Signal | Type | Route to |\n|--------|------|----------|\n| NEVER\u002FALWAYS\u002Fcondition→action | rule | Policy file (Steps 2–6) |\n| \"why X works\", \"pattern behind\", conceptual explanation | insight | `workspace\u002Finsights\u002F` or `companies\u002F{co}\u002Fknowledge\u002Finsights\u002F` (Step 5b) |\n| User correction (`\u002Flearn --hard`) | rule | Policy file (always) |\n| `back_pressure_failures` | rule | Policy file (always) |\n| `patterns_discovered` (educational) | insight | Insight file (Step 5b) |\n| `source: \"session-insight\"` | insight | Insight file (always — from `\u002Fhandoff` or `\u002Fcheckpoint` step 0c) |\n\n**Default:** if ambiguous, route as policy (existing behavior). Insights are opt-in.\n\nIf content type is **insight**, skip Steps 2 and 5 (rule extraction and policy creation). Proceed to Step 3 for scope classification, Step 4 for dedup, then Step 5b for insight file creation.\n\n## Step 2: Extract Rules\n\nFrom structured input, generate rules:\n\n- `back_pressure_failures` → `NEVER: {anti-pattern that caused failure}` (scope: worker:{id})\n- `retries > 0` → Rule about what caused retry and how to avoid it\n- `key_decisions` → `ALWAYS: {pattern}` if broadly applicable\n- `issues_encountered` → Scoped rule to prevent recurrence\n- `patterns_discovered` → `ALWAYS: {pattern}` for success patterns\n\nFrom free text:\n- Extract the core rule in NEVER\u002FALWAYS\u002Fcondition→action format\n\nIf no meaningful rules can be extracted (task completed cleanly, no failures, no notable patterns), skip injection — log to event log only.\n\n## Step 3: Classify Scope & Resolve Target\n\nFor each extracted rule, determine scope (most specific wins):\n\n| Signal | Scope | Policy directory |\n|--------|-------|------------------|\n| Related to specific company | `company` | `companies\u002F{co}\u002Fpolicies\u002F` |\n| Related to specific repo | `repo` | `repos\u002F{pub\\|priv}\u002F{repo}\u002F.claude\u002Fpolicies\u002F` |\n| Error in specific command | `command` | `personal\u002Fpolicies\u002F` (with `scope: command`) |\n| Failure in specific worker | `worker` | `core\u002Fworkers\u002F*\u002F{id}\u002Fworker.yaml` instructions block (legacy, still supported) |\n| Active session company (when no explicit repo, command, or global scope) | `company` | `companies\u002F{co}\u002Fpolicies\u002F` |\n| Universal pattern (**default** when no company\u002Frepo\u002Fsession context) | `global` | `personal\u002Fpolicies\u002F` |\n| User correction via \u002Flearn --hard | From context; default to active session company, then global | Detected scope directory |\n\n**`\u002Flearn` does not target `core\u002Fpolicies\u002F`.** The global\u002Fcommand rows route to `personal\u002Fpolicies\u002F` — operator-owned and upgrade-safe — and the policy trigger hook reads `personal\u002Fpolicies\u002F` directly (no `core\u002Fpolicies\u002F` mirror), so each entry still loads as a global\u002Fcommand policy. Reserve `core\u002Fpolicies\u002F` for release-shipped policies authored through the staging → `\u002Fpromote-hq-core` pipeline. If a rule is genuinely product-core (must ship to every HQ install), stop and direct the user to that pipeline; never Write it into `core\u002F`.\n\n**Primary output = policy files.** The canonical format for persistent rules is structured policy files (per `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`). Worker.yaml injection is still supported for worker-specific learnings.\n\n**Resolve company\u002Frepo context** (strongest signal wins):\n- An explicit scope in the input or user request wins. Honor explicit `repo`, `command`, or `global` scope rather than the session default; explicit global scope targets `personal\u002Fpolicies\u002F`. Verify an explicit company slug against the manifest.\n- From the current working directory — the **leaf** `companies\u002F\u003Cslug>\u002F` segment (the last one in the path, not the first)\n- From `prd.json` metadata if in project context\n- From `companies\u002Fmanifest.yaml` repo lookup if in repo context\n- From worker path if worker-scoped (`companies\u002F{co}\u002Fworkers\u002F` → `{co}`)\n- From the active session: `bash core\u002Fscripts\u002Fhq-session.sh get company_slug`. For free-text and `\u002Flearn --hard` input with no explicit repo, command, or global scope, default to this active company and target `companies\u002F{co}\u002Fpolicies\u002F`.\n\n**Verify every resolved slug, including the active session `company_slug`, exists in `companies\u002Fmanifest.yaml`** before targeting `companies\u002F{co}\u002Fpolicies\u002F`. If the session value is absent or invalid, do not treat it as company context.\n\n**Never silently fall back to a global target for a company-specific learning.** A global target (→ `personal\u002Fpolicies\u002F`) is correct ONLY when the rule is genuinely universal to this operator's HQ. If a rule is clearly company-specific but the company can't be resolved unambiguously, **stop and ask which company** — do not default the write into `personal\u002Fpolicies\u002F` (and never into `core\u002Fpolicies\u002F`). A misrouted company policy syncs into the wrong tenant vault on the next `hq-sync` (HQ-Pro), a category-1 cross-company bug. See `core\u002Fpolicies\u002Fhq-company-scoped-writes-verify-company.md`.\n\n## Step 4: Dedup Check\n\n**Primary (if qmd available):**\n```bash\nqmd search \"{rule text}\" --json -n 5\n```\n\nIn-turn dedup is BM25 (`qmd search`) only. **Never run `qmd vsearch`, `qmd query`, `qmd embed`, or `qmd pull` during `\u002Flearn`.** Those subcommands auto-download local GGUF models (~300MB–2GB) on first use and have stalled a single Windows HQ prompt for more than two hours. If embeddings are already cached under `~\u002F.cache\u002Fqmd\u002Fmodels\u002F` you may still prefer `qmd search` here — keyword overlap is enough to catch duplicate rules. Kick off `hq index background` out of band if semantic search is wanted later.\n\nCheck results for similarity to the new rule:\n- Similarity > 0.85 → **Skip** (already captured somewhere)\n- Similarity 0.6–0.85 → **Merge** (update existing rule to be more precise)\n- Similarity \u003C 0.6 → **Add new**\n\n**Fallback (if qmd unavailable):**\nUse Grep to search for key terms from the rule across the policy directories:\n- Pattern: key terms from the rule (2-3 significant words)\n- Files: `*.md` in `companies\u002F*\u002Fpolicies\u002F`, `personal\u002Fpolicies\u002F`, `core\u002Fpolicies\u002F` (the release-shipped set), and any repo policy dirs\n- If matching content found → review and decide whether to merge or skip\n\nRecord the dedup action for the Step 9 report. Do not print it to the user in the default HQ style.\n\n## Step 4.5: Scan Existing Policies\n\nBefore creating a new rule, check if an existing policy file already covers this topic:\n\n1. **Resolve policy directories** based on scope:\n   - Company scope → scan `companies\u002F{co}\u002Fpolicies\u002F` (skip `example-policy.md`)\n   - Repo scope → scan `{repoPath}\u002F.claude\u002Fpolicies\u002F`\n   - Global\u002Fcommand scope → scan `personal\u002Fpolicies\u002F` (the write target) and `core\u002Fpolicies\u002F` (the release-shipped set)\n\n2. **Search for matching policies:**\n   ```bash\n   # Grep policy titles and rules for keyword overlap\n   grep -rl \"{key terms from rule}\" {policy_dir}\u002F*.md 2>\u002Fdev\u002Fnull\n   ```\n   Also check `qmd search` results from Step 4 for hits in policy files.\n\n3. **If matching policy found:**\n   - Read the policy file\n   - **Update** the existing policy: append to `## Rule` section, bump `version`, update `updated` date\n   - If new learning contradicts existing policy, flag for user review instead of auto-merging\n   - Set `dedup_action: \"merged-into-policy\"` in event log\n\n4. **If no matching policy found:**\n   - Proceed to Step 5 (create new rule)\n   - For company\u002Frepo\u002Fglobal scoped rules, prefer creating a **policy file** (per `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`) over injecting into worker.yaml. Policy files are the canonical format for persistent rules. **Never** write a learned rule into `.claude\u002FCLAUDE.md` \u002F `AGENTS.md` — the charter is release-shipped scaffold, not a learning store (policy `learned-rules-never-in-claude-md`)\n\n## Step 5: Create or Update Policy File (rule content type)\n\n### Primary: Policy File (company\u002Frepo\u002Fglobal\u002Fcommand scopes)\n\nIf Step 4.5 found a matching policy → update was already handled. Otherwise, create a new policy file.\n\n**Target directory:**\n- Company scope → `companies\u002F{co}\u002Fpolicies\u002F{slug}.md`\n- Repo scope → `repos\u002F{pub|priv}\u002F{repo}\u002F.claude\u002Fpolicies\u002F{slug}.md`\n- Command scope (operator-authored) → `personal\u002Fpolicies\u002F{slug}.md` (scope: command)\n- Global scope → `personal\u002Fpolicies\u002F{slug}.md` (scope: global) — **default for non-company\u002Fnon-repo rules**\n\n> Never Write a `.md` into `core\u002Fpolicies\u002F` from `\u002Flearn` — it is lost on `\u002Fupdate-hq` and is now mechanically blocked by `protect-core.sh` (the block message points back here). `personal\u002Fpolicies\u002F` entries are read directly by the policy trigger hook (no `core\u002Fpolicies\u002F` mirror), so they load identically as global\u002Fcommand policies. Genuine product-core policies ship via the staging → `\u002Fpromote-hq-core` pipeline only.\n\n**Create the directory if needed:**\n```bash\nmkdir -p {target_directory}\n```\n\n**Policy file format** (per `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`):\n\n```markdown\n---\nid: {scope-prefix}-{slug}\ntitle: {Rule title}\nwhen: {boolean trigger expr over context words, or `always`}\non: {[PreToolUse, PostToolUse, UserPromptSubmit, AssistantIntent] | [SessionStart]}\nenforcement: {hard|soft}\n# company: \u003Cslug>   # optional; see Company field below. Uncomment only with one real manifest slug\npublic: {true|false — see Lifecycle stamping below}\nstatus: active\nversion: 1\ncreated: {YYYY-MM-DD}\nupdated: {YYYY-MM-DD}\nsource: {back-pressure-failure|user-correction|success-pattern|task-completion|hook-observation}\nnotes: {only when a global or command policy gets public: false — the portability reason}\nretire_when: {workaround rules only — the fix condition in plain words}\nlast_confirmed: {workaround rules only — today's date, YYYY-MM-DD}\n---\n\n## Rule\n\n{Rule in imperative form}\n\n## Rationale\n\n{Mechanism and trade-offs only: why the rule works and what it costs}\n\n## Provenance\n\n{Company slugs, customer or person names, ticket or PR numbers, and specific dates from the source learning. Omit the section when there are none.}\n```\n\n**Lifecycle stamping (set at authoring time, no confirmation prompt):** `\u002Flearn`\nfills these fields itself and never asks the user about them\n(`hq-learn-auto-no-confirmation`).\n\n- `status: active` on every policy `\u002Flearn` writes.\n- `public:`\n  - Company- and repo-scoped policies: always `public: false`.\n  - Global and command-scoped policies: `public: true` only when the `## Rule`\n    body passes the portability check in `hq-public-policy-rule-body-generic`:\n    no HQ-internal paths (HQ top-level directories such as the company, personal,\n    workspace, repos, or core trees, the `.claude` directory, home-relative or\n    absolute paths), no company, worker, or repo slugs, and no internal infra\n    names (account IDs, bucket, stack, or host names). Otherwise\n    `public: false` with the failing check in `notes:`, for example\n    `notes: public false because the Rule names an HQ-internal path`.\n- Workaround rules: classify the learning as a workaround when it works around\n  a named tool, a specific version, or a known defect. Signals are a tool or\n  package name with a version number, or the words bug, defect, regression,\n  workaround, upstream issue, or until a named tool fixes something. For these,\n  write `retire_when:` with the fix condition in plain words (for example\n  `retire_when: the CLI ships the native fix for the stale session cache`; no\n  quotes or regex symbols, per `policies-spec.md`) and `last_confirmed:` with\n  today's date. Rules that are not workarounds omit both fields.\n- Provenance routing: when the source learning mentions a company slug, a\n  customer or person name, a ticket or PR number, or a specific date, write that\n  text under `## Provenance`. `## Rationale` keeps only the mechanism and the\n  trade-offs. `## Provenance` is archival: it is not injected and does not count\n  toward the hard-body size limit.\n\n**Enforcement mapping:**\n- `source: user-correction` → `enforcement: hard`\n- `severity: critical` → `enforcement: hard`\n- Everything else → `enforcement: soft`\n\n**`when:` \u002F `on:` trigger (just-in-time injection):**\n- `when:` is a boolean expression over an OPEN token set — any word that appears in the relevant command\u002Fprompt\u002FAI-message text is a live token (`git && push`, `refactor`, `supabase`, `.tsx`, `\u002Fdeep-plan`, `secret || credential`). Pick the word(s) that naturally show up when the rule is relevant. Operators: `&& || ! ( )`. Full grammar + fact derivation: `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`.\n- **Every token needs an operator between it and the next one.** `when: merge || pull request` is not \"merge, or a pull request\" — nothing joins `pull` and `request`, so the expression is rejected outright. Write `merge || (pull && request)`, or pick the single word you meant. Quoted phrases, flags, and globs are outside the charset and are rejected the same way. `validate-policy-frontmatter.sh` blocks this at write time; `bash core\u002Fscripts\u002Flint-policy-triggers.sh` finds any that predate the check.\n- `on:` is where it is evaluated. Default for a real trigger: `[PreToolUse, PostToolUse, UserPromptSubmit, AssistantIntent]` (the `when:` does the filtering). `[SessionStart]` with `when: always` is a last resort for a hard rule with no nameable signal, not the home for \"baseline\" or \"ambient\" rules: those have a moment when they apply (writing a message, reading a file, choosing between options), and the keyword for that moment is the trigger. HP-4 of hq-hook-perf moved every core `when: always` policy to keywords for this reason.\n- **`enforcement: hard` + an unconditional `when:` on a reactive event is blocked.** `when: always` (or an OR-chain containing `always`) is TRUE for every command and every prompt, so on `PreToolUse`\u002F`UserPromptSubmit`\u002F`PostToolUse`\u002F`AssistantIntent` it takes a session cap slot from a policy that actually matched. Either move it to `on: [SessionStart]`, or name the real signal — usually the OR-chain is already there and `always` is redundant noise in front of it.\n- If you genuinely can't name a signal and the rule is `enforcement: hard`, `when: always` + `on: [SessionStart]` is allowed; a soft rule without a signal is not worth a policy file.\n- You may omit both fields when `tags:` or `trigger:` supplies a derivable signal: the SessionStart `migrate-policy-triggers.sh` hook backfills them on the next session. If no signal is derivable, only an `enforcement: hard` policy receives the `when: always` + `on: [SessionStart]` fallback; soft or unset policies remain untriggered. Setting the fields at authoring time is better — the migrator never overwrites an existing `when:`.\n\n**Keep an `enforcement: hard` body short.** A hard policy is quoted verbatim into\nevery session it fires in, so its length is a recurring context cost, and the\nwrite hook blocks a binding body over `HQ_POLICY_HARD_RULE_MAX_BYTES` (default\n6144). Only the text above the first archival heading counts and is injected:\nput the rule and its exceptions under `## Rule`, and move worked examples,\nincident write-ups, and API detail under `## Rationale`, `## Examples`,\n`## Reference`, or `## Related`, and who\u002Fwhen\u002Fwhich-ticket detail under\n`## Provenance`. Nothing is lost — the agent gets\nthe file path and can read the rest when it needs the reasoning.\n\n### Company field (client-scoped rules outside a company policies directory)\n\nStep 3 still decides where a rule goes: a company-specific rule goes to `companies\u002F{co}\u002Fpolicies\u002F` by default, and that directory already loads only for the session's own companies, so a file there gets no field. The field is only for a rule that Step 3 sends elsewhere because the user gave an explicit `repo`, `command`, or `global` scope, while the rule itself is about one client. Such a file in `personal\u002Fpolicies\u002F` or a repo's `.claude\u002Fpolicies\u002F` would otherwise load in every session. Write `company: {slug}` in its frontmatter (`companies: [a, b]` only when the user named several). The policy reminder (`inject-policy-on-trigger.sh`) then skips it, if soft or unset, in sessions locked to other companies (see the `company:` field in `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md`).\n\n- The slug must be one the user named, or the active or locked session company (`bash core\u002Fscripts\u002Fhq-session.sh get company_slug`), and the rule must be about that company. A rule merely learned while a company session is active, but true everywhere, gets no field. Never use this field to move a company rule out of `companies\u002F{co}\u002Fpolicies\u002F`.\n- Never infer a company from the policy name, slug, or wording. A word in the rule or in `when:` that matches a company folder name is not a company signal; company folder names are often ordinary words.\n- Verify each slug exists in `companies\u002Fmanifest.yaml` before writing it. If the company is unclear, ask; do not guess.\n- Write a real slug only. The template shows the line commented out (`# company: \u003Cslug>`); never copy a placeholder or explanation into the value. `validate-policy-frontmatter.sh` blocks a value that is not a slug in `companies\u002Fmanifest.yaml`, and the reminder ignores names with no company folder.\n- Never write a `company:` line unless Step 3 resolved a company scope (a manifest-verified slug) for this rule. If no scope resolved, leave the line out entirely; never write a placeholder, an empty value, or a guess.\n- Omit the field for a general rule. A missing field means the rule loads for every company, as before.\n- Do not add it to a file in a company policies directory (`companies\u002F{co}\u002Fpolicies\u002F`).\n- The field never hides a binding rule: the reminder never skips a `hard` or `gate` rule on this path, so a hard rule tagged with a company still loads in every session. Keep it that way; do not lower a rule's enforcement to make the company skip apply.\n\n**Policy frontmatter validation:** `when:` and `on:` are required and automatically checked by the `validate-policy-frontmatter.sh` write\u002Fedit hook. For stack-specific rules, express the service token in `when:` (for example, `when: vercel`); do not add retired applicability metadata. See `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Fpolicies-spec.md` for the complete schema.\n\n**Slug generation:** lowercase, hyphens, from rule keywords. Prefix: `{co}-` for company, `{repo}-` for repo, `hq-cmd-{name}-` for command, `hq-` for global.\n\n### Fallback: Worker.yaml (worker-scoped learnings)\n\nFor worker-specific learnings, still inject into `core\u002Fworkers\u002F*\u002F{id}\u002Fworker.yaml` instructions block:\n\n```yaml\ninstructions: |\n  ...existing instructions...\n\n  ## Learnings\n  - NEVER: {new rule}\n```\n\n\n## Step 5b: Create Insight File (insight content type only)\n\nIf Step 1.5 classified content as **insight**, skip Step 5 (policy creation) and create an insight file instead.\n\n**Target directory by scope:**\n- Global \u002F repo-scoped → `workspace\u002Finsights\u002Fglobal\u002F{slug}.md`\n- Company-scoped → `companies\u002F{co}\u002Fknowledge\u002Finsights\u002F{slug}.md` (create `insights\u002F` subdir if needed)\n- Tool-specific → `workspace\u002Finsights\u002Ftools\u002F{slug}.md`\n- Conceptual\u002Ftheoretical → `workspace\u002Finsights\u002Fconcepts\u002F{slug}.md`\n\n**Insight file format** (per `core\u002Fknowledge\u002Fpublic\u002Fhq-core\u002Finsights-spec.md`):\n\n```markdown\n---\ntype: insight\ndomain: [engineering]\ntags: [topic-tags]\nscope: global | company:{co} | repo:{repo} | tool:{tool}\nsource_session: T-{timestamp}-{slug}\ncreated: {YYYY-MM-DD}\nconfidence: high | medium\nrelates_to: []\n---\n\n# {Title}\n\n## Insight\n\n{Core conceptual understanding, 2-4 paragraphs. Educational, not directive.}\n\n## Context\n\n{When\u002Fwhy this matters. What situation makes this knowledge valuable.}\n\n## Example\n\n{Optional. Concrete example showing the insight in practice.}\n```\n\n**Slug generation:** kebab-case from title keywords, max 60 chars. No scope prefix (subdirectory provides scope).\n\n**Confidence mapping:**\n- Validated through execution\u002Ftesting → `confidence: high`\n- Observed but not extensively tested → `confidence: medium`\n\n**After writing:** proceed to Step 7 (event logging).\n\n## Step 6: Evaluate Global Promotion\n\nGlobal promotion means **raising a rule's enforcement to hard, in a policy file** — never injecting it into the charter. Learned rules never go in `.claude\u002FCLAUDE.md` \u002F `AGENTS.md` (policy `learned-rules-never-in-claude-md`); they already surface for every session through the policy trigger hook (`inject-policy-on-trigger.sh`), so a hard-enforcement policy file *is* the global path.\n\nIf a rule meets ANY of:\n- `severity == critical`\n- `source == user-correction` (explicit `\u002Flearn --hard` invocation)\n- Rule triggered 3+ times (check event log)\n\nthen ensure it lives as a **hard-enforcement policy file** at global scope:\n- Personal\u002Fowner learnings → `personal\u002Fpolicies\u002F{slug}.md` (read directly by the policy trigger hook — no `core\u002Fpolicies\u002F` mirror — so it rides global scope and survives `\u002Fupdate-hq`).\n- Release-shipped, all-users learnings → `core\u002Fpolicies\u002F{slug}.md` with the public marker (promoted via `hq-pack-admin`).\n\nSet `enforcement: hard` in the policy frontmatter and ensure the file carries `when:`\u002F`on:` frontmatter so the SessionStart trigger hook surfaces it (Step 8). Do **not** touch `CLAUDE.md`.\n\n## Step 6.5: Capture entity candidates (opt-in companies only)\n\nIf the bound company has `ontology_capture` on\n(`core\u002Fscripts\u002Fknowledge-prefs.sh get {co} ontology_capture`), follow\n`.claude\u002Fskills\u002F_shared\u002Fsession-close-capture.md`: for each person, project,\ncompany, or concept a new or merged rule names, write one entity candidate\n(max 5 per run) with `core\u002Fscripts\u002Fontology-candidate.sh --kind entity`, source\nref `learn:{event_id}`. Audience is `company` when the rule's scope is\n`company:{co}` and the session user otherwise. Off: skip silently. Fail-soft.\n\n## Step 7: Log Event\n\n```bash\nmkdir -p workspace\u002Flearnings\n```\n\nWrite `workspace\u002Flearnings\u002Flearn-{YYYYMMDD-HHMMSS}.json`:\n```json\n{\n  \"event_id\": \"learn-{timestamp}\",\n  \"content_type\": \"rule|insight\",\n  \"rules\": [\n    {\n      \"rule\": \"NEVER: ...\",\n      \"scope\": \"worker:frontend-dev\",\n      \"target_file\": \"core\u002Fworkers\u002Fpublic\u002Fdev-team\u002Ffrontend-dev\u002Fworker.yaml\",\n      \"severity\": \"high\"\n    }\n  ],\n  \"source\": \"back-pressure-failure|session-insight\",\n  \"task_id\": \"TASK-001\",\n  \"project\": \"my-project\",\n  \"dedup_action\": \"new|merged|skipped\",\n  \"promoted_to_global\": true,\n  \"created_at\": \"{ISO8601}\"\n}\n```\n\nThe event log write is mandatory for every invocation (even skipped\u002Ftrivial ones) — downstream tooling relies on it.\n\n## Step 8: Reindex\n\n```bash\nqmd update 2>\u002Fdev\u002Fnull || true\n```\n\n`qmd update` is FTS-only and does not download GGUF models. Do **not** follow it with `qmd embed` or `qmd vsearch` in this turn. If the index should grow embeddings, run `hq index background` (or `qmd embed` with `run_in_background: true`) after the report.\n\nNo manual digest rebuild is needed: policies surface automatically via the SessionStart trigger hook (`inject-policy-on-trigger.sh`) and the `migrate-policy-triggers.sh` backfill. Ensure any new policy file carries `when:`\u002F`on:` frontmatter so it gets injected. (Personal entries under `personal\u002Fpolicies\u002F` are read directly by the trigger hook — there is no mirror step, so a new file loads on the next qualifying event without any reindex.)\n\nInsight-only runs (content_type: insight) write no policy file, so there is nothing for the trigger hook to surface.\n\n**Batch mode note:** In batch mode (Mode 4), there is no per-item or end-of-batch digest step — each policy file written during the batch surfaces on its own via its `when:`\u002F`on:` frontmatter.\n\n## Step 9: Report\n\nChat report follows the active output style (`core\u002Fpolicies\u002Fhq-audience-mode.md`). Files this skill writes stay full prose. The templates below are chat-only.\n\nFor any `scope: company` rule, **surface the resolved company slug and the full target path and confirm them before writing** — this is the visible checkpoint that catches a misroute before it reaches a tenant vault (`hq-company-scoped-writes-verify-company`). After the write, the chat report still follows the audience below.\n\n**Default (`HQ`, and any style that is not `hq-operator`):** one short plain sentence. Do not print Scope, Dedup, Action, file paths, thread IDs, or PIDs. Do not paste the operator template.\n\nWhen trusted command-report details context is present, lead with a short plain-language summary. Put useful, non-sensitive diagnostics in a collapsed `Technical details` section. Without that context, keep the summary-only format.\n\nWhen trusted context is absent (for example, a routed natural-language or programmatic invocation), check the flag at report time with `HQ_CLI_BIN=\"$(command -v hq 2>\u002Fdev\u002Fnull || true)\" node .claude\u002Fhooks\u002Fcommand-report-details-flag.cjs`. Use expanded details only when it prints `true`; otherwise keep the summary-only format. This gate never overrides `hq-operator` style.\n\n- New or updated rule\u002Finsight: `Saved. I'll remember that next time.`\n- Multiple items: `Saved {n} lessons so I'll remember them next time.`\n- Skipped as duplicate: stay silent, or `Already had that. No change.`\n- Stopped because company could not be resolved: keep the one clarifying question (that is a decision, not a report).\n\n**Operator (`\u002Foutput-style hq-operator`):**\n\n**For rules (content_type: rule):**\n```\nLearning captured:\n  Scope: {company:\u003Cslug> | repo:\u003Cname> | command | global}\n  Rule: {rule}\n  Target: {policy file path | worker.yaml path}\n  Action: {created-policy | updated-policy | merged-into-policy | worker-yaml-injection}\n  Global: {promoted|not promoted}\n  Dedup: {new|merged|skipped}\n  Event: workspace\u002Flearnings\u002Flearn-{timestamp}.json\n```\n\n**For insights (content_type: insight):**\n```\nInsight captured:\n  Title: {insight title}\n  Target: {insight file path}\n  Action: {created-insight | updated-insight | merged-into-insight}\n  Dedup: {new|merged|skipped}\n  Event: workspace\u002Flearnings\u002Flearn-{timestamp}.json\n```\n\nIf multiple rules\u002Finsights were extracted, report each.\n\n## Rules\n\n- **Policy files first** — always create structured policy files for company\u002Frepo\u002Fglobal\u002Fcommand scoped rules. Worker.yaml injection only for worker-specific learnings\n- **Scan before create** — always check existing policies for updates before creating new files (Step 4.5)\n- **Never inject empty\u002Ftrivial rules** — \"task completed successfully\" is not a learning\n- **Dedup is mandatory** — always check before injecting (qmd first, Grep fallback)\n- **Learned rules never touch the charter** — never write a learned rule into `.claude\u002FCLAUDE.md` \u002F `AGENTS.md`; global promotion = a hard-enforcement policy file (policy `learned-rules-never-in-claude-md`)\n- **Reindex after every injection** — keeps qmd search current\n- **New policies need `when:`\u002F`on:` frontmatter** — that is what the SessionStart trigger hook (`inject-policy-on-trigger.sh`) uses to surface a policy; no manual digest rebuild exists anymore\n- **Event log is always written** — `workspace\u002Flearnings\u002Flearn-{timestamp}.json` is non-optional\n- **Preserve existing rules** — append only, never overwrite existing rules\n- **User corrections always promote** — \u002Flearn --hard delegations go to a hard-enforcement policy file (never CLAUDE.md)\n- **Match existing style** — use the same rule format as existing rules in the target file\n",{"name":5,"description":7,"allowed-tools":43},"Read, Write, Edit, Grep, Glob, Bash(qmd:*), Bash(grep:*), Bash(mkdir:*), Bash(date:*), Bash(ls:*), Bash(bash:*), Bash(git:*), Bash(rm:*), Bash(stat:*), Bash, Bash(bash core\u002Fscripts\u002Fread-policy-frontmatter.sh:*)","main","205cf61728a27ef15e9153aa38df5856fc8d82f5",".claude\u002Fskills\u002Flearn",18,"https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core","https:\u002F\u002Fgithub.com\u002Findigoai-us","https:\u002F\u002Fhqforwork.com\u002F","The AI Context Layer for Teams","2026-11-09 23:36:37",{"source":8,"skillName":5,"commands":54,"prompt":64,"zipAvailable":65},[55,58,61],{"tool":56,"command":57},"npx","npx skills add indigoai-us\u002Fhq-core --skill learn",{"tool":59,"command":60},"bunx","bunx skills add indigoai-us\u002Fhq-core --skill learn",{"tool":62,"command":63},"pnpm","pnpm dlx skills add indigoai-us\u002Fhq-core --skill learn","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Flearn\u002FSKILL.md",true,[67],{"path":68,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":71,"sourceHtmlUrl":72},"agents\u002Fopenai.yaml","REFERENCE","参考文档",116,"https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Flearn\u002Fagents\u002Fopenai.yaml",[74,92,122,146,157,167],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":80,"publishedAt":38},"01M71E4MQ04WPK1EVDS7VKJ3E8","social-graphic","Generate 1080x1080 HTML social graphics for HQ update posts.","https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core\u002Fblob\u002Fmain\u002Fcore\u002Fworkers\u002Fpublic\u002Fsocial-strategist\u002Fskills\u002Fsocial-graphic\u002FSKILL.md",[],[81,82,34,83,30,31,32,84,85,86,87,88,89,90,91],"social","social graphic","html","update","generate","graphics","browser","open","agent-browser","for","posts",{"skillNo":93,"name":89,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":105,"publishedAt":38},"01M71E4H33K4P5H5M7ENPTMFSC","Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to \"open a website\", \"fill out a form\", \"click a button\", \"take a screenshot\", \"scrape data from a page\", \"test this web app\", \"login to a site\", \"automate browser actions\", or any task requiring programmatic web interaction.","https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core\u002Fblob\u002Fmain\u002Fcore\u002Fworkers\u002Fpublic\u002Fqa-tester\u002Fskills\u002Fagent-browser\u002FSKILL.md",[97,102],{"categoryNo":98,"categoryName":99,"taxonomyType":100,"taxonomyTypeDesc":101},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":103,"categoryName":104,"taxonomyType":100,"taxonomyTypeDesc":101},"01JBSEEDCAT000000000000023","Skill与智能体开发",[106,87,107,108,109,110,34,111,112,113,114,115,88,116,89,117,118,119,120,121],"agent browser","click","ai agents","app","scrape","when","apps","actions","extracting","take a screenshot","testing","programmatic","browser automation","website","cli","interaction",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":128,"publishedAt":38},"01M71E4FDBD7J8Y45JJ9KMMYGN","work-mesh","Work Mesh Live — automatic presence via hq mesh daemon; context reconcile\u002Forganize; manual session task-status, blocked, and note only.","https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fwork-mesh\u002FSKILL.md",[],[129,130,131,34,132,133,134,135,136,137,138,139,140,141,32,142,143,144,145],"work mesh","work","mesh","status","context","note","live","presence","automatic","reconcile","only","organize","task","session","manual","daemon","via",{"skillNo":147,"name":148,"nameZh":6,"description":149,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":152,"publishedAt":38},"01M71E4EGHNAAGWAPV6KGBMQ9W","review","Parity worker review skill (materialization fixture).","https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core\u002Fblob\u002Fmain\u002Fcore\u002Fscripts\u002Ftests\u002Ffixtures\u002Fagent-session\u002Fworker-materialization\u002Fparity-worker\u002Fskills\u002Freview\u002FSKILL.md",[],[148,153,154,155,156],"review skill","fixture","parity","worker",{"skillNo":158,"name":159,"nameZh":6,"description":160,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":161,"categories":162,"keywords":163,"publishedAt":38},"01M71E4D1FZDJPDC15DRERKX8Y","run","Run or list HQ workers (parity fixture).","https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core\u002Fblob\u002Fmain\u002Fcore\u002Fscripts\u002Ftests\u002Ffixtures\u002Fagent-session\u002Fskills\u002Frun\u002FSKILL.md",[],[159,164,165,155,166,154],"parity fixture","workers","list",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":173,"publishedAt":38},"01M71E4BF4MV5SEWZJZ4T1QPQW","handoff","Preserve session state for a follow-up agent (parity fixture).","https:\u002F\u002Fgithub.com\u002Findigoai-us\u002Fhq-core\u002Fblob\u002Fmain\u002Fcore\u002Fscripts\u002Ftests\u002Ffixtures\u002Fagent-session\u002Fskills\u002Fhandoff\u002FSKILL.md",[],[169,164,174,175,142,176,90,155,177,178,154],"up","preserve","session state","follow-up","state"]