@windyroad/architect 0.21.1 → 0.21.2-preview.1079
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/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/agents/agent.md +23 -24
- package/hooks/architect-compendium-update-entry.sh +11 -1
- package/hooks/architect-detect.sh +1 -1
- package/hooks/architect-dispatch.sh +93 -0
- package/hooks/architect-oversight-marker-discipline.sh +1 -1
- package/hooks/architect-readme-pairing-check.sh +1 -1
- package/hooks/hooks.json +4 -13
- package/hooks/lib/architect-gate.sh +1 -1
- package/package.json +3 -1
- package/scripts/generate-decisions-compendium.sh +9 -3
- package/scripts/mark-oversight-confirmed.sh +1 -1
- package/skills/capture-adr/REFERENCE.md +39 -39
- package/skills/capture-adr/SKILL.md +34 -34
- package/skills/create-adr/SKILL.md +57 -56
- package/skills/review-decisions/SKILL.md +28 -28
- package/skills/review-design/SKILL.md +4 -4
- package/scripts/sync-codex-skills.mjs +0 -85
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ This walks you through creating an ADR in [MADR 4.0](https://adr.github.io/madr/
|
|
|
41
41
|
/wr-architect:capture-adr
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
The `capture-adr` skill is the foreground-lightweight aside-invocation variant of `create-adr` (per
|
|
44
|
+
The `capture-adr` skill is the foreground-lightweight aside-invocation variant of `create-adr` (per the governance-skill invocation rule background-capture pattern). Use it when an architecture decision surfaces mid-conversation and you want the ADR scaffold drafted without losing the operational thread.
|
|
45
45
|
|
|
46
46
|
**Review recorded decisions that lack human oversight:**
|
|
47
47
|
|
|
@@ -49,7 +49,7 @@ The `capture-adr` skill is the foreground-lightweight aside-invocation variant o
|
|
|
49
49
|
/wr-architect:review-decisions
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
The `review-decisions` skill drains the set of ADRs that were recorded without a human confirming the chosen option (per
|
|
52
|
+
The `review-decisions` skill drains the set of ADRs that were recorded without a human confirming the chosen option (per the architecture human-oversight rule). It surfaces each decision's chosen option and alternatives via AskUserQuestion so you confirm, amend, or reject the auto-made call, then writes a `human-oversight: confirmed` marker. Detection is a token-cheap grep over ADR frontmatter; a session-start nudge reports the unoversighted count. New ADRs created through `create-adr` are born oversighted, so the unconfirmed set only shrinks.
|
|
53
53
|
|
|
54
54
|
**Run an on-demand architecture compliance review:**
|
|
55
55
|
|
package/agents/agent.md
CHANGED
|
@@ -17,7 +17,7 @@ You are the Architect. You review proposed changes against the project's archite
|
|
|
17
17
|
|
|
18
18
|
## Your Role
|
|
19
19
|
|
|
20
|
-
1. Read `docs/decisions/README.md` — the generated **Decisions Compendium**. It carries every ADR's chosen option, confirmation criteria, and relationship graph in a compact form (~40 KB vs ~1.6 MB for the full body set; ~40× reduction). **This is the routine load surface for compliance review** (per
|
|
20
|
+
1. Read `docs/decisions/README.md` — the generated **Decisions Compendium**. It carries every ADR's chosen option, confirmation criteria, and relationship graph in a compact form (~40 KB vs ~1.6 MB for the full body set; ~40× reduction). **This is the routine load surface for compliance review** (per the decisions-compendium load rule). If `docs/decisions/README.md` does not exist, fall back to globbing `docs/decisions/*.md` (skip the absent README) — the project may predate the decisions-compendium load rule or be a fresh install. If `docs/decisions/` itself does not exist, that is fine; proceed noting that no prior decisions are recorded. **Load a specific ADR's full body** (`docs/decisions/<NNN>-*.md`) **only when the compendium entry is insufficient for the current review** — deep-dive on a contested change, evolving a decision, ratifying a new ADR via `/wr-architect:create-adr`, or confirming a substance question the compendium summary does not resolve. The per-ADR body remains the authoritative substance (the state-based problem-directory layout rule); the compendium is a derived view.
|
|
21
21
|
2. Read the file(s) being edited to understand the current state and proposed change
|
|
22
22
|
3. Review the proposed change against existing decisions (if any)
|
|
23
23
|
4. Determine if the change requires a new decision to be documented
|
|
@@ -25,20 +25,20 @@ You are the Architect. You review proposed changes against the project's archite
|
|
|
25
25
|
|
|
26
26
|
## Review Mode: Pre-edit / proposed-change vs. Post-edit / applied
|
|
27
27
|
|
|
28
|
-
You operate in one of two review modes depending on the calling prompt's framing. Recognising the mode is load-bearing — mis-classifying a pre-edit proposal as if it were post-edit drift is the
|
|
28
|
+
You operate in one of two review modes depending on the calling prompt's framing. Recognising the mode is load-bearing — mis-classifying a pre-edit proposal as if it were post-edit drift is the pre-edit review catch-22 catch-22 the gate is designed to close.
|
|
29
29
|
|
|
30
30
|
**Pre-edit mode (the default at a governance-gate firing).** The PreToolUse architect gate fires BEFORE a project-file edit lands on disk. The calling prompt describes a PROPOSED change, fix plan, RFC, ticket-body amendment, or about-to-be-made edit — the change is not yet on disk by design. Recognition signals (any one is sufficient): the prompt uses words like "proposed", "plan to", "about to", "PRE-EDIT", "PRE-EDIT alignment gate"; the prompt names the to-be-edited files but the edits are described in prose not yet applied; the prompt is an AFK orchestrator iter dispatch implementing a `## Fix Strategy` against a problem ticket; the prompt is a SKILL handing you an RFC body or story body before the implementation commit lands.
|
|
31
31
|
|
|
32
32
|
In pre-edit mode:
|
|
33
33
|
|
|
34
|
-
- If you receive a review request describing PROPOSED changes (not-yet-applied), classify alignment of the PROPOSAL itself. Not-yet-applied state of the proposed change is the EXPECTED baseline of a pre-edit gate. Do NOT treat "edits aren't applied yet" / "the residual old state is still live" / "the change isn't on disk yet" as ISSUES FOUND — that is the gate's design intent (
|
|
34
|
+
- If you receive a review request describing PROPOSED changes (not-yet-applied), classify alignment of the PROPOSAL itself. Not-yet-applied state of the proposed change is the EXPECTED baseline of a pre-edit gate. Do NOT treat "edits aren't applied yet" / "the residual old state is still live" / "the change isn't on disk yet" as ISSUES FOUND — that is the gate's design intent (the pre-edit review catch-22 closes this catch-22).
|
|
35
35
|
- The ground truth you classify against is the **proposal** as described in the calling prompt (the diff sketch, the fix-strategy prose, the file-edit plan). The disk state is the legitimate "old state" the proposal is about to replace.
|
|
36
36
|
- PASS the review when the proposal aligns with existing decisions, the proposal does not require a new decision the user hasn't pinned, and the proposal's substance is sound. ISSUES FOUND on a pre-edit review must cite a problem with the **proposal**, not with the not-yet-applied-ness of the proposal.
|
|
37
37
|
- All other review machinery below (Decision Staleness, Existing Decision Compliance, Confirmation Criteria, New Decision Detection, Runtime-Path Performance, Decision Quality, Unratified Dependency, Needs Direction) applies normally — pre-edit mode does not relax any of those substantive checks. It constrains only the verdict-grammar around the not-yet-applied baseline.
|
|
38
38
|
|
|
39
39
|
**Post-edit mode (the explicit drift-detection or applied-change review).** The calling prompt asks you to verify already-applied edits against decisions — typically a `/wr-architect:review-design` invocation against staged changes and recent commits, or a release-gate audit. Recognition signals: the prompt names "staged changes", "recent commits", "the current diff", "verify compliance", or "review the applied changes against …". In post-edit mode you may flag drift between disk state and decisions exactly as the original verdict grammar describes — the change is on disk by construction; the not-yet-applied carve-out does not apply.
|
|
40
40
|
|
|
41
|
-
**Default when ambiguous.** When the calling prompt does not name the mode explicitly, default to **pre-edit mode** if a PreToolUse gate context is plausible (the prompt was likely fired by `architect-detect.sh` or an AFK iter dispatch). The pre-edit default is the safer fail-mode: a true post-edit drift will still surface as ISSUES FOUND on the substance; a true pre-edit proposal mis-classified as post-edit fires the
|
|
41
|
+
**Default when ambiguous.** When the calling prompt does not name the mode explicitly, default to **pre-edit mode** if a PreToolUse gate context is plausible (the prompt was likely fired by `architect-detect.sh` or an AFK iter dispatch). The pre-edit default is the safer fail-mode: a true post-edit drift will still surface as ISSUES FOUND on the substance; a true pre-edit proposal mis-classified as post-edit fires the pre-edit review catch-22 catch-22.
|
|
42
42
|
|
|
43
43
|
## What You Check
|
|
44
44
|
|
|
@@ -79,9 +79,9 @@ Flag when a proposed change represents an undocumented decision:
|
|
|
79
79
|
- **New script**: Does this introduce a new workflow step?
|
|
80
80
|
- **Structural change**: Does this reorganize code in a way that affects how the team works?
|
|
81
81
|
|
|
82
|
-
### Runtime-Path Performance Review (per
|
|
82
|
+
### Runtime-Path Performance Review (per the grounded agent-output rule, specialised by the runtime-path performance review rule)
|
|
83
83
|
|
|
84
|
-
This review is grounded in
|
|
84
|
+
This review is grounded in the grounded agent-output rule — the parent no-ungrounded-claims principle — as specialised by the runtime-path performance review rule for runtime-path changes. The qualitative-claim ban below is the grounded agent-output rule grounding requirement applied to performance estimates.
|
|
85
85
|
|
|
86
86
|
When a proposed change touches any of the following runtime-path surfaces, you MUST perform a per-request performance review in addition to the ADR-conformance review:
|
|
87
87
|
|
|
@@ -102,7 +102,7 @@ When a proposed change touches any of the following runtime-path surfaces, you M
|
|
|
102
102
|
|
|
103
103
|
**Qualitative-claim ban:** You MUST NOT emit qualitative phrases like "load is negligible", "microseconds only", "no measurable impact", "minimal cost", or equivalent hedged wording without attaching the concrete numeric backing described in steps 1-3 above. A quantified estimate that is honestly "worst-case assumption, no data" is acceptable; a qualitative claim without numbers is not.
|
|
104
104
|
|
|
105
|
-
**Performance-budget ADR template:**
|
|
105
|
+
**Performance-budget ADR template:** the runtime-path performance review rule embeds a copy-paste template downstream projects place at `docs/decisions/<NNN>-performance-budget-<scope>.proposed.md`. When flagging a missing budget, point the user at the runtime-path performance review rule's Decision Outcome for the template.
|
|
106
106
|
|
|
107
107
|
**When the trigger does NOT fire**, skip this review — performance review is scoped to runtime-path changes to keep review cost proportionate.
|
|
108
108
|
|
|
@@ -124,11 +124,11 @@ When a change includes a new or modified decision file in `docs/decisions/`:
|
|
|
124
124
|
- Does it list at least 2 considered options with pros/cons?
|
|
125
125
|
- Does it include reassessment criteria?
|
|
126
126
|
- If it supersedes another decision, is the old decision properly updated?
|
|
127
|
-
- **Is `docs/decisions/README.md` (the compendium) refreshed and staged?** Per
|
|
127
|
+
- **Is `docs/decisions/README.md` (the compendium) refreshed and staged?** Per the decisions-compendium load rule, the compendium is the architect agent's routine load surface and MUST be regenerated whenever an ADR body changes. Skills (`/wr-architect:create-adr`, `/wr-architect:capture-adr`, `/wr-architect:review-decisions`) regenerate it automatically; off-skill hand-edits and bulk renames must regenerate it explicitly. Flag any change that touches `docs/decisions/<NNN>-*.md` without also staging a fresh `docs/decisions/README.md`. Recovery is mechanical: `wr-architect-generate-decisions-compendium && git add docs/decisions/README.md`. The `architect-compendium-refresh-discipline.sh` commit-time hook is the safety-net backstop, not the primary mechanism.
|
|
128
128
|
|
|
129
129
|
## Output Formatting
|
|
130
130
|
|
|
131
|
-
When referencing decision IDs (ADR-<NNN>), problem IDs (P<NNN>), or JTBD IDs in prose output, always include the human-readable title on first mention. Use the format `
|
|
131
|
+
When referencing decision IDs (ADR-<NNN>), problem IDs (P<NNN>), or JTBD IDs in prose output, always include the human-readable title on first mention. Use the format `the structured governance interaction rule`, not bare `the structured governance interaction rule`.
|
|
132
132
|
|
|
133
133
|
## How to Report
|
|
134
134
|
|
|
@@ -148,7 +148,7 @@ If there are issues:
|
|
|
148
148
|
>
|
|
149
149
|
> 2. ...
|
|
150
150
|
|
|
151
|
-
If a new decision must be recorded but it has 2+ viable options and no pinned direction (per
|
|
151
|
+
If a new decision must be recorded but it has 2+ viable options and no pinned direction (per the Needs-Direction handoff rule):
|
|
152
152
|
|
|
153
153
|
> **Architecture Review: NEEDS DIRECTION**
|
|
154
154
|
>
|
|
@@ -160,11 +160,11 @@ If a new decision must be recorded but it has 2+ viable options and no pinned di
|
|
|
160
160
|
> - (further options as applicable; include "do nothing / status quo" where relevant)
|
|
161
161
|
> - **Advisory lean (optional)**: <your recommendation + why — but do NOT auto-pick, and do NOT prose-ask>
|
|
162
162
|
>
|
|
163
|
-
> The main agent (or calling skill) translates this into an `AskUserQuestion` before the decision is recorded — never a prose ask. Under an AFK orchestrator that cannot ask mid-loop, the verdict queues to the iteration's `outstanding_questions` for batched return-presentation (
|
|
163
|
+
> The main agent (or calling skill) translates this into an `AskUserQuestion` before the decision is recorded — never a prose ask. Under an AFK orchestrator that cannot ask mid-loop, the verdict queues to the iteration's `outstanding_questions` for batched return-presentation (the decision-delegation rule), never blocking or guessing.
|
|
164
164
|
|
|
165
|
-
**Name the SUBSTANCE, not the grain (
|
|
165
|
+
**Name the SUBSTANCE, not the grain (the ratify substance before dependent work rule).** The decision question + options MUST be the **substantive choice** — the load-bearing options the decision actually records. Do NOT substitute a meta/grain framing question (e.g. "one ADR or two?", file split, naming, ID series) for the substance. A grain question is not a decision the user needs to own; the substantive option is. If both a grain question AND a substantive choice exist, surface the substance — the grain is mechanical (decide it yourself per the mechanical-stage over-asking failure). Confirming only the grain leaves the load-bearing content unconfirmed, which is exactly the unratified dependent-work failure failure the ratify substance before dependent work rule closes: dependent work then gets built on an unconfirmed substantive choice and a later rejection forces rework.
|
|
166
166
|
|
|
167
|
-
### When to emit Needs Direction (per
|
|
167
|
+
### When to emit Needs Direction (per the Needs-Direction handoff rule)
|
|
168
168
|
|
|
169
169
|
Emit **NEEDS DIRECTION** only when ALL of the following hold:
|
|
170
170
|
|
|
@@ -172,29 +172,29 @@ Emit **NEEDS DIRECTION** only when ALL of the following hold:
|
|
|
172
172
|
2. There are **2+ viable options**.
|
|
173
173
|
3. **No direction is pinned.** Direction counts as pinned — and you must NOT ask, instead reporting PASS / ISSUES FOUND and naming the pinned source — when the option is fixed by any of: a same-turn pin, a same-session pin, an accepted ADR, `RISK-POLICY.md` appetite, or a CLAUDE.md mandatory rule.
|
|
174
174
|
|
|
175
|
-
Do NOT emit Needs Direction for the "obvious choice" / only-one-viable-option case (see "When NOT to flag" above) — over-firing on obvious choices is the over-ask trap CLAUDE.md
|
|
175
|
+
Do NOT emit Needs Direction for the "obvious choice" / only-one-viable-option case (see "When NOT to flag" above) — over-firing on obvious choices is the over-ask trap CLAUDE.md the mechanical-stage over-asking failure warns against. Needs Direction is the architect-surface instance of the decision-delegation rule category 1 (direction-setting); `AskUserQuestion` remains a primary-agent affordance — you name the question + options, the main agent owns the ask.
|
|
176
176
|
|
|
177
|
-
### When to flag [Unratified Dependency] (
|
|
177
|
+
### When to flag [Unratified Dependency] (the ratify substance before dependent work rule surface 3)
|
|
178
178
|
|
|
179
|
-
When the change or plan under review **explicitly cites or implements** a specific ADR (e.g. the diff/prose says "per
|
|
179
|
+
When the change or plan under review **explicitly cites or implements** a specific ADR (e.g. the diff/prose says "per the propose-fix RFC requirement", a `Refs: ADR-NNN`, or it is authoring the work the ADR governs), check whether that ADR has been **ratified** before letting the change stand. You have Read/Glob/Grep (no Bash), so perform the **read-only equivalent** of `packages/architect/scripts/is-decision-unconfirmed.sh` — mirror ALL THREE halves of its "unconfirmed" definition:
|
|
180
180
|
|
|
181
181
|
1. **Frontmatter-scoped marker check.** Read the cited ADR file and inspect ONLY its YAML frontmatter (the block between the leading `---` and the next `---`). The ADR is **ratified** iff that frontmatter contains a line matching `human-oversight: confirmed` — case-insensitive, tolerating trailing whitespace (the canonical predicate greps `-iE '^human-oversight:[[:space:]]*confirmed[[:space:]]*$'`). A body mention of that string does NOT count — it must be in frontmatter.
|
|
182
182
|
2. **Superseded skip.** A `*.superseded.md` ADR is retired — treat it as ratified-equivalent (do NOT flag); a newer ADR replaced it.
|
|
183
|
-
3. **Rejected-pending-supersede skip** (
|
|
183
|
+
3. **Rejected-pending-supersede skip** (the architecture human-oversight rule amendment per the rejected-decision drain recurrence). An ADR whose frontmatter carries BOTH `human-oversight: rejected-pending-supersede` AND `supersede-ticket: P<NNN>` is ratified-equivalent — the user has explicitly rejected the ADR and pinned a supersede in flight. Treat as ratified (do NOT flag). The marker alone (without the `supersede-ticket:` scalar) does NOT skip — it is malformed and surfaces as unratified so the un-tracked case doesn't silently rot.
|
|
184
184
|
|
|
185
185
|
Emit **ISSUES FOUND / [Unratified Dependency]** only when the cited ADR's frontmatter lacks the `confirmed` marker AND it is not superseded AND it does not carry the rejected-pending-supersede + supersede-ticket pair — action: "ratify ADR-NNN via `/wr-architect:review-decisions` before this lands."
|
|
186
186
|
|
|
187
|
-
**Key the flag on the oversight marker, NEVER on `status:`.** `status: proposed`/`accepted` and `human-oversight:` are orthogonal axes (
|
|
187
|
+
**Key the flag on the oversight marker, NEVER on `status:`.** `status: proposed`/`accepted` and `human-oversight:` are orthogonal axes (the architecture human-oversight rule). Building on a **ratified** ADR is fine even when its `status` is still `proposed` — do NOT flag it. Only the *unratified* (marker-absent, non-superseded) case flags. In steady state almost every ADR is ratified (born-confirmed via `create-adr` + the review-decisions drain), so this fires on essentially nothing — do not over-scan or flag transitive/ambient dependence on governed code, only an explicit cite/implement of a specific unratified ADR (the inverse missed strong-correction capture failure / the mechanical-stage over-asking failure over-fire guard).
|
|
188
188
|
|
|
189
189
|
Issue types:
|
|
190
190
|
- **[Decision Conflict]**: Change conflicts with an accepted/proposed decision
|
|
191
191
|
- **[Undocumented Decision]**: Change represents an architectural choice not covered by any existing decision
|
|
192
|
-
- **[Needs Direction]**: A new decision must be recorded but has 2+ viable options with no pinned direction — name the question + options for the main agent to translate into an `AskUserQuestion` (
|
|
192
|
+
- **[Needs Direction]**: A new decision must be recorded but has 2+ viable options with no pinned direction — name the question + options for the main agent to translate into an `AskUserQuestion` (the Needs-Direction handoff rule)
|
|
193
193
|
- **[Decision Format]**: A decision file doesn't follow MADR 4.0 format
|
|
194
194
|
- **[Missing Supersession]**: A new decision should supersede an old one but doesn't
|
|
195
195
|
- **[Amendment To Ratified Decision]**: The change edits the body of a decision that carries `human-oversight: confirmed`, or adds an `### Amendment` section, or adds an `amends:` frontmatter key. A ratified decision is immutable — see below.
|
|
196
196
|
- **[Confirmation Violation]**: New code violates a confirmation criterion of an existing decision
|
|
197
|
-
- **[Unratified Dependency]**: The change/plan explicitly cites or implements an ADR that is **unratified** (its frontmatter lacks `human-oversight: confirmed`, and it is not `*.superseded.md`) — building on it before a human ratifies its substance is the
|
|
197
|
+
- **[Unratified Dependency]**: The change/plan explicitly cites or implements an ADR that is **unratified** (its frontmatter lacks `human-oversight: confirmed`, and it is not `*.superseded.md`) — building on it before a human ratifies its substance is the unratified dependent-work failure failure mode (the ratify substance before dependent work rule enforcement surface 3)
|
|
198
198
|
|
|
199
199
|
## Constraints
|
|
200
200
|
|
|
@@ -226,7 +226,7 @@ Statuses are reflected in the filename:
|
|
|
226
226
|
- **`accepted`** (`NNN-title.accepted.md`): Validated through production use. Must be followed in new implementations.
|
|
227
227
|
- **`rejected`** (`NNN-title.rejected.md`): Evaluated and determined not suitable. Preserved for institutional knowledge.
|
|
228
228
|
- **`deprecated`** (`NNN-title.deprecated.md`): No longer recommended, being phased out without specific replacement.
|
|
229
|
-
- **`superseded`** (`NNN-title.superseded.md`): Replaced by a newer decision.
|
|
229
|
+
- **`superseded`** (`NNN-title.superseded.md`): Replaced by a newer decision. The historical body remains unchanged.
|
|
230
230
|
|
|
231
231
|
Status transitions:
|
|
232
232
|
```
|
|
@@ -309,6 +309,5 @@ Do not flag an existing amendment section that a change merely touches nearby or
|
|
|
309
309
|
When decision NNN is superseded by decision MMM:
|
|
310
310
|
1. Create new decision (MMM) with `supersedes: [NNN-old-decision-title]`
|
|
311
311
|
2. Rename old decision file to `.superseded.md`
|
|
312
|
-
3.
|
|
313
|
-
4.
|
|
314
|
-
5. Update `docs/decisions/README.md` if it exists
|
|
312
|
+
3. Do not edit the old decision's frontmatter or body
|
|
313
|
+
4. Update `docs/decisions/README.md` if it exists
|
|
@@ -72,7 +72,8 @@ esac
|
|
|
72
72
|
# Must be a numbered ADR file (NNN-...).
|
|
73
73
|
echo "$base" | grep -qE '^[0-9]+-' || exit 0
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
# Preserve the zero-padded identifier for display.
|
|
76
|
+
adr_id_padded=$(echo "$base" | grep -oE '^[0-9]+')
|
|
76
77
|
adr_id=$((10#$adr_id_padded)) # numeric form for sort comparison (49)
|
|
77
78
|
|
|
78
79
|
# Resolve repo root so the README path + git staging are stable regardless of
|
|
@@ -107,6 +108,9 @@ adr_status=$(awk '
|
|
|
107
108
|
sub(/^ +/, ""); sub(/ +$/, ""); print; exit
|
|
108
109
|
}
|
|
109
110
|
' "$file_path")
|
|
111
|
+
case "$(basename "$file_path")" in
|
|
112
|
+
*.superseded.md) adr_status="superseded" ;;
|
|
113
|
+
esac
|
|
110
114
|
case "$adr_status" in
|
|
111
115
|
superseded|rejected|deprecated) target_section="historical" ;;
|
|
112
116
|
*) target_section="inforce" ;;
|
|
@@ -162,6 +166,12 @@ if [ "$subprocess_rc" -eq 0 ] && [ -n "$subprocess_out" ]; then
|
|
|
162
166
|
new_entry=$(printf '%s' "$subprocess_out" | jq -r '.result // empty' 2>/dev/null)
|
|
163
167
|
fi
|
|
164
168
|
|
|
169
|
+
# The immutable historical ADR keeps its original frontmatter. The filename is
|
|
170
|
+
# therefore authoritative when a decision is superseded.
|
|
171
|
+
if [ "$adr_status" = "superseded" ] && [ -n "$new_entry" ]; then
|
|
172
|
+
new_entry=$(printf '%s\n' "$new_entry" | sed -E 's/^\*\*Status:\*\* [^|]*/**Status:** superseded /')
|
|
173
|
+
fi
|
|
174
|
+
|
|
165
175
|
# Degraded mode (criterion l): no usable emit → warn + leave README unchanged,
|
|
166
176
|
# do NOT block the edit.
|
|
167
177
|
if [ -z "$new_entry" ] || ! printf '%s' "$new_entry" | grep -qE '^### ADR-[0-9]+'; then
|
|
@@ -67,7 +67,7 @@ NOTE: these exclusions are READ tolerance — the architect gate skips
|
|
|
67
67
|
user edits to these paths. They are NOT agent write targets. Never
|
|
68
68
|
write project-generated artefacts (plans, audits, scratch state) under
|
|
69
69
|
.claude/ — that is user-controlled config space. Project-generated
|
|
70
|
-
content belongs under docs/ or directly in problem-ticket bodies
|
|
70
|
+
content belongs under docs/ or directly in problem-ticket bodies.
|
|
71
71
|
HOOK_OUTPUT
|
|
72
72
|
mark_announced "architect" "$SESSION_ID"
|
|
73
73
|
fi
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Fan out architect hooks from one registered hook per lifecycle event.
|
|
3
|
+
|
|
4
|
+
set -u
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
EVENT="${1:-}"
|
|
8
|
+
INPUT="$(cat)"
|
|
9
|
+
|
|
10
|
+
tool_name() {
|
|
11
|
+
printf '%s' "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null || true
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run_gate() {
|
|
15
|
+
local output status
|
|
16
|
+
output="$(printf '%s' "$INPUT" | "$SCRIPT_DIR/$1")"
|
|
17
|
+
status=$?
|
|
18
|
+
[ -z "$output" ] || printf '%s\n' "$output"
|
|
19
|
+
[ "$status" -eq 0 ] || exit "$status"
|
|
20
|
+
[ -z "$output" ] || exit 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
run_side_effect() {
|
|
24
|
+
local output status
|
|
25
|
+
output="$(printf '%s' "$INPUT" | "$SCRIPT_DIR/$1")"
|
|
26
|
+
status=$?
|
|
27
|
+
[ -z "$output" ] || printf '%s\n' "$output"
|
|
28
|
+
return "$status"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
case "$EVENT" in
|
|
32
|
+
session-start)
|
|
33
|
+
agent_output="$(printf '%s' "$INPUT" | node "$SCRIPT_DIR/../scripts/codex-agent.mjs" --session-start)"
|
|
34
|
+
nudge_output="$(printf '%s' "$INPUT" | "$SCRIPT_DIR/architect-oversight-nudge.sh")"
|
|
35
|
+
if [ -n "${CODEX_THREAD_ID:-}" ]; then
|
|
36
|
+
AGENT_OUTPUT="$agent_output" NUDGE_OUTPUT="$nudge_output" python3 -c '
|
|
37
|
+
import json, os
|
|
38
|
+
messages = []
|
|
39
|
+
try:
|
|
40
|
+
message = json.loads(os.environ["AGENT_OUTPUT"]).get("systemMessage", "")
|
|
41
|
+
except (json.JSONDecodeError, AttributeError):
|
|
42
|
+
message = os.environ["AGENT_OUTPUT"]
|
|
43
|
+
if message:
|
|
44
|
+
messages.append(message)
|
|
45
|
+
if os.environ["NUDGE_OUTPUT"]:
|
|
46
|
+
messages.append(os.environ["NUDGE_OUTPUT"])
|
|
47
|
+
if messages:
|
|
48
|
+
print(json.dumps({"systemMessage": "\n".join(messages)}))
|
|
49
|
+
'
|
|
50
|
+
else
|
|
51
|
+
[ -z "$agent_output" ] || printf '%s\n' "$agent_output"
|
|
52
|
+
[ -z "$nudge_output" ] || printf '%s\n' "$nudge_output"
|
|
53
|
+
fi
|
|
54
|
+
;;
|
|
55
|
+
user-prompt)
|
|
56
|
+
detect_output="$(printf '%s' "$INPUT" | "$SCRIPT_DIR/architect-detect.sh")"
|
|
57
|
+
stale_output="$(printf '%s' "$INPUT" | "$SCRIPT_DIR/staleness-check.sh")"
|
|
58
|
+
[ -z "$detect_output" ] || printf '%s\n' "$detect_output"
|
|
59
|
+
[ -z "$stale_output" ] || printf '%s\n' "$stale_output"
|
|
60
|
+
;;
|
|
61
|
+
pre-tool)
|
|
62
|
+
case "$(tool_name)" in
|
|
63
|
+
Edit|Write)
|
|
64
|
+
run_gate architect-enforce-edit.sh
|
|
65
|
+
run_gate architect-oversight-marker-discipline.sh
|
|
66
|
+
;;
|
|
67
|
+
ExitPlanMode)
|
|
68
|
+
run_gate architect-plan-enforce.sh
|
|
69
|
+
;;
|
|
70
|
+
Bash)
|
|
71
|
+
run_gate architect-readme-pairing-check.sh
|
|
72
|
+
;;
|
|
73
|
+
esac
|
|
74
|
+
;;
|
|
75
|
+
post-tool)
|
|
76
|
+
case "$(tool_name)" in
|
|
77
|
+
Agent)
|
|
78
|
+
run_side_effect architect-mark-reviewed.sh || true
|
|
79
|
+
run_side_effect architect-slide-marker.sh || true
|
|
80
|
+
;;
|
|
81
|
+
collaborationspawn_agent|collaborationinterrupt_agent|spawn_agent|close_agent|multi_agent_v1__spawn_agent|multi_agent_v1__close_agent)
|
|
82
|
+
printf '%s' "$INPUT" | node "$SCRIPT_DIR/codex-agent-completion.mjs"
|
|
83
|
+
;;
|
|
84
|
+
Edit|Write)
|
|
85
|
+
run_side_effect architect-refresh-hash.sh || true
|
|
86
|
+
run_side_effect architect-compendium-update-entry.sh || true
|
|
87
|
+
;;
|
|
88
|
+
Bash|Skill)
|
|
89
|
+
run_side_effect architect-slide-marker.sh || true
|
|
90
|
+
;;
|
|
91
|
+
esac
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
@@ -156,7 +156,7 @@ cat <<EOF
|
|
|
156
156
|
"hookSpecificOutput": {
|
|
157
157
|
"hookEventName": "PreToolUse",
|
|
158
158
|
"permissionDecision": "deny",
|
|
159
|
-
"permissionDecisionReason": "BLOCKED: '${BASENAME}' is about to receive 'human-oversight: confirmed' but no substance-confirm evidence marker exists for this ADR in this session
|
|
159
|
+
"permissionDecisionReason": "BLOCKED: '${BASENAME}' is about to receive 'human-oversight: confirmed' but no substance-confirm evidence marker exists for this ADR in this session. The marker '/tmp/oversight-confirmed-<sha>-<sid>' is written by 'wr-architect-mark-oversight-confirmed <adr-path>' immediately after an AskUserQuestion lands the user's substance-confirm answer. If you are an AFK iter subprocess without AskUserQuestion access, write 'human-oversight: unconfirmed' instead — the drain (/wr-architect:review-decisions) will promote it interactively. To recover this Edit/Write: surface the substance-confirm AskUserQuestion to the user, call wr-architect-mark-oversight-confirmed with this ADR's path, then retry."
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
EOF
|
|
@@ -138,7 +138,7 @@ staged_compendium=$(git diff --cached --name-only 2>/dev/null \
|
|
|
138
138
|
if [ -z "$staged_compendium" ]; then
|
|
139
139
|
first_adr=$(echo "$staged_adrs" | head -1)
|
|
140
140
|
cat >&2 <<EOF
|
|
141
|
-
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "architect-readme-pairing-check: '${first_adr}' is staged for commit but 'docs/decisions/README.md' is NOT.
|
|
141
|
+
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "architect-readme-pairing-check: '${first_adr}' is staged for commit but 'docs/decisions/README.md' is NOT. Every ADR body change must be paired with its compendium entry refresh (the architect-compendium-update-entry PostToolUse hook does this automatically — if README is unstaged the hook did not run or hit degraded mode). Recover: re-run the ADR edit to re-trigger the hook, OR run 'wr-architect-generate-decisions-compendium && git add docs/decisions/README.md'. Intentional follow-up split: append 'RISK_BYPASS: architect-compendium-deferred' to the commit message. Batch/migration: set BYPASS_COMPENDIUM_REFRESH_GATE=1."}}
|
|
142
142
|
EOF
|
|
143
143
|
exit 2
|
|
144
144
|
fi
|
package/hooks/hooks.json
CHANGED
|
@@ -2,26 +2,17 @@
|
|
|
2
2
|
"hooks": {
|
|
3
3
|
"SessionStart": [
|
|
4
4
|
{ "matcher": "startup", "hooks": [
|
|
5
|
-
{ "type": "command", "command": "
|
|
6
|
-
{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-oversight-nudge.sh" }
|
|
5
|
+
{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-dispatch.sh session-start" }
|
|
7
6
|
] }
|
|
8
7
|
],
|
|
9
8
|
"UserPromptSubmit": [
|
|
10
|
-
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-
|
|
11
|
-
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/staleness-check.sh" }] }
|
|
9
|
+
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-dispatch.sh user-prompt" }] }
|
|
12
10
|
],
|
|
13
11
|
"PreToolUse": [
|
|
14
|
-
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-
|
|
15
|
-
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-oversight-marker-discipline.sh" }] },
|
|
16
|
-
{ "matcher": "ExitPlanMode", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-plan-enforce.sh" }] },
|
|
17
|
-
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-readme-pairing-check.sh" }] }
|
|
12
|
+
{ "matcher": "Bash|Edit|Write|ExitPlanMode", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-dispatch.sh pre-tool" }] }
|
|
18
13
|
],
|
|
19
14
|
"PostToolUse": [
|
|
20
|
-
{ "matcher": "Agent", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-
|
|
21
|
-
{ "matcher": "collaborationspawn_agent|collaborationinterrupt_agent|spawn_agent|close_agent|multi_agent_v1__spawn_agent|multi_agent_v1__close_agent", "hooks": [{ "type": "command", "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/codex-agent-completion.mjs" }] },
|
|
22
|
-
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-refresh-hash.sh" }] },
|
|
23
|
-
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-compendium-update-entry.sh" }] },
|
|
24
|
-
{ "matcher": "Agent|Bash|Skill", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-slide-marker.sh" }] }
|
|
15
|
+
{ "matcher": "Agent|Bash|Edit|Skill|Write|collaborationspawn_agent|collaborationinterrupt_agent|spawn_agent|close_agent|multi_agent_v1__spawn_agent|multi_agent_v1__close_agent", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-dispatch.sh post-tool" }] }
|
|
25
16
|
]
|
|
26
17
|
}
|
|
27
18
|
}
|
|
@@ -61,7 +61,7 @@ check_architect_gate() {
|
|
|
61
61
|
fi
|
|
62
62
|
fi
|
|
63
63
|
|
|
64
|
-
ARCHITECT_GATE_REASON="No architect review marker found for this session. Delegate to wr-architect:agent via the Agent tool (subagent_type: 'wr-architect:agent') so the architect can review and create the marker.
|
|
64
|
+
ARCHITECT_GATE_REASON="No architect review marker found for this session. Delegate to wr-architect:agent via the Agent tool (subagent_type: 'wr-architect:agent') so the architect can review and create the marker. After an ISSUES FOUND verdict, obtain the upgraded PASS from a fresh wr-architect:agent spawn: resuming the prior agent does not fire the completion hook and therefore cannot write the marker. If you already hold a genuine PASS, assert the marker manually: touch /tmp/architect-reviewed-\$SID && rm -f /tmp/architect-reviewed-\$SID.hash (SID = newest architect-plan-reviewed-* / architect-announced-* basename)."
|
|
65
65
|
return 1 # No marker, deny
|
|
66
66
|
}
|
|
67
67
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windyroad/architect",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.2-preview.1079",
|
|
4
4
|
"description": "Architecture decision enforcement for AI coding agents",
|
|
5
5
|
"bin": {
|
|
6
6
|
"windyroad-architect": "./bin/install.mjs"
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
"!agents/eval/",
|
|
37
37
|
"!agents/test/",
|
|
38
38
|
"!hooks/test/",
|
|
39
|
+
"!scripts/check-published-internal-ids.sh",
|
|
40
|
+
"!scripts/sync-codex-skills.mjs",
|
|
39
41
|
"!scripts/test/",
|
|
40
42
|
"!skills/*/test/"
|
|
41
43
|
]
|
|
@@ -43,7 +43,7 @@ export LC_ALL=C
|
|
|
43
43
|
# offline-reproducibility tool only, to be removed after one
|
|
44
44
|
# @windyroad/architect minor-version cycle (RFC-014 Story C). The notice fires
|
|
45
45
|
# on every invocation per ADR-078 Confirmation criterion (j).
|
|
46
|
-
echo "generate-decisions-compendium: DEPRECATED
|
|
46
|
+
echo "generate-decisions-compendium: DEPRECATED — the compendium is now architect-authored on every ADR edit by the architect-compendium-update-entry.sh PostToolUse hook. This script is a backstop/bootstrap tool only and will be removed after one @windyroad/architect minor-version cycle." >&2
|
|
47
47
|
|
|
48
48
|
# --- Flag parsing ----------------------------------------------------------
|
|
49
49
|
# `--check` (no write): generate to a temp file and diff against the on-disk
|
|
@@ -69,7 +69,7 @@ bodies. Idempotent — same in-force ADR set produces byte-identical output.
|
|
|
69
69
|
With --check: generates to a temp file and diffs against the existing
|
|
70
70
|
<decisions_dir>/README.md. Exits 0 if up-to-date, 1 if stale (with a
|
|
71
71
|
diff hint), 2 on directory error. Does NOT modify any file. Used by the
|
|
72
|
-
|
|
72
|
+
compendium consistency hook to verify the committed compendium matches the
|
|
73
73
|
current ADR bodies.
|
|
74
74
|
EOF
|
|
75
75
|
exit 0
|
|
@@ -229,6 +229,9 @@ emit_entry() {
|
|
|
229
229
|
id=$(basename "$file" | grep -oE '^[0-9]+')
|
|
230
230
|
title=$(get_title "$file")
|
|
231
231
|
status=$(get_frontmatter_field "$file" "status")
|
|
232
|
+
case "$(basename "$file")" in
|
|
233
|
+
*.superseded.md) status="superseded" ;;
|
|
234
|
+
esac
|
|
232
235
|
oversight=$(get_frontmatter_field "$file" "human-oversight")
|
|
233
236
|
superseded=$(get_frontmatter_field "$file" "supersedes")
|
|
234
237
|
# ADR-066 amendment (P316): when the oversight value is
|
|
@@ -312,6 +315,9 @@ in_force_files=()
|
|
|
312
315
|
historical_files=()
|
|
313
316
|
for f in "${all_files[@]}"; do
|
|
314
317
|
s=$(get_frontmatter_field "$f" "status")
|
|
318
|
+
case "$(basename "$f")" in
|
|
319
|
+
*.superseded.md) s="superseded" ;;
|
|
320
|
+
esac
|
|
315
321
|
case "$s" in
|
|
316
322
|
proposed|accepted)
|
|
317
323
|
in_force_files+=("$f")
|
|
@@ -339,7 +345,7 @@ total=$((in_force_total + historical_total))
|
|
|
339
345
|
{
|
|
340
346
|
echo "# Decisions Compendium"
|
|
341
347
|
echo ""
|
|
342
|
-
echo "<!-- AUTO-GENERATED by packages/architect/scripts/generate-decisions-compendium.sh
|
|
348
|
+
echo "<!-- AUTO-GENERATED by packages/architect/scripts/generate-decisions-compendium.sh under the decisions-compendium policy — do NOT hand-edit; regenerate via \`wr-architect-generate-decisions-compendium\`. -->"
|
|
343
349
|
echo ""
|
|
344
350
|
echo "Compact rendered index of every ADR's chosen option, confirmation criteria, and relationship graph. **Authoritative substance lives in the per-ADR body** (\`<NNN>-<slug>.<status>.md\`); this compendium is a derived view for routine \`wr-architect:agent\` compliance review."
|
|
345
351
|
echo ""
|
|
@@ -111,7 +111,7 @@ if [ -z "$candidates" ]; then
|
|
|
111
111
|
echo " NO oversight marker was written for: ${ABS_PATH}"
|
|
112
112
|
echo " The oversight-marker-discipline hook will DENY the 'human-oversight: confirmed' Edit"
|
|
113
113
|
echo " until a session announce marker exists. Start a fresh session, or point SESSION_MARKER_DIR"
|
|
114
|
-
echo " at a dir containing a *-announced-<sid> file, then re-run this shim
|
|
114
|
+
echo " at a dir containing a *-announced-<sid> file, then re-run this shim."
|
|
115
115
|
} >&2
|
|
116
116
|
exit 0
|
|
117
117
|
fi
|