@nusoft/nuos-build-catalogue 0.27.0 → 0.29.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nusoft/nuos-build-catalogue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.1",
|
|
4
4
|
"description": "NuOS build-catalogue tooling: semantic search (WU 110) + migration runner that lifts markdown artefacts into JSON-backed workflow records (WU 111, Phase G).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/scripts/hooks/pre-commit
CHANGED
|
@@ -68,7 +68,8 @@ check_index_drift() {
|
|
|
68
68
|
ids_in_tree=$(cd "$REPO_ROOT/$dir" && {
|
|
69
69
|
find . -maxdepth 2 -type f -name "*.md" \
|
|
70
70
|
-not -name "_index.md" \
|
|
71
|
-
-not -name "*-template.md"
|
|
71
|
+
-not -name "*-template.md" \
|
|
72
|
+
-not -name "*-template-*.md" 2>/dev/null \
|
|
72
73
|
| sed -nE "$file_regex" \
|
|
73
74
|
| sort -u
|
|
74
75
|
})
|
|
@@ -59,9 +59,35 @@ When you're done, write a brief to the coder agent in the work unit's notes —
|
|
|
59
59
|
|
|
60
60
|
If you find yourself writing *"likely"*, *"presumably"*, *"should work"* in your decision, that's a missing verification step. Replace it with a concrete check, or file the uncertainty as an open question. Hedge words leave room for plausible-looking work that doesn't match reality.
|
|
61
61
|
|
|
62
|
+
## No shortcuts. No workarounds. No provisional designs.
|
|
63
|
+
|
|
64
|
+
**This is absolute.** The architect's job is to produce the correct, fully-designed solution — not the fastest one, not the simplest one, not the one that fits inside this sprint. Speed of delivery is never a valid input to an architectural decision.
|
|
65
|
+
|
|
66
|
+
### What is prohibited
|
|
67
|
+
|
|
68
|
+
- **Provisional designs**: "For now we can just...", "This will do until we build the proper version", "A quick approach..."
|
|
69
|
+
- **Workarounds**: Any design that routes around a problem rather than solving it
|
|
70
|
+
- **Deferred correctness**: Designs that acknowledge a flaw and plan to fix it "in a follow-up WU" — security gaps, race conditions, missing validation, unhandled failure modes
|
|
71
|
+
- **Complexity avoidance**: Recommending a simpler approach because the correct approach is "a lot of work" — that is the coder's constraint to manage, not yours
|
|
72
|
+
- **Inline shortcuts**: Hard-coded values, collapsed abstractions, missing module boundaries "to keep it simple for now"
|
|
73
|
+
- **Pattern N shortcuts**: Producing one design and declaring it obvious — every non-trivial choice gets two genuinely different alternatives evaluated
|
|
74
|
+
|
|
75
|
+
### What to do instead
|
|
76
|
+
|
|
77
|
+
If the correct solution is large, complex, or blocked:
|
|
78
|
+
|
|
79
|
+
1. **If the WU is under-scoped**: Report this to the coordinator. Name concretely what proper scope looks like. The coordinator will surface it to the operator. Do not fill the gap with a lesser design.
|
|
80
|
+
2. **If an upstream decision is missing**: File the open question. Do not bridge the gap with an assumption or a hack.
|
|
81
|
+
3. **If you need more information**: Ask the coordinator to spawn a researcher agent. Do not design under uncertainty by choosing the safer-looking shortcut.
|
|
82
|
+
|
|
83
|
+
The coder will build exactly what you design. A shortcut architecture produces shortcut code. The coordinator will reject the brief and route it back. The total cost of a shortcut — design, code, review rejection, re-design — is always higher than producing the correct design once.
|
|
84
|
+
|
|
85
|
+
**When in doubt: more scope, more rigour, more time. Not less.**
|
|
86
|
+
|
|
62
87
|
## You do not
|
|
63
88
|
|
|
64
89
|
- Write production code (that's the coder's job)
|
|
65
90
|
- Write tests (that's the tester's job)
|
|
66
91
|
- Run code (that's not your role)
|
|
67
92
|
- Skip Pattern N for "obvious" choices — an obvious choice that survives Pattern N is a deeper commitment
|
|
93
|
+
- Suggest a workaround because the proper solution is hard — surface the scope gap instead
|
|
@@ -34,6 +34,32 @@ nuos-catalogue memory store --value="<what worked and why, or what to avoid>" --
|
|
|
34
34
|
|
|
35
35
|
If anything in the work unit is ambiguous, **stop and surface the ambiguity to the coordinator** rather than guessing. A guess produces work that may not match the design.
|
|
36
36
|
|
|
37
|
+
## Design system gate (UI work — enforced by hook)
|
|
38
|
+
|
|
39
|
+
**If this work unit touches any UI file (`.css`, `.scss`, `.less`, `.html`, `.tsx`, `.jsx`, `.vue`, `.svelte`, `.astro`), a write-gate hook is active. It will BLOCK the write and force you back here.**
|
|
40
|
+
|
|
41
|
+
Before writing any UI file, complete these steps in order:
|
|
42
|
+
|
|
43
|
+
1. **Read the design system — all of it:**
|
|
44
|
+
- `docs/build/design-system/tokens-colour.md` — every colour token and its hex value
|
|
45
|
+
- `docs/build/design-system/tokens-typography.md` — font sizes, weights, line heights
|
|
46
|
+
- `docs/build/design-system/tokens-spacing.md` — the spacing scale
|
|
47
|
+
- `docs/build/design-system/tokens-radius-elevation.md` — border radius, shadows
|
|
48
|
+
|
|
49
|
+
2. **Identify the token reference pattern this project uses** — read two or three existing UI files to confirm one of:
|
|
50
|
+
- CSS custom properties: `color: var(--colour-text-primary);`
|
|
51
|
+
- Theme/token object: `color: theme.colour.text.primary`
|
|
52
|
+
- Utility class config: project-configured Tailwind or similar
|
|
53
|
+
|
|
54
|
+
3. **Map every value to a token before writing a single line.** If a colour, size, or spacing value you need has no token in the design system, **stop and surface the gap to the coordinator** — do not invent a value or use a hardcode.
|
|
55
|
+
|
|
56
|
+
The hook checks for:
|
|
57
|
+
- Raw hex literals in colour properties: `color: #1a2b3c` — BLOCKED
|
|
58
|
+
- Hex strings in JSX inline styles: `color: '#fff'` — BLOCKED
|
|
59
|
+
- Hex colours in HTML style attributes — BLOCKED
|
|
60
|
+
|
|
61
|
+
CSS custom property definitions (`--colour-x: #hex`) are allowed — that is where the token value lives. Everything else must use the token by name.
|
|
62
|
+
|
|
37
63
|
## How you work
|
|
38
64
|
|
|
39
65
|
1. **Plan the change in your head first**, then state it in 1-2 sentences before writing code. Match existing code idioms; don't introduce new patterns the project hasn't adopted.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# NuOS Build Method — Design System Compliance Hook (PreToolUse)
|
|
4
|
+
#
|
|
5
|
+
# Blocks Write / Edit / MultiEdit when:
|
|
6
|
+
# (a) the target file is a UI file (.css, .scss, .less, .html, .tsx,
|
|
7
|
+
# .jsx, .vue, .svelte, .astro), AND
|
|
8
|
+
# (b) the written content contains hardcoded colour values (hex literals,
|
|
9
|
+
# CSS named colours) in colour-property positions, AND
|
|
10
|
+
# (c) the project has a design system at docs/build/design-system/
|
|
11
|
+
#
|
|
12
|
+
# Rationale
|
|
13
|
+
# ─────────
|
|
14
|
+
# Agents write UI code without first reading the design system, producing
|
|
15
|
+
# raw hex values that bypass the project's token contracts. The reviewer
|
|
16
|
+
# agent catches this after the fact — but by then the coder has already
|
|
17
|
+
# satisfied its acceptance criteria and treats the finding as "cleanup."
|
|
18
|
+
# This hook closes the gap at the moment of writing: the write is blocked
|
|
19
|
+
# and the agent is shown exactly where to look before it can retry.
|
|
20
|
+
#
|
|
21
|
+
# What is checked
|
|
22
|
+
# ───────────────
|
|
23
|
+
# 1. CSS/SCSS/Less: colour property with hardcoded hex literal
|
|
24
|
+
# color: #fff ← BLOCKED
|
|
25
|
+
# --colour-x: #fff ← allowed (token definition line; starts with --)
|
|
26
|
+
# color: var(--x) ← allowed
|
|
27
|
+
# 2. JSX/TSX: inline style object with hex string
|
|
28
|
+
# color: '#1a2b3c' ← BLOCKED
|
|
29
|
+
# 3. HTML: style attribute containing a hex colour
|
|
30
|
+
# style="color: #fff" ← BLOCKED
|
|
31
|
+
#
|
|
32
|
+
# Degrade-safe: if content cannot be reliably parsed (no jq or python3,
|
|
33
|
+
# or parse failure), the hook exits 0. Never block on ambiguous input.
|
|
34
|
+
#
|
|
35
|
+
# Exit codes
|
|
36
|
+
# ──────────
|
|
37
|
+
# 0 — allow (no violations, design system absent, or degrade-safe skip)
|
|
38
|
+
# 2 — block (stderr is surfaced to the model by Claude Code)
|
|
39
|
+
|
|
40
|
+
set -uo pipefail
|
|
41
|
+
|
|
42
|
+
INPUT="$(cat 2>/dev/null || true)"
|
|
43
|
+
|
|
44
|
+
# ── Project root ──────────────────────────────────────────────────────────────
|
|
45
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-}"
|
|
46
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
47
|
+
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
48
|
+
fi
|
|
49
|
+
if [[ -z "$PROJECT_ROOT" ]]; then exit 0; fi
|
|
50
|
+
|
|
51
|
+
# ── Extract file path ─────────────────────────────────────────────────────────
|
|
52
|
+
FILE=$(printf '%s' "$INPUT" \
|
|
53
|
+
| grep -oE '"(file_path|notebook_path)"[[:space:]]*:[[:space:]]*"[^"]+"' \
|
|
54
|
+
| head -1 \
|
|
55
|
+
| sed -E 's/"(file_path|notebook_path)"[[:space:]]*:[[:space:]]*"//' \
|
|
56
|
+
| tr -d '"')
|
|
57
|
+
|
|
58
|
+
if [[ -z "${FILE:-}" ]]; then exit 0; fi
|
|
59
|
+
|
|
60
|
+
# Normalise to absolute path
|
|
61
|
+
case "$FILE" in
|
|
62
|
+
/*) ABSOLUTE_FILE="$FILE" ;;
|
|
63
|
+
*) ABSOLUTE_FILE="$(pwd)/$FILE" ;;
|
|
64
|
+
esac
|
|
65
|
+
|
|
66
|
+
# ── Skip the design-system directory itself ───────────────────────────────────
|
|
67
|
+
if [[ "$ABSOLUTE_FILE" == *"/docs/build/design-system/"* ]]; then
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Skip generated output directories
|
|
72
|
+
case "$ABSOLUTE_FILE" in
|
|
73
|
+
*/node_modules/*|*/dist/*|*/.next/*|*/build/*|*/.nuxt/*) exit 0 ;;
|
|
74
|
+
esac
|
|
75
|
+
|
|
76
|
+
# ── Only check UI file types ──────────────────────────────────────────────────
|
|
77
|
+
EXTENSION="${FILE##*.}"
|
|
78
|
+
case "$EXTENSION" in
|
|
79
|
+
css|scss|less|html|tsx|jsx|vue|svelte|astro) : ;;
|
|
80
|
+
*) exit 0 ;;
|
|
81
|
+
esac
|
|
82
|
+
|
|
83
|
+
# ── Find the design system ────────────────────────────────────────────────────
|
|
84
|
+
DS_DIR=""
|
|
85
|
+
if [[ -d "$PROJECT_ROOT/docs/build/design-system" ]]; then
|
|
86
|
+
DS_DIR="$PROJECT_ROOT/docs/build/design-system"
|
|
87
|
+
else
|
|
88
|
+
# Split-repo pattern: look for a sibling catalogue that owns the design system
|
|
89
|
+
PARENT="$(dirname "$PROJECT_ROOT")"
|
|
90
|
+
for sibling in "$PARENT"/*/docs/build/design-system; do
|
|
91
|
+
if [[ -d "$sibling" ]]; then
|
|
92
|
+
DS_DIR="$sibling"
|
|
93
|
+
break
|
|
94
|
+
fi
|
|
95
|
+
done
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
if [[ -z "$DS_DIR" ]]; then exit 0; fi
|
|
99
|
+
|
|
100
|
+
COLOUR_FILE="$DS_DIR/tokens-colour.md"
|
|
101
|
+
if [[ ! -f "$COLOUR_FILE" ]]; then exit 0; fi
|
|
102
|
+
|
|
103
|
+
# ── Extract the content being written ─────────────────────────────────────────
|
|
104
|
+
# Write → tool_input.content
|
|
105
|
+
# Edit → tool_input.new_string
|
|
106
|
+
# MultiEdit → tool_input.edits[].new_string (joined)
|
|
107
|
+
CONTENT=""
|
|
108
|
+
if command -v jq &>/dev/null; then
|
|
109
|
+
CONTENT=$(printf '%s' "$INPUT" | jq -r '
|
|
110
|
+
.tool_input.content //
|
|
111
|
+
.tool_input.new_string //
|
|
112
|
+
(.tool_input.edits // [] | map(.new_string // "") | join("\n")) //
|
|
113
|
+
""
|
|
114
|
+
' 2>/dev/null || true)
|
|
115
|
+
elif command -v python3 &>/dev/null; then
|
|
116
|
+
CONTENT=$(printf '%s' "$INPUT" | python3 -c "
|
|
117
|
+
import sys, json
|
|
118
|
+
try:
|
|
119
|
+
d = json.load(sys.stdin)
|
|
120
|
+
ti = d.get('tool_input', {})
|
|
121
|
+
out = ti.get('content') or ti.get('new_string')
|
|
122
|
+
if out is None:
|
|
123
|
+
edits = ti.get('edits', [])
|
|
124
|
+
out = '\n'.join(e.get('new_string', '') for e in edits)
|
|
125
|
+
print(out or '')
|
|
126
|
+
except: pass
|
|
127
|
+
" 2>/dev/null || true)
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
if [[ -z "${CONTENT:-}" ]]; then exit 0; fi
|
|
131
|
+
|
|
132
|
+
# ── Detect hardcoded colour violations ───────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
# 1. CSS/SCSS/Less: colour property with a hex literal value.
|
|
135
|
+
# Excludes lines that start with optional whitespace followed by '--'
|
|
136
|
+
# (those are CSS custom property definitions — they SET the token value).
|
|
137
|
+
CSS_HEX=$(printf '%s' "$CONTENT" \
|
|
138
|
+
| grep -E '(color|background(-color)?|border(-color)?|fill|stroke|outline(-color)?|accent-color)[[:space:]]*:[[:space:]]*#[0-9a-fA-F]{3,8}' \
|
|
139
|
+
| grep -vE '^\s*--' \
|
|
140
|
+
| grep -oE '(color|background(-color)?|border(-color)?|fill|stroke|outline(-color)?|accent-color)[[:space:]]*:[[:space:]]*#[0-9a-fA-F]{3,8}' \
|
|
141
|
+
| head -3 || true)
|
|
142
|
+
|
|
143
|
+
# 2. JSX/TSX: inline style object with a hex colour string.
|
|
144
|
+
# e.g. color: '#fff' or backgroundColor: "#1a2b3c"
|
|
145
|
+
JSX_HEX=$(printf '%s' "$CONTENT" \
|
|
146
|
+
| grep -oE "(color|backgroundColor|borderColor|fill|stroke|outlineColor)[[:space:]]*:[[:space:]]*['\"]#[0-9a-fA-F]{3,8}" \
|
|
147
|
+
| head -3 || true)
|
|
148
|
+
|
|
149
|
+
# 3. HTML: style attribute that contains a hex colour value.
|
|
150
|
+
HTML_HEX=$(printf '%s' "$CONTENT" \
|
|
151
|
+
| grep -oE 'style=[^>]*#[0-9a-fA-F]{3,8}' \
|
|
152
|
+
| head -3 || true)
|
|
153
|
+
|
|
154
|
+
if [[ -z "${CSS_HEX:-}" && -z "${JSX_HEX:-}" && -z "${HTML_HEX:-}" ]]; then
|
|
155
|
+
exit 0
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
# ── Build the violation summary ───────────────────────────────────────────────
|
|
159
|
+
VIOLATIONS=""
|
|
160
|
+
[[ -n "${CSS_HEX:-}" ]] && VIOLATIONS="$VIOLATIONS
|
|
161
|
+
CSS: $(printf '%s' "$CSS_HEX" | head -1)"
|
|
162
|
+
[[ -n "${JSX_HEX:-}" ]] && VIOLATIONS="$VIOLATIONS
|
|
163
|
+
JSX: $(printf '%s' "$JSX_HEX" | head -1)"
|
|
164
|
+
[[ -n "${HTML_HEX:-}" ]] && VIOLATIONS="$VIOLATIONS
|
|
165
|
+
HTML: $(printf '%s' "$HTML_HEX" | head -1)"
|
|
166
|
+
|
|
167
|
+
# ── Read token excerpt for the error message ──────────────────────────────────
|
|
168
|
+
TOKEN_HINT=""
|
|
169
|
+
TOKEN_EXCERPT=$(grep -E '`colour\.' "$COLOUR_FILE" 2>/dev/null | head -12 || true)
|
|
170
|
+
if [[ -n "$TOKEN_EXCERPT" ]]; then
|
|
171
|
+
TOKEN_HINT="
|
|
172
|
+
Available colour tokens (from $(basename "$DS_DIR")/tokens-colour.md):
|
|
173
|
+
$TOKEN_EXCERPT
|
|
174
|
+
|
|
175
|
+
→ Use the token name. Do NOT use the raw hex value."
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# ── Block ─────────────────────────────────────────────────────────────────────
|
|
179
|
+
cat >&2 <<EOF
|
|
180
|
+
✖ nuos: design-system compliance block — hardcoded colour values detected.
|
|
181
|
+
|
|
182
|
+
File: $FILE
|
|
183
|
+
Violations:$VIOLATIONS
|
|
184
|
+
|
|
185
|
+
── Required action ──────────────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
Before writing any UI file you MUST:
|
|
188
|
+
|
|
189
|
+
1. Read the full design system:
|
|
190
|
+
$DS_DIR/tokens-colour.md
|
|
191
|
+
$DS_DIR/tokens-typography.md
|
|
192
|
+
$DS_DIR/tokens-spacing.md
|
|
193
|
+
$DS_DIR/tokens-radius-elevation.md
|
|
194
|
+
|
|
195
|
+
2. Identify how this project references tokens by reading existing UI
|
|
196
|
+
files in the codebase — look for one of these patterns:
|
|
197
|
+
CSS custom properties: color: var(--colour-text-primary);
|
|
198
|
+
JSX theme object: color: theme.colour.text.primary
|
|
199
|
+
Tailwind config tokens: text-text-primary (if configured)
|
|
200
|
+
|
|
201
|
+
3. Replace EVERY hardcoded hex or named colour with the correct token
|
|
202
|
+
reference. If no token covers the value you need, STOP and surface
|
|
203
|
+
the gap to the coordinator — do NOT invent a one-off value.
|
|
204
|
+
|
|
205
|
+
This hook will block every write that contains a raw colour value.
|
|
206
|
+
The design system is the contract; the implementation must honour it.
|
|
207
|
+
$TOKEN_HINT
|
|
208
|
+
|
|
209
|
+
EOF
|
|
210
|
+
|
|
211
|
+
exit 2
|
|
@@ -186,6 +186,7 @@ Every decision made by any agent during the swarm MUST land in the catalogue bef
|
|
|
186
186
|
- **Never let agents make architectural decisions without filing them.** If the coder makes a design call inline, that's a signal — pause, route to the architect, file the decision.
|
|
187
187
|
- **Never run the swarm to completion in the background.** Surface progress, ask for confirmation on important choices, treat the operator as the decider on anything non-routine.
|
|
188
188
|
- **Never use Opus for every agent.** The default routing in `methodfile.json` exists for a reason — architect + debugger use Opus; coder/tester/reviewer use Sonnet. Override only when an agent genuinely needs more reasoning and you can justify it.
|
|
189
|
+
- **Never accept a design brief that contains shortcuts, workarounds, or deferred correctness.** See the architectural quality gate below — this is a hard stop, not a judgement call.
|
|
189
190
|
|
|
190
191
|
---
|
|
191
192
|
|
|
@@ -193,6 +194,25 @@ Every decision made by any agent during the swarm MUST land in the catalogue bef
|
|
|
193
194
|
|
|
194
195
|
Protocol-level discipline (not tooling-enforced). Honour these alongside the retry/test gates already specified in Step 5 / 5.5.
|
|
195
196
|
|
|
197
|
+
### Architectural quality gate (after architect, before coder — mandatory)
|
|
198
|
+
|
|
199
|
+
Before routing the architect's brief to the coder, read it for shortcut indicators. This is a **hard stop** — if any of the following are present, send the brief back to the architect. Do not route to the coder until the brief is clean.
|
|
200
|
+
|
|
201
|
+
**Shortcut red flags — any one of these is a rejection:**
|
|
202
|
+
|
|
203
|
+
- Language: "for now", "temporary", "quick fix", "workaround", "pragmatic", "simplified", "good enough", "we can improve later", "follow-up WU will address"
|
|
204
|
+
- A design that defers security controls, input validation, authorisation, or error handling to "later"
|
|
205
|
+
- A design that acknowledges a known flaw (race condition, missing boundary, incorrect abstraction) without resolving it
|
|
206
|
+
- A single design presented without alternatives evaluated (Pattern N missing)
|
|
207
|
+
- A design chosen explicitly because it is "less work" or "faster to implement" rather than because it is correct
|
|
208
|
+
- Hard-coded values, collapsed module boundaries, or missing contracts "to keep the scope small"
|
|
209
|
+
|
|
210
|
+
**When you find a red flag**, send the brief back to the architect with this instruction (adapt the specific finding):
|
|
211
|
+
|
|
212
|
+
> "The brief contains [quote the specific shortcut language or describe the specific flaw]. This project builds properly — no workarounds, no deferred correctness. Produce the fully-designed solution. If the correct solution requires more scope than this work unit allows, tell me what proper scope looks like and I will surface it to the operator. Do not ship a lesser design."
|
|
213
|
+
|
|
214
|
+
Do not feel time or cost pressure. A proper design that takes longer is always preferred over a shortcut that ships sooner. Routing a shortcut brief to the coder does not save time — it produces code the reviewer will block, and the loop costs more than getting the design right once.
|
|
215
|
+
|
|
196
216
|
- **Time ceiling per agent.** If a run exceeds its rough budget (architect >1h, coder >2h, tester >1h, reviewer >30m), don't kill the agent (loses in-flight work) — surface the duration and ask whether to continue, redirect, or escalate (e.g. coder stuck → debugger).
|
|
197
217
|
- **Architectural drift.** If the coder or tester surfaces a design choice not in the architect's brief, STOP, route to the architect for a decision before re-spawning. Coders making design calls inline is the failure mode the swarm exists to prevent.
|
|
198
218
|
- **Midpoint coherence check** (full-feature swarms). After coder finishes, before tester spawns: are file paths and contracts the architect named present in the coder's output? If misaligned, escalate before spending tester tokens.
|