@event4u/agent-config 14.21.0 → 14.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +164 -187
- package/CREDITS.md +37 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/inbox.md +380 -5
- package/dist/agent-src/commands/analyze/repo.md +4 -2
- package/dist/agent-src/commands/pr/merge.md +47 -38
- package/dist/agent-src/commands/roadmap/next.md +8 -0
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +12 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
- package/dist/agent-src/rules/token-efficiency.md +26 -3
- package/dist/agent-src/scripts/_cli_entry.ts +51 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
- package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
- package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/dist/agent-src/scripts/stubs_due.ts +4 -18
- package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/dist/agent-src/skills/code-intelligence/SKILL.md +82 -1
- package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +52 -2
- package/dist/agent-src/templates/agent-settings.md +0 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/_cli_entry.js +8 -0
- package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
- package/dist/cli-delegate/archival_sweep.js +8 -0
- package/dist/cli-delegate/archive_completed_roadmaps.js +542 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-2KKXJW25.js} +23 -9
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-IAI3XL43.js} +15 -3
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-KCNTUSPI.js} +7 -1
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
- package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
- package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
- package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
- package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
- package/dist/cli-delegate/cmd_conformance.js +2 -2
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +45 -13
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_session_recycle.js +4 -14
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/cli-delegate/dashboard_mode.js +16 -0
- package/dist/cli-delegate/gate_budget.js +24 -0
- package/dist/cli-delegate/gate_execute.js +25 -0
- package/dist/cli-delegate/guarded_baseline.js +18 -0
- package/dist/cli-delegate/resume_probe.js +22 -0
- package/dist/cli-delegate/roadmap_gates.js +638 -0
- package/dist/cli-delegate/roadmap_set_step.js +173 -0
- package/dist/cli-delegate/stub_queue.js +47 -0
- package/dist/cli-delegate/stubs_due.js +38 -0
- package/dist/cli-delegate/ticket_bundles.js +59 -0
- package/dist/cli-delegate/update_roadmap_progress.js +60 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +13 -13
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1813 -946
- package/dist/install/claudeLayerCarriage.js +237 -0
- package/dist/install/claudePathsPlan.js +79 -0
- package/dist/install/hostLayerFingerprint.js +1 -1
- package/dist/install/install.mjs +21 -3
- package/dist/install/partitionEligibility.js +231 -117
- package/dist/install/ruleLayerPartition.js +10 -7
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +10870 -7641
- package/dist/scripts/_lib/lean_projection_mode.js +108 -0
- package/dist/scripts/_lib/router_match.js +243 -0
- package/dist/scripts/_lib/rule_injection.js +216 -0
- package/dist/scripts/condense.js +52 -45
- package/dist/scripts/project_thin_rules.js +139 -19
- package/dist/server/schemas/settings.js +16 -3
- package/docs/MIGRATION.md +105 -1
- package/docs/contracts/settings-classes.md +40 -8
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
- package/llms.txt +1 -1
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
- package/src/agent-src/scripts/roadmap_gates.ts +4 -20
- package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/src/agent-src/scripts/stubs_due.ts +4 -18
- package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/src/config/agent-settings.template.yml +47 -1
- package/src/config/continuity-surface.json +516 -0
- package/src/config/estate-count-budget.json +1 -1
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +29 -8
- package/src/config/gate-violation-baselines.json +8 -8
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-token-budget.json +7 -6
- package/src/config/host-injection-effect.json +73 -0
- package/src/config/packed-binary-manifest.json +33 -0
- package/src/config/preamble-payload-budget.json +112 -105
- package/src/config/publish-surface.json +4 -2
- package/src/config/routing-coverage-seed.json +9 -2
- package/src/install/claudeLayerCarriage.ts +273 -0
- package/src/install/claudePathsPlan.ts +76 -0
- package/src/install/hostLayerFingerprint.ts +1 -1
- package/src/install/partitionEligibility.ts +261 -127
- package/src/install/ruleLayerPartition.ts +19 -23
- package/src/scripts/_cli/cmd_session_recycle.ts +2 -25
- package/src/scripts/_cli/handoff_sessions.ts +14 -44
- package/src/scripts/_dispatch.bash +77 -27
- package/src/scripts/_lib/auto_dispatch.ts +2 -2
- package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
- package/src/scripts/_lib/continuity_slot.ts +295 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/continuity_writer.ts +253 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +86 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/lean_projection_mode.ts +127 -0
- package/src/scripts/_lib/packed_binary_predicate.ts +262 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
- package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/session_eol.ts +44 -0
- package/src/scripts/_lib/session_index_trust.ts +346 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/standing_bound_ratchet.ts +154 -0
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
- package/src/scripts/ai_council/api_transport.ts +5 -0
- package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
- package/src/scripts/ai_council/prompts.ts +9 -0
- package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
- package/src/scripts/audit_mcp_tools.ts +69 -25
- package/src/scripts/build_mcp_catalog.ts +24 -1
- package/src/scripts/capture_skill_catalogue.ts +24 -11
- package/src/scripts/check_candidate_lines.ts +499 -0
- package/src/scripts/check_continuity_surface.ts +475 -0
- package/src/scripts/check_dependency_floors.ts +13 -6
- package/src/scripts/check_estate_count.ts +40 -79
- package/src/scripts/check_host_tree_parity.ts +291 -0
- package/src/scripts/check_pack_size.ts +262 -23
- package/src/scripts/check_preamble_payload_budget.ts +136 -7
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_rule_projection_integrity.ts +102 -2
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/ci_settle.ts +205 -5
- package/src/scripts/code_graph/build.ts +217 -15
- package/src/scripts/code_graph/cli.ts +334 -15
- package/src/scripts/code_graph/detect.ts +38 -0
- package/src/scripts/code_graph/extract.ts +11 -0
- package/src/scripts/code_graph/grammar_probe.ts +43 -0
- package/src/scripts/code_graph/loader.ts +88 -14
- package/src/scripts/code_graph/query.ts +144 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +262 -6
- package/src/scripts/code_graph/types.ts +117 -3
- package/src/scripts/code_graph/validate.ts +26 -1
- package/src/scripts/code_graph/verbs.ts +590 -0
- package/src/scripts/condense.ts +54 -42
- package/src/scripts/corpus_manifest.ts +5 -5
- package/src/scripts/council_cli.ts +11 -12
- package/src/scripts/count_intent_disagreement.ts +426 -0
- package/src/scripts/handoff_context_hook.ts +118 -1
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +87 -12
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +201 -0
- package/src/scripts/hooks/concern_registry.ts +4 -2
- package/src/scripts/hooks/host_semantics.ts +1 -1
- package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
- package/src/scripts/hooks/push_settle_hook.ts +3 -2
- package/src/scripts/hooks/rule_inject_hook.ts +108 -21
- package/src/scripts/hooks/session_eol_hook.ts +120 -2
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/hot_context_hook.ts +62 -7
- package/src/scripts/inbox_source_census.ts +322 -0
- package/src/scripts/install-hooks.sh +82 -0
- package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
- package/src/scripts/lint_plan_risk_register.ts +13 -17
- package/src/scripts/lint_roadmap_blockers.ts +164 -1
- package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
- package/src/scripts/mcp_server/graph_tools.ts +294 -0
- package/src/scripts/mcp_server/path_util.ts +31 -0
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
- package/src/scripts/mcp_server/tools.ts +5 -69
- package/src/scripts/media/lib/adapter-contract.md +7 -2
- package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
- package/src/scripts/model_rule_injection.ts +61 -4
- package/src/scripts/project_thin_rules.ts +149 -16
- package/src/scripts/rdp_candidates_delta.ts +276 -0
- package/src/scripts/rdp_quality_eval.ts +217 -19
- package/src/scripts/release_mix_taxonomy.json +3 -2
- package/src/scripts/report_host_injection_effect.ts +189 -0
- package/src/scripts/report_layer_overlap.ts +41 -0
- package/src/scripts/report_skill_menu_census.ts +341 -0
- package/src/scripts/report_standing_payload_by_host.ts +650 -0
- package/src/scripts/schemas/agent-settings.schema.json +7 -1
- package/src/scripts/session_memory_index.ts +81 -8
- package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
- package/src/server/schemas/settings.ts +23 -4
- package/src/vendor/grammars/README.md +86 -0
- package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
- package/src/scripts/_lib/carrier_status.ts +0 -118
- package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
- package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
package/dist/scripts/condense.js
CHANGED
|
@@ -39,7 +39,7 @@ import { _domains_command_logical, artefact_roots as _agent_src_artefact_roots,
|
|
|
39
39
|
// bundled-CLI-entry-guard landmine. Verified before wiring, not assumed.
|
|
40
40
|
import { build_thin } from './project_thin_rules.js';
|
|
41
41
|
import { build_claude_hook_matrix } from './_lib/claude_settings_hooks.js';
|
|
42
|
-
import { normalizeLeanProjectionMode, writesThinFiles } from './_lib/lean_projection_mode.js';
|
|
42
|
+
import { describeDroppedHosts, normalizeLeanProjectionMode, resolveLeanProjectionHosts, thinsHost, writesThinFiles } from './_lib/lean_projection_mode.js';
|
|
43
43
|
import { is_claude_builtin_name } from './_lib/claude_builtin_names.js';
|
|
44
44
|
import { project_settings_path, load_agent_settings } from './_lib/agent_settings.js';
|
|
45
45
|
import { rule_is_compile_enabled } from './_lib/compile_time_toggles.js';
|
|
@@ -232,9 +232,9 @@ export { rule_in_scope } from '../install/ruleInScope.js';
|
|
|
232
232
|
export { CLAUDE_PATHS_PATTERN_BUDGET, _claude_paths_plan, _escape_claude_bracket, _expanded_pattern_count, _has_non_path_trigger, _is_unresolved_placeholder, derive_trigger_globs, } from '../install/claudePathsPlan.js';
|
|
233
233
|
import { rule_in_scope } from '../install/ruleInScope.js';
|
|
234
234
|
import { pruneEmptyDirs } from './_lib/prune_empty_dirs.js';
|
|
235
|
-
import {
|
|
235
|
+
import { claudeLayerHolds, commandWithheld, keepInProjectLayer, personaPartition } from '../install/partitionEligibility.js'; // ADR-236
|
|
236
236
|
import { dedupableRules, partitionRulesForDir } from '../install/ruleLayerPartition.js'; // ADR-236, per-host evidence
|
|
237
|
-
import { _claude_paths_plan, derive_trigger_globs } from '../install/claudePathsPlan.js';
|
|
237
|
+
import { _claude_paths_plan, applies_when, derive_trigger_globs } from '../install/claudePathsPlan.js';
|
|
238
238
|
const _HERE = path.dirname(fileURLToPath(import.meta.url)); // <repo>/src/scripts
|
|
239
239
|
const _DEFAULT_PROJECT_ROOT = path.resolve(_HERE, '..', '..');
|
|
240
240
|
function _deriveState(root) {
|
|
@@ -263,7 +263,6 @@ function _deriveState(root) {
|
|
|
263
263
|
};
|
|
264
264
|
}
|
|
265
265
|
export const MODULE_STATE = _deriveState(_DEFAULT_PROJECT_ROOT);
|
|
266
|
-
setPartitionAnnounce(success); // ADR-236: the mode line must be visible at the default level
|
|
267
266
|
/**
|
|
268
267
|
* Test seam — reassign one or more module-state fields, mirroring the pytest
|
|
269
268
|
* monkeypatch of `condense.<NAME> = ...`. When `PROJECT_ROOT` is overridden,
|
|
@@ -384,13 +383,16 @@ function _read_projection_scope_dedup() {
|
|
|
384
383
|
}
|
|
385
384
|
return false;
|
|
386
385
|
}
|
|
387
|
-
function
|
|
386
|
+
function _lean_projection_settings() {
|
|
388
387
|
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
389
388
|
const lean = data['lean_projection'];
|
|
390
|
-
const
|
|
391
|
-
? lean
|
|
392
|
-
:
|
|
393
|
-
return
|
|
389
|
+
const obj = typeof lean === 'object' && lean !== null && !Array.isArray(lean)
|
|
390
|
+
? lean
|
|
391
|
+
: {};
|
|
392
|
+
return {
|
|
393
|
+
mode: normalizeLeanProjectionMode(obj['mode'] ?? ''),
|
|
394
|
+
hosts: resolveLeanProjectionHosts(obj['hosts']),
|
|
395
|
+
};
|
|
394
396
|
}
|
|
395
397
|
// --- hashing -----------------------------------------------------------------
|
|
396
398
|
export function apply_path_rewriter(relative_path) {
|
|
@@ -986,20 +988,17 @@ export function projected_rule_trees(user_home = process.env['HOME'] ?? os.homed
|
|
|
986
988
|
export function generate_rule_symlinks() {
|
|
987
989
|
const rules = _scoped_rule_basenames();
|
|
988
990
|
const tool_dirs = _filter_tool_dirs(TOOL_DIRS);
|
|
991
|
+
const lean = _lean_projection_settings();
|
|
992
|
+
for (const warning of describeDroppedHosts(lean.hosts))
|
|
993
|
+
_print(` ⚠️ ${warning}`);
|
|
989
994
|
let thin_files = null;
|
|
990
|
-
if (writesThinFiles(
|
|
991
|
-
//
|
|
992
|
-
//
|
|
993
|
-
//
|
|
994
|
-
//
|
|
995
|
-
//
|
|
996
|
-
|
|
997
|
-
// Scope note: this repairs the SWITCH only. The default stays
|
|
998
|
-
// `eager-all`. Flipping it is parked behind the thin-projection honest
|
|
999
|
-
// null (thin win-rate 36.2% < the 48% pre-registered threshold), and
|
|
1000
|
-
// nothing here disturbs that verdict — but a mode that throws cannot
|
|
1001
|
-
// even be re-measured, which is why the repair earns its place alone.
|
|
1002
|
-
thin_files = Object.fromEntries(build_thin(MODULE_STATE.RULES_SOURCE, _read_rule_workspaces()));
|
|
995
|
+
if (writesThinFiles(lean.mode)) {
|
|
996
|
+
// `thin` stays parked behind the thin-projection honest null (win-rate
|
|
997
|
+
// 36.2% against a 48% pre-registered threshold, ADR-202). `delivery` is
|
|
998
|
+
// NOT gated by that null and citing it as one is K7: delivery re-delivers
|
|
999
|
+
// identical bytes, is licensed by `docs/CLAIMS.md:365` on delivery
|
|
1000
|
+
// equivalence and cost, and needs a recall floor rather than a judge.
|
|
1001
|
+
thin_files = Object.fromEntries(build_thin(MODULE_STATE.RULES_SOURCE, _read_rule_workspaces(), (m) => _print(` ⚠️ ${m}`)));
|
|
1003
1002
|
}
|
|
1004
1003
|
const user_home = process.env['HOME'] ?? os.homedir();
|
|
1005
1004
|
// Read the emit plan from the exported function rather than recomputing the
|
|
@@ -1045,7 +1044,7 @@ export function generate_rule_symlinks() {
|
|
|
1045
1044
|
if (_existsOrSymlink(link)) {
|
|
1046
1045
|
fs.unlinkSync(link);
|
|
1047
1046
|
}
|
|
1048
|
-
if (thin_files !== null) {
|
|
1047
|
+
if (thin_files !== null && thinsHost(lean.mode, lean.hosts.hosts, _DIR_TOOL_ID[tool_dir] ?? '')) {
|
|
1049
1048
|
_writeText(link, thin_files[rule]);
|
|
1050
1049
|
}
|
|
1051
1050
|
else if (tool_dir === '.claude/rules') {
|
|
@@ -1149,7 +1148,7 @@ export function _emit_cursor_mdc(source, target) {
|
|
|
1149
1148
|
const globs = always_apply ? [] : derive_trigger_globs(meta);
|
|
1150
1149
|
const lines = [
|
|
1151
1150
|
'---',
|
|
1152
|
-
`description: ${_yaml_scalar(description)}`,
|
|
1151
|
+
`description: ${_yaml_scalar(always_apply ? description : applies_when(description, meta))}`,
|
|
1153
1152
|
`globs: ${globs.join(',')}`,
|
|
1154
1153
|
`alwaysApply: ${always_apply ? 'true' : 'false'}`,
|
|
1155
1154
|
'---',
|
|
@@ -1170,7 +1169,7 @@ export function _emit_windsurf_rule(source, target) {
|
|
|
1170
1169
|
const lines = [
|
|
1171
1170
|
'---',
|
|
1172
1171
|
`trigger: ${trigger}`,
|
|
1173
|
-
`description: ${_yaml_scalar(description)}`,
|
|
1172
|
+
`description: ${_yaml_scalar(always_apply ? description : applies_when(description, meta))}`,
|
|
1174
1173
|
`globs: ${globs.join(',')}`,
|
|
1175
1174
|
'---',
|
|
1176
1175
|
'',
|
|
@@ -1448,23 +1447,21 @@ function _render_native_model_md(src_md, tier) {
|
|
|
1448
1447
|
return text.replace(_MODEL_TIER_RE, `model: ${model}`);
|
|
1449
1448
|
}
|
|
1450
1449
|
export function generate_claude_skills(active_skill_names = null) {
|
|
1451
|
-
if (partitionActive(MODULE_STATE.PROJECT_ROOT))
|
|
1452
|
-
active_skill_names = new Set();
|
|
1453
1450
|
if (!_exists(MODULE_STATE.SKILLS_SOURCE)) {
|
|
1454
1451
|
process.stderr.write(' ⚠️ dist/agent-src/skills/ not found — skipping skills\n');
|
|
1455
1452
|
return 0;
|
|
1456
1453
|
}
|
|
1457
|
-
|
|
1454
|
+
const all_skills = _iterdirSorted(MODULE_STATE.SKILLS_SOURCE)
|
|
1458
1455
|
.filter((p) => _isDir(p))
|
|
1459
1456
|
.map((p) => path.basename(p))
|
|
1460
1457
|
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
1458
|
+
// ADR-236: withhold every skill `~/.claude/skills` holds — see `keepInProjectLayer`.
|
|
1459
|
+
let skills = keepInProjectLayer('skills', all_skills);
|
|
1461
1460
|
if (active_skill_names !== null) {
|
|
1462
1461
|
skills = skills.filter((s) => active_skill_names.has(s));
|
|
1463
1462
|
}
|
|
1464
1463
|
const skill_set = new Set(skills);
|
|
1465
|
-
const command_slugs =
|
|
1466
|
-
? new Set()
|
|
1467
|
-
: new Set([...iterCommands()].map(([, slug]) => slug));
|
|
1464
|
+
const command_slugs = _emitted_wrapper_slugs(new Set(all_skills));
|
|
1468
1465
|
_mkdirp(MODULE_STATE.CLAUDE_SKILLS_DIR);
|
|
1469
1466
|
const auto = _read_model_auto_switch() === 'auto';
|
|
1470
1467
|
for (const item of _iterdirSorted(MODULE_STATE.CLAUDE_SKILLS_DIR)) {
|
|
@@ -1549,6 +1546,17 @@ function _nested_command_subpath(source_file) {
|
|
|
1549
1546
|
const sub = logical.slice('commands/'.length).replace(/\.md$/, '');
|
|
1550
1547
|
return sub.includes('/') ? sub : null;
|
|
1551
1548
|
}
|
|
1549
|
+
/** The emitter's four skips, in one place — see `emittedWrapperSlugs`. */
|
|
1550
|
+
const _wrapper_is_emitted = (f, s, skills) => !skills.has(s) &&
|
|
1551
|
+
_nested_command_subpath(f) === null &&
|
|
1552
|
+
!is_claude_builtin_name(s) &&
|
|
1553
|
+
!claudeLayerHolds('skills', s);
|
|
1554
|
+
/** Wrapper slugs the command emitter really writes — see `emittedWrapperSlugs`. */
|
|
1555
|
+
function _emitted_wrapper_slugs(skill_names) {
|
|
1556
|
+
return new Set([...iterCommands()]
|
|
1557
|
+
.filter(([f, s]) => _wrapper_is_emitted(f, s, skill_names))
|
|
1558
|
+
.map(([, slug]) => slug));
|
|
1559
|
+
}
|
|
1552
1560
|
/**
|
|
1553
1561
|
* Project-scope `.claude/commands/<cluster>/<sub>.md` — the colon form.
|
|
1554
1562
|
*
|
|
@@ -1561,15 +1569,12 @@ function _nested_command_subpath(source_file) {
|
|
|
1561
1569
|
*
|
|
1562
1570
|
* Emitting the nested form here lets `generate_claude_commands` stop wrapping
|
|
1563
1571
|
* the clustered commands: one listing, same reachability, and reachability no
|
|
1564
|
-
* longer depends on a global deploy. Dedup AND precedence: `
|
|
1572
|
+
* longer depends on a global deploy. Dedup AND precedence: `commandWithheld`.
|
|
1565
1573
|
*
|
|
1566
1574
|
* ADR-003 (colon canonical for clusters) and ADR-044 (flat commands stay
|
|
1567
1575
|
* hyphenated) both hold: flat commands are untouched and keep their wrapper.
|
|
1568
1576
|
*/
|
|
1569
1577
|
export function generate_claude_project_commands(active_command_slugs = null) {
|
|
1570
|
-
if (commandsWithheld(MODULE_STATE.PROJECT_ROOT))
|
|
1571
|
-
active_command_slugs = new Set();
|
|
1572
|
-
// Returns before the stale sweep — invariant, see `commandsWithheld`.
|
|
1573
1578
|
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
|
|
1574
1579
|
return 0;
|
|
1575
1580
|
}
|
|
@@ -1579,7 +1584,7 @@ export function generate_claude_project_commands(active_command_slugs = null) {
|
|
|
1579
1584
|
continue;
|
|
1580
1585
|
}
|
|
1581
1586
|
const sub = _nested_command_subpath(source_file);
|
|
1582
|
-
if (sub !== null) {
|
|
1587
|
+
if (sub !== null && !commandWithheld(`${sub}.md`)) {
|
|
1583
1588
|
nested.push([source_file, sub]);
|
|
1584
1589
|
}
|
|
1585
1590
|
}
|
|
@@ -1613,8 +1618,6 @@ export function generate_claude_project_commands(active_command_slugs = null) {
|
|
|
1613
1618
|
return count;
|
|
1614
1619
|
}
|
|
1615
1620
|
export function generate_claude_commands(active_command_slugs = null) {
|
|
1616
|
-
if (partitionActive(MODULE_STATE.PROJECT_ROOT))
|
|
1617
|
-
active_command_slugs = new Set();
|
|
1618
1621
|
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
|
|
1619
1622
|
process.stderr.write(' ⚠️ src/domains/ not found — skipping commands\n');
|
|
1620
1623
|
return 0;
|
|
@@ -1630,6 +1633,7 @@ export function generate_claude_commands(active_command_slugs = null) {
|
|
|
1630
1633
|
let count = 0;
|
|
1631
1634
|
let skipped = 0;
|
|
1632
1635
|
let reserved = 0;
|
|
1636
|
+
let withheld = 0;
|
|
1633
1637
|
let rendered = 0;
|
|
1634
1638
|
const auto = _read_model_auto_switch() === 'auto';
|
|
1635
1639
|
for (const [source_file, slug] of iterCommands()) {
|
|
@@ -1658,6 +1662,12 @@ export function generate_claude_commands(active_command_slugs = null) {
|
|
|
1658
1662
|
reserved += 1;
|
|
1659
1663
|
continue;
|
|
1660
1664
|
}
|
|
1665
|
+
// ADR-236 per-name: the INSTALLER re-projects visible flat commands as
|
|
1666
|
+
// `~/.claude/skills/<slug>/SKILL.md` (install.ts:_apply_claude_flat_command_wrappers).
|
|
1667
|
+
if (claudeLayerHolds('skills', slug)) {
|
|
1668
|
+
withheld += 1;
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1661
1671
|
current_slugs.add(slug);
|
|
1662
1672
|
const skill_dir = path.join(MODULE_STATE.CLAUDE_SKILLS_DIR, slug);
|
|
1663
1673
|
_mkdirp(skill_dir);
|
|
@@ -1706,6 +1716,9 @@ export function generate_claude_commands(active_command_slugs = null) {
|
|
|
1706
1716
|
if (reserved) {
|
|
1707
1717
|
msg += ` (${reserved} withheld — Claude Code built-in name)`;
|
|
1708
1718
|
}
|
|
1719
|
+
if (withheld > 0) {
|
|
1720
|
+
msg += ` (${withheld} withheld — ~/.claude/skills carries the wrapper)`;
|
|
1721
|
+
}
|
|
1709
1722
|
if (removed_dirs) {
|
|
1710
1723
|
msg += ` (${removed_dirs} stale dirs removed)`;
|
|
1711
1724
|
}
|
|
@@ -2019,19 +2032,13 @@ export function generate_persona_symlinks() {
|
|
|
2019
2032
|
_print(' ⚠️ dist/agent-src/personas/ not found — skipping personas');
|
|
2020
2033
|
return 0;
|
|
2021
2034
|
}
|
|
2022
|
-
const personas = _rglobSorted(MODULE_STATE.PERSONAS_SOURCE, '*.md')
|
|
2023
|
-
.filter((p) => _isFile(p) && path.basename(p, '.md') !== 'README')
|
|
2024
|
-
.map((p) => path.basename(p))
|
|
2025
|
-
// glob('*.md') is non-recursive in Python; mirror that — only top level.
|
|
2026
|
-
.filter((name) => _isFile(path.join(MODULE_STATE.PERSONAS_SOURCE, name)));
|
|
2027
2035
|
// Python uses PERSONAS_SOURCE.glob("*.md") (non-recursive). Re-derive
|
|
2028
2036
|
// from a direct listing to match exactly.
|
|
2029
2037
|
const direct = _iterdirSorted(MODULE_STATE.PERSONAS_SOURCE)
|
|
2030
2038
|
.filter((p) => p.endsWith('.md') && _isFile(p) && path.basename(p, '.md') !== 'README')
|
|
2031
2039
|
.map((p) => path.basename(p))
|
|
2032
2040
|
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
2033
|
-
|
|
2034
|
-
const partition = personaPartition(MODULE_STATE.PROJECT_ROOT, direct);
|
|
2041
|
+
const partition = personaPartition(direct);
|
|
2035
2042
|
const tool_dirs = _filter_tool_dirs(PERSONA_TOOL_DIRS);
|
|
2036
2043
|
let total = 0;
|
|
2037
2044
|
for (const [tool_dir, rel_prefix] of Object.entries(tool_dirs)) {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* `.augment/` projections.
|
|
20
20
|
*
|
|
21
21
|
* NOTE (faithful literal): the thin-entry pointer keeps the verbatim
|
|
22
|
-
* `Body: [`{rule_id}`](
|
|
22
|
+
* `Body: [`{rule_id}`](../../dist/agent-src/rules/{rule_id}.md)`
|
|
23
23
|
* Markdown link from the retired Python implementation. The same-basename `.py` twin
|
|
24
24
|
* carries that literal, so the ADR-051 legacy-path guard exempts this `.ts`
|
|
25
25
|
* file by twin-parity.
|
|
@@ -34,6 +34,7 @@ import * as fs from 'node:fs';
|
|
|
34
34
|
import * as path from 'node:path';
|
|
35
35
|
import process from 'node:process';
|
|
36
36
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
37
|
+
import { pathOnlyRuleIds, triggerlessRuleIds, } from './_lib/rule_injection.js';
|
|
37
38
|
import * as token_count from './_lib/token_count.js';
|
|
38
39
|
const _HERE = fileURLToPath(import.meta.url);
|
|
39
40
|
// REPO_ROOT = Path(__file__).resolve().parent.parent.parent — three dirs up
|
|
@@ -72,21 +73,85 @@ export function kernel_ids() {
|
|
|
72
73
|
* visible instead of being absorbed into the pointer count
|
|
73
74
|
* (road-to-trigger-delivered-rule-bodies 1.3).
|
|
74
75
|
*/
|
|
76
|
+
/**
|
|
77
|
+
* Rules whose ONLY triggers are path-shaped — reachable from `pre_tool_use`
|
|
78
|
+
* and from no other slot (road-to-delivery-for-every-host 3.2).
|
|
79
|
+
*
|
|
80
|
+
* They must never be thinned while the delivery concern is NOT bound on
|
|
81
|
+
* `pre_tool_use`, because a stub plus a slot nobody listens on is a rule that
|
|
82
|
+
* reaches the model at no scope at all. This is the same failure the
|
|
83
|
+
* `no_trigger_ids` exemption prevents, one step out: there the rule has nothing
|
|
84
|
+
* to match on, here it has nothing to match ON.
|
|
85
|
+
*
|
|
86
|
+
* Stated as a PROPERTY rather than a list of ids. If the concern is ever bound
|
|
87
|
+
* on `pre_tool_use` again, this exemption is what should be reconsidered — not
|
|
88
|
+
* three names someone has to remember.
|
|
89
|
+
*
|
|
90
|
+
* WHAT THIS EXEMPTION DOES **NOT** COVER — recorded 2026-09-08, R2 finding 1,
|
|
91
|
+
* because the silence read as coverage. The predicate is `every` trigger
|
|
92
|
+
* path-shaped. A rule with BOTH path and non-path triggers is therefore THINNED,
|
|
93
|
+
* and its path-shaped half then has no carrier at all: the stub carries no
|
|
94
|
+
* frontmatter (`condense.ts` writes it with `_writeText`, bypassing
|
|
95
|
+
* `_emit_claude_rule`'s host-native `paths:` key), `pre_tool_use` lost the
|
|
96
|
+
* binding under owner ruling E2, and `user_prompt_submit` never populates
|
|
97
|
+
* `openFiles`. Measured over the current `dist/router.json`: **18 such rules**,
|
|
98
|
+
* all non-kernel and all thinned — augment-edit-discipline, design-fidelity,
|
|
99
|
+
* doc-screenshot-hygiene, domain-adoption-policy,
|
|
100
|
+
* framework-neutrality-in-generic-skills, image-likeness-and-rights,
|
|
101
|
+
* laravel-translations, lethal-trifecta-guard, linked-projects-onboarding-gate,
|
|
102
|
+
* low-impact-corpus-privacy-floor, markdown-safe-codeblocks, onboarding-gate,
|
|
103
|
+
* persona-governance, php-coding, provider-lifecycle-discipline,
|
|
104
|
+
* roadmap-ci-steps-policy, roadmap-progress-sync, settings-ask-protocol.
|
|
105
|
+
*
|
|
106
|
+
* They do NOT reach the model at no scope: every non-path route
|
|
107
|
+
* (keyword / phrase / command) still delivers the body through the concern, and
|
|
108
|
+
* on `claude-code` they never had a separate host-native path route to lose —
|
|
109
|
+
* `_claude_paths_plan` emits no `paths:` for a mixed-trigger rule on purpose, so
|
|
110
|
+
* under `eager-all` they loaded UNCONDITIONALLY. What is lost is the difference
|
|
111
|
+
* between unconditional and prompt-triggered: a session that touches a matching
|
|
112
|
+
* file and says nothing that matches gets nothing.
|
|
113
|
+
*
|
|
114
|
+
* The labelled corpus cannot see this and the endpoint says so rather than
|
|
115
|
+
* implying otherwise — `model_rule_injection --endpoints` (b) now publishes the
|
|
116
|
+
* shipped `user_prompt_submit` reach beside the pre-registered reading, and
|
|
117
|
+
* every one of these 18 has at least one prompt-matching positive, so none
|
|
118
|
+
* appears in its "reachable only via a path trigger" column.
|
|
119
|
+
*
|
|
120
|
+
* WHY THIS IS NOT WIDENED TO `some` HERE. Measured: exempting all 18 moves the
|
|
121
|
+
* thin rule layer 23,592 -> 39,921 GPT tok (+16,329, +69 %), which is a budget
|
|
122
|
+
* move of a magnitude no agent may take, and it would erase most of the saving
|
|
123
|
+
* the flip is licensed on. Restoring a path route needs either that exemption
|
|
124
|
+
* under a re-anchored baseline or a `pre_tool_use` binding under a raised slot
|
|
125
|
+
* cap — both owner-reserved, so the closure is tracked rather than taken here.
|
|
126
|
+
* ADR-267's Consequences section names the receiver.
|
|
127
|
+
*/
|
|
128
|
+
/**
|
|
129
|
+
* ONE spelling of the router-derived sets, shared with the injector (R2
|
|
130
|
+
* finding 11).
|
|
131
|
+
*
|
|
132
|
+
* `path_only_ids` and `no_trigger_ids` were each a second, hand-rolled
|
|
133
|
+
* implementation of `_lib/rule_injection.ts::pathOnlyRuleIds` /
|
|
134
|
+
* `triggerlessRuleIds` — verified identical over the current router at the time
|
|
135
|
+
* (symmetric difference empty), so drift risk rather than a live defect. It is
|
|
136
|
+
* exactly the hazard `THIN_ENTRY_MARKER`'s own comment argues against one
|
|
137
|
+
* screen below ("a gate that re-spelled this string would drift from the writer
|
|
138
|
+
* silently"), and the two consumers here are the PROJECTOR's exemption set and
|
|
139
|
+
* the INJECTOR's reachability set: they must not be able to disagree about which
|
|
140
|
+
* rules the delivery path can reach.
|
|
141
|
+
*
|
|
142
|
+
* The finding named `path_only_ids`. The defect-pattern sweep found the SECOND
|
|
143
|
+
* instance one function down — `no_trigger_ids` against `triggerlessRuleIds` —
|
|
144
|
+
* and it is delegated in the same change. Count: 2 of 2 duplicated
|
|
145
|
+
* router-derived sets in this file, 0 remaining.
|
|
146
|
+
*/
|
|
147
|
+
function _router() {
|
|
148
|
+
return JSON.parse(fs.readFileSync(ROUTER, 'utf-8'));
|
|
149
|
+
}
|
|
150
|
+
export function path_only_ids() {
|
|
151
|
+
return pathOnlyRuleIds(_router());
|
|
152
|
+
}
|
|
75
153
|
export function no_trigger_ids() {
|
|
76
|
-
|
|
77
|
-
const out = new Set();
|
|
78
|
-
for (const tier of ['tier_1', 'tier_2']) {
|
|
79
|
-
const entries = data[tier];
|
|
80
|
-
if (!Array.isArray(entries))
|
|
81
|
-
continue;
|
|
82
|
-
for (const e of entries) {
|
|
83
|
-
const obj = e;
|
|
84
|
-
const t = obj.triggers;
|
|
85
|
-
if (!Array.isArray(t) || t.length === 0)
|
|
86
|
-
out.add(String(obj.id));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return out;
|
|
154
|
+
return new Set(triggerlessRuleIds(_router()));
|
|
90
155
|
}
|
|
91
156
|
/**
|
|
92
157
|
* id → workspaces for every non-kernel router entry (router.json schema v2,
|
|
@@ -179,6 +244,42 @@ function _trigger_hint(fm) {
|
|
|
179
244
|
function _title(s) {
|
|
180
245
|
return s.replace(/[A-Za-z]+/g, (w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase());
|
|
181
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* The marker that makes a projected entry recognisable as a stub.
|
|
249
|
+
*
|
|
250
|
+
* Exported so the WRITER and every DETECTOR share one definition. A gate that
|
|
251
|
+
* re-spelled this string would drift from the writer silently, and the failure
|
|
252
|
+
* would be invisible in exactly the direction that matters: a stub the gate
|
|
253
|
+
* fails to recognise reads as a complete rule body.
|
|
254
|
+
*/
|
|
255
|
+
export const THIN_ENTRY_MARKER = '> Routed rule — load the body on trigger-match.';
|
|
256
|
+
/** Is this projected entry a pointer stub rather than a rule body? */
|
|
257
|
+
export function is_thin_entry(text) {
|
|
258
|
+
return text.includes(THIN_ENTRY_MARKER);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Where a stub tells a reader the body actually is.
|
|
262
|
+
*
|
|
263
|
+
* FIXED 2026-09-07: this pointed into the uncondensed source tree ADR-051
|
|
264
|
+
* retired — the literal path is deliberately NOT reproduced here, because
|
|
265
|
+
* `check_no_uncondensed_refs` matches the string and cannot tell a repair
|
|
266
|
+
* record from a live reference; `git log -S` on this file finds the old value.
|
|
267
|
+
* It exists in no checkout, so EVERY stub's fallback pointer
|
|
268
|
+
* resolved to nothing — invisible under `delivery`, where the hook loads the
|
|
269
|
+
* body from `dist/agent-src/rules` and never follows this link, and total under
|
|
270
|
+
* `thin`, where the pointer is the only path to the body there is.
|
|
271
|
+
*
|
|
272
|
+
* KNOWN LIMIT, stated rather than discovered later: one relative prefix cannot
|
|
273
|
+
* be right for every tree, because they sit at different depths — `.claude/rules`
|
|
274
|
+
* and `.cursor/rules` are two levels below the repo root and `.clinerules` is
|
|
275
|
+
* one. `../../` is correct for the first two, which are the only trees the
|
|
276
|
+
* shipped `lean_projection.hosts` default can thin. A stub in `.clinerules`
|
|
277
|
+
* needs an operator to have added `cline` to that list, and its pointer is off
|
|
278
|
+
* by one level. That is strictly better than the previous state, where the
|
|
279
|
+
* pointer was dead in all three, and it is recorded here so the remaining case
|
|
280
|
+
* is a known limit rather than a surprise.
|
|
281
|
+
*/
|
|
282
|
+
const BODY_LINK_PREFIX = '../../dist/agent-src/rules/';
|
|
182
283
|
/** Build the minimal progressive-disclosure pointer for a non-kernel rule. */
|
|
183
284
|
export function thin_entry(rule_id, text) {
|
|
184
285
|
const [fm] = split_frontmatter(text);
|
|
@@ -187,13 +288,25 @@ export function thin_entry(rule_id, text) {
|
|
|
187
288
|
const title = _title(rule_id.replace(/-/g, ' '));
|
|
188
289
|
const fires = hint ? ` Fires on: ${hint}.` : '';
|
|
189
290
|
return (`## ${title}\n` +
|
|
190
|
-
|
|
191
|
-
`Body: [\`${rule_id}\`](
|
|
291
|
+
`${THIN_ENTRY_MARKER}${fires} ${desc} ` +
|
|
292
|
+
`Body: [\`${rule_id}\`](${BODY_LINK_PREFIX}${rule_id}.md)\n`);
|
|
192
293
|
}
|
|
193
294
|
/** Map {filename: thin_or_full_text} for every rule. Kernel stays full. */
|
|
194
|
-
export function build_thin(rules_dir = RULES_SOURCE, scope = null
|
|
295
|
+
export function build_thin(rules_dir = RULES_SOURCE, scope = null,
|
|
296
|
+
/**
|
|
297
|
+
* Optional sink for the D3 diagnostic
|
|
298
|
+
* (road-to-delivery-for-every-host 2.2).
|
|
299
|
+
*
|
|
300
|
+
* An `auto` rule the router gives NO trigger is already kept full-bodied by
|
|
301
|
+
* `noTrigger` below, which is the substantive protection and predates this
|
|
302
|
+
* roadmap. What did not exist is any way to SEE it happen: the rule is
|
|
303
|
+
* silently exempted, so an author who removes a rule's last trigger gets a
|
|
304
|
+
* silently eager rule and no signal. The line makes the exemption audible.
|
|
305
|
+
*/
|
|
306
|
+
announce = null) {
|
|
195
307
|
const kernel = kernel_ids();
|
|
196
308
|
const noTrigger = no_trigger_ids();
|
|
309
|
+
const pathOnly = path_only_ids();
|
|
197
310
|
const wsMap = scope !== null ? rule_workspaces_map() : new Map();
|
|
198
311
|
const out = new Map();
|
|
199
312
|
for (const p of _globSortedMd(rules_dir)) {
|
|
@@ -202,7 +315,14 @@ export function build_thin(rules_dir = RULES_SOURCE, scope = null) {
|
|
|
202
315
|
if (!id_in_scope(stem, scope, kernel, wsMap, fm_workspaces(text))) {
|
|
203
316
|
continue; // out of workspace scope — no body, no pointer line
|
|
204
317
|
}
|
|
205
|
-
const full = kernel.has(stem) || noTrigger.has(stem);
|
|
318
|
+
const full = kernel.has(stem) || noTrigger.has(stem) || pathOnly.has(stem);
|
|
319
|
+
if (announce !== null && noTrigger.has(stem) && !kernel.has(stem)) {
|
|
320
|
+
announce(`D3: trigger-less auto rule ${path.basename(p)} — kept full-bodied, never thinned`);
|
|
321
|
+
}
|
|
322
|
+
if (announce !== null && pathOnly.has(stem) && !kernel.has(stem) && !noTrigger.has(stem)) {
|
|
323
|
+
announce(`E2: path-only auto rule ${path.basename(p)} — kept full-bodied; its only triggers ` +
|
|
324
|
+
`are path-shaped and the delivery concern is not bound on pre_tool_use`);
|
|
325
|
+
}
|
|
206
326
|
out.set(path.basename(p), full ? text : thin_entry(stem, text));
|
|
207
327
|
}
|
|
208
328
|
return out;
|
|
@@ -29,7 +29,8 @@ const confidenceBand = z.enum(['off', 'low', 'medium', 'high']);
|
|
|
29
29
|
const onBlock = z.enum(['stop', 'ask', 'warn']);
|
|
30
30
|
const onBlockFallback = z.enum(['stop', 'warn']);
|
|
31
31
|
const modelAutoSwitch = z.enum(['auto', 'suggest', 'off']);
|
|
32
|
-
const leanProjectionMode = z.enum(['eager-all', 'thin']);
|
|
32
|
+
const leanProjectionMode = z.enum(['eager-all', 'thin', 'delivery']);
|
|
33
|
+
const leanProjectionHost = z.enum(['claude-code', 'cursor', 'cline']);
|
|
33
34
|
const projectionMode = z.enum(['legacy-all', 'scoped']);
|
|
34
35
|
const memoryCadence = z.enum(['auto', 'always', 'never']);
|
|
35
36
|
const projectAudience = z.enum(['self', 'internal', 'client', 'public']);
|
|
@@ -46,8 +47,16 @@ export const settingsSchema = z.object({
|
|
|
46
47
|
rule_loading_tier: ruleLoadingTier.default('balanced').describe('Master switch for which rule tiers load and how cautiously the agent spends tokens. minimal = only the 9 kernel rules (cheapest, fewest guardrails). balanced = kernel + tier-1 (recommended default). full = kernel + tier-1 + tier-2 (most guardrails, highest token cost). custom = roll your own in agents/overrides/. LEGACY: superseded by discipline_profile — when that key is set it wins (mapping: minimal→off, balanced→essential, full→full).'),
|
|
47
48
|
discipline_profile: disciplineProfile.optional().describe('The ONE runtime knob for the discipline-rule tier (successor of rule_loading_tier; council 2026-07-07). off = kernel only (~1x tokens). essential = kernel + the measured lift-carrying rules (~3.3x, keeps the weak-host discipline lift). full = everything (~11.7x, EXPERIMENTAL — residual lift over essential not established). auto = resolve per session against the evidence-gated NULL-lift disable-list in src/config/host-capabilities.yml (measured-null host → off, otherwise → essential). Optional and opt-in until the P1/P2 evidence gates pass (agents/roadmaps/road-to-discipline-profile-tiering.md); absent = legacy rule_loading_tier applies.'),
|
|
48
49
|
lean_projection: z.object({
|
|
49
|
-
mode: leanProjectionMode.default('eager-all').describe('How the per-tool projector emits the rule layer. eager-all = every rule body inlined into every projection (
|
|
50
|
-
|
|
50
|
+
mode: leanProjectionMode.default('eager-all').describe('How the per-tool projector emits the rule layer. eager-all = every rule body inlined into every projection (safe; what an ABSENT key still resolves to, per ADR-267 decision point 4). thin = kernel rules full-bodied + non-kernel rules as router-resolved pointers (~45k GPT-tok lighter per session), with no delivery mechanism. delivery = thin stubs PLUS the rule-inject hook concern, which delivers a body back on a trigger match; this is the value the SHIPPED TEMPLATE carries for Claude Code since ADR-267, and the activation charge for it is paid in src/config/hook-token-budget.json (user_prompt_submit slot sum 4,096 -> 16,384).'),
|
|
51
|
+
hosts: z.array(leanProjectionHost).default(['claude-code']).describe('Which hosts a thinning mode may thin (ADR-267). Absent resolves to [claude-code]. '
|
|
52
|
+
+ 'Only the three hosts with a per-rule rule tree are accepted, and this enum is the '
|
|
53
|
+
+ 'AUTHORING layer: an id outside it is a hard validation FAILURE here and under '
|
|
54
|
+
+ 'validate_agent_settings, never a warning. resolveLeanProjectionHosts is the '
|
|
55
|
+
+ 'READ-TIME backstop for a hand-edited .agent-settings.yml that passed neither, and '
|
|
56
|
+
+ 'THERE the id is dropped with a warning (R2 finding 6 — the two layers answer '
|
|
57
|
+
+ 'differently and neither said so). Every host outside this list receives exactly '
|
|
58
|
+
+ 'what eager-all writes, asserted byte-for-byte by check_host_tree_parity.'),
|
|
59
|
+
}).default({ mode: 'eager-all', hosts: ['claude-code'] }),
|
|
51
60
|
cost: z.object({
|
|
52
61
|
budgets: z.object({
|
|
53
62
|
daily: z.number().min(0).default(0).describe('Daily USD ceiling across all model calls. The agent warns at 50% / 75% / 90% and either stops or warns at 100% depending on cost.enforcement. Set 0 to disable the daily budget entirely.'),
|
|
@@ -229,6 +238,10 @@ export const settingsSchema = z.object({
|
|
|
229
238
|
api_paths: z.array(z.string()).default([]).describe('Optional glob list that makes API-endpoint detection explicit instead of heuristic (e.g. ["app/Http/Controllers/Api/**", "src/pages/api/**"]). Empty (default) = a light path/extension heuristic that fails open.'),
|
|
230
239
|
}),
|
|
231
240
|
}),
|
|
241
|
+
continuity: z.object({
|
|
242
|
+
auto_record: z.enum(['on', 'off']).default('off').describe('Deterministic continuity-record writer at session end (road-to-continuity-writer-activation Phase 1). on = the session-eol concern writes the continuity_record capsule variant on Stop for a substantive session that has claimed a roadmap; every field is computed from on-disk state, with no model spend and no subprocess. off (default) = no automatic record — while session:recycle is still the normal path, a second producer on it before the parity evidence is in would be unverified.'),
|
|
243
|
+
run_checkpoints: z.enum(['on', 'off']).default('on').describe('Deterministic run-checkpoint production at session end (road-to-continuity-writer-activation Phase 1.4). on (default) = a session above the recycle threshold and inside a running roadmap contract leaves agents/runtime/state/checkpoints/<run>.json, so a killed run resumes from a derived checkpoint rather than from bookkeeping. off = no checkpoint; continuity writing, the recycle advisory and the context-fill surface are unaffected. Default ON because it is the behaviour the tree already had — the switch exists to make the three session-end handlers independently disableable, not to change what ships.'),
|
|
244
|
+
}),
|
|
232
245
|
memory: z.object({
|
|
233
246
|
cadence: memoryCadence.default('always').describe('Cadence of the 🧠 memory-visibility line after a memory-consulting step. always (default) = show whenever a memory type was asked; auto = show only when 3+ types were consulted (less noise); never = suppress. Distinct from rule_loading_tier — owns its own key since the 2026-06-01 untangle.'),
|
|
234
247
|
review_threshold: z.number().int().min(0).default(10).describe('Maximum number of memory entries /memory:load surfaces inline before falling back to a summary view. Default 10 keeps the chat readable. Raise to see more candidates, lower to keep the context tight.'),
|
package/docs/MIGRATION.md
CHANGED
|
@@ -17,7 +17,7 @@ releaser here on every `release:major`.
|
|
|
17
17
|
| Surface | Committed | Deprecation notice due | Removal due | Reversal condition |
|
|
18
18
|
|---|---|---|---|---|
|
|
19
19
|
| Free-text `compatibility` skill-frontmatter field (`src/scripts/schemas/skill.schema.json`) | 2026-08-14 | shipped 2026-08-14 — `deprecated: true` + superseded-by note in the schema; `harness_compat` lands beside it, additively | next major after the notice — **not pinned here** (removal date is maintainer-owned, same stance as the manifest `tier` entry, whose `sunset` shipped `null`) | The public Agent-Skills spec makes `compatibility` load-bearing for cross-host portability, or an external consumer is found reading it. `harness_compat` is package-local; `compatibility` mirrors the public spec, so keeping the mirror stays cheaply reversible. Superseded by `harness_compat` (class) + `runtime_requires` (probeable detail); both users (`docx-authoring`, `pdf-tools`) already carry both fields, so nothing breaks on removal day. |
|
|
20
|
-
| `code_graph` native code-graph engine (`code_graph.enabled`, the `code-intelligence` skill's native arm, `code_graph_nudge_hook`) | 2026-07-28 | next major after 9.x | **REVISED 2026-08-15 — no removal date; see below** | A consumer case the graph answers and disciplined grep cannot. Measured null: recall 0.365 vs grep 0.797 (`docs/CLAIMS.md` `code-graph-retrieval-null`), measured 2026-07-28 against a build predating the 2026-08-22 extractor repair. `enabled: false` stays the DEFAULT — "permanent" is retracted here on 2026-08-26, because this row's own revision below withdraws the removal commitment and a withdrawn schedule cannot leave a permanence behind it. **Revision (ADR-232 track, maintainer decision 2026-08-15).** The original row committed removal to 11.0; the tree reached 12.0.0 with the runtime paths still registered, i.e. the commitment was missed. Rather than quietly re-dating it, the commitment is withdrawn with its reason recorded, because the measurement changed what removal is worth: **the payload this deprecation existed for has already shipped** — the parser pair (`web-tree-sitter@0.24.7` / `tree-sitter-wasms@0.1.13`, ~51 MB unpacked) moved from `dependencies` to `devDependencies` ahead of schedule, so no consumer
|
|
20
|
+
| `code_graph` native code-graph engine (`code_graph.enabled`, the `code-intelligence` skill's native arm, `code_graph_nudge_hook`) | 2026-07-28 | next major after 9.x | **REVISED 2026-08-15 — no removal date; see below** | A consumer case the graph answers and disciplined grep cannot. Measured null: recall 0.365 vs grep 0.797 (`docs/CLAIMS.md` `code-graph-retrieval-null`), measured 2026-07-28 against a build predating the 2026-08-22 extractor repair. `enabled: false` stays the DEFAULT — "permanent" is retracted here on 2026-08-26, because this row's own revision below withdraws the removal commitment and a withdrawn schedule cannot leave a permanence behind it. **Revision (ADR-232 track, maintainer decision 2026-08-15).** The original row committed removal to 11.0; the tree reached 12.0.0 with the runtime paths still registered, i.e. the commitment was missed. Rather than quietly re-dating it, the commitment is withdrawn with its reason recorded, because the measurement changed what removal is worth: **the payload this deprecation existed for has already shipped** — the parser pair (`web-tree-sitter@0.24.7` / `tree-sitter-wasms@0.1.13`, ~51 MB unpacked) moved from `dependencies` to `devDependencies` ahead of schedule, so no consumer installed it. **REVERSED 2026-09-07 by ADR-259 (as amended) — this clause no longer holds and is kept only so the reversal is legible.** Delivery and measurement are decoupled: `web-tree-sitter` is a runtime `dependency` again and the three loadable grammars are vendored at `src/vendor/grammars/`, so a consumer now DOES install the engine. The measured null above is untouched and `enabled: false` stays the default — what changed is that the reopen trigger ("a consumer case the graph answers") became reachable at all, which it was not while no consumer could receive the engine. The install delta is recorded below under "14.21.x — the code-graph engine ships to consumers". What source removal would still free is ~112 K against a 27 M tree (0.4 %). Against that it costs a breaking change across four consumer-visible surfaces (CLI verb, `code-intelligence` skill, `external-code-graph-interop` rule, `hooks.code_graph.enabled`) plus one surface that is re-plumbing rather than deletion: `_lib/auto_dispatch.ts` routes the `definition` / `references` lookup classes to `primitive: 'code-graph-query'`, and `_lib/judgment_ladder.ts` calls that live at Rung 0. There is also no cheap middle — `code_graph/detect.ts` handles the `consumer` / `scip` / `native` source kinds in one type union, so stripping only the native engine while keeping consumer-index interop is a redesign. **New commitment:** the surfaces stay registered and disabled; removal happens when a concrete reason appears (a maintenance cost that bites, a conflicting redesign, or a consumer asking), not on a date. This row stays in the table so the decision is visible rather than forgotten — a withdrawn commitment recorded is not the folklore this table exists to prevent; an unrecorded one would be. |
|
|
21
21
|
| `telegraph-speak` condenser (`telegraph.speak`, `src/rules/telegraph-speak.md`, `src/scripts/_lib/compile_time_toggles.ts`, `src/scripts/validate_telegraph_carveouts.ts`) | 2026-07-29 | shipped 2026-07-29 — dormant by default, `speak` defaults `false` | **not pinned here** — removal is authorized in principle and deliberately not executed (ADR `telegraph/0002` § Decision part 3), pending a `prose_only` bench (~$0.80). The date is maintainer-owned, same stance as the `compatibility` row above | An output-side bench clears the kill-criterion bar, at which point `telegraph.speak: true` restores the feature intact. Measured basis for dormancy: median vs_terse **−9.27 %** (API) / −5.47 % (exact `cl100k_base`) — the condenser emits MORE than a plain "be terse". Tracked as a row rather than left dormant-and-unlisted: dormancy without a row is untracked in *both* directions, neither scheduled nor recorded as a keep. Deliberately NOT a due version (that would invent a commitment ADR 0002 declined to make) and NOT a permanent keep (that would contradict its authorized removal). |
|
|
22
22
|
|
|
23
23
|
### Row status — what is currently late, and who owns it
|
|
@@ -42,6 +42,110 @@ reason, which no arithmetic can.
|
|
|
42
42
|
or that promises a future breaking removal, gets a row here in the same commit
|
|
43
43
|
that makes the promise. A promise with no row is not tracked and will be missed.
|
|
44
44
|
|
|
45
|
+
## 14.22.x — the code-graph engine gets readers, and the MCP install hint changes
|
|
46
|
+
|
|
47
|
+
💡 advisory · 🔄 automatic — **one exception, below.**
|
|
48
|
+
|
|
49
|
+
The 14.21.x entry above shipped the engine to consumers. This one ships the
|
|
50
|
+
things that read it (`road-to-a-graph-that-is-shipped`, Phases 3 and 4).
|
|
51
|
+
|
|
52
|
+
### Additive — nothing to do
|
|
53
|
+
|
|
54
|
+
- **Four new `code-graph` subcommands**, all answering questions about a DIFF
|
|
55
|
+
rather than a symbol: `impact --diff <rev>`, `tests-for <symbol>`,
|
|
56
|
+
`untested --diff <rev>`, `dead`. Each prints the resolution mechanisms it
|
|
57
|
+
accepted, the mechanisms it refused, and the graph's three-state staleness.
|
|
58
|
+
No existing subcommand changed, and no new top-level verb was added.
|
|
59
|
+
- **Five new MCP tools** on the existing stdio server — `graph_impact`,
|
|
60
|
+
`graph_tests_for`, `graph_dead`, `graph_query`, `graph_path` — taking the
|
|
61
|
+
catalogue from 31 tools to 36. Four are read-only; `graph_impact` runs
|
|
62
|
+
`git diff --name-only` to resolve its rev and declares `side_effect: shell`
|
|
63
|
+
for that reason.
|
|
64
|
+
- **A new `tests` edge relation**, derived in the build pass from a test file's
|
|
65
|
+
imports. `SCHEMA_VERSION` 3 → 4 and `GRAPH_STORE_VERSION` 3 → 4, so an
|
|
66
|
+
existing cache and its SQLite twin are refused and rebuilt on the next
|
|
67
|
+
`code-graph build`. The cache is derived and disposable
|
|
68
|
+
(ADR-129) — there is nothing to migrate, and a rebuild takes seconds.
|
|
69
|
+
|
|
70
|
+
### The one thing to check — `dead` refuses by default
|
|
71
|
+
|
|
72
|
+
`agent-config code-graph dead` exits **1** with an empty list unless you either
|
|
73
|
+
supply an entry-point list (`--entry-points <file>`) or accept the gap
|
|
74
|
+
(`--accept-missing-exports`). That is deliberate: this engine records no
|
|
75
|
+
exportedness, so without a supplied list an exported-but-not-yet-imported public
|
|
76
|
+
symbol is indistinguishable from a dead one, and a confident false "dead" invites
|
|
77
|
+
a deletion the graph cannot justify. If you wire `dead` into a script, handle the
|
|
78
|
+
refusal — an exit 1 here is not a crash, and treating it as an empty dead list
|
|
79
|
+
is the false negative the refusal exists to prevent.
|
|
80
|
+
|
|
81
|
+
### One consumer-visible string changed
|
|
82
|
+
|
|
83
|
+
`install_hint_stdio` in the published MCP tool catalogue was
|
|
84
|
+
`npx -y @event4u/agent-config mcp-server` and is now **`agent-config mcp-server`**
|
|
85
|
+
— the installed-binary form the setup docs already lead with. It resolves no
|
|
86
|
+
dist-tag, so the server you run is the version you installed rather than
|
|
87
|
+
whatever the registry served most recently. **It assumes `agent-config` is on
|
|
88
|
+
PATH.** If you read that field programmatically and rely on `npx`, keep using
|
|
89
|
+
the pinned form from [`mcp-server.md`](mcp-server.md) —
|
|
90
|
+
`npx -y @event4u/agent-config@<version> mcp-server` — which is unchanged and
|
|
91
|
+
still the documented client configuration.
|
|
92
|
+
|
|
93
|
+
### Standing context cost
|
|
94
|
+
|
|
95
|
+
Registering the stdio MCP server now costs **4,876 tokens** of standing context
|
|
96
|
+
per session, up from 3,886 — the five graph tools measure 993 between them. It
|
|
97
|
+
remains roughly 4x under the ~20,000-token Tool Search deferral threshold at a
|
|
98
|
+
200k window, so the surface still loads upfront. Measured, with the prior reading
|
|
99
|
+
kept: `agents/evidence/metrics/mcp-tool-standing-cost.jsonl`.
|
|
100
|
+
|
|
101
|
+
## 14.21.x — the code-graph engine ships to consumers
|
|
102
|
+
|
|
103
|
+
💡 advisory · 🔄 automatic — no action required.
|
|
104
|
+
|
|
105
|
+
`npm install @event4u/agent-config` now carries everything the native
|
|
106
|
+
code-graph engine needs, so `agent-config code-graph build` works on the next
|
|
107
|
+
command with no manual parser install (ADR-259, as amended 2026-09-07).
|
|
108
|
+
|
|
109
|
+
### What changed
|
|
110
|
+
|
|
111
|
+
- `web-tree-sitter@0.24.7` moved from `devDependencies` to `dependencies`.
|
|
112
|
+
- Three compiled tree-sitter grammars are **vendored** in the published tarball
|
|
113
|
+
at `src/vendor/grammars/` — php, typescript (which also serves `.tsx`), and
|
|
114
|
+
javascript. These are the three `GRAMMAR_WASM` entries the engine can load.
|
|
115
|
+
- `tree-sitter-wasms` stays a `devDependency`. It is the source the vendored
|
|
116
|
+
copies are refreshed from, not a runtime dependency, so consumers do **not**
|
|
117
|
+
receive the 36-grammar pack.
|
|
118
|
+
|
|
119
|
+
### Install delta, measured
|
|
120
|
+
|
|
121
|
+
Measured on 2026-09-07 with `npm pack --dry-run --json --ignore-scripts`,
|
|
122
|
+
against the same tree with the vendored directory excluded from `files[]`:
|
|
123
|
+
|
|
124
|
+
| Axis | Before | After | Delta |
|
|
125
|
+
|---|---|---|---|
|
|
126
|
+
| Compressed tarball | 9,821,600 B | 10,195,522 B | **+373,922 B** (+0.357 MiB) |
|
|
127
|
+
| Vendored grammars, on disk | 0 B | 3,802,618 B | **+3,802,618 B** (+3.63 MiB) |
|
|
128
|
+
| `web-tree-sitter` in `node_modules` | 0 B (dev-only) | ~389,120 B | **+~380 KiB** |
|
|
129
|
+
|
|
130
|
+
Per-grammar, uncompressed: `tree-sitter-php.wasm` 812,594 B ·
|
|
131
|
+
`tree-sitter-typescript.wasm` 2,342,690 B · `tree-sitter-javascript.wasm`
|
|
132
|
+
647,334 B.
|
|
133
|
+
|
|
134
|
+
For contrast, the two rejected shapes: depending on `tree-sitter-wasms`
|
|
135
|
+
directly would have delivered all 36 grammars (51,765,657 B apparent, 49 MiB on
|
|
136
|
+
disk), and the 13-grammar set ADR-259 originally named measures 9,085,798 B
|
|
137
|
+
apparent / 1,016,804 B compressed — the latter would have put the tarball at
|
|
138
|
+
~10.84 MB. Only three grammars are loadable today, so the other ten would have
|
|
139
|
+
been payload with no reader.
|
|
140
|
+
|
|
141
|
+
### Rolling back
|
|
142
|
+
|
|
143
|
+
Set `hooks.code_graph.enabled: false` — which is already the default; the
|
|
144
|
+
engine ships disabled. Nothing runs the parsers unless you build a graph. To
|
|
145
|
+
drop the payload entirely, remove `src/vendor/grammars/` from `files[]` and
|
|
146
|
+
move `web-tree-sitter` back to `devDependencies`; the loader falls back to the
|
|
147
|
+
`tree-sitter-wasms` pack, which is this repository's own dev path.
|
|
148
|
+
|
|
45
149
|
## 8.x → 9.0.0 — consumer rule projection scoped by default
|
|
46
150
|
|
|
47
151
|
9.0.0 flips the consumer rule-projection default to **scoped**: fresh
|