@windyroad/architect 1.0.1 → 1.1.0-preview.1304

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.
@@ -123,5 +123,5 @@
123
123
  }
124
124
  },
125
125
  "name": "wr-architect",
126
- "version": "1.0.1"
126
+ "version": "1.1.0"
127
127
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wr-architect",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Architecture decision enforcement for AI coding agents",
5
5
  "author": {
6
6
  "name": "Windy Road Technology",
@@ -11,22 +11,44 @@ tool_name() {
11
11
  printf '%s' "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null || true
12
12
  }
13
13
 
14
+ ADVISORIES=""
15
+ collect_gate_output() {
16
+ local output="$1" parsed
17
+ [ -n "$output" ] || return 0
18
+ if ! parsed="$(printf '%s' "$output" | jq -cs '
19
+ if any(.[]; .hookSpecificOutput.permissionDecision == "deny")
20
+ then map(select(.hookSpecificOutput.permissionDecision == "deny"))[0]
21
+ else {systemMessage: map(.systemMessage // .hookSpecificOutput.permissionDecisionReason // "Governance evidence unavailable; action permitted.") | join("\n")} end' 2>/dev/null)"; then
22
+ parsed='{"systemMessage":"Governance evidence unavailable: malformed evaluator output; action permitted."}'
23
+ fi
24
+ if printf '%s' "$parsed" | jq -e '.hookSpecificOutput.permissionDecision == "deny"' >/dev/null 2>&1; then
25
+ printf '%s\n' "$parsed"
26
+ exit 0
27
+ fi
28
+ ADVISORIES="$ADVISORIES
29
+ $parsed"
30
+ }
31
+ flush_advisories() {
32
+ [ -n "$ADVISORIES" ] || return 0
33
+ printf '%s' "$ADVISORIES" | jq -s '{systemMessage: map(.systemMessage // .hookSpecificOutput.permissionDecisionReason // "Governance evidence unavailable; action permitted.") | join("\n")}'
34
+ }
14
35
  run_gate() {
15
- local output status
16
- local child="$1"
36
+ local output status child="$1"
17
37
  shift
18
- output="$(printf '%s' "$INPUT" | "$SCRIPT_DIR/$child" "$@")"
38
+ output="$(printf '%s' "$INPUT" | "$SCRIPT_DIR/$child" "$@" 2>/dev/null)"
19
39
  status=$?
20
- [ -z "$output" ] || printf '%s\n' "$output"
21
- [ "$status" -eq 0 ] || exit "$status"
22
- [ -z "$output" ] || exit 0
40
+ if [ "$status" -ne 0 ]; then
41
+ collect_gate_output '{"systemMessage":"Governance evaluator unavailable; action permitted. A crashed hook output was discarded."}'
42
+ return 0
43
+ fi
44
+ collect_gate_output "$output"
23
45
  }
24
46
 
25
47
  run_side_effect() {
26
48
  local output status
27
49
  local child="$1"
28
50
  shift
29
- output="$(printf '%s' "$INPUT" | "$SCRIPT_DIR/$child" "$@")"
51
+ output="$(printf '%s' "$INPUT" | "$SCRIPT_DIR/$child" "$@" 2>/dev/null)"
30
52
  status=$?
31
53
  [ -z "$output" ] || printf '%s\n' "$output"
32
54
  return "$status"
@@ -109,3 +131,5 @@ if messages:
109
131
  esac
110
132
  ;;
111
133
  esac
134
+
135
+ [ "$EVENT" != pre-tool ] || flush_advisories
@@ -7,7 +7,7 @@
7
7
  _ARCHITECT_GATE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
8
  source "$_ARCHITECT_GATE_DIR/gate-helpers.sh"
9
9
 
10
- # Check architect gate marker. Returns 0 if marker is valid (allow), 1 if invalid (deny).
10
+ # Validate architect receipt; the public gate below converts unavailable evidence to an advisory.
11
11
  # Sets ARCHITECT_GATE_REASON on failure with an explicit recovery directive
12
12
  # naming the wr-architect:agent subagent_type (P215 / RFC-021 — mirrors the
13
13
  # sibling REVIEW_GATE_REASON pattern in review-gate.sh). Downstream
@@ -15,7 +15,7 @@ source "$_ARCHITECT_GATE_DIR/gate-helpers.sh"
15
15
  # append this reason to their BLOCKED deny message so the agent sees a clear
16
16
  # recovery affordance without having to read source.
17
17
  # Usage: check_architect_gate "$SESSION_ID"
18
- check_architect_gate() {
18
+ _check_architect_receipt() {
19
19
  local SESSION_ID="$1"
20
20
  local MARKER="/tmp/architect-reviewed-${SESSION_ID}"
21
21
  local TTL_SECONDS="${ARCHITECT_TTL:-3600}"
@@ -66,10 +66,16 @@ check_architect_gate() {
66
66
  return 1 # No marker, deny
67
67
  }
68
68
 
69
- # Emit fail-closed deny JSON for parse failures
69
+ # Permit parsing failures with an unavailable-evidence advisory
70
70
  architect_gate_parse_error() {
71
71
  cat <<'EOF'
72
- { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny",
73
- "permissionDecisionReason": "BLOCKED: Could not parse hook input. Gate is fail-closed." } }
72
+ { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "allow",
73
+ "permissionDecisionReason": "Governance evidence unavailable: could not parse hook input; action permitted." } }
74
74
  EOF
75
75
  }
76
+
77
+ check_architect_gate() {
78
+ if _check_architect_receipt "$@"; then return 0; fi
79
+ _governance_advisory "$ARCHITECT_GATE_REASON"
80
+ return 0
81
+ }
@@ -154,6 +154,9 @@ _HOOK_INPUT=""
154
154
 
155
155
  _parse_input() {
156
156
  _HOOK_INPUT=$(cat)
157
+ if ! printf '%s' "$_HOOK_INPUT" | python3 -c 'import json,sys; assert isinstance(json.load(sys.stdin),dict)' 2>/dev/null; then
158
+ _governance_advisory "Hook input could not be parsed."
159
+ fi
157
160
  }
158
161
 
159
162
  _get_tool_name() {
@@ -327,3 +330,8 @@ _is_doc_file() {
327
330
  esac
328
331
  return 1
329
332
  }
333
+
334
+ # Mechanical evaluation failures are advisory; never mint approval receipts.
335
+ _governance_advisory() {
336
+ python3 -c 'import json,sys; print(json.dumps({"systemMessage": "Governance evidence unavailable; action permitted. " + sys.argv[1]}))' "$1" || echo 'Governance evidence unavailable; action permitted.' >&2
337
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/architect",
3
- "version": "1.0.1",
3
+ "version": "1.1.0-preview.1304",
4
4
  "description": "Architecture decision enforcement for AI coding agents",
5
5
  "bin": {
6
6
  "windyroad-architect": "./bin/install.mjs"