@event4u/agent-config 14.13.0 → 14.14.1

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.
Files changed (138) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +138 -376
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/analyze/inbox.md +36 -0
  7. package/dist/agent-src/commands/pr/create.md +16 -1
  8. package/dist/agent-src/contexts/execution/terminal-states.md +25 -4
  9. package/dist/agent-src/rules/code-comment-discipline.md +6 -0
  10. package/dist/agent-src/rules/missing-skill-recovery.md +2 -2
  11. package/dist/agent-src/rules/token-budget-discipline.md +2 -2
  12. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +6 -6
  13. package/dist/agent-src/scripts/guarded_baseline.ts +22 -7
  14. package/dist/agent-src/scripts/stubs_due.ts +118 -2
  15. package/dist/agent-src/scripts/update_roadmap_progress.ts +8 -8
  16. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  17. package/dist/cli/registry.js +1 -0
  18. package/dist/cli-delegate/{chunk-ILMUPLGW.js → chunk-7RXIKF62.js} +159 -86
  19. package/dist/cli-delegate/cmd_conformance.js +39 -2
  20. package/dist/cli-delegate/cmd_doctor.js +1 -1
  21. package/dist/discovery/deprecation-report.md +1 -1
  22. package/dist/discovery/discovery-manifest.json +7 -7
  23. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  24. package/dist/discovery/discovery-manifest.summary.md +1 -1
  25. package/dist/discovery/dormancy-report.md +1 -1
  26. package/dist/discovery/orphan-report.md +1 -1
  27. package/dist/discovery/packs.json +2 -2
  28. package/dist/discovery/trust-report.md +1 -1
  29. package/dist/discovery/workspaces.json +2 -2
  30. package/dist/hooks/dispatch.js +1696 -812
  31. package/dist/mcp/registry-manifest.json +2 -2
  32. package/dist/mcp/server.json +2 -2
  33. package/dist/mcp/server.mjs +297 -115
  34. package/docs/guidelines/agent-infra/guarded-baseline.md +1 -1
  35. package/docs/guidelines/code-clarity.md +18 -0
  36. package/package.json +1 -1
  37. package/src/agent-src/scripts/archive_completed_roadmaps.ts +6 -6
  38. package/src/agent-src/scripts/guarded_baseline.ts +22 -7
  39. package/src/agent-src/scripts/stubs_due.ts +118 -2
  40. package/src/agent-src/scripts/update_roadmap_progress.ts +8 -8
  41. package/src/config/assurance-capability-registry.json +1 -1
  42. package/src/config/estate-count-budget.json +20 -1
  43. package/src/config/evaluator-budgets.json +4 -4
  44. package/src/config/gate-coverage.yml +211 -1
  45. package/src/config/gate-reachability-exemptions.json +1 -0
  46. package/src/config/gate-violation-baselines.json +9 -3
  47. package/src/config/preamble-payload-budget.json +19 -11
  48. package/src/config/rule-stub-ceilings.json +14 -7
  49. package/src/config/web-launch-readiness.json +4 -2
  50. package/src/scripts/_cli/_doctor_emit.ts +71 -0
  51. package/src/scripts/_cli/_doctor_strict.ts +163 -0
  52. package/src/scripts/_cli/cmd_doctor.ts +16 -37
  53. package/src/scripts/_dispatch.bash +11 -4
  54. package/src/scripts/_lib/activation_ladder.ts +47 -3
  55. package/src/scripts/_lib/activation_receipt_producer.ts +341 -0
  56. package/src/scripts/_lib/adr_frontmatter.ts +26 -0
  57. package/src/scripts/_lib/branch_convergence.ts +186 -0
  58. package/src/scripts/_lib/candidate_pair_delta.ts +349 -0
  59. package/src/scripts/_lib/carrier_status.ts +118 -0
  60. package/src/scripts/_lib/cascade_stage_enumeration.ts +92 -0
  61. package/src/scripts/_lib/context_observation.ts +132 -0
  62. package/src/scripts/_lib/continuation_ladder.ts +188 -0
  63. package/src/scripts/_lib/corpus_manifest.ts +726 -0
  64. package/src/scripts/_lib/delivery_arm_experiment.ts +18 -2
  65. package/src/scripts/_lib/estate_carrier_cases.ts +72 -0
  66. package/src/scripts/_lib/evaluation_cascade.ts +452 -0
  67. package/src/scripts/_lib/evolution_roi.ts +562 -0
  68. package/src/scripts/_lib/experiment_binding.ts +95 -0
  69. package/src/scripts/_lib/harness_evolution_guards.ts +10 -0
  70. package/src/scripts/_lib/ignored_blocker.ts +8 -1
  71. package/src/scripts/_lib/lexical_shortlist.ts +215 -0
  72. package/src/scripts/_lib/llm_candidate_proposer.ts +461 -0
  73. package/src/scripts/_lib/llm_proposer_transport.ts +153 -0
  74. package/src/scripts/_lib/outcome_envelope.ts +6 -1
  75. package/src/scripts/_lib/outcome_vocabularies.ts +135 -5
  76. package/src/scripts/_lib/pathology_archive.ts +393 -0
  77. package/src/scripts/_lib/promotion_capability.ts +315 -0
  78. package/src/scripts/_lib/promotion_evidence.ts +414 -0
  79. package/src/scripts/_lib/promotion_review.ts +294 -0
  80. package/src/scripts/_lib/prompt_shape.ts +92 -0
  81. package/src/scripts/_lib/release_highlights.ts +79 -1
  82. package/src/scripts/_lib/release_material.ts +125 -2
  83. package/src/scripts/_lib/repeated_failure.ts +19 -2
  84. package/src/scripts/_lib/routing_corpus.ts +307 -0
  85. package/src/scripts/_lib/routing_index_input.ts +104 -0
  86. package/src/scripts/_lib/rule_injection.ts +22 -1
  87. package/src/scripts/_lib/runtime_journal.ts +11 -4
  88. package/src/scripts/_lib/semantic_noop.ts +167 -0
  89. package/src/scripts/activation_receipt.ts +258 -0
  90. package/src/scripts/ai_council/cross_exam.ts +128 -0
  91. package/src/scripts/ai_council/explain_route.ts +157 -0
  92. package/src/scripts/ai_council/information_gain.ts +293 -0
  93. package/src/scripts/ai_council/leakage_corpus.ts +385 -0
  94. package/src/scripts/ai_council/leakage_patterns.ts +453 -0
  95. package/src/scripts/ai_council/minority_retention.ts +171 -0
  96. package/src/scripts/ai_council/recouncil_savings.ts +299 -0
  97. package/src/scripts/ai_council/replay_route.ts +223 -0
  98. package/src/scripts/ai_council/reviewer_assignment.ts +279 -0
  99. package/src/scripts/ai_council/routing_training_row.ts +197 -0
  100. package/src/scripts/ai_council/synthesis_strategy.ts +159 -0
  101. package/src/scripts/check_adr_frontmatter.ts +4 -9
  102. package/src/scripts/check_agent_artifact_location.ts +337 -0
  103. package/src/scripts/check_artefact_count_messaging.ts +74 -8
  104. package/src/scripts/check_enforcement_coverage.ts +40 -1
  105. package/src/scripts/check_estate_count.ts +205 -24
  106. package/src/scripts/check_release_highlights.ts +113 -14
  107. package/src/scripts/check_roadmap_trackable.ts +17 -2
  108. package/src/scripts/check_web_launch_readiness.ts +310 -20
  109. package/src/scripts/corpus_manifest.ts +235 -0
  110. package/src/scripts/evolution_lab.ts +234 -9
  111. package/src/scripts/generate_capability_matrix.ts +16 -9
  112. package/src/scripts/grade_target_readiness.ts +221 -2
  113. package/src/scripts/hook_effect_doctor.ts +47 -4
  114. package/src/scripts/hook_manifest.json +1 -1
  115. package/src/scripts/hook_manifest.yaml +28 -6
  116. package/src/scripts/hooks/comment_discipline_hook.ts +146 -0
  117. package/src/scripts/hooks/concern_registry.ts +2 -0
  118. package/src/scripts/hooks/run_continuation_hook.ts +78 -88
  119. package/src/scripts/hooks/session_eol_hook.ts +12 -1
  120. package/src/scripts/hooks/turn_end_gate_hook.ts +9 -1
  121. package/src/scripts/install-hooks.sh +18 -0
  122. package/src/scripts/language_mirror_hook.ts +14 -2
  123. package/src/scripts/lint_carrier_integrity.ts +820 -0
  124. package/src/scripts/lint_code_comments.ts +748 -0
  125. package/src/scripts/lint_plan_risk_register.ts +39 -19
  126. package/src/scripts/lint_promotion_paths.ts +779 -0
  127. package/src/scripts/llm_propose.ts +256 -0
  128. package/src/scripts/measure_delivery_sets.ts +361 -0
  129. package/src/scripts/measure_routing_signal.ts +307 -0
  130. package/src/scripts/model_rule_injection.ts +19 -3
  131. package/src/scripts/probe_council_retention.ts +495 -0
  132. package/src/scripts/release.ts +21 -37
  133. package/src/scripts/release_drill.ts +199 -0
  134. package/src/scripts/release_env.ts +35 -0
  135. package/src/scripts/release_publication.ts +142 -0
  136. package/src/scripts/roadmap_context.ts +15 -0
  137. package/src/scripts/skill_scout.ts +633 -0
  138. package/src/scripts/sync_pr_branch.ts +521 -42
@@ -64,6 +64,13 @@
64
64
  * inert mechanism that says nothing is
65
65
  * indistinguishable from a healthy idle run,
66
66
  * which is the failure this file exists over)
67
+ * plan premise moved → allow (event: halt-premise-invalidated, ONCE per
68
+ * run. Engaged-under fingerprint and newest
69
+ * observation both known and differing. Before the
70
+ * counter rungs: iterating against a stale plan
71
+ * does not make it current, and capping out would
72
+ * report `exhausted`, a budget word, for staleness.
73
+ * Never fires on traffic the run did not observe)
67
74
  * quality-gate refusal → defer (event: deferred-quality-gate)
68
75
  * duplicate stop fire → repeat the BLOCK, no count (event: none —
69
76
  * an allow here would END the reply the block
@@ -173,8 +180,35 @@ import {
173
180
  readUnavailableDependency,
174
181
  stallSignal,
175
182
  type StallLevel,
176
- type UnavailableDependency,
177
183
  } from '../_lib/loop_guards.js';
184
+ import { premiseMoved, readContextObservation } from '../_lib/context_observation.js';
185
+ import { RUN_TERMINAL_VOCABULARY_VERSION } from '../_lib/outcome_vocabularies.js';
186
+ import {
187
+ HALT_ACTIONS,
188
+ ladder,
189
+ MAX_ITERATIONS,
190
+ parseHaltStamp,
191
+ STALL_WINDOW,
192
+ terminalStateFor,
193
+ WALL_CLOCK_CAP_MS,
194
+ type LadderAction,
195
+ } from '../_lib/continuation_ladder.js';
196
+
197
+ /**
198
+ * DECLARED in `_lib/continuation_ladder.ts`, re-exported here: one declaration,
199
+ * two import paths, no second copy to drift. That module's header says why the
200
+ * move happened (`road-to-wired-instruments` Phase 2).
201
+ */
202
+ export {
203
+ HALT_ACTIONS,
204
+ ladder,
205
+ MAX_ITERATIONS,
206
+ parseHaltStamp,
207
+ STALL_WINDOW,
208
+ terminalStateFor,
209
+ WALL_CLOCK_CAP_MS,
210
+ type LadderAction,
211
+ };
178
212
 
179
213
  /** Re-exported so a test can assert the IDENTITY, not two matching literals. */
180
214
  export { TRANSCRIPT_READ_MAX_BYTES };
@@ -198,11 +232,6 @@ export const EVENTS_RELPATH = path.join(
198
232
  );
199
233
  const STATE_DIR_REL = path.join('agents', 'runtime', 'state');
200
234
 
201
- /** Ladder defaults. Phase 1.1 pre-registers all three for revisit, not tuning. */
202
- export const MAX_ITERATIONS = 25;
203
- export const WALL_CLOCK_CAP_MS = 4 * 60 * 60 * 1000;
204
- export const STALL_WINDOW = 3;
205
-
206
235
  /**
207
236
  * How many consecutive absent-roadmap fires confirm the absence.
208
237
  *
@@ -285,6 +314,14 @@ export interface RunState {
285
314
  * cannot finish") named these rungs as its mitigation.
286
315
  */
287
316
  halted?: LadderAction;
317
+ /**
318
+ * The fingerprint this run ENGAGED under — its plan premise, recorded once on
319
+ * the first engagement from `_lib/context_observation` and then left alone.
320
+ * Refreshing it every fire would make both sides of the premise comparison
321
+ * equal by construction and the rung unreachable. Absent means the run never
322
+ * observed one, which is never a disagreement (`premiseMoved`).
323
+ */
324
+ context_fingerprint?: string;
288
325
  }
289
326
 
290
327
  /**
@@ -347,34 +384,6 @@ export interface ScanResult {
347
384
  next: NextStep | null;
348
385
  }
349
386
 
350
- export type LadderAction =
351
- | 'engage'
352
- | 'complete'
353
- | 'blocked'
354
- | 'halt-max-iterations'
355
- | 'halt-wall-clock'
356
- | 'halt-stall'
357
- | 'halt-dependency-unavailable';
358
-
359
- /** The terminal rungs — the set `RunState.halted` may hold. */
360
- /**
361
- * `halt-roadmap-absent` is deliberately NOT in this union and not in
362
- * `LadderAction`. It is an event name the caller emits directly when the claimed
363
- * roadmap is unreadable from the authoritative tree, and it CLEARS the state
364
- * rather than stamping `halted` — widening the union would let `parseRecord`
365
- * accept it as a `halted` value, which would mean a run whose budget was cleared
366
- * also carried a halt stamp. The two are mutually exclusive by construction.
367
- */
368
- export const HALT_ACTIONS: readonly LadderAction[] = [
369
- 'halt-max-iterations',
370
- 'halt-wall-clock',
371
- 'halt-stall',
372
- // Phase 5.4. Checked BEFORE the counter rungs: a dependency the run cannot
373
- // obtain is not something more iterations close, so consuming the budget
374
- // against it is the waste the rung exists to stop.
375
- 'halt-dependency-unavailable',
376
- ];
377
-
378
387
  /**
379
388
  * Frontmatter `execution.mode`, or null. Line-oriented on purpose: the
380
389
  * frontmatter this repo writes is two levels deep at most, and a YAML parser
@@ -480,50 +489,6 @@ export function extractVerify(stepText: string): string | null {
480
489
  return null;
481
490
  }
482
491
 
483
- /**
484
- * The termination ladder, pure. `state` is the record BEFORE this stop;
485
- * `openCount` is the scan of the claimed roadmap as it stands now.
486
- */
487
- export function ladder(
488
- state: RunState,
489
- openCount: number,
490
- nowMs: number,
491
- // No default: `= 0` would silently restore the complete-instead-of-blocked
492
- // behaviour for any caller that omitted it.
493
- blockedCount: number,
494
- caps: { maxIterations: number; wallClockMs: number; stallWindow: number } = {
495
- maxIterations: MAX_ITERATIONS,
496
- wallClockMs: WALL_CLOCK_CAP_MS,
497
- stallWindow: STALL_WINDOW,
498
- },
499
- // Phase 5.4. `null` when nothing unobtainable was detected. Optional so every
500
- // existing caller and test keeps its meaning unchanged.
501
- unavailable: UnavailableDependency | null = null,
502
- ): LadderAction {
503
- // A halt is terminal for this run id. Checked BEFORE `complete` so a
504
- // halted run whose roadmap later reads zero-open does not report a
505
- // completion it never reached.
506
- if (state.halted) return state.halted;
507
- // `scanOpenSteps` EXCLUDES `blocked-by:` steps from `openCount`, so zero-open
508
- // with blocked steps left is exhaustion of runnable work, not completion —
509
- // ADR-235's own terminal outcome, and never a sixth halt.
510
- if (openCount === 0) return blockedCount > 0 ? 'blocked' : 'complete';
511
- // BEFORE the counter rungs, deliberately: a missing credential, an absent
512
- // binary or an exhausted quota is not closed by iterating, so spending the
513
- // budget on it converts a nameable blocker into an anonymous cap-out.
514
- if (unavailable !== null) return 'halt-dependency-unavailable';
515
- if (state.iterations >= caps.maxIterations) return 'halt-max-iterations';
516
- const started = Date.parse(state.started_at);
517
- if (Number.isFinite(started) && nowMs - started >= caps.wallClockMs) {
518
- return 'halt-wall-clock';
519
- }
520
- const tail = state.history.slice(-caps.stallWindow);
521
- if (tail.length >= caps.stallWindow && tail.every((n) => n === openCount)) {
522
- return 'halt-stall';
523
- }
524
- return 'engage';
525
- }
526
-
527
492
  /**
528
493
  * Where a run's state lives: keyed on the session AND the roadmap.
529
494
  *
@@ -686,16 +651,17 @@ function readState(file: string): RunState | null {
686
651
  if (o['inert_reported'] === true) {
687
652
  rec.inert_reported = true;
688
653
  }
689
- // Validated against the halt set rather than accepted as any string:
690
- // an unrecognised value would be returned verbatim by `ladder` and
691
- // become an action no branch below handles.
692
- const halted = o['halted'];
693
- if (
694
- typeof halted === 'string' &&
695
- (HALT_ACTIONS as readonly string[]).includes(halted)
696
- ) {
697
- rec.halted = halted as LadderAction;
654
+ // Round-tripped for the same reason `history_source` is: dropped, the
655
+ // premise would be re-recorded every fire from the newest observation, so
656
+ // the two sides could never differ and the rung would be dead code.
657
+ if (typeof o['context_fingerprint'] === 'string' && o['context_fingerprint'] !== '') {
658
+ rec.context_fingerprint = o['context_fingerprint'];
698
659
  }
660
+ // Tolerant, not permissive — see `parseHaltStamp` for what it accepts
661
+ // beyond the halt set and why dropping a newer build's stamp was the
662
+ // wrong failure direction for a budget.
663
+ const halted = parseHaltStamp(o['halted']);
664
+ if (halted !== null) rec.halted = halted;
699
665
  return rec;
700
666
  } catch {
701
667
  return null;
@@ -1412,7 +1378,21 @@ export function main(): number {
1412
1378
  // differently. One producer, one definition.
1413
1379
  state.stall = stallSignal(state.history, STALL_WINDOW).level;
1414
1380
 
1415
- const action = ladder(state, scan.open, Date.now(), scan.blocked, undefined, unavailable);
1381
+ // 2.1 — repository drift, from the newest observation `roadmap:context`
1382
+ // recorded. No probe here: the fingerprint costs a `gh` call and this is the
1383
+ // Stop path. An unknown on either side is never a halt.
1384
+ const observed = readContextObservation(workspaceRoot)?.fingerprint ?? null;
1385
+ const premiseInvalidated = premiseMoved(state.context_fingerprint, observed);
1386
+
1387
+ const action = ladder(
1388
+ state,
1389
+ scan.open,
1390
+ Date.now(),
1391
+ scan.blocked,
1392
+ undefined,
1393
+ unavailable,
1394
+ premiseInvalidated,
1395
+ );
1416
1396
 
1417
1397
  if (action !== 'engage') {
1418
1398
  // A run that is ALREADY stamped emits nothing further — round 6 finding 6.
@@ -1436,6 +1416,12 @@ export function main(): number {
1436
1416
  iterations: state.iterations,
1437
1417
  open: scan.open,
1438
1418
  blocked: scan.blocked,
1419
+ // A ledger line is a persisted shape carrying a `RunTerminalState`,
1420
+ // so it names the vocabulary version it was written against: a
1421
+ // reader meeting an unknown value can then tell a newer writer
1422
+ // from a corrupt row.
1423
+ terminal_state: terminalStateFor(action),
1424
+ terminal_vocabulary_version: RUN_TERMINAL_VOCABULARY_VERSION,
1439
1425
  at: new Date().toISOString(),
1440
1426
  ...roots,
1441
1427
  });
@@ -1472,6 +1458,10 @@ export function main(): number {
1472
1458
 
1473
1459
  state.iterations += 1;
1474
1460
  state.last_turn = turnOrdinal;
1461
+ // Recorded ONCE — the premise this run engaged under. See the field's docblock.
1462
+ if (state.context_fingerprint === undefined && observed !== null) {
1463
+ state.context_fingerprint = observed;
1464
+ }
1475
1465
  state.history.push(scan.open);
1476
1466
  state.last_engaged_at = new Date().toISOString();
1477
1467
  if (!writeState(stateFile, state)) {
@@ -54,6 +54,7 @@ import {
54
54
  scanEolSlice,
55
55
  type EolCounters,
56
56
  } from '../_lib/session_eol.js';
57
+ import { readContextObservation } from '../_lib/context_observation.js';
57
58
  import { buildCheckpoint, writeCheckpoint } from '../_lib/run_checkpoint.js';
58
59
  import { read_claimed_slug } from '../session_register_hook.js';
59
60
  import { unwrap, type JsonObject, type JsonValue } from './envelope.js';
@@ -381,7 +382,17 @@ export function main(): number {
381
382
  try {
382
383
  const slug = read_claimed_slug(workspaceRoot, checkpointRunId);
383
384
  if (slug !== null) {
384
- const cp = buildCheckpoint(workspaceRoot, eolSessionKey(checkpointRunId), slug);
385
+ // `road-to-wired-instruments` 2.1. The fingerprint was never
386
+ // passed here, so `context_fingerprint` was null in every
387
+ // checkpoint this package has ever written and the resume-time
388
+ // drift check it exists for could not fire. The value comes from
389
+ // the newest recorded probe rather than from a fresh one: this is
390
+ // the Stop path, and the probe costs a `gh` call. Absent stays
391
+ // absent, which reads as "not known" and never as "unchanged".
392
+ const observed = readContextObservation(workspaceRoot)?.fingerprint ?? null;
393
+ const cp = buildCheckpoint(workspaceRoot, eolSessionKey(checkpointRunId), slug, {
394
+ ...(observed !== null ? { contextFingerprint: observed } : {}),
395
+ });
385
396
  if (cp !== null) writeCheckpoint(workspaceRoot, cp);
386
397
  }
387
398
  } catch {
@@ -579,12 +579,20 @@ export function readLanguagePin(workspaceRoot: string, session_id: string): Verd
579
579
  * WHAT IS ACTUALLY CLASSIFIED, stated accurately because R2 finding 9 caught
580
580
  * this file claiming otherwise in three places. `visibleProse` strips code,
581
581
  * quotes, tables, URLs and paths from the whole reply — but `classify` then
582
- * applies `instructionText` (which drops output-shaped lines and their
582
+ * applies `stripInjectedRegions` (which removes balanced host wrapper regions
583
+ * such as `<launch-selected-element>`, plus the host's advisory sentence beside
584
+ * them), then `instructionText` (which drops output-shaped lines and their
583
585
  * followers) and `humanAuthoredLead`, and RETURNS ON THE LEAD ALONE when the
584
586
  * lead is determined. So in the common case the verdict comes from the reply's
585
587
  * opening chunk, not from all of it, and indented prose is discarded before
586
588
  * scoring.
587
589
  *
590
+ * The region strip reaches this path too, and its effect here is small by
591
+ * construction: `visibleProse` has already removed fenced code, so a balanced
592
+ * bare-tag pair surviving into the scorer is prose the reply wrote around a tag
593
+ * rather than markup. Named rather than omitted, because enumerating the
594
+ * scoring surface accurately is this comment's entire job.
595
+ *
588
596
  * That is deliberately left as-is rather than swapped for a full-text scorer:
589
597
  * sharing one classifier with `language_mirror_hook` is what stops the pin and
590
598
  * the detector disagreeing about what language a turn is in, and changing the
@@ -26,6 +26,24 @@ cat > "$HOOKS_DIR/pre-push" << 'EOF'
26
26
  # Pre-push hook: mirror the CI "Sync + Generate Tools Consistency" gate LOCALLY
27
27
  # so derived-output drift is caught BEFORE push, not in remote CI.
28
28
  #
29
+ # THIS HOOK REFUSES. IT NEVER MERGES, AND NO ONE MAY "IMPROVE" IT INTO ONE.
30
+ # A merge inside pre-push rewrites the working tree at the exact moment the
31
+ # contributor believes their work is finished: one rejected push silently
32
+ # becomes an unreviewed commit, and a conflict lands them mid-merge with no
33
+ # warning and no way back to the state they typed `git push` from. Detection
34
+ # belongs here; resolution belongs to a step run with the result in front of
35
+ # you. When this hook refuses for staleness, the thing that fixes it is
36
+ #
37
+ # task push-ready # fetch -> integrate the base SET -> regenerate
38
+ # # -> verify -> re-check freshness -> push
39
+ # task push-ready DRY=1 # the same six steps, read-only
40
+ #
41
+ # The base is a SET: a branch targeting a release line or a stacked parent may
42
+ # also have to integrate the default branch, per the branch-convergence policy
43
+ # read at the TARGET commit (src/scripts/_lib/branch_convergence.ts). The
44
+ # default is merged FIRST, so a conflict surfaces against the broader base
45
+ # where it is cheapest to abandon.
46
+ #
29
47
  # History: the remote "Consistency" check failed in a large share of PRs because
30
48
  # the old hook only verified dist sync + the COMMAND count — it missed
31
49
  # guideline/skill/rule count drift and the generate-tools / router / corpus
@@ -83,7 +83,11 @@ import {
83
83
  session_state_file,
84
84
  } from "./hooks/state_io.js";
85
85
  import { readHookStdin } from "./hooks/hook_stdin.js";
86
- import { humanAuthoredLead, isSyntheticPrompt } from "./_lib/prompt_shape.js";
86
+ import {
87
+ humanAuthoredLead,
88
+ isSyntheticPrompt,
89
+ stripInjectedRegions,
90
+ } from "./_lib/prompt_shape.js";
87
91
 
88
92
  const EXIT_ALLOW = 0;
89
93
  // Severity is taken from the EXIT CODE, not from the `decision` field in the
@@ -296,9 +300,17 @@ export function instructionText(prompt: string): string {
296
300
  * an English instruction over a German paste resolves to English by the same
297
301
  * step. The fallback is what keeps it conservative: a prompt with no document
298
302
  * marker classifies exactly as it did before.
303
+ *
304
+ * `stripInjectedRegions` runs FIRST, and the order is load-bearing rather than
305
+ * tidy. A host wrapper the user never typed — `<launch-selected-element>` — is
306
+ * a bare tag line, which `humanAuthoredLead` read as the END of the human lead;
307
+ * prepended, as the host prepends it, that returned an empty lead and handed the
308
+ * verdict to ~4 KB of injected markup. Stripping before `instructionText` also
309
+ * keeps the block's indented lines from arming that filter's paste state, which
310
+ * was deleting the user's own sentence outright.
299
311
  */
300
312
  export function classify(prompt: string): Classification {
301
- const text = instructionText(prompt);
313
+ const text = instructionText(stripInjectedRegions(prompt));
302
314
  const lead = humanAuthoredLead(text);
303
315
  const leadVerdict = _score(lead);
304
316
  if (leadVerdict.language !== "und") {