Skip to content

Commit 730088b

Browse files
ipasechnikovclaude
andcommitted
Speed up check-errors workflow: status/verify-retry/list-preprocessors/wire-preprocessor scripts, richer inspect-error
New scripts under scripts/errors/: - status.sh: concise {id,status,type,error,sender} for a message. - verify-retry.sh: chains check-message-support -> mark-for-retry -> reprocess -> status; aborts when verdict is not "supported". - list-preprocessors.sh: lists registered preprocessor IDs with their JSDoc summary. - wire-preprocessor.ts: idempotent scaffold that ensures the MessageTypeConfig.preprocess slot in src/v2-to-fhir/config.ts and appends the entry to config/hl7v2-to-fhir.json. inspect-error.sh: - For HTTP 422 conversion errors, also prints the current HL7v2 value of every candidate field so the fix is obvious without another --values call. - For code_mapping_error with any observation-code-loinc task, dumps peer OBX rows (code, value, unit, reference range) to help pick the right LOINC from neighbors. check-errors SKILL.md: points at the new scripts, collapses the post-fix step to a single command, and adds a script-reference table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1aff467 commit 730088b

6 files changed

Lines changed: 384 additions & 22 deletions

File tree

.claude/skills/check-errors/SKILL.md

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ scripts/errors/inspect-error.sh <id>
2727

2828
Emits: status, type, sender, full error, unmapped codes (if present), raw HL7v2 saved to `/tmp/hl7v2-<id>.hl7`, and an `hl7v2-inspect` overview for `parsing_error`/`conversion_error`. **You do not need to curl the resource yourself.**
2929

30+
For `HTTP 422` conversion errors the script also prints the **current values** of each candidate HL7v2 field, so you typically don't need an additional `hl7v2-inspect --field` call. For `code_mapping_error` with any `observation-code-loinc` task, peer OBX rows are dumped automatically — use them (code, unit, ref range) to pick the right LOINC.
31+
3032
Pick the playbook below by the `Status` line from Step 2.
3133

3234
## Step 3: Diagnose by status
@@ -70,11 +72,21 @@ Do **not** run `hl7v2-inspect --values` unless the mapping is ambiguous. Diagnos
7072

7173
#### Adding a preprocessor (recipe)
7274

73-
Three files, in order:
75+
If the preprocessor **already exists** (check `scripts/errors/list-preprocessors.sh`), wire it in with one command — edits both config files atomically:
76+
77+
```sh
78+
bun scripts/errors/wire-preprocessor.ts <msgType> <SEG> <FIELD> <preprocessorId> [paramsJson]
79+
80+
# Example:
81+
bun scripts/errors/wire-preprocessor.ts ADT-A01 IN1 12 swap-if-reversed '{"a":12,"b":13}'
82+
```
83+
84+
Idempotent — re-running with the same args is a no-op. The script adds the missing slot to `MessageTypeConfig.preprocess.<SEG>` in `src/v2-to-fhir/config.ts` and the JSON entry under the matching message type.
85+
86+
If a **new** preprocessor function is needed, write it manually first:
7487

7588
1. **`src/v2-to-fhir/preprocessor-registry.ts`** — add key + function to `SEGMENT_PREPROCESSORS`. Function receives the whole segment; the field key in config is only a trigger guard (preprocessor runs when that field is present, except `fallback-rxa3-from-msh7` which runs even when absent).
76-
2. **`src/v2-to-fhir/config.ts`** — add the field slot to `MessageTypeConfig.preprocess.<SEG>` (e.g. `IN1?: { "12"?: SegmentPreprocessorId[] }`).
77-
3. **`config/hl7v2-to-fhir.json`** — add the entry under the relevant message type. Use the `Read` tool for this file — `bun -e` and `python3` fail on JSONC comments.
89+
2. Then run `wire-preprocessor.ts` to wire it in.
7890

7991
### `code_mapping_error` — local code has no FHIR mapping
8092

@@ -115,25 +127,22 @@ Inspect output lists each unmapped code with `localCode`, `localDisplay`, `local
115127
116128
## Step 4: After a fix
117129
118-
1. Verify locally:
119-
```sh
120-
bun scripts/check-message-support.ts /tmp/hl7v2-<id>.hl7
121-
```
122-
Only proceed when verdict is `supported` or `supported with caveats`.
123-
2. Mark for retry (app endpoint, not Aidbox):
124-
```sh
125-
curl -sf -X POST 'http://localhost:3000/mark-for-retry/<id>'
126-
```
127-
3. Trigger reprocessing:
128-
```sh
129-
curl -sf -X POST 'http://localhost:3000/process-incoming-messages'
130-
```
131-
4. Re-check status:
132-
```sh
133-
SECRET=$(awk -F': ' '/^[[:space:]]*BOX_ROOT_CLIENT_SECRET:/ {print $2}' docker-compose.yaml)
134-
curl -sf -u "root:$SECRET" 'http://localhost:8080/fhir/IncomingHL7v2Message/<id>?_elements=id,status,error' | jq
135-
```
136-
5. Report result.
130+
One command chains verify → mark-for-retry → reprocess → status:
131+
132+
```sh
133+
scripts/errors/verify-retry.sh <id>
134+
```
135+
136+
Aborts if `check-message-support.ts` verdict is not `supported`. Status is printed via `scripts/errors/status.sh` (id/status/type/error/sender).
137+
138+
Use the individual commands below only when you need a partial step (e.g. verify without retrying):
139+
140+
```sh
141+
bun scripts/check-message-support.ts /tmp/hl7v2-<id>.hl7
142+
curl -sf -X POST 'http://localhost:3000/mark-for-retry/<id>'
143+
curl -sf -X POST 'http://localhost:3000/process-incoming-messages'
144+
scripts/errors/status.sh <id>
145+
```
137146
138147
## Rules
139148
@@ -142,3 +151,14 @@ Inspect output lists each unmapped code with `localCode`, `localDisplay`, `local
142151
- Skip `deferred` rows in the summary unless the user asks about them.
143152
- Don't hand-count pipes — the inspect script already ran `hl7v2-inspect.sh`. For deeper field lookup use `scripts/hl7v2-inspect.sh --field SEG.N`.
144153
- Use the `hl7v2-info` skill to verify HL7v2 spec compliance when needed.
154+
155+
## Script reference
156+
157+
| Script | Purpose |
158+
|---|---|
159+
| `scripts/errors/list-errors.sh` | Active errors + deferred reminder (markdown table). |
160+
| `scripts/errors/inspect-error.sh <id>` | Full diagnosis: resource fields, saved raw file, hl7v2-inspect overview, 422 candidates w/ values, peer OBX for LOINC tasks. |
161+
| `scripts/errors/status.sh <id>` | Concise `{id,status,type,error,sender}` for a single message. |
162+
| `scripts/errors/verify-retry.sh <id>` | After a fix: verify → mark-for-retry → reprocess → status. Aborts if not supported. |
163+
| `scripts/errors/list-preprocessors.sh` | Available preprocessors w/ JSDoc summary. |
164+
| `scripts/errors/wire-preprocessor.ts` | Wire a preprocessor into `config.ts` + `hl7v2-to-fhir.json` atomically. Idempotent. |

scripts/errors/inspect-error.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,23 @@ if [ "$STATUS" = "conversion_error" ] && printf '%s' "$ERROR_TEXT" | grep -q 'HT
168168
if [ -n "$CANDIDATES" ]; then
169169
echo " HL7v2 source candidates:"
170170
printf '%s\n' "$CANDIDATES" | sed 's/^/ - /'
171+
# Print current values for each candidate field so the fix is obvious
172+
# without another hl7v2-inspect call.
173+
FIELDS_TO_SHOW=$(printf '%s\n' "$CANDIDATES" | awk '{print $1}' | sort -u)
174+
if [ -n "$FIELDS_TO_SHOW" ]; then
175+
echo " Current values:"
176+
printf '%s\n' "$FIELDS_TO_SHOW" | while IFS= read -r FLD; do
177+
[ -z "$FLD" ] && continue
178+
DOTTED=$(printf '%s' "$FLD" | tr '-' '.')
179+
VAL_LINE=$("$PROJECT_DIR/scripts/hl7v2-inspect.sh" "$TMP" --field "$DOTTED" --values 2>/dev/null \
180+
| grep -E "^\s+$FLD:" | head -1 | sed 's/^[[:space:]]*//')
181+
if [ -n "$VAL_LINE" ]; then
182+
echo " - $VAL_LINE"
183+
else
184+
echo " - $FLD: (empty)"
185+
fi
186+
done
187+
fi
171188
else
172189
echo " (no matching row in IG segment CSVs for path \`$PATH_TAIL\`)"
173190
fi
@@ -178,3 +195,25 @@ if [ "$STATUS" = "conversion_error" ] && printf '%s' "$ERROR_TEXT" | grep -q 'HT
178195
fi
179196
fi
180197
fi
198+
199+
# For code_mapping_error: when any unmapped code is observation-code-loinc,
200+
# dump peer OBX rows (code + value + unit + ref range) so the reviewer can
201+
# pick the right LOINC from neighbors without another hl7v2-inspect call.
202+
if [ "$STATUS" = "code_mapping_error" ] && [ "$UNMAPPED" != "[]" ]; then
203+
HAS_LOINC=$(printf '%s' "$JSON" | jq -r '
204+
. as $root |
205+
[.unmappedCodes[]? |
206+
. as $u |
207+
(($u.mappingTask.reference // "") | sub("^Task/"; "")) as $tid |
208+
([$root.entries[]? | select(.resourceType=="Task" and .id==$tid)] | first) as $t |
209+
($t.code.coding[0].code // "")
210+
] | any(. == "observation-code-loinc")
211+
' 2>/dev/null || echo "false")
212+
if [ "$HAS_LOINC" = "true" ]; then
213+
echo
214+
echo "### Peer OBX context (pick LOINC from neighbors + units)"
215+
echo '```'
216+
"$PROJECT_DIR/scripts/hl7v2-inspect.sh" "$TMP" --segment OBX --values 2>&1 || true
217+
echo '```'
218+
fi
219+
fi
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/bin/bash
2+
# List available HL7v2 segment preprocessors (by ID) with the first line
3+
# of the JSDoc above each implementation, so the check-errors skill can
4+
# pick the right preprocessor without grepping the registry.
5+
#
6+
# Usage: scripts/errors/list-preprocessors.sh
7+
set -e
8+
9+
PROJECT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
10+
REG="$PROJECT_DIR/src/v2-to-fhir/preprocessor-registry.ts"
11+
12+
if [ ! -f "$REG" ]; then
13+
echo "ERROR: $REG not found" >&2
14+
exit 1
15+
fi
16+
17+
awk '
18+
# Phase 1: collect id → function-name mapping from SEGMENT_PREPROCESSORS literal.
19+
/^export const SEGMENT_PREPROCESSORS/ { in_map = 1; next }
20+
in_map && /^\};/ { in_map = 0; next }
21+
in_map {
22+
if (match($0, /"([a-z0-9-]+)":[[:space:]]+([a-zA-Z0-9_]+)/, m)) {
23+
fn2id[m[2]] = m[1]
24+
order[++n] = m[1]
25+
}
26+
}
27+
28+
# Phase 2: capture JSDoc + function-name pairs anywhere in the file.
29+
/^\/\*\*/ { doc = ""; in_doc = 1; next }
30+
in_doc && /\*\// { in_doc = 0; next }
31+
in_doc {
32+
line = $0
33+
sub(/^[[:space:]]*\*[[:space:]]?/, "", line)
34+
if (line != "" && doc == "") doc = line
35+
next
36+
}
37+
/^function [a-zA-Z0-9_]+\(/ {
38+
if (match($0, /^function ([a-zA-Z0-9_]+)\(/, m)) {
39+
if (m[1] in fn2id) {
40+
id = fn2id[m[1]]
41+
desc[id] = doc
42+
}
43+
doc = ""
44+
}
45+
}
46+
47+
END {
48+
for (i = 1; i <= n; i++) {
49+
id = order[i]
50+
d = (id in desc) ? desc[id] : ""
51+
if (d == "") printf "- %s\n", id
52+
else printf "- %s — %s\n", id, d
53+
}
54+
}
55+
' "$REG"

scripts/errors/status.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
# Print status, error, and meta for one IncomingHL7v2Message.
3+
# Centralizes the docker-compose secret parsing + curl + jq boilerplate
4+
# that appears in every check-errors follow-up.
5+
#
6+
# Usage: scripts/errors/status.sh <message-id>
7+
set -e
8+
9+
if [ -z "$1" ]; then
10+
echo "Usage: $0 <message-id>" >&2
11+
exit 2
12+
fi
13+
ID="$1"
14+
15+
PROJECT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
16+
SECRET=$(awk -F': ' '/^[[:space:]]*BOX_ROOT_CLIENT_SECRET:/ {print $2}' "$PROJECT_DIR/docker-compose.yaml")
17+
if [ -z "$SECRET" ]; then
18+
echo "ERROR: BOX_ROOT_CLIENT_SECRET missing from docker-compose.yaml" >&2
19+
exit 1
20+
fi
21+
22+
curl -sf -u "root:$SECRET" \
23+
"http://localhost:8080/fhir/IncomingHL7v2Message/$ID" \
24+
| jq '{id, status, type, error, sender: ((.sendingApplication // "") + "/" + (.sendingFacility // ""))}'

scripts/errors/verify-retry.sh

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/bin/bash
2+
# After a fix, verify the message converts cleanly, then mark for retry,
3+
# trigger reprocessing, and poll status. Chains the typical 3-4 commands
4+
# into one call so a fix → verify → retry cycle is a single step.
5+
#
6+
# Requires: scripts/errors/inspect-error.sh <id> has been run first so the
7+
# raw HL7v2 lives at /tmp/hl7v2-<id>.hl7.
8+
#
9+
# Usage: scripts/errors/verify-retry.sh <message-id>
10+
set -e
11+
12+
if [ -z "$1" ]; then
13+
echo "Usage: $0 <message-id>" >&2
14+
exit 2
15+
fi
16+
ID="$1"
17+
18+
PROJECT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
19+
FIXTURE="/tmp/hl7v2-$ID.hl7"
20+
if [ ! -f "$FIXTURE" ]; then
21+
echo "ERROR: $FIXTURE missing. Run scripts/errors/inspect-error.sh $ID first." >&2
22+
exit 1
23+
fi
24+
25+
echo "### Verify"
26+
VERIFY_OUT=$(bun "$PROJECT_DIR/scripts/check-message-support.ts" "$FIXTURE")
27+
echo "$VERIFY_OUT"
28+
if ! printf '%s\n' "$VERIFY_OUT" | grep -qiE '^Verdict: +supported'; then
29+
echo
30+
echo "### Aborted — verdict is not 'supported'. Fix before retrying." >&2
31+
exit 1
32+
fi
33+
34+
echo
35+
echo "### Retry"
36+
curl -sf -X POST "http://localhost:3000/mark-for-retry/$ID"
37+
curl -sf -X POST 'http://localhost:3000/process-incoming-messages'
38+
echo "retry triggered"
39+
echo
40+
41+
echo "### Status"
42+
"$PROJECT_DIR/scripts/errors/status.sh" "$ID"

0 commit comments

Comments
 (0)