@event4u/agent-config 1.18.0 → 1.19.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.
Files changed (126) hide show
  1. package/.agent-src/commands/council/default.md +74 -76
  2. package/.agent-src/commands/feature/roadmap.md +22 -0
  3. package/.agent-src/commands/roadmap/create.md +38 -6
  4. package/.agent-src/commands/roadmap/execute.md +36 -9
  5. package/.agent-src/rules/agent-authority.md +1 -0
  6. package/.agent-src/rules/agent-docs.md +1 -0
  7. package/.agent-src/rules/analysis-skill-routing.md +1 -0
  8. package/.agent-src/rules/architecture.md +1 -0
  9. package/.agent-src/rules/artifact-drafting-protocol.md +1 -0
  10. package/.agent-src/rules/artifact-engagement-recording.md +1 -0
  11. package/.agent-src/rules/ask-when-uncertain.md +1 -0
  12. package/.agent-src/rules/augment-portability.md +1 -0
  13. package/.agent-src/rules/augment-source-of-truth.md +1 -0
  14. package/.agent-src/rules/autonomous-execution.md +1 -0
  15. package/.agent-src/rules/capture-learnings.md +1 -0
  16. package/.agent-src/rules/chat-history-cadence.md +34 -0
  17. package/.agent-src/rules/chat-history-ownership.md +1 -0
  18. package/.agent-src/rules/chat-history-visibility.md +1 -0
  19. package/.agent-src/rules/cli-output-handling.md +2 -2
  20. package/.agent-src/rules/command-suggestion-policy.md +1 -0
  21. package/.agent-src/rules/commit-conventions.md +1 -0
  22. package/.agent-src/rules/commit-policy.md +1 -0
  23. package/.agent-src/rules/context-hygiene.md +22 -0
  24. package/.agent-src/rules/direct-answers.md +1 -0
  25. package/.agent-src/rules/docker-commands.md +1 -0
  26. package/.agent-src/rules/docs-sync.md +1 -0
  27. package/.agent-src/rules/downstream-changes.md +1 -0
  28. package/.agent-src/rules/e2e-testing.md +1 -0
  29. package/.agent-src/rules/guidelines.md +1 -0
  30. package/.agent-src/rules/improve-before-implement.md +1 -0
  31. package/.agent-src/rules/language-and-tone.md +1 -0
  32. package/.agent-src/rules/laravel-translations.md +1 -0
  33. package/.agent-src/rules/markdown-safe-codeblocks.md +1 -0
  34. package/.agent-src/rules/minimal-safe-diff.md +1 -0
  35. package/.agent-src/rules/missing-tool-handling.md +1 -0
  36. package/.agent-src/rules/model-recommendation.md +1 -0
  37. package/.agent-src/rules/no-cheap-questions.md +1 -0
  38. package/.agent-src/rules/no-roadmap-references.md +1 -0
  39. package/.agent-src/rules/non-destructive-by-default.md +1 -0
  40. package/.agent-src/rules/onboarding-gate.md +26 -0
  41. package/.agent-src/rules/package-ci-checks.md +1 -0
  42. package/.agent-src/rules/php-coding.md +1 -0
  43. package/.agent-src/rules/preservation-guard.md +1 -0
  44. package/.agent-src/rules/review-routing-awareness.md +1 -0
  45. package/.agent-src/rules/reviewer-awareness.md +1 -0
  46. package/.agent-src/rules/roadmap-progress-sync.md +22 -0
  47. package/.agent-src/rules/role-mode-adherence.md +2 -2
  48. package/.agent-src/rules/rule-type-governance.md +1 -0
  49. package/.agent-src/rules/runtime-safety.md +1 -0
  50. package/.agent-src/rules/scope-control.md +1 -0
  51. package/.agent-src/rules/security-sensitive-stop.md +1 -0
  52. package/.agent-src/rules/size-enforcement.md +1 -0
  53. package/.agent-src/rules/skill-improvement-trigger.md +1 -0
  54. package/.agent-src/rules/skill-quality.md +1 -0
  55. package/.agent-src/rules/slash-command-routing-policy.md +39 -0
  56. package/.agent-src/rules/think-before-action.md +1 -0
  57. package/.agent-src/rules/token-efficiency.md +1 -0
  58. package/.agent-src/rules/tool-safety.md +1 -0
  59. package/.agent-src/rules/ui-audit-gate.md +1 -0
  60. package/.agent-src/rules/upstream-proposal.md +1 -0
  61. package/.agent-src/rules/user-interaction.md +1 -0
  62. package/.agent-src/rules/verify-before-complete.md +1 -0
  63. package/.agent-src/skills/roadmap-management/SKILL.md +29 -4
  64. package/.agent-src/skills/verify-completion-evidence/SKILL.md +8 -1
  65. package/.agent-src/templates/agent-settings.md +16 -0
  66. package/.agent-src/templates/roadmaps.md +8 -3
  67. package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +9 -0
  68. package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +4 -0
  69. package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +4 -0
  70. package/.agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +163 -0
  71. package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +111 -0
  72. package/.agent-src/templates/scripts/work_engine/hooks/settings.py +36 -0
  73. package/.agent-src/templates/scripts/work_engine/scoring/decision_trace.py +141 -0
  74. package/.agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +125 -0
  75. package/.claude-plugin/marketplace.json +1 -1
  76. package/CHANGELOG.md +62 -0
  77. package/README.md +19 -19
  78. package/config/agent-settings.template.yml +23 -0
  79. package/docs/catalog.md +5 -2
  80. package/docs/contracts/adr-settings-sync-engine.md +127 -0
  81. package/docs/contracts/decision-trace-v1.md +146 -0
  82. package/docs/contracts/file-ownership-matrix.json +7 -0
  83. package/docs/contracts/hook-architecture-v1.md +213 -0
  84. package/docs/contracts/memory-visibility-v1.md +138 -0
  85. package/docs/contracts/one-off-script-lifecycle.md +109 -0
  86. package/docs/contracts/rule-interactions.yml +22 -0
  87. package/docs/customization.md +1 -0
  88. package/docs/development.md +4 -1
  89. package/docs/guidelines/agent-infra/layered-settings.md +32 -13
  90. package/package.json +1 -1
  91. package/scripts/agent-config +44 -0
  92. package/scripts/ai_council/bundler.py +3 -3
  93. package/scripts/ai_council/clients.py +24 -8
  94. package/scripts/ai_council/one_off_archive/2026-05/README.md +22 -0
  95. package/scripts/ai_council/one_off_archive/2026-05/_one_off_roundtrip.py +13 -8
  96. package/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +180 -0
  97. package/scripts/ai_council/session.py +92 -0
  98. package/scripts/capture_showcase_session.py +361 -0
  99. package/scripts/chat_history.py +11 -1
  100. package/scripts/check_always_budget.py +7 -2
  101. package/scripts/context_hygiene_hook.py +14 -6
  102. package/scripts/council_cli.py +357 -0
  103. package/scripts/hook_manifest.yaml +184 -0
  104. package/scripts/hooks/__init__.py +1 -0
  105. package/scripts/hooks/augment-dispatcher.sh +72 -0
  106. package/scripts/hooks/cline-dispatcher.sh +86 -0
  107. package/scripts/hooks/cursor-dispatcher.sh +76 -0
  108. package/scripts/hooks/dispatch_hook.py +348 -0
  109. package/scripts/hooks/envelope.py +98 -0
  110. package/scripts/hooks/gemini-dispatcher.sh +117 -0
  111. package/scripts/hooks/state_io.py +122 -0
  112. package/scripts/hooks/windsurf-dispatcher.sh +123 -0
  113. package/scripts/hooks_status.py +146 -0
  114. package/scripts/install.py +725 -87
  115. package/scripts/install.sh +1 -1
  116. package/scripts/lint_hook_manifest.py +216 -0
  117. package/scripts/lint_one_off_age.py +184 -0
  118. package/scripts/lint_rule_tiers.py +78 -0
  119. package/scripts/lint_showcase_sessions.py +148 -0
  120. package/scripts/minimal_safe_diff_hook.py +245 -0
  121. package/scripts/onboarding_gate_hook.py +13 -8
  122. package/scripts/readme_linter.py +12 -3
  123. package/scripts/roadmap_progress_hook.py +5 -0
  124. package/scripts/sync_agent_settings.py +32 -129
  125. package/scripts/sync_yaml_rt.py +734 -0
  126. package/scripts/verify_before_complete_hook.py +216 -0
@@ -0,0 +1,163 @@
1
+ """``DecisionTraceHook`` — emit a decision-trace JSON per phase.
2
+
3
+ Implements the v1 envelope from ``docs/contracts/decision-trace-v1.md``.
4
+ Default-off; opt-in via ``.agent-settings.yml``
5
+ ``decision_engine.surface_traces: true`` (mirrored into
6
+ ``hooks.decision_trace.enabled`` by :mod:`work_engine.hooks.settings`).
7
+
8
+ The hook is purely observational — it never mutates ``DeliveryState``,
9
+ never raises terminal errors. Stream / disk failures surface as
10
+ :class:`HookError` (non-fatal per the three-tier contract).
11
+
12
+ Trace layout (matches the contract):
13
+
14
+ * ``schema_version: 1``
15
+ * ``work_id`` — derived from the state-file directory name when the
16
+ caller follows the ``agents/state/work/<id>/state.json`` convention,
17
+ else from the state-file stem.
18
+ * ``phase`` — engine ``step_name`` (refine/memory/.../report).
19
+ * ``started_at`` / ``ended_at`` — ISO-8601 UTC timestamps captured on
20
+ ``BEFORE_STEP`` and ``AFTER_STEP``.
21
+ * ``confidence_band`` / ``risk_class`` — heuristics defined in
22
+ :mod:`work_engine.scoring.decision_trace`.
23
+ * ``rules`` — empty by default; the engine layer populates rule
24
+ applications when concerns wire into the trace bus (later phase).
25
+ * ``memory`` — counts and ids snapshotted from ``state.memory``.
26
+ * ``verify`` — claims/first-try-passes derived from ``state.verify``.
27
+ """
28
+ from __future__ import annotations
29
+
30
+ import json
31
+ import time
32
+ from datetime import datetime, timezone
33
+ from pathlib import Path
34
+ from typing import Any
35
+
36
+ from ...scoring.decision_trace import (
37
+ derive_confidence_band,
38
+ derive_risk_class,
39
+ summarise_memory,
40
+ summarise_verify,
41
+ )
42
+ from ..context import HookContext
43
+ from ..events import HookEvent
44
+ from ..exceptions import HookError
45
+ from ..registry import HookRegistry
46
+
47
+ SCHEMA_VERSION = 1
48
+ _MAX_MEMORY_IDS = 32
49
+
50
+
51
+ class DecisionTraceHook:
52
+ """Emit one decision-trace JSON file per dispatcher step.
53
+
54
+ Parameters
55
+ ----------
56
+ output_dir:
57
+ Optional override for the trace destination. When ``None`` the
58
+ hook writes alongside the WorkState file: if the state file
59
+ sits under ``agents/state/work/<id>/state.json`` the trace
60
+ lands at ``agents/state/work/<id>/decision-trace-<phase>.json``;
61
+ otherwise the trace lands next to the state file as
62
+ ``<stem>.decision-trace-<phase>.json``.
63
+ """
64
+
65
+ def __init__(self, output_dir: Path | None = None) -> None:
66
+ self._output_dir = output_dir
67
+ self._state_file: Path | None = None
68
+ self._step_started: dict[str, float] = {}
69
+
70
+ def register(self, registry: HookRegistry) -> None:
71
+ """Register the trace callbacks on the lifecycle events used."""
72
+ registry.register(HookEvent.BEFORE_LOAD, self._capture_state_file)
73
+ registry.register(HookEvent.AFTER_LOAD, self._capture_state_file)
74
+ registry.register(HookEvent.BEFORE_STEP, self._mark_step_start)
75
+ registry.register(HookEvent.AFTER_STEP, self._emit_trace)
76
+
77
+ # -- lifecycle callbacks ------------------------------------------
78
+
79
+ def _capture_state_file(self, ctx: HookContext) -> None:
80
+ if ctx.state_file is not None:
81
+ self._state_file = Path(ctx.state_file)
82
+
83
+ def _mark_step_start(self, ctx: HookContext) -> None:
84
+ if ctx.step_name:
85
+ self._step_started[ctx.step_name] = time.time()
86
+
87
+ def _emit_trace(self, ctx: HookContext) -> None:
88
+ if not ctx.step_name:
89
+ return
90
+ started = self._step_started.pop(ctx.step_name, time.time())
91
+ envelope = self._build_envelope(ctx, started)
92
+ target = self._target_path(ctx.step_name)
93
+ try:
94
+ target.parent.mkdir(parents=True, exist_ok=True)
95
+ target.write_text(
96
+ json.dumps(envelope, indent=2, sort_keys=False) + "\n",
97
+ encoding="utf-8",
98
+ )
99
+ except OSError as exc:
100
+ raise HookError(f"decision-trace write failed: {exc}") from exc
101
+
102
+ # -- envelope construction ----------------------------------------
103
+
104
+ def _build_envelope(
105
+ self, ctx: HookContext, started: float,
106
+ ) -> dict[str, Any]:
107
+ delivery = ctx.delivery
108
+ memory = summarise_memory(
109
+ getattr(delivery, "memory", None),
110
+ limit=_MAX_MEMORY_IDS,
111
+ )
112
+ verify = summarise_verify(getattr(delivery, "verify", None))
113
+ ambiguity = bool(getattr(delivery, "questions", None))
114
+ return {
115
+ "schema_version": SCHEMA_VERSION,
116
+ "work_id": self._work_id(),
117
+ "phase": ctx.step_name,
118
+ "started_at": _iso_utc(started),
119
+ "ended_at": _iso_utc(time.time()),
120
+ "confidence_band": derive_confidence_band(
121
+ memory_hits=memory["hits"],
122
+ verify_claims=verify["claims"],
123
+ verify_first_try_passes=verify["first_try_passes"],
124
+ ambiguity_flag=ambiguity,
125
+ ),
126
+ "risk_class": derive_risk_class(
127
+ getattr(delivery, "changes", None),
128
+ ),
129
+ "rules": [],
130
+ "memory": memory,
131
+ "verify": verify,
132
+ }
133
+
134
+ # -- path helpers --------------------------------------------------
135
+
136
+ def _work_id(self) -> str:
137
+ if self._state_file is None:
138
+ return "unknown"
139
+ parent = self._state_file.parent
140
+ if parent.name and parent.parent.name == "work":
141
+ return parent.name
142
+ return self._state_file.stem
143
+
144
+ def _target_path(self, phase: str) -> Path:
145
+ filename = f"decision-trace-{phase}.json"
146
+ if self._output_dir is not None:
147
+ return self._output_dir / filename
148
+ if self._state_file is None:
149
+ return Path(filename)
150
+ parent = self._state_file.parent
151
+ if parent.name and parent.parent.name == "work":
152
+ return parent / filename
153
+ return parent / f"{self._state_file.stem}.{filename}"
154
+
155
+
156
+ def _iso_utc(epoch: float) -> str:
157
+ return (
158
+ datetime.fromtimestamp(epoch, tz=timezone.utc)
159
+ .strftime("%Y-%m-%dT%H:%M:%SZ")
160
+ )
161
+
162
+
163
+ __all__ = ["DecisionTraceHook", "SCHEMA_VERSION"]
@@ -0,0 +1,111 @@
1
+ """``MemoryVisibilityHook`` — emit the visibility line on save.
2
+
3
+ Implements the producer side of
4
+ ``docs/contracts/memory-visibility-v1.md``: derive ``asks/hits/ids``
5
+ from ``state.memory`` and thread the rendered line into
6
+ ``state.report`` so the agent's reply naturally carries the memory
7
+ heartbeat.
8
+
9
+ Fires on ``before_save`` for the same reason as
10
+ ``ChatHistoryHeartbeatHook``: ``cli._sync_back`` runs between
11
+ ``after_dispatch`` and ``before_save`` and reassigns
12
+ ``work.report = delivery.report``. A line written on
13
+ ``after_dispatch`` would be overwritten before ``_save``; firing on
14
+ ``before_save`` lands after the sync.
15
+
16
+ Default-off; opt-in via ``.agent-settings.yml``
17
+ ``hooks.memory_visibility.enabled: true`` (or implicitly when
18
+ ``memory.visibility`` is not ``off`` and the master switch is on).
19
+ The hook is purely observational: failures surface as
20
+ :class:`HookError` (non-fatal per the three-tier contract); the
21
+ engine never crashes on a visibility-line write.
22
+ """
23
+ from __future__ import annotations
24
+
25
+ from typing import Any, Iterable
26
+
27
+ from ...scoring.memory_visibility import (
28
+ DEFAULT_ASKED_TYPES,
29
+ format_line,
30
+ should_emit,
31
+ summarise_visibility,
32
+ )
33
+ from ..context import HookContext
34
+ from ..events import HookEvent
35
+ from ..exceptions import HookError
36
+ from ..registry import HookRegistry
37
+
38
+
39
+ class MemoryVisibilityHook:
40
+ """Thread the ``🧠 Memory: <hits>/<asks> · ids=[…]`` line into the report.
41
+
42
+ Parameters
43
+ ----------
44
+ cost_profile:
45
+ Cadence profile from ``.agent-settings.yml`` (``lean`` /
46
+ ``standard`` / ``verbose``). ``lean`` suppresses the line
47
+ unless ``asks ≥ 3`` per the contract's cadence table.
48
+ visibility_off:
49
+ When ``True``, the hook stays silent — used to mirror
50
+ ``memory.visibility: off`` in the consumer settings.
51
+ asked_types:
52
+ Optional override for the list of memory types treated as
53
+ ``asks`` in the visibility line. Defaults to the four types
54
+ the engine's memory step retrieves over.
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ *,
60
+ cost_profile: str = "standard",
61
+ visibility_off: bool = False,
62
+ asked_types: Iterable[str] | None = None,
63
+ ) -> None:
64
+ self._cost_profile = cost_profile
65
+ self._visibility_off = visibility_off
66
+ self._asked_types = (
67
+ tuple(asked_types) if asked_types is not None else DEFAULT_ASKED_TYPES
68
+ )
69
+
70
+ def register(self, registry: HookRegistry) -> None:
71
+ """Register the visibility-line emitter on ``before_save``."""
72
+ registry.register(HookEvent.BEFORE_SAVE, self._on_before_save)
73
+
74
+ def _on_before_save(self, ctx: HookContext) -> None:
75
+ work = ctx.work
76
+ if work is None:
77
+ return
78
+ memory = getattr(work, "memory", None)
79
+ summary = summarise_visibility(memory, asked_types=self._asked_types)
80
+ if not should_emit(
81
+ summary,
82
+ cost_profile=self._cost_profile,
83
+ visibility_off=self._visibility_off,
84
+ ):
85
+ return
86
+ line = format_line(summary)
87
+ if not line:
88
+ return
89
+ existing = getattr(work, "report", "") or ""
90
+ if line in existing:
91
+ return
92
+ sep = "\n\n" if existing else ""
93
+ try:
94
+ work.report = f"{existing}{sep}{line}"
95
+ except AttributeError as exc:
96
+ raise HookError(
97
+ "memory-visibility: state.report not writable",
98
+ ) from exc
99
+
100
+
101
+ def derive_visibility(memory: Any) -> str | None:
102
+ """Convenience helper: render the line directly from a memory list.
103
+
104
+ Used by external callers (CLI ad-hoc smoke tests, the audit-as-
105
+ memory consumer) that have a ``memory`` list but no ``HookContext``.
106
+ Returns ``None`` when ``asks == 0``.
107
+ """
108
+ return format_line(summarise_visibility(memory))
109
+
110
+
111
+ __all__ = ["MemoryVisibilityHook", "derive_visibility"]
@@ -39,6 +39,10 @@ class HookSettings:
39
39
  halt_surface_audit: bool = False
40
40
  state_shape_validation: bool = False
41
41
  directive_set_guard: bool = False
42
+ decision_trace: bool = False
43
+ memory_visibility: bool = False
44
+ memory_visibility_off: bool = False
45
+ cost_profile: str = "standard"
42
46
  chat_history_enabled: bool = False
43
47
  chat_history_script: str = DEFAULT_CHAT_HISTORY_SCRIPT
44
48
 
@@ -102,6 +106,34 @@ def _settings_from_raw(data: dict[str, Any]) -> HookSettings:
102
106
  and _coerce_bool(global_chat.get("enabled"), False)
103
107
  )
104
108
 
109
+ decision_engine = data.get("decision_engine")
110
+ decision_trace_on = (
111
+ isinstance(decision_engine, dict)
112
+ and _coerce_bool(decision_engine.get("surface_traces"), False)
113
+ )
114
+
115
+ memory_section = data.get("memory")
116
+ visibility_off = False
117
+ if isinstance(memory_section, dict):
118
+ raw = memory_section.get("visibility")
119
+ if isinstance(raw, str) and raw.strip().lower() == "off":
120
+ visibility_off = True
121
+ elif isinstance(raw, bool) and raw is False:
122
+ visibility_off = True
123
+
124
+ memory_hooks = hooks.get("memory_visibility")
125
+ if isinstance(memory_hooks, dict):
126
+ memory_visibility_on = _coerce_bool(
127
+ memory_hooks.get("enabled"), True,
128
+ )
129
+ else:
130
+ memory_visibility_on = True
131
+
132
+ cost_profile_raw = data.get("cost_profile") or "standard"
133
+ cost_profile = (
134
+ str(cost_profile_raw).strip().lower() or "standard"
135
+ )
136
+
105
137
  return HookSettings(
106
138
  enabled=True,
107
139
  trace=_coerce_bool(hooks.get("trace"), False),
@@ -114,6 +146,10 @@ def _settings_from_raw(data: dict[str, Any]) -> HookSettings:
114
146
  directive_set_guard=_coerce_bool(
115
147
  hooks.get("directive_set_guard"), True
116
148
  ),
149
+ decision_trace=decision_trace_on,
150
+ memory_visibility=memory_visibility_on,
151
+ memory_visibility_off=visibility_off,
152
+ cost_profile=cost_profile,
117
153
  chat_history_enabled=chat_block_enabled and global_chat_on,
118
154
  chat_history_script=chat_script,
119
155
  )
@@ -0,0 +1,141 @@
1
+ """Confidence-band + risk-class heuristics for decision-trace v1.
2
+
3
+ These heuristics back the JSON envelope emitted by
4
+ :class:`work_engine.hooks.builtin.DecisionTraceHook`. They live here
5
+ (under ``scoring/``) so the rules and the hook share a single source
6
+ of truth, and so unit tests can exercise the heuristics without
7
+ spinning up a dispatcher.
8
+
9
+ Confidence-band heuristic (per
10
+ ``docs/contracts/decision-trace-v1.md``):
11
+
12
+ * ``high`` — ``memory.hits ≥ 2`` AND
13
+ ``verify.first_try_passes == verify.claims`` AND no ambiguity flag.
14
+ * ``medium`` — ``memory.hits ≥ 1`` OR ``verify.first_try_passes ≥ 1``.
15
+ * ``low`` — otherwise.
16
+
17
+ Edge case: ``verify.claims == 0`` is **not** ``high`` by default; it
18
+ folds into ``medium`` if at least one memory hit landed, ``low``
19
+ otherwise.
20
+
21
+ Risk-class heuristic: maximum risk across the files the phase
22
+ touched. With no file-ownership matrix wired in yet, the
23
+ implementation defaults to ``low`` and exposes a ``files`` argument
24
+ so a future hook can pass concrete paths. If the phase touched any
25
+ files at all the heuristic returns ``medium`` so reviewers stay
26
+ nudged toward a closer look until the matrix lands.
27
+ """
28
+ from __future__ import annotations
29
+
30
+ from typing import Any, Iterable
31
+
32
+ BAND_HIGH = "high"
33
+ BAND_MEDIUM = "medium"
34
+ BAND_LOW = "low"
35
+
36
+ RISK_HIGH = "high"
37
+ RISK_MEDIUM = "medium"
38
+ RISK_LOW = "low"
39
+
40
+
41
+ def derive_confidence_band(
42
+ *,
43
+ memory_hits: int,
44
+ verify_claims: int,
45
+ verify_first_try_passes: int,
46
+ ambiguity_flag: bool,
47
+ ) -> str:
48
+ """Return ``high`` / ``medium`` / ``low`` per the v1 heuristic."""
49
+ if (
50
+ memory_hits >= 2
51
+ and verify_claims > 0
52
+ and verify_first_try_passes == verify_claims
53
+ and not ambiguity_flag
54
+ ):
55
+ return BAND_HIGH
56
+ if memory_hits >= 1 or verify_first_try_passes >= 1:
57
+ return BAND_MEDIUM
58
+ return BAND_LOW
59
+
60
+
61
+ def derive_risk_class(changes: Any) -> str:
62
+ """Return the trace-level risk class.
63
+
64
+ ``changes`` is the ``delivery.changes`` slice — a list of dicts in
65
+ the canonical engine shape, or ``None`` for pure planning phases.
66
+ Until the file-ownership matrix is wired in, "any change touched"
67
+ maps to ``medium``; "no change" maps to ``low``. ``high`` is
68
+ reserved for the future ownership-matrix lookup.
69
+ """
70
+ if not changes:
71
+ return RISK_LOW
72
+ if isinstance(changes, Iterable):
73
+ try:
74
+ count = sum(1 for _ in changes)
75
+ except TypeError:
76
+ return RISK_LOW
77
+ return RISK_MEDIUM if count > 0 else RISK_LOW
78
+ return RISK_LOW
79
+
80
+
81
+ def summarise_memory(
82
+ memory: Any, *, limit: int = 32,
83
+ ) -> dict[str, Any]:
84
+ """Reduce ``state.memory`` into the trace-envelope ``memory`` slice.
85
+
86
+ The engine stores memory entries as dicts with at least an ``id``
87
+ or ``rule_id`` key plus arbitrary per-entry payload. The trace
88
+ only carries ids — bodies stay behind the privacy floor.
89
+ """
90
+ if not memory:
91
+ return {"asks": 0, "hits": 0, "ids": []}
92
+ ids: list[str] = []
93
+ asks = 0
94
+ hits = 0
95
+ for entry in memory:
96
+ if not isinstance(entry, dict):
97
+ continue
98
+ asks += int(entry.get("asks", 1) or 0) or 1
99
+ if entry.get("hit", True):
100
+ hits += 1
101
+ entry_id = entry.get("id") or entry.get("rule_id")
102
+ if entry_id and len(ids) < limit:
103
+ ids.append(str(entry_id))
104
+ return {"asks": asks, "hits": hits, "ids": ids}
105
+
106
+
107
+ def summarise_verify(verify: Any) -> dict[str, int]:
108
+ """Reduce ``state.verify`` into the trace-envelope ``verify`` slice.
109
+
110
+ ``verify`` may be ``None`` (no verify run yet), a dict carrying
111
+ ``claims`` / ``first_try_passes``, or a list of attempt records.
112
+ Anything else collapses to zeros.
113
+ """
114
+ if verify is None:
115
+ return {"claims": 0, "first_try_passes": 0}
116
+ if isinstance(verify, dict):
117
+ claims = int(verify.get("claims", 0) or 0)
118
+ passes = int(verify.get("first_try_passes", 0) or 0)
119
+ return {"claims": claims, "first_try_passes": passes}
120
+ if isinstance(verify, list):
121
+ claims = len(verify)
122
+ passes = sum(
123
+ 1 for entry in verify
124
+ if isinstance(entry, dict) and entry.get("first_try_pass")
125
+ )
126
+ return {"claims": claims, "first_try_passes": passes}
127
+ return {"claims": 0, "first_try_passes": 0}
128
+
129
+
130
+ __all__ = [
131
+ "BAND_HIGH",
132
+ "BAND_MEDIUM",
133
+ "BAND_LOW",
134
+ "RISK_HIGH",
135
+ "RISK_MEDIUM",
136
+ "RISK_LOW",
137
+ "derive_confidence_band",
138
+ "derive_risk_class",
139
+ "summarise_memory",
140
+ "summarise_verify",
141
+ ]
@@ -0,0 +1,125 @@
1
+ """Producer-side helpers for the memory-visibility line.
2
+
3
+ Implements the v1 line shape from
4
+ ``docs/contracts/memory-visibility-v1.md``:
5
+
6
+ 🧠 Memory: <hits>/<asks> · ids=[<comma-separated-ids>]
7
+
8
+ The semantics matched to the work-engine model:
9
+
10
+ * The ``memory`` step retrieves across the four allowed memory types
11
+ (``MEMORY_TYPES`` in ``directives.backend.memory``). Each type is
12
+ one ``ask`` from the visibility-line perspective.
13
+ * ``hits`` counts distinct types that returned at least one entry.
14
+ * ``ids`` is the deduped list of returned entry ids preserving the
15
+ retrieval order encoded in ``state.memory``.
16
+
17
+ Privacy floor: this module never emits entry bodies, summaries,
18
+ ``path``/``source`` fields, or anything beyond ``id`` and ``type``.
19
+ The privacy regression test (``tests/contracts/test_memory_
20
+ visibility_redaction.py``) keeps this guarantee enforced.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ from typing import Any, Iterable
25
+
26
+ ICON = "\U0001F9E0" # 🧠
27
+ DEFAULT_MAX_INLINE_IDS = 5
28
+ DEFAULT_ASKED_TYPES = (
29
+ "domain-invariants",
30
+ "architecture-decisions",
31
+ "incident-learnings",
32
+ "historical-patterns",
33
+ )
34
+
35
+
36
+ def summarise_visibility(
37
+ memory: Any,
38
+ *,
39
+ asked_types: Iterable[str] = DEFAULT_ASKED_TYPES,
40
+ ) -> dict[str, Any]:
41
+ """Reduce ``state.memory`` into the visibility-line slice.
42
+
43
+ ``memory`` is the list of hit dicts produced by
44
+ ``directives.backend.memory``. Returns ``{"asks", "hits", "ids"}``
45
+ with privacy-safe values only.
46
+ """
47
+ asked = tuple(asked_types)
48
+ if not memory or not isinstance(memory, list):
49
+ return {"asks": 0, "hits": 0, "ids": []}
50
+ asks = len(asked)
51
+ seen_types: set[str] = set()
52
+ ids: list[str] = []
53
+ seen_ids: set[str] = set()
54
+ for entry in memory:
55
+ if not isinstance(entry, dict):
56
+ continue
57
+ type_value = entry.get("type")
58
+ if isinstance(type_value, str):
59
+ seen_types.add(type_value)
60
+ entry_id = entry.get("id") or entry.get("rule_id")
61
+ if not isinstance(entry_id, (str, int)):
62
+ continue
63
+ sid = str(entry_id)
64
+ if sid in seen_ids:
65
+ continue
66
+ seen_ids.add(sid)
67
+ ids.append(sid)
68
+ hits = len(seen_types) if seen_types else (1 if ids else 0)
69
+ return {"asks": asks, "hits": hits, "ids": ids}
70
+
71
+
72
+ def format_line(
73
+ summary: dict[str, Any],
74
+ *,
75
+ max_inline_ids: int = DEFAULT_MAX_INLINE_IDS,
76
+ ) -> str | None:
77
+ """Render the visibility line; return ``None`` when ``asks == 0``.
78
+
79
+ Cap inline ids at ``max_inline_ids`` and append ``…+N`` when the
80
+ list is longer. Returning ``None`` enforces the contract clause
81
+ "If ``asks == 0``, the engine MUST suppress the line entirely".
82
+ """
83
+ asks = int(summary.get("asks", 0) or 0)
84
+ if asks <= 0:
85
+ return None
86
+ hits = int(summary.get("hits", 0) or 0)
87
+ raw_ids = summary.get("ids") or []
88
+ ids = [str(i) for i in raw_ids if isinstance(i, (str, int))]
89
+ if max_inline_ids < 0:
90
+ max_inline_ids = 0
91
+ inline = ids[:max_inline_ids]
92
+ overflow = len(ids) - len(inline)
93
+ rendered_ids = ", ".join(inline)
94
+ if overflow > 0:
95
+ suffix = ", " if rendered_ids else ""
96
+ rendered_ids = f"{rendered_ids}{suffix}\u2026+{overflow}"
97
+ return f"{ICON} Memory: {hits}/{asks} \u00b7 ids=[{rendered_ids}]"
98
+
99
+
100
+ def should_emit(
101
+ summary: dict[str, Any],
102
+ *,
103
+ cost_profile: str = "standard",
104
+ visibility_off: bool = False,
105
+ ) -> bool:
106
+ """Apply the cadence + opt-out gates from the contract."""
107
+ if visibility_off:
108
+ return False
109
+ asks = int(summary.get("asks", 0) or 0)
110
+ if asks <= 0:
111
+ return False
112
+ profile = (cost_profile or "standard").strip().lower()
113
+ if profile == "lean":
114
+ return asks >= 3
115
+ return True
116
+
117
+
118
+ __all__ = [
119
+ "DEFAULT_ASKED_TYPES",
120
+ "DEFAULT_MAX_INLINE_IDS",
121
+ "ICON",
122
+ "format_line",
123
+ "should_emit",
124
+ "summarise_visibility",
125
+ ]
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Shared agent configuration \u2014 skills for AI coding tools (Claude Code, Augment, Cursor, Cline, Windsurf, Gemini CLI).",
9
- "version": "1.18.0"
9
+ "version": "1.19.0"
10
10
  },
11
11
  "plugins": [
12
12
  {
package/CHANGELOG.md CHANGED
@@ -318,6 +318,68 @@ our recommendation order, not its support status.
318
318
  users" tension without removing any path that an existing user
319
319
  might rely on.
320
320
 
321
+ ## [1.19.0](https://github.com/event4u-app/agent-config/compare/1.18.0...1.19.0) (2026-05-05)
322
+
323
+ ### Features
324
+
325
+ * **rules:** treat slash command as operator and prose as target ([f73947e](https://github.com/event4u-app/agent-config/commit/f73947e93bc8121524d811a5f17d548499023751))
326
+ * **ai-council:** auto-prune session folders past retention window ([bbfaa93](https://github.com/event4u-app/agent-config/commit/bbfaa93fd28bc3a84030c30ab9cea429ac68bea5))
327
+ * **settings-sync:** additive sync with verbatim user-line preservation ([f996cf3](https://github.com/event4u-app/agent-config/commit/f996cf3811e924d2ffecbc3d11bac447308efc5a))
328
+ * **roadmap:** add quality_cadence setting to throttle /roadmap execute pipeline runs ([d5ef7bf](https://github.com/event4u-app/agent-config/commit/d5ef7bf85e70c95103c6fc65507f99fdd35050dd))
329
+ * **ci:** add lint_showcase_sessions gate ([c4f48bb](https://github.com/event4u-app/agent-config/commit/c4f48bba05d266899282742320b7f83bc636cc7c))
330
+ * **council:** add CLI entry-point with estimate/run/render subcommands (Phase 6.7) ([f20f599](https://github.com/event4u-app/agent-config/commit/f20f5993dda1325f19f8e26a130ef37f995b5a9d))
331
+ * **taskfile:** modularize Taskfile.yml into taskfiles/ groups (Phase 6.1) ([41cbe46](https://github.com/event4u-app/agent-config/commit/41cbe46d6f9289c98bb3f717dcac74c44154fc21))
332
+ * **hooks:** add verify-before-complete and minimal-safe-diff concerns ([2507aef](https://github.com/event4u-app/agent-config/commit/2507aefcc88857fd120cfd09d5c93555cb89b868))
333
+ * **governance:** one-off-script lifecycle + showcase capture + tier-retrofit archive ([0940f91](https://github.com/event4u-app/agent-config/commit/0940f91d74a759de6601f5367e8fd3e2925f4c51))
334
+ * **work_engine:** decision-trace + memory-visibility hooks, scoring, contracts ([91cb0a5](https://github.com/event4u-app/agent-config/commit/91cb0a547971ff5f9597997bc36607ca6049be89))
335
+ * **rules:** tier frontmatter sweep across all 58 rules + lint + budget rebaseline ([a9c3694](https://github.com/event4u-app/agent-config/commit/a9c3694bb22cbd87574c1ffd53d0cb9b76a60479))
336
+ * **hooks:** per-platform bridges, install integration, hooks:status, smoke ([c33058a](https://github.com/event4u-app/agent-config/commit/c33058a49a20c6d4d21a7933b85ea3b618a6d433))
337
+ * **hooks:** universal dispatcher, manifest, envelope, state I/O ([28c4f8c](https://github.com/event4u-app/agent-config/commit/28c4f8c7f0e7bd50b6403dfeab0dcaa623dd5ca3))
338
+ * add collision check to roadmap creation flow ([19b65d5](https://github.com/event4u-app/agent-config/commit/19b65d5f2500ddb7c8bfcbb7466c33de0d64043c))
339
+
340
+ ### Bug Fixes
341
+
342
+ * **ai-council:** use max_completion_tokens for OpenAI o1 models ([8d9e179](https://github.com/event4u-app/agent-config/commit/8d9e179a36d3ba2c68de09ba426782fd43792b85))
343
+ * **skills/roadmap-management:** drop task-ci literal to satisfy portability lint ([790af5e](https://github.com/event4u-app/agent-config/commit/790af5e61a3af3472027d2eef1d939106c95d6ea))
344
+ * **install.sh:** avoid SIGPIPE race in clean_stale that deleted live files ([08670d6](https://github.com/event4u-app/agent-config/commit/08670d66c6285a84d15e44d00e5cfecc6870a4e7))
345
+ * **roadmaps:** re-anchor relative paths in archived 3a protocol ([e0017de](https://github.com/event4u-app/agent-config/commit/e0017de1b55b43e2c8ea5b3f9b51f077cb68e0e3))
346
+
347
+ ### Performance
348
+
349
+ * **ci:** parallelise test suite — bash xargs -P + pytest-xdist ([2e74b31](https://github.com/event4u-app/agent-config/commit/2e74b3104d0a881832cbebb4dcaddecc865b65c3))
350
+ * **ai-council/bundler:** fix catastrophic regex backtracking in redact() ([0e277e7](https://github.com/event4u-app/agent-config/commit/0e277e7ca36b452cc1cc4d8abdeaaa14e9aebbaf))
351
+
352
+ ### Documentation
353
+
354
+ * **roadmaps:** add chat-history hook-only reduction roadmap ([529f3b7](https://github.com/event4u-app/agent-config/commit/529f3b7c06fae8b560ce0bb436fc701b1418a024))
355
+ * **roadmaps:** add road-to-proof-not-features draft ([4664bd4](https://github.com/event4u-app/agent-config/commit/4664bd42a0e0bd8ff9eddc6d8907ff9a217a8f11))
356
+ * tighten wording in three rules and one command ([14ed864](https://github.com/event4u-app/agent-config/commit/14ed8640f18e3bfc52819bb150066ecc453fb17a))
357
+ * **roadmap:** archive road-to-feedback-consolidation (43/43 done) ([f053c6d](https://github.com/event4u-app/agent-config/commit/f053c6df94620a4cbf89e75ba73a7bb369c9395b))
358
+ * **roadmap:** close Phase 1 — showcase infra shipped, sessions deferred ([49a156b](https://github.com/event4u-app/agent-config/commit/49a156b40eecd728841b2eaef3565850f1020e0a))
359
+ * **roadmap:** close Phase 6 — mark 6.1/6.4/6.5/6.7 complete (89%) ([4b645cf](https://github.com/event4u-app/agent-config/commit/4b645cf9fc9acb7b7bc227f790e00c3efae66790))
360
+ * **readme:** 3-path entry table + tier-coverage drift sentinel (Phase 6.4 + 6.5) ([ae712f0](https://github.com/event4u-app/agent-config/commit/ae712f034e5eb67481175835d7a9eded921ee222))
361
+ * **roadmap:** mark Phase 5 complete (Tier-1 hook concerns shipped) ([433c5d1](https://github.com/event4u-app/agent-config/commit/433c5d1efb352de26671caed0e24a6b1e673eab6))
362
+ * **readme:** trim under 500-line linter ceiling ([2c3b496](https://github.com/event4u-app/agent-config/commit/2c3b496552cdd73b02e7b08d5c231a9f86e326c3))
363
+ * **agents:** roadmap progress, ownership matrix, agents index sync ([2368e37](https://github.com/event4u-app/agent-config/commit/2368e37aee886125997bfda4299599ebea9a14ec))
364
+ * add feedback consolidation roadmap and regenerate dashboard ([96fce45](https://github.com/event4u-app/agent-config/commit/96fce45055b2da367b8a45536c7a5bdcd1e03d84))
365
+
366
+ ### Refactoring
367
+
368
+ * **hooks:** concerns share atomic-write state I/O + dispatcher-friendly stdin ([5cac705](https://github.com/event4u-app/agent-config/commit/5cac705edfd46ad8d27ffd0008bdf4723317a4eb))
369
+
370
+ ### Tests
371
+
372
+ * **hooks:** parity tests for tier-1 hook concerns ([540468c](https://github.com/event4u-app/agent-config/commit/540468cd150447d6b588d3015865660a555e4f8d))
373
+
374
+ ### Chores
375
+
376
+ * **tools:** regenerate .windsurfrules with slash-command-routing-policy operator/target sections ([7825600](https://github.com/event4u-app/agent-config/commit/78256000a2da9cc313eb272059bb363acbc5290e))
377
+ * **rules:** sync compressed slash-command-routing-policy with operator/target sections ([916357c](https://github.com/event4u-app/agent-config/commit/916357c7d185d717187845478e8318025330d3a9))
378
+ * **rules:** promote verify-before-complete and minimal-safe-diff to tier 2a ([d62fb84](https://github.com/event4u-app/agent-config/commit/d62fb845a14065706c46c4425b261b57ac155c27))
379
+ * ignore .agent-chat-history.session sidecar ([42741c8](https://github.com/event4u-app/agent-config/commit/42741c87f83bf24416eb3916cbced865ae642688))
380
+ * **infra:** wire lint-rule-tiers + lint-hook-manifest + lint-one-off-age tasks; pyproject pytest importlib; .windsurfrules Copilot fallback ([5b88093](https://github.com/event4u-app/agent-config/commit/5b8809398dd332a3ebad3478f7a38cf48ef119e1))
381
+ * **roadmaps:** archive structural-optimization companion artefacts ([928c884](https://github.com/event4u-app/agent-config/commit/928c88469787cd037c54acd0006039ee9b86f9bb))
382
+
321
383
  ## [1.18.0](https://github.com/event4u-app/agent-config/compare/1.17.0...1.18.0) (2026-05-04)
322
384
 
323
385
  ### Features