test(hook): bind the conflict notice with a portable reproduce-first test

The end-to-end shell regression could not run on CI (recorded in the
previous commit), which left the fix without a gating test. Move the
notice DECISION out of main.c - which is not linked into the test
runner, and is why no C test could reach it - into src/cli/
hook_augment.c, which is. main.c now calls the shared API for both the
absent-daemon and build-conflict cases instead of carrying its own
copies of the strings, so the test binds the real production path
rather than a parallel one.

cli_hook_conflict_emits_stdout_notice_issue1388 asserts what the bug
actually broke: a build conflict yields a stdout systemMessage naming a
different build and pointing at 'daemon stop'; the absent-daemon notice
stays distinct and keeps pointing at 'daemon start' (which cannot heal
a conflict); and non-Claude dialects receive no bare stdout JSON, whose
channel it would corrupt.

RED-on-revert verified: restoring the pre-fix behaviour (conflict emits
nothing on stdout) fails this test alone, for the right reason
('conflict is NULL'). cli suite 258 passed, the local end-to-end shell
test still green against a seam-bearing binary, lint-ci clean.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel committed 2026-08-05 15:56:18 +02:00
1 parent 0e0e481b67
commit ea466487e8
4 files changed
+71 -12

No files matched your search

+14
View File
@@ -477,6 +477,20 @@ char *cbm_hook_augment_lifecycle_json_for(const char *input, const char *forced_
void cbm_hook_augment_arm_deadline(void);
char *cbm_hook_augment_read_stdin(void);
/* Why a hook client is not augmenting. The hook caller only ever sees stdout,
* so each reason that is actionable by the user must have a stdout notice
* (#1388: a build-conflicted daemon used to report on stderr alone, which is
* invisible in-session and reads as silent skips). */
typedef enum {
CBM_HOOK_ADMISSION_DAEMON_ABSENT = 0, /* no daemon running: `daemon start` heals it */
CBM_HOOK_ADMISSION_BUILD_CONFLICT /* daemon runs another build: needs `daemon stop` */
} cbm_hook_admission_t;
/* The JSON systemMessage a hook client must print on stdout for `reason`, or
* NULL when nothing should be printed. hook_dialect NULL = Claude Code, the
* only dialect that surfaces a stdout systemMessage to the user. */
const char *cbm_hook_admission_notice(cbm_hook_admission_t reason, const char *hook_dialect);
/* Process one already-read hook payload using a caller-owned MCP session.
* Returns a malloc-owned hook output JSON string, or NULL for fail-open/no
* augmentation. This is the daemon entry; it never arms a process-global