@groundnuty/macf 0.2.4 → 0.2.5
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/dist/.build-info.json +2 -2
- package/dist/cli/settings-writer.d.ts +22 -3
- package/dist/cli/settings-writer.d.ts.map +1 -1
- package/dist/cli/settings-writer.js +49 -19
- package/dist/cli/settings-writer.js.map +1 -1
- package/package.json +2 -2
- package/plugin/rules/mention-routing-hygiene.md +23 -0
- package/scripts/check-mention-routing.sh +160 -0
package/dist/.build-info.json
CHANGED
|
@@ -20,6 +20,16 @@
|
|
|
20
20
|
* literally) because the basename matcher is path-agnostic.
|
|
21
21
|
*/
|
|
22
22
|
export declare const MACF_HOOK_COMMAND = "$CLAUDE_PROJECT_DIR/.claude/scripts/check-gh-token.sh";
|
|
23
|
+
/**
|
|
24
|
+
* Mention-routing-hygiene hook command (groundnuty/macf#244 + #272).
|
|
25
|
+
* Blocks `gh issue comment` / `gh pr comment` / `gh issue close --comment`
|
|
26
|
+
* invocations whose `--body` contains raw `@<bot>[bot]` mentions in
|
|
27
|
+
* describing-context (mid-line, not backticked). Implements
|
|
28
|
+
* `mention-routing-hygiene.md` §5 enforcement structurally; codification
|
|
29
|
+
* alone produced ~80% catch rate (see science-agent's research note
|
|
30
|
+
* `2026-04-27-self-observed-canonical-rule-breach-pattern-analysis.md`).
|
|
31
|
+
*/
|
|
32
|
+
export declare const MACF_MENTION_HOOK_COMMAND = "$CLAUDE_PROJECT_DIR/.claude/scripts/check-mention-routing.sh";
|
|
23
33
|
/**
|
|
24
34
|
* Permission patterns pre-approving the 4 `macf-agent` plugin skills.
|
|
25
35
|
* Without these, every first invocation of a skill (e.g. `/macf-status`
|
|
@@ -167,11 +177,20 @@ export declare function getSandboxExcludedCommands(workspaceDir: string): readon
|
|
|
167
177
|
*/
|
|
168
178
|
export declare function installPluginSkillPermissions(workspaceDir: string): void;
|
|
169
179
|
/**
|
|
170
|
-
* Install (or refresh) the MACF PreToolUse hook
|
|
171
|
-
*
|
|
180
|
+
* Install (or refresh) the MACF PreToolUse hook entries in
|
|
181
|
+
* `<workspaceDir>/.claude/settings.json`. Currently installs:
|
|
182
|
+
* - `check-gh-token.sh` (groundnuty/macf#140 — attribution-trap defense)
|
|
183
|
+
* - `check-mention-routing.sh` (groundnuty/macf#244 + #272 — routing
|
|
184
|
+
* leak detector for raw `@<bot>[bot]` in describing-context)
|
|
185
|
+
*
|
|
172
186
|
* Creates the `.claude/` directory and the file if either is missing.
|
|
187
|
+
* Idempotent: repeated calls don't duplicate entries.
|
|
173
188
|
*
|
|
174
|
-
*
|
|
189
|
+
* Both hooks share `matcher: "Bash"` because Claude Code's matcher field
|
|
190
|
+
* gates which tool fires the hook; the wrapped-command detection (gh vs
|
|
191
|
+
* git-push for token, gh issue/pr comment for routing) happens INSIDE
|
|
192
|
+
* each script. Distinct entries per script keep them independently
|
|
193
|
+
* upgradeable + diagnosable in `gh issue list` style settings audits.
|
|
175
194
|
*/
|
|
176
195
|
export declare function installGhTokenHook(workspaceDir: string): void;
|
|
177
196
|
//# sourceMappingURL=settings-writer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-writer.d.ts","sourceRoot":"","sources":["../../src/cli/settings-writer.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,0DAA0D,CAAC;
|
|
1
|
+
{"version":3,"file":"settings-writer.d.ts","sourceRoot":"","sources":["../../src/cli/settings-writer.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,0DAA0D,CAAC;AAEzF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,iEAAiE,CAAC;AA6DxG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAKrD,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,uBAAuB,kBAAkB,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAS3E;AAaD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CA+CpE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EA0CtD,CAAC;AAWF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CA8CzE;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAQlF;AASD;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CA+BxE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CA0C7D"}
|
|
@@ -43,15 +43,28 @@ import { join, resolve } from 'node:path';
|
|
|
43
43
|
*/
|
|
44
44
|
export const MACF_HOOK_COMMAND = '$CLAUDE_PROJECT_DIR/.claude/scripts/check-gh-token.sh';
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
46
|
+
* Mention-routing-hygiene hook command (groundnuty/macf#244 + #272).
|
|
47
|
+
* Blocks `gh issue comment` / `gh pr comment` / `gh issue close --comment`
|
|
48
|
+
* invocations whose `--body` contains raw `@<bot>[bot]` mentions in
|
|
49
|
+
* describing-context (mid-line, not backticked). Implements
|
|
50
|
+
* `mention-routing-hygiene.md` §5 enforcement structurally; codification
|
|
51
|
+
* alone produced ~80% catch rate (see science-agent's research note
|
|
52
|
+
* `2026-04-27-self-observed-canonical-rule-breach-pattern-analysis.md`).
|
|
53
|
+
*/
|
|
54
|
+
export const MACF_MENTION_HOOK_COMMAND = '$CLAUDE_PROJECT_DIR/.claude/scripts/check-mention-routing.sh';
|
|
55
|
+
/**
|
|
56
|
+
* The hook filenames used to identify MACF-managed entries on refresh.
|
|
47
57
|
* Matched by path-end equality (see isMacfManagedCommand) so operator
|
|
48
58
|
* files with a similar-but-distinct basename are not misclassified.
|
|
49
59
|
*/
|
|
50
|
-
const
|
|
60
|
+
const MACF_HOOK_FILENAMES = [
|
|
61
|
+
'check-gh-token.sh',
|
|
62
|
+
'check-mention-routing.sh',
|
|
63
|
+
];
|
|
51
64
|
/**
|
|
52
|
-
* True iff the command string represents our managed
|
|
53
|
-
* command invokes a file whose basename equals
|
|
54
|
-
* (ignoring any trailing flags/arguments). Defensive against
|
|
65
|
+
* True iff the command string represents one of our managed hooks — i.e.
|
|
66
|
+
* the command invokes a file whose basename equals any MACF_HOOK_FILENAMES
|
|
67
|
+
* entry (ignoring any trailing flags/arguments). Defensive against
|
|
55
68
|
* operator-authored commands that happen to contain our filename as a
|
|
56
69
|
* substring (e.g. `./my-check-gh-token.sh-wrapper --flag`).
|
|
57
70
|
*/
|
|
@@ -61,7 +74,7 @@ function isMacfManagedCommand(command) {
|
|
|
61
74
|
const program = command.trim().split(/\s+/)[0] ?? '';
|
|
62
75
|
const slash = program.lastIndexOf('/');
|
|
63
76
|
const basename = slash >= 0 ? program.slice(slash + 1) : program;
|
|
64
|
-
return basename
|
|
77
|
+
return MACF_HOOK_FILENAMES.includes(basename);
|
|
65
78
|
}
|
|
66
79
|
function readSettings(path) {
|
|
67
80
|
if (!existsSync(path))
|
|
@@ -412,11 +425,20 @@ export function installPluginSkillPermissions(workspaceDir) {
|
|
|
412
425
|
writeFileSync(path, JSON.stringify(updated, null, 2) + '\n');
|
|
413
426
|
}
|
|
414
427
|
/**
|
|
415
|
-
* Install (or refresh) the MACF PreToolUse hook
|
|
416
|
-
*
|
|
428
|
+
* Install (or refresh) the MACF PreToolUse hook entries in
|
|
429
|
+
* `<workspaceDir>/.claude/settings.json`. Currently installs:
|
|
430
|
+
* - `check-gh-token.sh` (groundnuty/macf#140 — attribution-trap defense)
|
|
431
|
+
* - `check-mention-routing.sh` (groundnuty/macf#244 + #272 — routing
|
|
432
|
+
* leak detector for raw `@<bot>[bot]` in describing-context)
|
|
433
|
+
*
|
|
417
434
|
* Creates the `.claude/` directory and the file if either is missing.
|
|
435
|
+
* Idempotent: repeated calls don't duplicate entries.
|
|
418
436
|
*
|
|
419
|
-
*
|
|
437
|
+
* Both hooks share `matcher: "Bash"` because Claude Code's matcher field
|
|
438
|
+
* gates which tool fires the hook; the wrapped-command detection (gh vs
|
|
439
|
+
* git-push for token, gh issue/pr comment for routing) happens INSIDE
|
|
440
|
+
* each script. Distinct entries per script keep them independently
|
|
441
|
+
* upgradeable + diagnosable in `gh issue list` style settings audits.
|
|
420
442
|
*/
|
|
421
443
|
export function installGhTokenHook(workspaceDir) {
|
|
422
444
|
const absDir = resolve(workspaceDir);
|
|
@@ -426,21 +448,29 @@ export function installGhTokenHook(workspaceDir) {
|
|
|
426
448
|
const settings = readSettings(path);
|
|
427
449
|
const hooks = settings.hooks ?? {};
|
|
428
450
|
const preToolUse = hooks.PreToolUse ?? [];
|
|
429
|
-
// Drop any prior MACF-managed entries
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
//
|
|
433
|
-
//
|
|
451
|
+
// Drop any prior MACF-managed entries (any hook file in
|
|
452
|
+
// MACF_HOOK_FILENAMES) so we can replace them cleanly — guards against
|
|
453
|
+
// stale flags from older CLI versions + handles renames/additions to
|
|
454
|
+
// the canonical hook set. Match by path-end equality so an
|
|
455
|
+
// operator-authored file with a similar-but-distinct name
|
|
456
|
+
// (e.g. `my-check-gh-token.sh-helper.sh`) doesn't get misclassified
|
|
457
|
+
// as ours and accidentally clobbered.
|
|
434
458
|
const preserved = preToolUse.filter((entry) => !entry.hooks.some((h) => isMacfManagedCommand(h.command)));
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
459
|
+
const macfEntries = [
|
|
460
|
+
{
|
|
461
|
+
matcher: 'Bash',
|
|
462
|
+
hooks: [{ type: 'command', command: MACF_HOOK_COMMAND }],
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
matcher: 'Bash',
|
|
466
|
+
hooks: [{ type: 'command', command: MACF_MENTION_HOOK_COMMAND }],
|
|
467
|
+
},
|
|
468
|
+
];
|
|
439
469
|
const updated = {
|
|
440
470
|
...settings,
|
|
441
471
|
hooks: {
|
|
442
472
|
...hooks,
|
|
443
|
-
PreToolUse: [...preserved,
|
|
473
|
+
PreToolUse: [...preserved, ...macfEntries],
|
|
444
474
|
},
|
|
445
475
|
};
|
|
446
476
|
writeFileSync(path, JSON.stringify(updated, null, 2) + '\n');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-writer.js","sourceRoot":"","sources":["../../src/cli/settings-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,uDAAuD,CAAC;AAEzF;;;;GAIG;AACH,MAAM,
|
|
1
|
+
{"version":3,"file":"settings-writer.js","sourceRoot":"","sources":["../../src/cli/settings-writer.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@groundnuty/macf",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Multi-Agent Coordination Framework CLI — coordinate Claude Code agents via GitHub. Installs as `macf` binary; use `macf init` to set up an agent workspace, `macf update` to refresh rules + version pins.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"test:watch": "vitest"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@groundnuty/macf-core": "0.2.
|
|
38
|
+
"@groundnuty/macf-core": "0.2.5",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
40
|
"reflect-metadata": "^0.2.2",
|
|
41
41
|
"zod": "^4.0.0"
|
|
@@ -103,3 +103,26 @@ Without the backtick convention, every describing use of a handle produces a fal
|
|
|
103
103
|
The failure-mode was observed on `macf-testbed#9` and `#18` (2026-04-24): a single rules-loaded tester received three ambient routing pings across two scenario PRs, correctly disciplined each response with scope-preserving rationale, and escalated the third firing into a cross-session-commitment-tracking critique of the author. That sequence of responses was appropriate — but it was also three response turns that could have been prevented by one keystroke of backticks per handle reference in the PR bodies.
|
|
104
104
|
|
|
105
105
|
The rule is cheap to apply, symmetric across the fleet, and eliminates a class of false-positive routing that otherwise compounds with every describing use of an agent handle.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 7. Structural enforcement — `check-mention-routing.sh` PreToolUse hook
|
|
110
|
+
|
|
111
|
+
Per `groundnuty/macf#244` + `#272` (closed via shared PR), this rule is also enforced by a Claude Code PreToolUse hook on `Bash` tool calls. The hook intercepts `gh issue comment` / `gh pr comment` / `gh issue close --comment` / `gh pr close --comment` invocations, parses the `--body` content, and blocks (`exit 2` with a stderr explanation) when raw `@<bot>[bot]` patterns appear in describing-context positions (mid-line, not backticked, not at line-start).
|
|
112
|
+
|
|
113
|
+
The hook is the same shape as `check-gh-token.sh` (#140 attribution-trap defense) — bash command-type hook distributed via `macf init` / `macf update` / `macf rules refresh` to every workspace's `.claude/scripts/check-mention-routing.sh` with the entry registered in `.claude/settings.json` `hooks.PreToolUse`. Substrate workspaces, tester agents, CV consumers, and future MACF-consumer projects all get the protection uniformly.
|
|
114
|
+
|
|
115
|
+
**Heuristic** (subject to refinement; documented for transparency):
|
|
116
|
+
|
|
117
|
+
- Already wrapped in backticks (`` `@<bot>[bot]` ``) → allowed (canonical describing form §5)
|
|
118
|
+
- At line-start (after optional whitespace, blockquote `>`, or list-item markers `* ` / `- ` / `1. `) → allowed (canonical addressing form §3)
|
|
119
|
+
- Otherwise → BLOCK with stderr citing this rule + the offending line + the `MACF_SKIP_MENTION_CHECK=1` operator override
|
|
120
|
+
|
|
121
|
+
**False-positive trade-off:** The heuristic leans toward false-positive over false-negative. Edge cases the heuristic flags:
|
|
122
|
+
|
|
123
|
+
- Single-line bodies with addressing form right after `--body "` (no preceding newline) — operator should typically put addressing on its own line in multi-line bodies
|
|
124
|
+
- Line-start mentions that are actually describing-with-bot-as-subject ("`@bot`'s response was clean") — these are uncommon; canonical idiom puts describing references inside prose
|
|
125
|
+
|
|
126
|
+
The override (`MACF_SKIP_MENTION_CHECK=1`) handles legitimate cases. Per the `check-gh-token.sh` precedent, structural enforcement plus an escape hatch outperforms behavioral discipline alone.
|
|
127
|
+
|
|
128
|
+
**Empirical motivation:** `groundnuty/macf-science-agent:research/2026-04-27-self-observed-canonical-rule-breach-pattern-analysis.md` recorded 6 self-observed routing-hygiene class breaches in 1.5 days. Codification of this rule (§1-6 above) caught ~80%; the structural hook closes the remaining 20%.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# check-mention-routing.sh — Claude Code PreToolUse hook that blocks
|
|
4
|
+
# `gh issue comment` / `gh pr comment` / `gh issue close --comment` /
|
|
5
|
+
# `gh pr close --comment` invocations when the `--body` content contains
|
|
6
|
+
# raw `@macf-<role>-agent[bot]` mentions in describing contexts (mid-line,
|
|
7
|
+
# not backticked). Implements `mention-routing-hygiene.md` §5 structurally.
|
|
8
|
+
#
|
|
9
|
+
# Hook contract: JSON on stdin, exit 0 = allow, exit 2 = block (stderr
|
|
10
|
+
# is fed back to Claude as the error). Mirrors the shape of #140's
|
|
11
|
+
# check-gh-token.sh per groundnuty/macf#272 design alignment.
|
|
12
|
+
#
|
|
13
|
+
# Override: MACF_SKIP_MENTION_CHECK=1 bypasses (for legitimate raw-mention
|
|
14
|
+
# cases the heuristic catches; rare per the canonical rule's structure
|
|
15
|
+
# but mirrors check-gh-token.sh's escape hatch).
|
|
16
|
+
#
|
|
17
|
+
# Refs: groundnuty/macf#244 (must-have-mention class — orthogonal, deferred),
|
|
18
|
+
# groundnuty/macf#272 (must-not-leak — what this script enforces),
|
|
19
|
+
# DR-023 UC-4 (bash-form per substrate-compat — mcp_tool variant
|
|
20
|
+
# won't fire on substrate workspaces where the macf-agent MCP server
|
|
21
|
+
# isn't loaded, but the breach pattern is concentrated on substrate).
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
# Cheap exit on operator override — no stdin read, no parsing.
|
|
25
|
+
if [[ "${MACF_SKIP_MENTION_CHECK:-}" == "1" ]]; then
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Read PreToolUse payload. Fall through to allow on parse error — a
|
|
30
|
+
# broken hook must not brick the harness. Same defense-in-depth as
|
|
31
|
+
# check-gh-token.sh.
|
|
32
|
+
INPUT_JSON="$(cat)"
|
|
33
|
+
COMMAND="$(jq -r '.tool_input.command // ""' <<<"$INPUT_JSON" 2>/dev/null || echo "")"
|
|
34
|
+
|
|
35
|
+
# Wrapper-aware match for the comment-posting subcommands. Mirrors
|
|
36
|
+
# check-gh-token.sh's pattern shape — covers sudo, env VAR=, watch,
|
|
37
|
+
# ionice, setsid, nice, time prefix wrappers + chained-form leadins
|
|
38
|
+
# `;` `|` `&`. The subcommands we care about are exactly those that
|
|
39
|
+
# accept --body and post text content visible to other agents:
|
|
40
|
+
# gh issue comment gh pr comment
|
|
41
|
+
# gh issue close gh pr close (only when --comment is present;
|
|
42
|
+
# plain close has no body)
|
|
43
|
+
GH_COMMENT_PATTERN='(^|[[:space:];|&])(sudo[[:space:]]+|env[[:space:]]+([A-Za-z_][A-Za-z_0-9]*=[^[:space:]]*[[:space:]]+)*|watch[[:space:]]+|ionice[[:space:]]+|setsid[[:space:]]+|nice[[:space:]]+|time[[:space:]]+|[A-Za-z_][A-Za-z_0-9]*=[^[:space:]]*[[:space:]]+)*gh[[:space:]]+(issue|pr)[[:space:]]+(comment|close)([[:space:]]|$)'
|
|
44
|
+
|
|
45
|
+
# Shell-wrapper bypass: catches `bash -c "gh issue comment ..."` and
|
|
46
|
+
# variants. Same flag-handling logic as check-gh-token.sh.
|
|
47
|
+
SHELL_C_GH_COMMENT_PATTERN='(^|[[:space:];|&])(sudo[[:space:]]+|env[[:space:]]+([A-Za-z_][A-Za-z_0-9]*=[^[:space:]]*[[:space:]]+)*|[A-Za-z_][A-Za-z_0-9]*=[^[:space:]]*[[:space:]]+)*(bash|sh|zsh)[[:space:]]+(-[a-zA-Z]+[[:space:]]+)*-[a-zA-Z]*c[[:space:]]+[^[:space:]].*gh[[:space:]]+(issue|pr)[[:space:]]+(comment|close)([[:space:]]|$)'
|
|
48
|
+
|
|
49
|
+
if [[ ! "$COMMAND" =~ $GH_COMMENT_PATTERN ]] && [[ ! "$COMMAND" =~ $SHELL_C_GH_COMMENT_PATTERN ]]; then
|
|
50
|
+
# Not a comment-posting command — allow.
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# `gh issue close` / `gh pr close` without --comment doesn't post text.
|
|
55
|
+
# Skip — nothing to check.
|
|
56
|
+
if [[ "$COMMAND" =~ gh[[:space:]]+(issue|pr)[[:space:]]+close ]] && [[ ! "$COMMAND" =~ --comment ]]; then
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# `--body-file` reads content from a file path; we don't lint file
|
|
61
|
+
# contents (the file may not exist at hook-fire time, or may be
|
|
62
|
+
# regenerated). Accept the trade-off and allow. The canonical rule
|
|
63
|
+
# still applies; operator discipline catches it without the hook.
|
|
64
|
+
if [[ "$COMMAND" =~ --body-file([[:space:]]|=) ]] && [[ ! "$COMMAND" =~ --body([[:space:]]|=)[[:space:]]*[\"\']*[@] ]]; then
|
|
65
|
+
exit 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Per-occurrence scan for raw @macf-<role>-agent[bot] patterns in the
|
|
69
|
+
# command string. Heuristic per groundnuty/macf#272 design synthesis:
|
|
70
|
+
# - Already wrapped in backticks (`@bot[bot]`) → allowed (describing form §5)
|
|
71
|
+
# - At line start (only whitespace, blockquote `>`, or list markers
|
|
72
|
+
# `* ` `- ` `1. ` before it on the same line) → allowed (addressing
|
|
73
|
+
# form §3 — typical PR-closing-line / handoff / escalation shape)
|
|
74
|
+
# - Otherwise → BLOCK as describing-context leak
|
|
75
|
+
#
|
|
76
|
+
# False-positive trade-off: single-line bodies with the addressing form
|
|
77
|
+
# right after `--body "` (no preceding newline) are flagged. The canonical
|
|
78
|
+
# rule's examples (§3) all show addressing on its own line, so this
|
|
79
|
+
# matches the expected idiom. Override available for rare exceptions.
|
|
80
|
+
#
|
|
81
|
+
# False-negative trade-off: line-start mentions that are actually
|
|
82
|
+
# describing-with-bot-as-subject ("@bot's response was clean" — line
|
|
83
|
+
# starts with the handle but the sentence is descriptive) pass through.
|
|
84
|
+
# This is rare in practice; canonical idiom puts describing references
|
|
85
|
+
# inside prose, not at line-start. Operator discipline catches the residual.
|
|
86
|
+
# awk regex: `[[]` and `[]]` express literal `[` and `]` in a char class
|
|
87
|
+
# context (awk's `\[` escape would either warn-and-strip or be ambiguous
|
|
88
|
+
# across awk variants). Char body is `[a-zA-Z0-9_-]+` so digit-suffixed
|
|
89
|
+
# names like `macf-tester-1-agent` match alongside pure-letter forms.
|
|
90
|
+
HANDLE_PATTERN='@macf-[a-zA-Z0-9_-]+-agent[[]bot[]]'
|
|
91
|
+
|
|
92
|
+
OFFENDING="$(awk -v pat="$HANDLE_PATTERN" '
|
|
93
|
+
{
|
|
94
|
+
# Process every match on this line. After each match, advance the
|
|
95
|
+
# search-substring past it (RSTART+RLENGTH from the original line $0
|
|
96
|
+
# tracked via abs_offset).
|
|
97
|
+
abs_offset = 0
|
|
98
|
+
line = $0
|
|
99
|
+
while ( match(line, pat) ) {
|
|
100
|
+
abs_start = abs_offset + RSTART
|
|
101
|
+
abs_end = abs_start + RLENGTH
|
|
102
|
+
|
|
103
|
+
# Surrounding chars from the ORIGINAL line $0
|
|
104
|
+
char_before = (abs_start - 1 >= 1) ? substr($0, abs_start - 1, 1) : ""
|
|
105
|
+
char_after = substr($0, abs_end, 1)
|
|
106
|
+
|
|
107
|
+
# Already-backticked? Allowed describing form (§5).
|
|
108
|
+
if (char_before == "`" && char_after == "`") {
|
|
109
|
+
line = substr(line, RSTART + RLENGTH)
|
|
110
|
+
abs_offset = abs_start + RLENGTH - 1
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Line-start (after optional whitespace, blockquote, or list-item
|
|
115
|
+
# markers)? Allowed addressing form (§3).
|
|
116
|
+
prefix = substr($0, 1, abs_start - 1)
|
|
117
|
+
if (prefix ~ /^[[:space:]>]*([0-9]+\.[[:space:]]+|[-*][[:space:]]+)?$/) {
|
|
118
|
+
line = substr(line, RSTART + RLENGTH)
|
|
119
|
+
abs_offset = abs_start + RLENGTH - 1
|
|
120
|
+
continue
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Mid-line raw mention — describing-context leak.
|
|
124
|
+
print NR ": " $0
|
|
125
|
+
next # skip remaining matches on this line; one report per line
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
' <<<"$COMMAND")"
|
|
129
|
+
|
|
130
|
+
if [[ -n "$OFFENDING" ]]; then
|
|
131
|
+
cat >&2 <<ERR
|
|
132
|
+
BLOCKED by MACF mention-routing-hygiene hook: this comment contains raw
|
|
133
|
+
@<bot>[bot] mention(s) in describing-context (mid-line, not backticked) which
|
|
134
|
+
would fire false-positive routing per mention-routing-hygiene.md §5.
|
|
135
|
+
|
|
136
|
+
Offending line(s) within the command:
|
|
137
|
+
$OFFENDING
|
|
138
|
+
|
|
139
|
+
Fix per the canonical rule — wrap describing-context mentions in backticks:
|
|
140
|
+
Wrong: @macf-tester-2-agent[bot] response quoted coordination.md ...
|
|
141
|
+
Right: \`@macf-tester-2-agent[bot]\` response quoted coordination.md ...
|
|
142
|
+
|
|
143
|
+
Or use one of the equivalent suppression forms (§5):
|
|
144
|
+
- Backticks: \`@macf-tester-2-agent[bot]\` (preferred — semantic markup)
|
|
145
|
+
- Escapes: \\@macf-tester-2-agent\\[bot\\]
|
|
146
|
+
- Label form: "tester-2" or "the tester-2 agent"
|
|
147
|
+
|
|
148
|
+
Addressing form (line-start, expected to fire routing) is allowed:
|
|
149
|
+
@macf-science-agent[bot] PR ready for review.
|
|
150
|
+
|
|
151
|
+
Override (ONLY for legitimate raw-mention cases the heuristic catches):
|
|
152
|
+
export MACF_SKIP_MENTION_CHECK=1
|
|
153
|
+
|
|
154
|
+
Refs: groundnuty/macf#244, #272 (this hook); mention-routing-hygiene.md
|
|
155
|
+
(canonical rule, distributed via \`macf rules refresh\`).
|
|
156
|
+
ERR
|
|
157
|
+
exit 2
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
exit 0
|