@event4u/agent-config 9.27.0 → 9.28.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/.claude-plugin/marketplace.json +1 -1
- package/AGENTS.md +12 -11
- package/CHANGELOG.md +99 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/conformance.md +24 -0
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +10 -3
- package/dist/agent-src/rules/artifact-engagement-recording.md +4 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -1
- package/dist/agent-src/rules/brand-source-of-truth.md +33 -6
- package/dist/agent-src/rules/context-hygiene.md +3 -1
- package/dist/agent-src/rules/council-availability.md +93 -0
- package/dist/agent-src/rules/cross-source-consistency.md +3 -0
- package/dist/agent-src/rules/delegation-policy.md +29 -5
- package/dist/agent-src/rules/design-fidelity.md +3 -0
- package/dist/agent-src/rules/evaluator-independence.md +2 -1
- package/dist/agent-src/rules/external-code-graph-interop.md +2 -1
- package/dist/agent-src/rules/finance-safety-floor.md +1 -1
- package/dist/agent-src/rules/git-history-discipline.md +1 -1
- package/dist/agent-src/rules/history-discipline.md +3 -1
- package/dist/agent-src/rules/legal-safety-floor.md +1 -1
- package/dist/agent-src/rules/no-pr-progress-comments.md +3 -1
- package/dist/agent-src/rules/onboarding-gate.md +3 -1
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +4 -1
- package/dist/agent-src/rules/role-mode-adherence.md +1 -1
- package/dist/agent-src/rules/scale-discipline.md +3 -1
- package/dist/agent-src/rules/self-repair-loop.md +103 -0
- package/dist/agent-src/rules/session-canary.md +2 -1
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
- package/dist/agent-src/rules/token-budget-discipline.md +3 -1
- package/dist/agent-src/rules/tool-safety.md +5 -1
- package/dist/agent-src/skills/adversarial-review/SKILL.md +1 -1
- package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
- package/dist/agent-src/skills/performance-analysis/SKILL.md +1 -1
- package/dist/agent-src/skills/persona-improvement/SKILL.md +1 -1
- package/dist/agent-src/skills/project-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/security-audit/SKILL.md +1 -1
- package/dist/agent-src/skills/sequential-thinking/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/universal-project-analysis/SKILL.md +1 -1
- package/dist/agent-src/templates/AGENTS.md +6 -10
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +7 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-22GCIJ2O.js +270 -0
- package/dist/cli-delegate/{chunk-BCPIOAGV.js → chunk-2BE5I2MN.js} +8 -8
- package/dist/cli-delegate/{chunk-AIIUU73N.js → chunk-7OMQCH4V.js} +1 -1
- package/dist/cli-delegate/{chunk-HJ6DAJHR.js → chunk-GB2FSUEE.js} +57 -7
- package/dist/cli-delegate/{chunk-WR6GTRHJ.js → chunk-JXARCGVN.js} +3 -3
- package/dist/cli-delegate/{chunk-3KJIMFTB.js → chunk-PQSAQKHW.js} +466 -304
- package/dist/cli-delegate/chunk-REKCMYOU.js +2991 -0
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +134 -0
- package/dist/cli-delegate/cmd_conformance.js +7 -7
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +4 -4
- package/dist/cli-delegate/cmd_explain.js +80 -330
- package/dist/cli-delegate/cmd_export.js +5 -5
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_handoff.js +2 -2
- package/dist/cli-delegate/cmd_mcp_available.js +433 -0
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +153 -0
- package/dist/cli-delegate/cmd_preflight.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +5 -5
- package/dist/cli-delegate/cmd_route_audit.js +8 -8
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_settings_get.js +272 -0
- package/dist/cli-delegate/cmd_settings_migrate.js +5 -5
- package/dist/cli-delegate/cmd_settings_set.js +7 -2967
- package/dist/cli-delegate/cmd_sync.js +5 -5
- package/dist/cli-delegate/cmd_uninstall.js +5 -5
- package/dist/cli-delegate/cmd_update.js +4 -4
- package/dist/cli-delegate/cmd_upgrade.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +5 -5
- package/dist/cli-delegate/cmd_versions.js +4 -4
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +100 -40
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -5
- package/dist/discovery/trust-report.md +11 -11
- package/dist/discovery/workspaces.json +6 -4
- package/dist/hooks/dispatch.js +9004 -6132
- package/dist/install/install.mjs +697 -507
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +127 -20
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +80 -15
- package/dist/scripts/_lib/agent_settings.js.map +1 -1
- package/docs/contracts/settings-classes.md +29 -8
- package/docs/guidelines/agent-infra/memory-access.md +14 -3
- package/package.json +1 -1
- package/src/agent-src/templates/AGENTS.md +6 -10
- package/src/config/budgets.yml +29 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +26 -0
- package/src/scripts/_cli/cmd_brand_status.ts +174 -0
- package/src/scripts/_cli/cmd_mcp_available.ts +266 -0
- package/src/scripts/_cli/cmd_packs_active.ts +225 -0
- package/src/scripts/_cli/cmd_settings_get.ts +324 -0
- package/src/scripts/_dispatch.bash +57 -0
- package/src/scripts/_lib/agent_settings.ts +82 -16
- package/src/scripts/_lib/carrier_divergence.ts +135 -0
- package/src/scripts/_lib/host_capability.ts +84 -1
- package/src/scripts/_lib/prompt_shape.ts +88 -0
- package/src/scripts/_lib/review_skipped_record.ts +102 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +254 -0
- package/src/scripts/_lib/self_repair.ts +342 -0
- package/src/scripts/_lib/self_repair_store.ts +100 -0
- package/src/scripts/_lib/transcript_entry.ts +80 -0
- package/src/scripts/check_branch_freshness.ts +255 -0
- package/src/scripts/check_council_config_location.ts +125 -1
- package/src/scripts/check_standing_rule_delivery.ts +314 -0
- package/src/scripts/config/profiles.ts +13 -3
- package/src/scripts/conformance_scan.ts +272 -24
- package/src/scripts/council_availability_hook.ts +188 -0
- package/src/scripts/council_cli.ts +126 -7
- package/src/scripts/hook_manifest.yaml +107 -26
- package/src/scripts/hooks/block_no_verify.ts +39 -2
- package/src/scripts/hooks/block_unauthorized_git.ts +156 -7
- package/src/scripts/hooks/concern_registry.ts +10 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +450 -0
- package/src/scripts/hooks/end_review_nudge_hook.ts +726 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +342 -0
- package/src/scripts/install.ts +151 -0
- package/src/scripts/language_mirror_hook.ts +30 -37
- package/src/scripts/measure_scope_dedup.ts +19 -13
- package/src/scripts/onboarding_gate_hook.ts +40 -1
- package/src/scripts/orchestration_record.ts +4 -1
- package/src/scripts/preamble_byte_census.ts +9 -20
- package/src/scripts/report_carrier_divergence.ts +364 -0
- package/src/scripts/report_skill_activation.ts +257 -0
- package/src/scripts/report_skill_obligation_violations.ts +579 -0
- package/src/scripts/routing_doctor.ts +77 -6
- package/src/scripts/self_repair_cli.ts +240 -0
- package/src/scripts/self_repair_hook.ts +241 -0
|
@@ -63,6 +63,8 @@ import { format_install_hints } from './ai_council/cli_hints.js';
|
|
|
63
63
|
import {
|
|
64
64
|
type AdvisorConfig,
|
|
65
65
|
type CouncilConfig,
|
|
66
|
+
COUNCIL_CONFIG_ENV,
|
|
67
|
+
COUNCIL_CONFIG_USER_GLOBAL_REL,
|
|
66
68
|
CouncilConfigError,
|
|
67
69
|
load_council_config,
|
|
68
70
|
resolve_api_key,
|
|
@@ -558,9 +560,22 @@ function build_members(settings: Dict, opts: BuildMembersOptions = {}): External
|
|
|
558
560
|
|
|
559
561
|
const ai = _isDict(settings) ? ((settings['ai_council'] as Dict) || {}) : {};
|
|
560
562
|
if (!_pyBool(ai['enabled'])) {
|
|
563
|
+
// The file this message names is load-bearing, and it named the wrong one
|
|
564
|
+
// for months. Measured across 10 sessions in one consumer project: the
|
|
565
|
+
// agent reasoned from `.agent-settings.yml` six times, concluded no
|
|
566
|
+
// council existed, and substituted a weaker path — and one session
|
|
567
|
+
// "fixed" it by COPYING the user-global config into the project tree,
|
|
568
|
+
// because this message told it that was where the switch lived. An error
|
|
569
|
+
// from the authoritative tool is the strongest signal in the room; when
|
|
570
|
+
// it points at the wrong file it does not merely fail to help, it
|
|
571
|
+
// manufactures the wrong belief. Name the real path (ADR-104: always
|
|
572
|
+
// user-global, never project-local) and name the verb that reports it.
|
|
561
573
|
throw new CouncilDisabledError(
|
|
562
|
-
|
|
563
|
-
'flip it on before invoking
|
|
574
|
+
`enabled is false in the council config (${COUNCIL_CONFIG_USER_GLOBAL_REL} ` +
|
|
575
|
+
'under the user-global agent-config root) — flip it on before invoking ' +
|
|
576
|
+
'council:* commands. This config is NEVER project-local (ADR-104), so do ' +
|
|
577
|
+
'not look for it, or create it, in the project tree. ' +
|
|
578
|
+
'`agent-config council:status` prints the resolved path.',
|
|
564
579
|
);
|
|
565
580
|
}
|
|
566
581
|
const members_cfg = (ai['members'] as Dict) || {};
|
|
@@ -608,7 +623,8 @@ function build_members(settings: Dict, opts: BuildMembersOptions = {}): External
|
|
|
608
623
|
if (name in siblings) {
|
|
609
624
|
throw new CouncilDisabledError(
|
|
610
625
|
`--siblings targets member ${_pyReprStr(name)} but it is not ` +
|
|
611
|
-
`enabled in
|
|
626
|
+
`enabled in the council config (members.${name}.enabled in ` +
|
|
627
|
+
`${COUNCIL_CONFIG_USER_GLOBAL_REL} under the user-global root).`,
|
|
612
628
|
);
|
|
613
629
|
}
|
|
614
630
|
continue;
|
|
@@ -714,8 +730,10 @@ function build_members(settings: Dict, opts: BuildMembersOptions = {}): External
|
|
|
714
730
|
);
|
|
715
731
|
}
|
|
716
732
|
throw new CouncilDisabledError(
|
|
717
|
-
'no council member has `enabled: true` — enable at least one
|
|
718
|
-
|
|
733
|
+
'no council member has `enabled: true` — enable at least one under ' +
|
|
734
|
+
`members.* in ${COUNCIL_CONFIG_USER_GLOBAL_REL} (user-global root; ` +
|
|
735
|
+
'never the project tree, ADR-104). `agent-config council:status` ' +
|
|
736
|
+
'prints the resolved path.',
|
|
719
737
|
);
|
|
720
738
|
}
|
|
721
739
|
return members;
|
|
@@ -1855,6 +1873,95 @@ function _emit_shadow_slo_banner(): void {
|
|
|
1855
1873
|
}
|
|
1856
1874
|
}
|
|
1857
1875
|
|
|
1876
|
+
/**
|
|
1877
|
+
* `council:status` — answer "is a council configured, and from where" without
|
|
1878
|
+
* spending anything, so no caller has to infer it from a filename.
|
|
1879
|
+
*
|
|
1880
|
+
* WHY THIS EXISTS. Measured 2026-08-08: an agent working in a consumer project
|
|
1881
|
+
* announced "Kein Council konfiguriert (keine `.agent-settings.yml`)" and
|
|
1882
|
+
* substituted a weaker subagent fan-out. The council was configured the whole
|
|
1883
|
+
* time, user-globally. The inference was wrong twice over — the council config
|
|
1884
|
+
* has not lived in `.agent-settings.yml` since the Phase-0 migration, and per
|
|
1885
|
+
* ADR-104 the project tree is **never** searched, so the presence or absence of
|
|
1886
|
+
* any project file says nothing at all about council availability.
|
|
1887
|
+
*
|
|
1888
|
+
* The guidance was not missing: `/council default` states this emphatically in
|
|
1889
|
+
* four places. But a command file only loads when the command is invoked, and
|
|
1890
|
+
* the agent was deciding *whether a capability existed* — a question it reached
|
|
1891
|
+
* before invoking anything. The always-loaded layer says "when the council is
|
|
1892
|
+
* enabled" and never says how to find out.
|
|
1893
|
+
*
|
|
1894
|
+
* So the fix is not more prose. It is a check that costs nothing to run and
|
|
1895
|
+
* leaves nothing to infer. Exit is always 0 — this reports a state, it does not
|
|
1896
|
+
* gate — and the verdict line is machine-greppable in both directions.
|
|
1897
|
+
*/
|
|
1898
|
+
export function cmd_status(args: Args, opts: { env?: Record<string, string | undefined> } = {}): number {
|
|
1899
|
+
const env = opts.env ?? process.env;
|
|
1900
|
+
const override = env[COUNCIL_CONFIG_ENV];
|
|
1901
|
+
const path_ = resolve_config_path(REPO_ROOT, { env: env as never });
|
|
1902
|
+
const exists = fs.existsSync(String(path_));
|
|
1903
|
+
const provenance = override ? `${COUNCIL_CONFIG_ENV} override` : 'user-global';
|
|
1904
|
+
|
|
1905
|
+
let cfg: CouncilConfig | null = null;
|
|
1906
|
+
let parse_error: string | null = null;
|
|
1907
|
+
if (exists) {
|
|
1908
|
+
try {
|
|
1909
|
+
cfg = load_council_config(path_);
|
|
1910
|
+
} catch (exc) {
|
|
1911
|
+
parse_error = exc instanceof Error ? exc.message : String(exc);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
const enabledMembers = cfg === null ? [] : [...cfg.members.entries()].filter(([, m]) => m.enabled);
|
|
1916
|
+
// "Configured" deliberately means: a readable file whose `enabled` is true and
|
|
1917
|
+
// which has at least one enabled member. A file that parses but enables
|
|
1918
|
+
// nothing is the case a caller most needs told apart from a missing file,
|
|
1919
|
+
// because only one of the two is fixed by writing a config.
|
|
1920
|
+
const configured = cfg !== null && cfg.enabled && enabledMembers.length > 0;
|
|
1921
|
+
|
|
1922
|
+
if (_getattr(args, 'json', false)) {
|
|
1923
|
+
_stdout(
|
|
1924
|
+
`${JSON.stringify({
|
|
1925
|
+
configured,
|
|
1926
|
+
path: String(path_),
|
|
1927
|
+
exists,
|
|
1928
|
+
provenance,
|
|
1929
|
+
enabled: cfg?.enabled ?? null,
|
|
1930
|
+
members_total: cfg === null ? null : cfg.members.size,
|
|
1931
|
+
members_enabled: cfg === null ? null : enabledMembers.length,
|
|
1932
|
+
member_names: enabledMembers.map(([n]) => n),
|
|
1933
|
+
parse_error,
|
|
1934
|
+
project_tree_consulted: false,
|
|
1935
|
+
})}\n`,
|
|
1936
|
+
);
|
|
1937
|
+
return 0;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
_stdout(`council:status · ${configured ? 'CONFIGURED' : 'NOT CONFIGURED'}\n`);
|
|
1941
|
+
_stdout(` config path ${String(path_)}\n`);
|
|
1942
|
+
_stdout(` resolved by ${provenance}\n`);
|
|
1943
|
+
_stdout(` file exists ${exists ? 'yes' : 'no'}\n`);
|
|
1944
|
+
if (parse_error !== null) {
|
|
1945
|
+
_stdout(` parse error ${parse_error}\n`);
|
|
1946
|
+
}
|
|
1947
|
+
if (cfg !== null) {
|
|
1948
|
+
_stdout(` enabled ${cfg.enabled ? 'yes' : 'no'}\n`);
|
|
1949
|
+
_stdout(` members ${enabledMembers.length} enabled of ${cfg.members.size}\n`);
|
|
1950
|
+
if (enabledMembers.length > 0) {
|
|
1951
|
+
_stdout(` ${enabledMembers.map(([n]) => n).join(', ')}\n`);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
_stdout('\n');
|
|
1955
|
+
_stdout(' The project tree is NEVER consulted (ADR-104). No project file —\n');
|
|
1956
|
+
_stdout(' `.agent-settings.yml` included — indicates council availability in\n');
|
|
1957
|
+
_stdout(' either direction. Do not infer it from one; run this instead.\n');
|
|
1958
|
+
if (!configured) {
|
|
1959
|
+
_stdout(`\n To configure: write ${COUNCIL_CONFIG_USER_GLOBAL_REL} under the user-global\n`);
|
|
1960
|
+
_stdout(' agent-config root, with `enabled: true` and at least one enabled member.\n');
|
|
1961
|
+
}
|
|
1962
|
+
return 0;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1858
1965
|
function _apply_solo_dispatch(members: ExternalAIClient[]): [ExternalAIClient[], string | null] {
|
|
1859
1966
|
let cfg: CouncilConfig;
|
|
1860
1967
|
try {
|
|
@@ -2826,13 +2933,15 @@ interface Args {
|
|
|
2826
2933
|
// shadow-report
|
|
2827
2934
|
log: string | null;
|
|
2828
2935
|
window_days: number;
|
|
2936
|
+
// status
|
|
2937
|
+
json: boolean;
|
|
2829
2938
|
}
|
|
2830
2939
|
|
|
2831
|
-
const _SUBCOMMANDS = ['estimate', 'run', 'debate', 'render', 'replay', 'quota', 'shadow-report'];
|
|
2940
|
+
const _SUBCOMMANDS = ['estimate', 'run', 'debate', 'render', 'replay', 'quota', 'shadow-report', 'status'];
|
|
2832
2941
|
|
|
2833
2942
|
const _TOP_USAGE =
|
|
2834
2943
|
`usage: ${_PROG} [-h]\n` +
|
|
2835
|
-
` {estimate,run,debate,render,replay,quota,shadow-report}\n` +
|
|
2944
|
+
` {estimate,run,debate,render,replay,quota,shadow-report,status}\n` +
|
|
2836
2945
|
` ...\n`;
|
|
2837
2946
|
|
|
2838
2947
|
function _topError(message: string): never {
|
|
@@ -2886,6 +2995,7 @@ function _defaultArgs(): Args {
|
|
|
2886
2995
|
reset: null,
|
|
2887
2996
|
log: null,
|
|
2888
2997
|
window_days: 7,
|
|
2998
|
+
json: false,
|
|
2889
2999
|
};
|
|
2890
3000
|
}
|
|
2891
3001
|
|
|
@@ -3009,6 +3119,12 @@ function _specsFor(cmd: string): { positionals: string[]; opts: OptSpec[]; requi
|
|
|
3009
3119
|
],
|
|
3010
3120
|
requiredOpts: [],
|
|
3011
3121
|
};
|
|
3122
|
+
case 'status':
|
|
3123
|
+
return {
|
|
3124
|
+
positionals: [],
|
|
3125
|
+
opts: [{ flag: '--json', takesValue: false, apply: (o) => (o.json = true) }],
|
|
3126
|
+
requiredOpts: [],
|
|
3127
|
+
};
|
|
3012
3128
|
default:
|
|
3013
3129
|
return { positionals: [], opts: [], requiredOpts: [] };
|
|
3014
3130
|
}
|
|
@@ -3213,6 +3329,9 @@ function main(argv: string[] | null = null): number {
|
|
|
3213
3329
|
if (args.cmd === 'shadow-report') {
|
|
3214
3330
|
return cmd_shadow_report(args);
|
|
3215
3331
|
}
|
|
3332
|
+
if (args.cmd === 'status') {
|
|
3333
|
+
return cmd_status(args);
|
|
3334
|
+
}
|
|
3216
3335
|
} catch (exc) {
|
|
3217
3336
|
if (exc instanceof CouncilDisabledError) {
|
|
3218
3337
|
_stderr(`❌ council:${args.cmd}: ${exc.message}\n`);
|
|
@@ -319,6 +319,29 @@ concerns:
|
|
|
319
319
|
args: []
|
|
320
320
|
fail_closed: false
|
|
321
321
|
severity: advisory
|
|
322
|
+
# Council-availability fact. Carries whether a council is configured and from
|
|
323
|
+
# where, because the always-loaded layer refers to the council conditionally
|
|
324
|
+
# and never says how to check. Measured 2026-08-08: an agent announced "kein
|
|
325
|
+
# Council konfiguriert (keine .agent-settings.yml)" and substituted a weaker
|
|
326
|
+
# path; the council was configured user-globally the whole time, and the
|
|
327
|
+
# project tree is never searched for it (ADR-104). Advisory: supplies a fact,
|
|
328
|
+
# never blocks.
|
|
329
|
+
council-availability:
|
|
330
|
+
script: src/scripts/council_availability_hook.ts
|
|
331
|
+
args: []
|
|
332
|
+
fail_closed: false
|
|
333
|
+
severity: advisory
|
|
334
|
+
# Self-repair intake (road-to-self-repair Phase 1). Two slots, one queue:
|
|
335
|
+
# user_prompt_submit opens a record when the user states the agent worked
|
|
336
|
+
# wrongly and injects the open-queue line; stop runs the deterministic
|
|
337
|
+
# detectors over the finished turn and records silently (a reminder fired
|
|
338
|
+
# after the reply cannot shape it). ADVISORY by construction — it records,
|
|
339
|
+
# it never gates; the outward step is a separate, user-run CLI command.
|
|
340
|
+
self-repair:
|
|
341
|
+
script: src/scripts/self_repair_hook.ts
|
|
342
|
+
args: []
|
|
343
|
+
fail_closed: false
|
|
344
|
+
severity: advisory
|
|
322
345
|
# Shared per-session register (road-to-parallel-session-coordination) — writes
|
|
323
346
|
# this session into <git-common-dir>/agent-sessions/<session_id>.json so every
|
|
324
347
|
# other worktree of the repo can see it, and on session_start injects the live
|
|
@@ -351,22 +374,70 @@ concerns:
|
|
|
351
374
|
args: []
|
|
352
375
|
fail_closed: false
|
|
353
376
|
severity: advisory
|
|
377
|
+
# road-to-orchestrator-discipline-carriers Phase 3 (F6) — deterministic
|
|
378
|
+
# PostToolUse orchestration-telemetry capture. Fires only on an Agent/Task
|
|
379
|
+
# tool-use completion (the two observed subagent-dispatch tool names) and
|
|
380
|
+
# appends one `orchestration_record` line — no model step, replacing the
|
|
381
|
+
# measured 0.27% model-carried capture rate for future dispatches. Silent
|
|
382
|
+
# on every path (never writes to stdout); exit 0 always, including a
|
|
383
|
+
# malformed envelope or a write failure. fail_closed: false.
|
|
384
|
+
orchestration-record:
|
|
385
|
+
script: src/scripts/hooks/orchestration_record_hook.ts
|
|
386
|
+
args: []
|
|
387
|
+
fail_closed: false
|
|
388
|
+
severity: advisory
|
|
389
|
+
tools: [Agent, Task]
|
|
390
|
+
# road-to-orchestrator-discipline-carriers Phase 4 (F3-lite) — conditional
|
|
391
|
+
# delegation nudge. Extracts cheap structural signals from the SUBMITTED
|
|
392
|
+
# prompt (enumerated file lists, "for each"/"alle …" shapes, explicit slice
|
|
393
|
+
# counts, ordered-plan markers, multi-deliverable conjunctions), classifies
|
|
394
|
+
# via `classifyTask`, and — only on a positive do-in-parallel/do-in-steps
|
|
395
|
+
# verdict — reports `{decision:"warn", reason, additional_context}` at exit
|
|
396
|
+
# 2, mirroring `language-mirror`'s shipped delivery pattern so the verdict
|
|
397
|
+
# line actually reaches `hookSpecificOutput.additionalContext` on the
|
|
398
|
+
# verified `claude` platform. Silence is the anti-canary default: no
|
|
399
|
+
# enumerated signal → no injection at all. Never blocks the turn — see the
|
|
400
|
+
# hook's file header for the `host_semantics.emitFor` proof. fail_closed:
|
|
401
|
+
# false.
|
|
402
|
+
delegation-nudge:
|
|
403
|
+
script: src/scripts/hooks/delegation_nudge_hook.ts
|
|
404
|
+
args: []
|
|
405
|
+
fail_closed: false
|
|
406
|
+
severity: advisory
|
|
407
|
+
# road-to-orchestrator-discipline-carriers Phase 5 (F4-lite) — end-review
|
|
408
|
+
# nudge. Fires ONLY when the session mutated tracked+untracked, non-doc
|
|
409
|
+
# files beyond MUTATION_LINE_THRESHOLD (50 lines; untracked files added
|
|
410
|
+
# post-review, F6) AND no reviewer ran this session (scanned from the
|
|
411
|
+
# Stop payload's `transcript_path`) AND this session has not already
|
|
412
|
+
# fired once (F2 — a once-per-session gate; state under
|
|
413
|
+
# agents/runtime/state/end-review-nudge/, gitignored). On fire, reports
|
|
414
|
+
# `{decision:"warn", reason, additional_context}` at exit 2 (same delivery
|
|
415
|
+
# pattern as `language-mirror`/`delegation-nudge` above) and appends one
|
|
416
|
+
# `review_skipped` telemetry line — hook-carried, not model-carried — so a
|
|
417
|
+
# future blocking decision has a measured distribution to cite. Never
|
|
418
|
+
# blocks the turn — see the hook's file header for the `host_semantics.
|
|
419
|
+
# emitFor` proof. fail_closed: false.
|
|
420
|
+
end-review-nudge:
|
|
421
|
+
script: src/scripts/hooks/end_review_nudge_hook.ts
|
|
422
|
+
args: []
|
|
423
|
+
fail_closed: false
|
|
424
|
+
severity: advisory
|
|
354
425
|
|
|
355
426
|
platforms:
|
|
356
427
|
augment:
|
|
357
|
-
session_start: [chat-history, hot-context, handoff-context, session-canary, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
428
|
+
session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
358
429
|
session_end: [chat-history, memory-learn, session-register]
|
|
359
|
-
stop: [chat-history, hot-context, verify-before-complete, session-register]
|
|
430
|
+
stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register]
|
|
360
431
|
pre_tool_use: [block-no-verify, block-unauthorized-git, evidence-independence, block-kernel-rule-writes, block-config-weakening, rtk-wrap, design-slop, code-graph-nudge]
|
|
361
|
-
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder]
|
|
432
|
+
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record]
|
|
362
433
|
|
|
363
434
|
claude:
|
|
364
|
-
session_start: [chat-history, hot-context, handoff-context, session-canary, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
435
|
+
session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
365
436
|
session_end: [chat-history, memory-learn, session-register]
|
|
366
|
-
stop: [chat-history, hot-context, verify-before-complete, team-review-gate, session-register]
|
|
367
|
-
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, session-register]
|
|
437
|
+
stop: [chat-history, hot-context, verify-before-complete, team-review-gate, end-review-nudge, self-repair, session-register]
|
|
438
|
+
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, delegation-nudge, git-authorization, session-canary, self-repair, session-register]
|
|
368
439
|
pre_tool_use: [block-no-verify, block-unauthorized-git, evidence-independence, block-kernel-rule-writes, block-config-weakening, rtk-wrap, design-slop, code-graph-nudge]
|
|
369
|
-
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder]
|
|
440
|
+
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record]
|
|
370
441
|
|
|
371
442
|
# Cowork — the Claude desktop app's local-agent-mode runtime, built
|
|
372
443
|
# on top of the Claude Code CLI. Same lifecycle vocabulary, same
|
|
@@ -383,12 +454,22 @@ platforms:
|
|
|
383
454
|
# Decision matrix + upstream blockers tracked in
|
|
384
455
|
# agents/settings/contexts/chat-history-platform-hooks.md § Cowork.
|
|
385
456
|
cowork:
|
|
386
|
-
session_start: [chat-history, hot-context, handoff-context, session-canary, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
457
|
+
session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
387
458
|
session_end: [chat-history, memory-learn, session-register]
|
|
388
|
-
|
|
389
|
-
|
|
459
|
+
# end-review-nudge and delegation-nudge are DELIBERATELY NOT bound on
|
|
460
|
+
# this platform (F10, review) — cowork-dispatcher.sh's own header states
|
|
461
|
+
# it "always exit[s] 0" and its dispatch line discards the dispatcher's
|
|
462
|
+
# exit code AND stdout unconditionally (`>/dev/null 2>&1 || true`). Both
|
|
463
|
+
# concerns' delivery depends entirely on `additional_context` reaching
|
|
464
|
+
# the model via stdout at the verified exit-2 warn path — a delivery this
|
|
465
|
+
# trampoline makes structurally impossible regardless of which platform
|
|
466
|
+
# is later verified to forward it. Binding them here would be dead
|
|
467
|
+
# weight: the concern still runs (git diff, transcript scan, classifier),
|
|
468
|
+
# still costs the dispatch, and its entire output is thrown away.
|
|
469
|
+
stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register]
|
|
470
|
+
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, self-repair, session-register]
|
|
390
471
|
pre_tool_use: [block-no-verify, block-unauthorized-git, evidence-independence, block-kernel-rule-writes, block-config-weakening, rtk-wrap, design-slop, code-graph-nudge]
|
|
391
|
-
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder]
|
|
472
|
+
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record]
|
|
392
473
|
|
|
393
474
|
# Phase 7.5 — Cursor. `.cursor/hooks.json` (project) is read by the
|
|
394
475
|
# IDE and CLI; `~/.cursor/hooks.json` (user) is opt-in via
|
|
@@ -398,11 +479,11 @@ platforms:
|
|
|
398
479
|
# IDE-only — CLI-only users fall back to /checkpoint per
|
|
399
480
|
# agents/settings/contexts/chat-history-platform-hooks.md.
|
|
400
481
|
cursor:
|
|
401
|
-
session_start: [chat-history, hot-context, handoff-context, session-canary, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
482
|
+
session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
402
483
|
session_end: [chat-history, memory-learn, session-register]
|
|
403
|
-
stop: [chat-history, hot-context, verify-before-complete, session-register]
|
|
404
|
-
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, session-register]
|
|
405
|
-
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder]
|
|
484
|
+
stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register]
|
|
485
|
+
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, self-repair, session-register]
|
|
486
|
+
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record]
|
|
406
487
|
|
|
407
488
|
# Phase 7.6 — Cline. Hooks live under `.clinerules/hooks/<HookName>`
|
|
408
489
|
# (project, no file extension, must be executable per Cline docs) or
|
|
@@ -413,11 +494,11 @@ platforms:
|
|
|
413
494
|
# both map to session_start. TaskCancel maps to stop because the
|
|
414
495
|
# session is interrupted with partial state (mirrors Augment Stop).
|
|
415
496
|
cline:
|
|
416
|
-
session_start: [chat-history, hot-context, handoff-context, session-canary, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
497
|
+
session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
417
498
|
session_end: [chat-history, memory-learn, session-register]
|
|
418
|
-
stop: [chat-history, hot-context, verify-before-complete, session-register]
|
|
419
|
-
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, session-register]
|
|
420
|
-
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder]
|
|
499
|
+
stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register]
|
|
500
|
+
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, self-repair, session-register]
|
|
501
|
+
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record]
|
|
421
502
|
|
|
422
503
|
# Phase 7.7 — Windsurf (Cascade). Hooks live at `.windsurf/hooks.json`
|
|
423
504
|
# (project) or `~/.codeium/windsurf/hooks.json` (user). Cascade has
|
|
@@ -432,11 +513,11 @@ platforms:
|
|
|
432
513
|
# surface to record verification commands; documented limitation).
|
|
433
514
|
# minimal-safe-diff is omitted entirely on Windsurf for the same reason.
|
|
434
515
|
windsurf:
|
|
435
|
-
session_start: [chat-history, hot-context, handoff-context, session-canary, first-run-gate, onboarding-gate, verify-before-complete, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
436
|
-
stop: [chat-history, hot-context, verify-before-complete, session-register]
|
|
516
|
+
session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, first-run-gate, onboarding-gate, verify-before-complete, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
517
|
+
stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register]
|
|
437
518
|
# language-mirror IS bound here: unlike minimal-safe-diff it needs only the
|
|
438
519
|
# user_prompt_submit slot, which Windsurf has.
|
|
439
|
-
user_prompt_submit: [chat-history, verify-before-complete, language-mirror, git-authorization, session-canary, session-register]
|
|
520
|
+
user_prompt_submit: [chat-history, verify-before-complete, language-mirror, git-authorization, session-canary, self-repair, session-register]
|
|
440
521
|
|
|
441
522
|
# Phase 7.8 — Gemini CLI. Hooks live at `.gemini/settings.json`
|
|
442
523
|
# (project) or `~/.gemini/settings.json` (user). Per Gemini docs
|
|
@@ -449,11 +530,11 @@ platforms:
|
|
|
449
530
|
# turn-check semantics. AfterAgent fires when the agent loop ends
|
|
450
531
|
# — this is our `stop` slot.
|
|
451
532
|
gemini:
|
|
452
|
-
session_start: [chat-history, hot-context, handoff-context, session-canary, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
533
|
+
session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
|
|
453
534
|
session_end: [chat-history, memory-learn, session-register]
|
|
454
|
-
stop: [chat-history, hot-context, verify-before-complete, session-register]
|
|
455
|
-
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, session-register]
|
|
456
|
-
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder]
|
|
535
|
+
stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register]
|
|
536
|
+
user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, self-repair, session-register]
|
|
537
|
+
post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record]
|
|
457
538
|
|
|
458
539
|
# Phase 7.9 — Copilot has no hook surface. Concerns route through
|
|
459
540
|
# rule-only fallback; the dispatcher silently no-ops on --platform copilot.
|
|
@@ -20,12 +20,21 @@
|
|
|
20
20
|
* 2 — warn (not used by this guard)
|
|
21
21
|
*
|
|
22
22
|
* No ALLOW_NO_VERIFY-style env bypass is provided. See src/rules/git-history-discipline.md
|
|
23
|
+
*
|
|
24
|
+
* WHAT THIS GUARD DOES NOT SEE. Command substitutions ARE now checked — see
|
|
25
|
+
* `_check_command` for why they were not, and why the well-formed case was the
|
|
26
|
+
* unprotected one. What remains uncovered is an invocation whose command word
|
|
27
|
+
* only exists at runtime (`P=commit; git $P --no-verify` via indirection, or a
|
|
28
|
+
* word composed by `xargs`). Those execute under bash and are not classified
|
|
29
|
+
* here. Stated rather than left implied: this file's own round-5 defect was a
|
|
30
|
+
* docstring that described coverage it did not have.
|
|
23
31
|
*/
|
|
24
32
|
|
|
25
33
|
import * as fs from 'node:fs';
|
|
26
34
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
27
35
|
import * as path from 'node:path';
|
|
28
36
|
import { readHookStdin } from './hook_stdin.js';
|
|
37
|
+
import { substitutionPayloads } from './block_unauthorized_git.js';
|
|
29
38
|
|
|
30
39
|
const _HERE = fileURLToPath(import.meta.url);
|
|
31
40
|
|
|
@@ -384,8 +393,36 @@ export function _looks_like_git_invocation(cmd: string, depth = 0): boolean {
|
|
|
384
393
|
return false;
|
|
385
394
|
}
|
|
386
395
|
|
|
387
|
-
/**
|
|
388
|
-
|
|
396
|
+
/**
|
|
397
|
+
* Return (blocked, reason). Fail-closed on parse error for git commands.
|
|
398
|
+
*
|
|
399
|
+
* COMMAND SUBSTITUTIONS ARE CHECKED SEPARATELY, and the reason they were not is
|
|
400
|
+
* the shape of the hole: this file does split on `$(` and backtick, but only
|
|
401
|
+
* inside `_looks_like_git_invocation`, which runs exclusively in the
|
|
402
|
+
* shlex-failure branch. A well-formed `echo "$(git commit --no-verify)"` parses
|
|
403
|
+
* cleanly, so shlex succeeds, the substitution stays inside one token, and
|
|
404
|
+
* `_git_base` never sees git. The more parseable the command, the less it was
|
|
405
|
+
* protected. Classification inside the payload is by command position, so a
|
|
406
|
+
* substitution that INVOKES git is checked and one that merely names it in an
|
|
407
|
+
* argument is not — matching what `block_unauthorized_git` does one level up.
|
|
408
|
+
*
|
|
409
|
+
* Heredoc bodies are removed before extraction: a backtick or `$(` inside a
|
|
410
|
+
* commit message being written to a file is data, and feeding it back through
|
|
411
|
+
* this function is exactly the false positive the round-5 repair closed.
|
|
412
|
+
*/
|
|
413
|
+
function _check_command(cmd: string, depth = 0): [boolean, string] {
|
|
414
|
+
if (depth <= 3) {
|
|
415
|
+
const withoutHeredocs = cmd.replace(
|
|
416
|
+
/<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1[\s\S]*?^\s*\2\s*$/gm,
|
|
417
|
+
'<<HEREDOC',
|
|
418
|
+
);
|
|
419
|
+
for (const payload of substitutionPayloads(withoutHeredocs)) {
|
|
420
|
+
const [blocked, reason] = _check_command(payload, depth + 1);
|
|
421
|
+
if (blocked) {
|
|
422
|
+
return [true, reason];
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
389
426
|
let tokens: string[];
|
|
390
427
|
try {
|
|
391
428
|
tokens = shlexSplit(cmd);
|