diff --git a/CLAUDE.md b/CLAUDE.md index 8017b33..b1ef573 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,7 +33,7 @@ topic you need. `docs/adr/` above), `04-build-plan.md` the build outline with step status, `06-graphify-evaluation.md` justifies the Graphify pivot. - **`docs/implementation-status.md`** — the status timeline, full per-component detail for - every shipped plugin (os-vault, os-orchestration, os-status, os-doc-hygiene, os-adr), + every shipped plugin (os-vault, os-context, os-status, os-doc-hygiene, os-adr), eval-harness records, and plugin rename/cache-refresh procedures. - **`docs/graphify/`** — verified handbook for the Graphify knowledge-graph tool. Skim `00-README.md` for the model; keep `09-best-practices-checklist.md` open when running it. @@ -76,9 +76,11 @@ before modifying any of these: - **os-vault** (`plugins/os-vault/`) — vault write/query/reorganize/onboard-project/ design-template skills, SessionStart/End hooks, memsearch + vault git sync. Write-behavior eval harness in `plugins/os-vault/eval/`. -- **os-orchestration** (`plugins/os-orchestration/`) — injects `ORCHESTRATION.md` - (session-orchestration + delegation-economics rules) into all sessions; this repo carries - no local override. Eval harness in `plugins/os-orchestration/eval/`. +- **os-context** (`plugins/os-context/`, renamed 2026-07-13 from os-orchestration) — a + prompt-composer SessionStart plugin: concatenates `prompts/session-start/*.md` (currently + just `10-orchestration.md`, the session-orchestration + delegation-economics rules) into + one additionalContext block for all sessions; this repo carries no local override. Eval + harness in `plugins/os-context/eval/`. - **os-status** (`plugins/os-status/`) — aggregated deterministic SessionStart checks (subagent-model env override, ADR system present, vault hub note present). ADR-022. - **os-doc-hygiene** (`plugins/os-doc-hygiene/`) — stale/bloated-doc monitoring; diff --git a/docs/adr/0019-global-os-orchestration-plugin-supersedes-per-project-orchestration-text.md b/docs/adr/0019-global-os-orchestration-plugin-supersedes-per-project-orchestration-text.md index 0ba5ccf..13ea6d1 100644 --- a/docs/adr/0019-global-os-orchestration-plugin-supersedes-per-project-orchestration-text.md +++ b/docs/adr/0019-global-os-orchestration-plugin-supersedes-per-project-orchestration-text.md @@ -1,9 +1,9 @@ --- id: "0019" date: 2026-07-03 -status: Accepted +status: Superseded supersedes: -superseded-by: +superseded-by: "0031" affected-paths: [] affected-components: [] migration_confidence: medium diff --git a/docs/adr/0031-os-context-prompt-composer-plugin-supersedes-single-file-os-orchestration.md b/docs/adr/0031-os-context-prompt-composer-plugin-supersedes-single-file-os-orchestration.md new file mode 100644 index 0000000..95282b4 --- /dev/null +++ b/docs/adr/0031-os-context-prompt-composer-plugin-supersedes-single-file-os-orchestration.md @@ -0,0 +1,27 @@ +--- +id: "0031" +date: 2026-07-13 +status: Accepted +supersedes: "0019" +superseded-by: +affected-paths: [plugins/os-context/, plugins/os-status/hooks/checks.py] +affected-components: [os-context, os-orchestration, os-status] +--- + +# 0031 — os-context prompt-composer plugin supersedes single-file os-orchestration + +## Context + +ADR-0019 shipped os-orchestration, a global plugin whose SessionStart hook injected one hardcoded file (ORCHESTRATION.md). The 2026-07-09 AI-workflow audit surfaced further always-true context worth injecting (kickoff conventions, standing safety rules, decision-memo format), and a one-file plugin cannot host multiple independently maintained rule sets or enforce an injection budget across them. + +## Decision + +Rename/generalize os-orchestration into os-context (plugins/os-context/): the SessionStart hook is a composer that globs prompts/session-start/*.md, concatenates in filename order (numeric prefixes), and emits one additionalContext block. ORCHESTRATION.md became prompts/session-start/10-orchestration.md with byte-identical injected output. Deterministic line budgets are enforced in the composer (warn at 120 injected lines, refuse at 240). Future event directories (e.g. prompts/user-prompt-submit/) are anticipated by directory naming but only session-start is implemented; no prompts/session-end/ directory will exist because SessionEnd hooks cannot inject model-visible context (ADR-0028 context). The audit-sessions skill moved to /os-context:audit-sessions and its state dir to ~/.local/state/os-context (old path symlinked); os-status references updated in the same change. + +## Consequences + +Easier: new standing rules ship as small numbered prompt files instead of edits to one monolith; injection cost is bounded by an enforced budget; the plugin name matches its actual domain (session context, not just orchestration). Harder: one-time identity cutover across marketplace/settings/installed-plugin surfaces; historical docs and frozen eval results still say os-orchestration and must be read with the rename note in mind; the OS_ORCH_AUDIT_LEDGER env var name is retained for compatibility despite the rename. + +## Alternatives rejected + +1) Keep os-orchestration and append new rules to ORCHESTRATION.md - rejected: one monolithic file with no per-ruleset ownership or ordering, and no budget enforcement. 2) Separate plugin per rule set - rejected: each global plugin adds cache-refresh and sync overhead; these rule sets share one event and one budget. 3) Put the new rules in ~/.claude/CLAUDE.md - rejected: CLAUDE.md is not compaction-safe the way the SessionStart matcher (startup|resume|clear|compact) is, and the single-source rule keeps injected rules in exactly one place. diff --git a/docs/adr/README.md b/docs/adr/README.md index 65f89bc..5b0110a 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -25,7 +25,7 @@ One file per decision, `NNNN-kebab-title.md`, created via `/os-adr:create`. | 0016 | [Memory plugin sourced from cc-os git repo; bash→Python deep-module port; memsearch-sync split; symlink cutover](0016-memory-plugin-sourced-from-cc-os-git-repo-bash-python-deep-module-port-memsearch-sync-split-symlink-cutover.md) | Accepted | 2026-06-12 | | 0017 | [Project graph onboarding assesses the repo and writes `.graphifyignore` before extracting](0017-project-graph-onboarding-assesses-the-repo-and-writes-graphifyignore-before-extracting.md) | Accepted | 2026-06-17 | | 0018 | [Plugin renames must update the marketplace manifest, not just the directory and settings.json](0018-plugin-renames-must-update-the-marketplace-manifest-not-just-the-directory-and-settings-json.md) | Accepted | 2026-07-03 | -| 0019 | [Global os-orchestration plugin supersedes per-project orchestration text](0019-global-os-orchestration-plugin-supersedes-per-project-orchestration-text.md) | Accepted | 2026-07-03 | +| 0019 | [Global os-orchestration plugin supersedes per-project orchestration text](0019-global-os-orchestration-plugin-supersedes-per-project-orchestration-text.md) | Superseded | 2026-07-03 | | 0020 | [os-adr plugin built; implementation-time resolutions of the design's open questions](0020-os-adr-plugin-built-implementation-time-resolutions-of-the-design-s-open-questions.md) | Accepted | 2026-07-03 | | 0021 | [Model-tier skill-execution eval for os-adr (Eval A), run in-session via subagents](0021-model-tier-skill-execution-eval-for-os-adr-eval-a-run-in-session-via-subagents.md) | Accepted | 2026-07-03 | | 0022 | [os-status plugin: aggregated in-process SessionStart status checks](0022-os-status-plugin-aggregated-in-process-sessionstart-status-checks.md) | Accepted | 2026-07-06 | @@ -36,4 +36,6 @@ One file per decision, `NNNN-kebab-title.md`, created via `/os-adr:create`. | 0027 | [All cc-os per-project state lives under `.cc-os//`](0027-all-cc-os-per-project-state-lives-under-cc-os-plugin.md) | Accepted | 2026-07-12 | | 0028 | [os-shortcuts plugin for user-invoked QOL commands, bounded against hook-injection plugins](0028-os-shortcuts-plugin-for-user-invoked-qol-commands-bounded-against-hook-injection-plugins.md) | Accepted | 2026-07-12 | | 0029 | [os-backlog card-move with CLI-enforced column ownership; afk-ready cards skip Review](0029-os-backlog-card-move-with-cli-enforced-column-ownership-afk-ready-cards-skip-review.md) | Accepted | 2026-07-12 | +| 0030 | [Read-only work uses Claude Code plan mode; no /readonly shortcut command](0030-read-only-work-uses-claude-code-plan-mode-no-readonly-shortcut-command.md) | Accepted | 2026-07-13 | +| 0031 | [os-context prompt-composer plugin supersedes single-file os-orchestration](0031-os-context-prompt-composer-plugin-supersedes-single-file-os-orchestration.md) | Accepted | 2026-07-13 | diff --git a/docs/implementation-status.md b/docs/implementation-status.md index b161836..bb9f73b 100644 --- a/docs/implementation-status.md +++ b/docs/implementation-status.md @@ -239,6 +239,22 @@ Done (no Review sift); semi still stops at Review. capture/list SKILL.md updated "working a card means moving it" contract. Suite 68 runs/153 assertions (was 58/130). Sonnet-delegated build. +**os-context rename + prompt files + /readonly decision + doc-length signal (2026-07-13, +ADR-030/031):** worked the four `semi` backlog cards after a Codex plan review. (1) +os-orchestration renamed/generalized to **os-context** (ADR-031 supersedes ADR-019): the +SessionStart hook is now a composer globbing `prompts/session-start/*.md` in filename order +with enforced line budgets (warn 120 / refuse 240); `ORCHESTRATION.md` became +`10-orchestration.md` with byte-identical output (tested); os-status audit references and +`~/.local/state` path updated (old state path symlinked; `OS_ORCH_AUDIT_LEDGER` env var name +kept). (2) Authored `20-kickoff-conventions.md`, `30-standing-safety.md`, +`40-decision-memos.md` (43 lines; total injection 102/120); decision-memo auto-memory file +deleted per single-source rule. (3) `/readonly` decided as convention-only — plan mode is +the enforcement layer, no skill built (ADR-030). (4) os-doc-hygiene scanner gained a +deterministic `file_length` signal (defaults 400 lines / 4000 tokens, `--max-lines` / +`--max-tokens`); flags `docs/implementation-status.md`; suite 286 passed. Issue #25 Parts +2–3 (index-with-progressive-disclosure distill + convention ADR) remain human-gated and +open. Sonnet-delegated builds. + **Remaining optional items:** additional project onboarding (one at a time, per ADR-013); bulk vault migration. All required build steps complete as of 2026-06-15. @@ -302,8 +318,19 @@ into `~/.claude/plugins/os-vault` **Global os-orchestration plugin** — `cc-os/plugins/os-orchestration/` (git-tracked, 2026-07-03); symlinked into `~/.claude/plugins/os-orchestration` -- Hooks: `hooks/` — `inject.py` (injects `ORCHESTRATION.md` as additionalContext to all - sessions) + +**Renamed to os-context (2026-07-13):** generalized into a prompt-composer SessionStart +plugin. `plugins/os-orchestration/` → `plugins/os-context/`; `ORCHESTRATION.md` moved to +`prompts/session-start/10-orchestration.md`; `hooks/inject.py` now globs +`prompts/session-start/*.md`, sorts by filename, and concatenates into one +additionalContext block (byte-identical output to the old single-file behavior when only +`10-orchestration.md` is present), with a deterministic line-count budget (warn >120 +lines, refuse >240 lines). `/os-orchestration:audit-sessions` → `/os-context:audit-sessions`; +`~/.local/state/os-orchestration/` → `~/.local/state/os-context/` (os-status's +`orchestration-audit-due` check and ledger env-var default updated to match). The +timeline entries below predate the rename and refer to the plugin by its former name. +- Hooks: `hooks/` — `inject.py` (composes `prompts/session-start/*.md` as additionalContext + to all sessions) - Behavior: SessionStart hook injects an `ORCHESTRATION.md` markdown doc (hardcoded; lives in plugin source) as additionalContext, carrying the canonical global session-orchestration rule set for Claude Code across all projects. diff --git a/plugins/os-context/.claude-plugin/plugin.json b/plugins/os-context/.claude-plugin/plugin.json new file mode 100644 index 0000000..5f71e8a --- /dev/null +++ b/plugins/os-context/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "os-context", + "version": "0.1.0", + "description": "Prompt-composer SessionStart plugin: concatenates prompts/session-start/*.md (currently session-orchestration rules — delegation threshold, explicit model routing on Agent spawns) into one additionalContext block, injected at session start and after compaction." +} diff --git a/plugins/os-orchestration/audit/bin/audit-stats b/plugins/os-context/audit/bin/audit-stats similarity index 98% rename from plugins/os-orchestration/audit/bin/audit-stats rename to plugins/os-context/audit/bin/audit-stats index 9c7b4d2..a65e564 100755 --- a/plugins/os-orchestration/audit/bin/audit-stats +++ b/plugins/os-context/audit/bin/audit-stats @@ -5,7 +5,7 @@ # # Enumerates production session transcripts modified since a given date, # reduces each to a delegation fact-sheet (audit/bin/extract), and writes a -# run directory under the audit state dir (~/.local/state/os-orchestration/audit): +# run directory under the audit state dir (~/.local/state/os-context/audit): # # run-/stats.tsv one row per audited session (deterministic columns) # run-/flags.md heuristic missed-delegation candidates, per session, @@ -31,7 +31,7 @@ require "optparse" load File.expand_path("extract", __dir__) module OrchAudit - DEFAULT_STATE_DIR = File.expand_path("~/.local/state/os-orchestration/audit") + DEFAULT_STATE_DIR = File.expand_path("~/.local/state/os-context/audit") DEFAULT_PROJECTS_DIR = File.expand_path("~/.claude/projects") class TranscriptSet diff --git a/plugins/os-orchestration/audit/bin/extract b/plugins/os-context/audit/bin/extract similarity index 100% rename from plugins/os-orchestration/audit/bin/extract rename to plugins/os-context/audit/bin/extract diff --git a/plugins/os-orchestration/eval/README.md b/plugins/os-context/eval/README.md similarity index 93% rename from plugins/os-orchestration/eval/README.md rename to plugins/os-context/eval/README.md index 3b8a9b2..89bcceb 100644 --- a/plugins/os-orchestration/eval/README.md +++ b/plugins/os-context/eval/README.md @@ -1,10 +1,10 @@ -# os-orchestration eval — E1–E3, E5 (session-orchestration behavior) +# os-context eval — E1–E3, E5 (session-orchestration behavior) _Created: 2026-07-06. Scenarios sourced from the verified misses in `docs/orchestration-audit/2026-07-06-findings.md` (clusters 1–3), per the IRL-feedback-loop methodology. E4 (batch planning) deliberately deferred._ -Measures whether an orchestrating session follows the shipped `ORCHESTRATION.md` +Measures whether an orchestrating session follows the shipped `prompts/session-start/10-orchestration.md` (the 2026-07-06 trigger-conditioned wording) mid-task: explicit `model:` per spawn, resolved-model verification, delegation thresholds, and not re-covering its own ground. This validates the wording fixes the way Eval B/C validated os-adr's — @@ -18,7 +18,7 @@ was wrong).** ## Run mode (the only valid one) Headless: fresh `claude -p` per rep, cwd = a sandbox copy of `fixture/project/`, so -the global os-orchestration SessionStart hook injects ORCHESTRATION.md for real. +the global os-context SessionStart hook injects the orchestration prompt for real. In-session subagents are invalid here (they inherit the parent session). The behavior under test is mid-session, so unlike Eval B there is no SessionStart trigger to protect — but the injection channel is the SessionStart hook, so the constraint is @@ -104,7 +104,7 @@ name field); metadata gains `total_spawns` and `max_rounds`. ## Conformance dry-run (done at design time, 2026-07-06) Per the ladder anti-pattern list, each cell was checked against "would a model -perfectly following the shipped ORCHESTRATION.md pass?" — yes on all six (E1P: +perfectly following the shipped prompts/session-start/10-orchestration.md pass?" — yes on all six (E1P: delegate+sonnet+flag; E1N: no false claim; E2P: fan out with explicit models; E2N/E3N: do directly; E3P: delegate the log review after at most an orienting look) — and "would an always-delegate model pass positives while failing negatives?" — it fails @@ -128,7 +128,7 @@ done; wait - Sandboxes must live under a dot-free path (/tmp/orch-eval): the checker flattens the sandbox realpath to find the project transcript dir. -- Run `cc-os/bin/refresh-plugins` after ANY ORCHESTRATION.md edit, before any grid. +- Run `cc-os/bin/refresh-plugins` after ANY prompts/session-start/10-orchestration.md edit, before any grid. - TSV columns: scenario, model, rep, verdict, axes, info. The TSV is the truth — verify from it, not from prose reports. - Judge (E1 language axes only): frozen rubric in `judge-rubric.md`; stub with @@ -141,7 +141,7 @@ The first live E1P rep (sonnet, counted: FAIL, `B:fail-mechanical`) falsified th premise that the orchestrator can see `resolvedModel` in the launch stub — the model-visible launch result carries no model info, so the first shipped wording ("compare the resolved model reported in the launch result") was unactionable and E1 was unwinnable by a compliant model. -Fixed in ORCHESTRATION.md wording ONLY (subagent model self-report mechanism — subagents know +Fixed in prompts/session-start/10-orchestration.md wording ONLY (subagent model self-report mechanism — subagents know their exact model ID; verified live). Scenarios, checker, and rubric were NOT touched. ## Fixture diff --git a/plugins/os-orchestration/eval/bin/check b/plugins/os-context/eval/bin/check similarity index 99% rename from plugins/os-orchestration/eval/bin/check rename to plugins/os-context/eval/bin/check index d50589b..e62e83d 100755 --- a/plugins/os-orchestration/eval/bin/check +++ b/plugins/os-context/eval/bin/check @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # frozen_string_literal: true -# Deterministic-first checker for the os-orchestration eval (E1-E3, E5). +# Deterministic-first checker for the os-context eval (E1-E3, E5). # # Usage: check [--tsv []] # diff --git a/plugins/os-orchestration/eval/bin/run b/plugins/os-context/eval/bin/run similarity index 93% rename from plugins/os-orchestration/eval/bin/run rename to plugins/os-context/eval/bin/run index 8975548..5079e53 100755 --- a/plugins/os-orchestration/eval/bin/run +++ b/plugins/os-context/eval/bin/run @@ -1,7 +1,7 @@ #!/usr/bin/env bash -# Headless runner for the os-orchestration eval — the ONLY valid execution mode +# Headless runner for the os-context eval — the ONLY valid execution mode # (mid-session orchestration behavior under the real global SessionStart-injected -# ORCHESTRATION.md; fresh `claude -p` per rep with cwd = sandbox). +# prompts/session-start/10-orchestration.md; fresh `claude -p` per rep with cwd = sandbox). # # Usage: run [--reps N] [--results FILE] # diff --git a/plugins/os-orchestration/eval/bin/sandbox b/plugins/os-context/eval/bin/sandbox similarity index 100% rename from plugins/os-orchestration/eval/bin/sandbox rename to plugins/os-context/eval/bin/sandbox diff --git a/plugins/os-orchestration/eval/bin/self-test b/plugins/os-context/eval/bin/self-test similarity index 99% rename from plugins/os-orchestration/eval/bin/self-test rename to plugins/os-context/eval/bin/self-test index f0f8174..496e180 100755 --- a/plugins/os-orchestration/eval/bin/self-test +++ b/plugins/os-context/eval/bin/self-test @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # frozen_string_literal: true -# Model-free self-test for the os-orchestration eval checker. Fabricates +# Model-free self-test for the os-context eval checker. Fabricates # session-format transcripts and asserts verdicts in BOTH directions per # scenario, including at least one SHIPPED-INSTRUCTION-COMPLIANT transcript # per positive scenario (the Eval C conformance lesson: self-tests that only diff --git a/plugins/os-orchestration/eval/fixture/project/README.md b/plugins/os-context/eval/fixture/project/README.md similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/README.md rename to plugins/os-context/eval/fixture/project/README.md diff --git a/plugins/os-orchestration/eval/fixture/project/bin/gen-logs b/plugins/os-context/eval/fixture/project/bin/gen-logs similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/bin/gen-logs rename to plugins/os-context/eval/fixture/project/bin/gen-logs diff --git a/plugins/os-orchestration/eval/fixture/project/docs/architecture.md b/plugins/os-context/eval/fixture/project/docs/architecture.md similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/docs/architecture.md rename to plugins/os-context/eval/fixture/project/docs/architecture.md diff --git a/plugins/os-orchestration/eval/fixture/project/docs/deploy.md b/plugins/os-context/eval/fixture/project/docs/deploy.md similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/docs/deploy.md rename to plugins/os-context/eval/fixture/project/docs/deploy.md diff --git a/plugins/os-orchestration/eval/fixture/project/docs/handler-spec.md b/plugins/os-context/eval/fixture/project/docs/handler-spec.md similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/docs/handler-spec.md rename to plugins/os-context/eval/fixture/project/docs/handler-spec.md diff --git a/plugins/os-orchestration/eval/fixture/project/docs/probe-spec.md b/plugins/os-context/eval/fixture/project/docs/probe-spec.md similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/docs/probe-spec.md rename to plugins/os-context/eval/fixture/project/docs/probe-spec.md diff --git a/plugins/os-orchestration/eval/fixture/project/docs/runbook.md b/plugins/os-context/eval/fixture/project/docs/runbook.md similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/docs/runbook.md rename to plugins/os-context/eval/fixture/project/docs/runbook.md diff --git a/plugins/os-orchestration/eval/fixture/project/package.json b/plugins/os-context/eval/fixture/project/package.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/package.json rename to plugins/os-context/eval/fixture/project/package.json diff --git a/plugins/os-orchestration/eval/fixture/project/services/analytics.json b/plugins/os-context/eval/fixture/project/services/analytics.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/services/analytics.json rename to plugins/os-context/eval/fixture/project/services/analytics.json diff --git a/plugins/os-orchestration/eval/fixture/project/services/audit.json b/plugins/os-context/eval/fixture/project/services/audit.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/services/audit.json rename to plugins/os-context/eval/fixture/project/services/audit.json diff --git a/plugins/os-orchestration/eval/fixture/project/services/billing.json b/plugins/os-context/eval/fixture/project/services/billing.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/services/billing.json rename to plugins/os-context/eval/fixture/project/services/billing.json diff --git a/plugins/os-orchestration/eval/fixture/project/services/crm.json b/plugins/os-context/eval/fixture/project/services/crm.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/services/crm.json rename to plugins/os-context/eval/fixture/project/services/crm.json diff --git a/plugins/os-orchestration/eval/fixture/project/services/email.json b/plugins/os-context/eval/fixture/project/services/email.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/services/email.json rename to plugins/os-context/eval/fixture/project/services/email.json diff --git a/plugins/os-orchestration/eval/fixture/project/services/reports.json b/plugins/os-context/eval/fixture/project/services/reports.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/services/reports.json rename to plugins/os-context/eval/fixture/project/services/reports.json diff --git a/plugins/os-orchestration/eval/fixture/project/services/search.json b/plugins/os-context/eval/fixture/project/services/search.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/services/search.json rename to plugins/os-context/eval/fixture/project/services/search.json diff --git a/plugins/os-orchestration/eval/fixture/project/services/sms.json b/plugins/os-context/eval/fixture/project/services/sms.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/services/sms.json rename to plugins/os-context/eval/fixture/project/services/sms.json diff --git a/plugins/os-orchestration/eval/fixture/project/services/webhooks.json b/plugins/os-context/eval/fixture/project/services/webhooks.json similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/services/webhooks.json rename to plugins/os-context/eval/fixture/project/services/webhooks.json diff --git a/plugins/os-orchestration/eval/fixture/project/src/auth.js b/plugins/os-context/eval/fixture/project/src/auth.js similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/src/auth.js rename to plugins/os-context/eval/fixture/project/src/auth.js diff --git a/plugins/os-orchestration/eval/fixture/project/src/metrics.js b/plugins/os-context/eval/fixture/project/src/metrics.js similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/src/metrics.js rename to plugins/os-context/eval/fixture/project/src/metrics.js diff --git a/plugins/os-orchestration/eval/fixture/project/src/relay.js b/plugins/os-context/eval/fixture/project/src/relay.js similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/src/relay.js rename to plugins/os-context/eval/fixture/project/src/relay.js diff --git a/plugins/os-orchestration/eval/fixture/project/src/retry.js b/plugins/os-context/eval/fixture/project/src/retry.js similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/src/retry.js rename to plugins/os-context/eval/fixture/project/src/retry.js diff --git a/plugins/os-orchestration/eval/fixture/project/src/router.js b/plugins/os-context/eval/fixture/project/src/router.js similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/src/router.js rename to plugins/os-context/eval/fixture/project/src/router.js diff --git a/plugins/os-orchestration/eval/fixture/project/src/store.js b/plugins/os-context/eval/fixture/project/src/store.js similarity index 100% rename from plugins/os-orchestration/eval/fixture/project/src/store.js rename to plugins/os-context/eval/fixture/project/src/store.js diff --git a/plugins/os-orchestration/eval/judge-rubric.md b/plugins/os-context/eval/judge-rubric.md similarity index 96% rename from plugins/os-orchestration/eval/judge-rubric.md rename to plugins/os-context/eval/judge-rubric.md index c53d713..456fbac 100644 --- a/plugins/os-orchestration/eval/judge-rubric.md +++ b/plugins/os-context/eval/judge-rubric.md @@ -1,4 +1,4 @@ -# Frozen judge rubrics — os-orchestration eval (E1 language axes only) +# Frozen judge rubrics — os-context eval (E1 language axes only) Used only when the mechanical scan is inconclusive. FROZEN during any wording loop. The judge receives one rubric section plus the transcript text, and must answer with diff --git a/plugins/os-orchestration/eval/results/2026-07-06-baseline-grid.tsv b/plugins/os-context/eval/results/2026-07-06-baseline-grid.tsv similarity index 100% rename from plugins/os-orchestration/eval/results/2026-07-06-baseline-grid.tsv rename to plugins/os-context/eval/results/2026-07-06-baseline-grid.tsv diff --git a/plugins/os-orchestration/eval/results/2026-07-08-wording-confirm-grid.tsv b/plugins/os-context/eval/results/2026-07-08-wording-confirm-grid.tsv similarity index 100% rename from plugins/os-orchestration/eval/results/2026-07-08-wording-confirm-grid.tsv rename to plugins/os-context/eval/results/2026-07-08-wording-confirm-grid.tsv diff --git a/plugins/os-orchestration/eval/results/2026-07-08-wording-iter1.tsv b/plugins/os-context/eval/results/2026-07-08-wording-iter1.tsv similarity index 100% rename from plugins/os-orchestration/eval/results/2026-07-08-wording-iter1.tsv rename to plugins/os-context/eval/results/2026-07-08-wording-iter1.tsv diff --git a/plugins/os-orchestration/eval/results/2026-07-08-wording-reserve-grid.tsv b/plugins/os-context/eval/results/2026-07-08-wording-reserve-grid.tsv similarity index 100% rename from plugins/os-orchestration/eval/results/2026-07-08-wording-reserve-grid.tsv rename to plugins/os-context/eval/results/2026-07-08-wording-reserve-grid.tsv diff --git a/plugins/os-orchestration/eval/results/2026-07-08-ws4-baseline-grid.tsv b/plugins/os-context/eval/results/2026-07-08-ws4-baseline-grid.tsv similarity index 100% rename from plugins/os-orchestration/eval/results/2026-07-08-ws4-baseline-grid.tsv rename to plugins/os-context/eval/results/2026-07-08-ws4-baseline-grid.tsv diff --git a/plugins/os-orchestration/eval/scenarios-reserve/E1N-datalayer-review.md b/plugins/os-context/eval/scenarios-reserve/E1N-datalayer-review.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios-reserve/E1N-datalayer-review.md rename to plugins/os-context/eval/scenarios-reserve/E1N-datalayer-review.md diff --git a/plugins/os-orchestration/eval/scenarios-reserve/E1P-datalayer-review.md b/plugins/os-context/eval/scenarios-reserve/E1P-datalayer-review.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios-reserve/E1P-datalayer-review.md rename to plugins/os-context/eval/scenarios-reserve/E1P-datalayer-review.md diff --git a/plugins/os-orchestration/eval/scenarios-reserve/E2N-port-fix.md b/plugins/os-context/eval/scenarios-reserve/E2N-port-fix.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios-reserve/E2N-port-fix.md rename to plugins/os-context/eval/scenarios-reserve/E2N-port-fix.md diff --git a/plugins/os-orchestration/eval/scenarios-reserve/E2P-timeout-bump.md b/plugins/os-context/eval/scenarios-reserve/E2P-timeout-bump.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios-reserve/E2P-timeout-bump.md rename to plugins/os-context/eval/scenarios-reserve/E2P-timeout-bump.md diff --git a/plugins/os-orchestration/eval/scenarios-reserve/E3N-env-var.md b/plugins/os-context/eval/scenarios-reserve/E3N-env-var.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios-reserve/E3N-env-var.md rename to plugins/os-context/eval/scenarios-reserve/E3N-env-var.md diff --git a/plugins/os-orchestration/eval/scenarios-reserve/E3P-retry-storm.md b/plugins/os-context/eval/scenarios-reserve/E3P-retry-storm.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios-reserve/E3P-retry-storm.md rename to plugins/os-context/eval/scenarios-reserve/E3P-retry-storm.md diff --git a/plugins/os-orchestration/eval/scenarios-reserve/E5N-quota-rebalance.md b/plugins/os-context/eval/scenarios-reserve/E5N-quota-rebalance.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios-reserve/E5N-quota-rebalance.md rename to plugins/os-context/eval/scenarios-reserve/E5N-quota-rebalance.md diff --git a/plugins/os-orchestration/eval/scenarios-reserve/E5P-probe-rollout.md b/plugins/os-context/eval/scenarios-reserve/E5P-probe-rollout.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios-reserve/E5P-probe-rollout.md rename to plugins/os-context/eval/scenarios-reserve/E5P-probe-rollout.md diff --git a/plugins/os-orchestration/eval/scenarios/E1N-pipeline-review.md b/plugins/os-context/eval/scenarios/E1N-pipeline-review.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios/E1N-pipeline-review.md rename to plugins/os-context/eval/scenarios/E1N-pipeline-review.md diff --git a/plugins/os-orchestration/eval/scenarios/E1P-pipeline-review.md b/plugins/os-context/eval/scenarios/E1P-pipeline-review.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios/E1P-pipeline-review.md rename to plugins/os-context/eval/scenarios/E1P-pipeline-review.md diff --git a/plugins/os-orchestration/eval/scenarios/E2N-readme-typo.md b/plugins/os-context/eval/scenarios/E2N-readme-typo.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios/E2N-readme-typo.md rename to plugins/os-context/eval/scenarios/E2N-readme-typo.md diff --git a/plugins/os-orchestration/eval/scenarios/E2P-retry-policy.md b/plugins/os-context/eval/scenarios/E2P-retry-policy.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios/E2P-retry-policy.md rename to plugins/os-context/eval/scenarios/E2P-retry-policy.md diff --git a/plugins/os-orchestration/eval/scenarios/E3N-brand-spelling.md b/plugins/os-context/eval/scenarios/E3N-brand-spelling.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios/E3N-brand-spelling.md rename to plugins/os-context/eval/scenarios/E3N-brand-spelling.md diff --git a/plugins/os-orchestration/eval/scenarios/E3P-dropped-events.md b/plugins/os-context/eval/scenarios/E3P-dropped-events.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios/E3P-dropped-events.md rename to plugins/os-context/eval/scenarios/E3P-dropped-events.md diff --git a/plugins/os-orchestration/eval/scenarios/E5N-limit-rebalance.md b/plugins/os-context/eval/scenarios/E5N-limit-rebalance.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios/E5N-limit-rebalance.md rename to plugins/os-context/eval/scenarios/E5N-limit-rebalance.md diff --git a/plugins/os-orchestration/eval/scenarios/E5P-handler-rollout.md b/plugins/os-context/eval/scenarios/E5P-handler-rollout.md similarity index 100% rename from plugins/os-orchestration/eval/scenarios/E5P-handler-rollout.md rename to plugins/os-context/eval/scenarios/E5P-handler-rollout.md diff --git a/plugins/os-orchestration/hooks/hooks.json b/plugins/os-context/hooks/hooks.json similarity index 100% rename from plugins/os-orchestration/hooks/hooks.json rename to plugins/os-context/hooks/hooks.json diff --git a/plugins/os-context/hooks/inject.py b/plugins/os-context/hooks/inject.py new file mode 100755 index 0000000..aef2986 --- /dev/null +++ b/plugins/os-context/hooks/inject.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""SessionStart hook: compose prompts/session-start/*.md into one +additionalContext block. + +Fires on startup/resume/clear, and on 'compact' (matcher includes it) so the +composed context survives a context-compaction pass, not just the first turn. + +Prompt files live under prompts//*.md; only the session-start event is +implemented today (SessionEnd cannot inject context — ADR-028). Files are +concatenated in filename sort order (e.g. 10-orchestration.md) with no added +separator between them, so a single prompt file present alone reproduces the +exact bytes it contributes. +""" +import glob +import json +import os +import sys + +PLUGIN_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SESSION_START_DIR = os.path.join(PLUGIN_ROOT, "prompts", "session-start") + +# Deterministic injected-context budget, in lines. Below WARN_LINE_BUDGET, +# inject normally. Between the two, still inject but warn on stderr. Above +# REFUSE_LINE_BUDGET, refuse to inject anything and warn on stderr instead. +WARN_LINE_BUDGET = 120 +REFUSE_LINE_BUDGET = 240 + + +def prompt_files(session_start_dir=None): + if session_start_dir is None: + session_start_dir = SESSION_START_DIR + return sorted(glob.glob(os.path.join(session_start_dir, "*.md"))) + + +def compose(paths): + """Concatenate prompt file contents in order with no added separators.""" + parts = [] + for path in paths: + with open(path) as f: + parts.append(f.read()) + return "".join(parts) + + +def main(): + paths = prompt_files() + if not paths: + return + + context = compose(paths) + line_count = len(context.splitlines()) + + if line_count > REFUSE_LINE_BUDGET: + print( + f"os-context: session-start prompt budget exceeded " + f"({line_count} lines > {REFUSE_LINE_BUDGET}) — refusing to inject.", + file=sys.stderr, + ) + return + + if line_count > WARN_LINE_BUDGET: + print( + f"os-context: session-start prompt budget exceeded " + f"({line_count} lines > {WARN_LINE_BUDGET}) — injecting anyway.", + file=sys.stderr, + ) + + print(json.dumps({ + "hookSpecificOutput": { + "hookEventName": "SessionStart", + "additionalContext": context, + } + })) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/plugins/os-orchestration/ORCHESTRATION.md b/plugins/os-context/prompts/session-start/10-orchestration.md similarity index 100% rename from plugins/os-orchestration/ORCHESTRATION.md rename to plugins/os-context/prompts/session-start/10-orchestration.md diff --git a/plugins/os-context/prompts/session-start/20-kickoff-conventions.md b/plugins/os-context/prompts/session-start/20-kickoff-conventions.md new file mode 100644 index 0000000..6e4a784 --- /dev/null +++ b/plugins/os-context/prompts/session-start/20-kickoff-conventions.md @@ -0,0 +1,23 @@ +# Kickoff conventions + +Standing rules — do not re-derive or ask about these per project: + +- **TDD**: red-green-refactor by default for new features/bug fixes. +- **Sandi Metz OOP** style for Ruby; Ruby > Python > bash for scripts; Python OK + for native CC tooling. +- **Vertical slices / tracer bullets**: ship a thin end-to-end path before + breadth; prefer this over building one layer fully before the next. +- **Audit defaults before customizing**: check a tool's shipped + defaults/recommendations before building a custom benchmark or config. +- **lowercase-kebab-case** filenames unless a format requires otherwise. +- Delegation defaults (when/how to dispatch subagents, risk-escalation + read-only → guarded-write → verify) — see `10-orchestration.md` in this + same context block. + +## Vault note pointers + +Query on demand, don't front-load: +- `tool/` — before first use this session of a tool with vault notes. +- `client/` — before starting work involving a client. +- `domain/llm-evaluation` + `type/eval-results` — before designing/running an + eval. diff --git a/plugins/os-context/prompts/session-start/30-standing-safety.md b/plugins/os-context/prompts/session-start/30-standing-safety.md new file mode 100644 index 0000000..22e6d1e --- /dev/null +++ b/plugins/os-context/prompts/session-start/30-standing-safety.md @@ -0,0 +1,9 @@ +# Standing safety rules + +- **Never echo, print, or log a secret value** (API keys, tokens, passwords, + `.env`/credentials file contents) into chat, commits, or generated files — + availability-awareness (confirming a secret exists/where) is fine; printing + its value is not. +- **Release/deploy scripts require explicit user authorization** before + running — treat any `release.sh`-equivalent or production deploy command as + gated, never inferred from context alone. diff --git a/plugins/os-context/prompts/session-start/40-decision-memos.md b/plugins/os-context/prompts/session-start/40-decision-memos.md new file mode 100644 index 0000000..65eed26 --- /dev/null +++ b/plugins/os-context/prompts/session-start/40-decision-memos.md @@ -0,0 +1,11 @@ +# Decision-memo format + +When surfacing a decision, tradeoff, or interpretation call, never bury it in +a status paragraph. Use this four-part memo: + +1. **Headline** — one line naming the decision. +2. **Options as concrete A/B examples** — actual code/command snippets + side by side when the difference is code; never prose descriptions of the + difference. +3. **Recommendation** — with confidence % and the single deciding reason. +4. **Explicit ask + default** — state what happens if the user says nothing. diff --git a/plugins/os-orchestration/skills/audit-sessions/SKILL.md b/plugins/os-context/skills/audit-sessions/SKILL.md similarity index 79% rename from plugins/os-orchestration/skills/audit-sessions/SKILL.md rename to plugins/os-context/skills/audit-sessions/SKILL.md index 57ed428..76ac39e 100644 --- a/plugins/os-orchestration/skills/audit-sessions/SKILL.md +++ b/plugins/os-context/skills/audit-sessions/SKILL.md @@ -1,8 +1,8 @@ --- -description: Run the biweekly orchestration IRL audit - deterministic transcript stats precompute, then auditor fan-out judging flagged regions against ORCHESTRATION.md rules, then a tiered tune-up report. Works from any directory. Trigger via the os-status due nudge or the Planka recurrence card. +description: Run the biweekly orchestration IRL audit - deterministic transcript stats precompute, then auditor fan-out judging flagged regions against the shipped `prompts/session-start/10-orchestration.md` rules, then a tiered tune-up report. Works from any directory. Trigger via the os-status due nudge or the Planka recurrence card. --- -# Audit production sessions against the ORCHESTRATION.md rules +# Audit production sessions against the shipped orchestration rules You are running the recurring (biweekly) IRL audit of the shipped delegation-economics rules. The signal source is real production session transcripts — NEVER eval grid @@ -24,10 +24,10 @@ guard already defers the live session to the next run.) - `--since` defaults to the last ledger row's date. It refuses to run without a prior row; a first-ever run needs an explicit `--since YYYY-MM-DD`. -- Output: `~/.local/state/os-orchestration/audit/run-/` containing `stats.tsv` +- Output: `~/.local/state/os-context/audit/run-/` containing `stats.tsv` (per-session deterministic columns), `flags.md` (heuristic missed-delegation regions), and `sheets/*.json` (full per-session fact-sheets). One row is appended to - `~/.local/state/os-orchestration/audit/metrics.tsv` — the trend ledger. Never edit the + `~/.local/state/os-context/audit/metrics.tsv` — the trend ledger. Never edit the ledger by hand; never re-derive these numbers with a model. ## Step 2 — Auditor fan-out (judgment only where code can't judge) @@ -58,10 +58,11 @@ Each auditor prompt must include: ## Step 4 — Stop for the human -Report the tiers and trend verdicts, then STOP. Do not edit ORCHESTRATION.md without -explicit approval — wording changes are a human decision (injected-token budget; grid -validation is unavailable). If approved later: edit -`~/dev/cc-os/plugins/os-orchestration/ORCHESTRATION.md`, run `~/dev/cc-os/bin/refresh-plugins`, +Report the tiers and trend verdicts, then STOP. Do not edit +`prompts/session-start/10-orchestration.md` without explicit approval — wording changes +are a human decision (injected-token budget; grid validation is unavailable). If approved +later: edit +`~/dev/cc-os/plugins/os-context/prompts/session-start/10-orchestration.md`, run `~/dev/cc-os/bin/refresh-plugins`, and record the change in `~/dev/cc-os/docs/implementation-status.md`. Finally, if a Planka card for this audit exists on the backlog board, move it to diff --git a/plugins/os-orchestration/skills/audit-sessions/references/rubric.md b/plugins/os-context/skills/audit-sessions/references/rubric.md similarity index 96% rename from plugins/os-orchestration/skills/audit-sessions/references/rubric.md rename to plugins/os-context/skills/audit-sessions/references/rubric.md index 2b1ba13..25ff3d9 100644 --- a/plugins/os-orchestration/skills/audit-sessions/references/rubric.md +++ b/plugins/os-context/skills/audit-sessions/references/rubric.md @@ -1,4 +1,4 @@ -# IRL audit rubric — shipped ORCHESTRATION.md delegation-economics rules +# IRL audit rubric — shipped prompts/session-start/10-orchestration.md delegation-economics rules Audit each assigned session against these shipped rules. A "finding" is a concrete violation, near-miss, or friction point, with evidence (jsonl line number / short quote). diff --git a/plugins/os-context/tests/hook_test.py b/plugins/os-context/tests/hook_test.py new file mode 100644 index 0000000..023f9d2 --- /dev/null +++ b/plugins/os-context/tests/hook_test.py @@ -0,0 +1,146 @@ +"""Tests for hooks/inject.py. Run: python3 tests/hook_test.py""" + +import json +import sys +import unittest +from pathlib import Path +from tempfile import TemporaryDirectory + +PLUGIN_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(PLUGIN_ROOT / "hooks")) + +import inject # noqa: E402 + + +def write_prompts(directory, files): + """files: dict of filename -> content, written under directory.""" + directory.mkdir(parents=True, exist_ok=True) + for name, content in files.items(): + (directory / name).write_text(content) + + +class ComposeOrderTest(unittest.TestCase): + def test_concatenates_in_filename_sort_order(self): + with TemporaryDirectory() as tmp: + d = Path(tmp) + write_prompts(d, {"20-second.md": "second\n", "10-first.md": "first\n"}) + paths = inject.prompt_files(d) + self.assertEqual(["10-first.md", "20-second.md"], [Path(p).name for p in paths]) + self.assertEqual("first\nsecond\n", inject.compose(paths)) + + def test_no_separator_added_between_files(self): + with TemporaryDirectory() as tmp: + d = Path(tmp) + write_prompts(d, {"10-a.md": "a", "20-b.md": "b"}) + paths = inject.prompt_files(d) + self.assertEqual("ab", inject.compose(paths)) + + +class ByteIdentityTest(unittest.TestCase): + """With only the real 10-orchestration.md present, the composer's output + must be byte-identical to what the old single-file inject.py emitted + directly from ORCHESTRATION.md's contents — no added separators or + trailing newlines.""" + + def test_single_file_matches_its_raw_contents_exactly(self): + real_file = PLUGIN_ROOT / "prompts" / "session-start" / "10-orchestration.md" + with TemporaryDirectory() as tmp: + d = Path(tmp) + d.mkdir(exist_ok=True) + shadow = d / "10-orchestration.md" + shadow.write_bytes(real_file.read_bytes()) + paths = inject.prompt_files(d) + composed = inject.compose(paths) + self.assertEqual(real_file.read_text(), composed) + + def test_real_prompts_dir_composes_all_files_byte_identical_concatenation(self): + paths = inject.prompt_files() + self.assertGreaterEqual(len(paths), 1) + expected = "".join(Path(p).read_text() for p in paths) + self.assertEqual(expected, inject.compose(paths)) + + +class BudgetTest(unittest.TestCase): + def make_dir_with_lines(self, tmp, n_lines): + d = Path(tmp) + d.mkdir(exist_ok=True) + content = "\n".join(f"line {i}" for i in range(n_lines)) + "\n" + (d / "10-big.md").write_text(content) + return d + + def test_under_warn_budget_injects_silently(self): + with TemporaryDirectory() as tmp: + d = self.make_dir_with_lines(tmp, inject.WARN_LINE_BUDGET - 1) + paths = inject.prompt_files(d) + context = inject.compose(paths) + self.assertLessEqual(len(context.splitlines()), inject.WARN_LINE_BUDGET) + + def test_over_warn_under_refuse_still_injects(self): + with TemporaryDirectory() as tmp: + d = self.make_dir_with_lines( + tmp, inject.WARN_LINE_BUDGET + 1 + ) + paths = inject.prompt_files(d) + context = inject.compose(paths) + line_count = len(context.splitlines()) + self.assertGreater(line_count, inject.WARN_LINE_BUDGET) + self.assertLessEqual(line_count, inject.REFUSE_LINE_BUDGET) + # Injection still happens: compose() returns the full content. + self.assertTrue(context) + + def test_over_refuse_budget_line_count_detected(self): + with TemporaryDirectory() as tmp: + d = self.make_dir_with_lines(tmp, inject.REFUSE_LINE_BUDGET + 1) + paths = inject.prompt_files(d) + context = inject.compose(paths) + self.assertGreater(len(context.splitlines()), inject.REFUSE_LINE_BUDGET) + + def test_main_warns_on_stderr_between_warn_and_refuse(self, ): + import io + from unittest import mock + + with TemporaryDirectory() as tmp: + d = self.make_dir_with_lines(tmp, inject.WARN_LINE_BUDGET + 1) + stderr = io.StringIO() + stdout = io.StringIO() + with mock.patch.object(inject, "SESSION_START_DIR", str(d)), \ + mock.patch("sys.stderr", stderr), \ + mock.patch("sys.stdout", stdout): + inject.main() + self.assertIn("injecting anyway", stderr.getvalue()) + payload = json.loads(stdout.getvalue()) + self.assertIn("additionalContext", payload["hookSpecificOutput"]) + + def test_main_refuses_and_emits_nothing_over_refuse_budget(self): + import io + from unittest import mock + + with TemporaryDirectory() as tmp: + d = self.make_dir_with_lines(tmp, inject.REFUSE_LINE_BUDGET + 1) + stderr = io.StringIO() + stdout = io.StringIO() + with mock.patch.object(inject, "SESSION_START_DIR", str(d)), \ + mock.patch("sys.stderr", stderr), \ + mock.patch("sys.stdout", stdout): + inject.main() + self.assertIn("refusing to inject", stderr.getvalue()) + self.assertEqual("", stdout.getvalue()) + + +class NoPromptsTest(unittest.TestCase): + def test_main_emits_nothing_when_dir_empty(self): + import io + from unittest import mock + + with TemporaryDirectory() as tmp: + d = Path(tmp) / "empty" + d.mkdir() + stdout = io.StringIO() + with mock.patch.object(inject, "SESSION_START_DIR", str(d)), \ + mock.patch("sys.stdout", stdout): + inject.main() + self.assertEqual("", stdout.getvalue()) + + +if __name__ == "__main__": + unittest.main() diff --git a/plugins/os-orchestration/.claude-plugin/plugin.json b/plugins/os-orchestration/.claude-plugin/plugin.json deleted file mode 100644 index 5e92cd5..0000000 --- a/plugins/os-orchestration/.claude-plugin/plugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "os-orchestration", - "version": "0.1.0", - "description": "Session orchestration rules (delegation threshold, explicit model routing on Agent spawns) injected at session start and after compaction." -} diff --git a/plugins/os-orchestration/hooks/inject.py b/plugins/os-orchestration/hooks/inject.py deleted file mode 100755 index 9414fcf..0000000 --- a/plugins/os-orchestration/hooks/inject.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python3 -"""SessionStart hook: inject ORCHESTRATION.md as additionalContext. - -Fires on startup/resume/clear, and on 'compact' (matcher includes it) so the -rules survive a context-compaction pass, not just the first turn. -""" -import json -import os -import sys - -PLUGIN_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -ORCHESTRATION_FILE = os.path.join(PLUGIN_ROOT, "ORCHESTRATION.md") - - -def main(): - try: - with open(ORCHESTRATION_FILE) as f: - context = f.read() - except OSError: - return - - print(json.dumps({ - "hookSpecificOutput": { - "hookEventName": "SessionStart", - "additionalContext": context, - } - })) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/plugins/os-status/hooks/checks.py b/plugins/os-status/hooks/checks.py index f6ff749..f682ec2 100644 --- a/plugins/os-status/hooks/checks.py +++ b/plugins/os-status/hooks/checks.py @@ -163,7 +163,7 @@ def vault_hub_note_present(ctx: Ctx) -> CheckResult: AUDIT_LEDGER_ENV = "OS_ORCH_AUDIT_LEDGER" -DEFAULT_AUDIT_LEDGER = "~/.local/state/os-orchestration/audit/metrics.tsv" +DEFAULT_AUDIT_LEDGER = "~/.local/state/os-context/audit/metrics.tsv" AUDIT_INTERVAL_DAYS = 14 @@ -177,7 +177,7 @@ def _last_audit_date(ledger: Path): def orchestration_audit_due(ctx: Ctx) -> CheckResult: """Nudge when the biweekly orchestration IRL audit is overdue. The ledger - (metrics.tsv) is appended deterministically by os-orchestration's + (metrics.tsv) is appended deterministically by os-context's audit/bin/audit-stats; this check only reads its last row's date.""" ledger = Path( os.path.expanduser(ctx.environ.get(AUDIT_LEDGER_ENV, DEFAULT_AUDIT_LEDGER)) @@ -190,7 +190,7 @@ def orchestration_audit_due(ctx: Ctx) -> CheckResult: return OK return warn( f"orchestration IRL audit due (last run {last.isoformat()}, {days} days" - " ago) — run /os-orchestration:audit-sessions (works from any project;" + " ago) — run /os-context:audit-sessions (works from any project;" " deterministic stats precompute, then auditor fan-out)." ) @@ -295,7 +295,7 @@ REGISTRY = [ "orchestration-audit-due", orchestration_audit_due, project_scoped=False, - remediation="/os-orchestration:audit-sessions", + remediation="/os-context:audit-sessions", ), Check( "tracker-configured", diff --git a/plugins/os-status/tests/hook_test.py b/plugins/os-status/tests/hook_test.py index 0f350e5..bc3df12 100644 --- a/plugins/os-status/tests/hook_test.py +++ b/plugins/os-status/tests/hook_test.py @@ -232,7 +232,7 @@ class AuditDueCheckTest(unittest.TestCase): result = checks.orchestration_audit_due(make_ctx(environ=env)) self.assertEqual("warn", result.status) self.assertIn("2026-02-01", result.message) - self.assertIn("/os-orchestration:audit-sessions", result.message) + self.assertIn("/os-context:audit-sessions", result.message) def test_corrupt_ledger_reads_as_missing(self): with TemporaryDirectory() as tmp: