@sdd-method/sdd-cli 0.110.2 → 0.111.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/dist/lib/sync/adr-transitions.d.ts +43 -0
- package/dist/lib/sync/adr-transitions.d.ts.map +1 -0
- package/dist/lib/sync/adr-transitions.js +177 -0
- package/dist/lib/sync/adr-transitions.js.map +1 -0
- package/dist/lib/sync/index.d.ts.map +1 -1
- package/dist/lib/sync/index.js +19 -1
- package/dist/lib/sync/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/README.md +37 -11
- package/scripts/sync-vendored-script.sh +65 -25
- package/scripts/validate-app-dependencies.sh +43 -8
- package/scripts/validate-integration-docs.sh +182 -0
- package/scripts/sync-method-baseline.sh +0 -660
package/scripts/README.md
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
# Bundled upstream scripts
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[`sdd-method`](https://github.com/tomfosteruk/sdd-method) that
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Copies of bash scripts from
|
|
4
|
+
[`sdd-method`](https://github.com/tomfosteruk/sdd-method) that `sdd-cli` shells
|
|
5
|
+
out to at runtime (`validate-*`). These SHIP in the npm package, because the
|
|
6
|
+
`sdd-cli validate` dispatcher spawns them.
|
|
7
|
+
|
|
8
|
+
⚠ `sync-method-baseline.sh` is no longer here. It was never shelled out to — it is
|
|
9
|
+
a parity oracle — and it now lives at `tests/parity/fixtures/`, which is where its
|
|
10
|
+
only consumer is and, crucially, is not published. While it sat in this directory it
|
|
11
|
+
shipped to adopters, and it had drifted 386 lines behind upstream: it wrote a lock
|
|
12
|
+
with a single bundle entry, which would have dropped every other bundle's entry on
|
|
13
|
+
the 97-of-105 adopter locks that are multi-bundle. Adopters receive the
|
|
14
|
+
authoritative script in `platform-baseline`; nothing needed a copy here.
|
|
8
15
|
|
|
9
16
|
Files in this directory are **byte-identical** to their upstream source. Do
|
|
10
17
|
not edit here — edit upstream, then run the refresh procedure below to copy
|
|
@@ -13,9 +20,13 @@ signal.
|
|
|
13
20
|
|
|
14
21
|
## Provenance
|
|
15
22
|
|
|
23
|
+
⚠ **These SHAs are historical.** Byte-identity is now asserted by
|
|
24
|
+
`tests/parity/vendored-script-drift.test.ts` against the sibling sdd-method checkout,
|
|
25
|
+
so the pin is the test rather than this table. The table is retained only as a record
|
|
26
|
+
of where each file came from.
|
|
27
|
+
|
|
16
28
|
| File | Upstream path | Pinned to |
|
|
17
29
|
|---|---|---|
|
|
18
|
-
| `sync-method-baseline.sh` | `orchestration/scripts/sync-method-baseline.sh` | `4d80826` |
|
|
19
30
|
| `validate-app-docs.sh` | `orchestration/scripts/validate-app-docs.sh` | `db4cb25` |
|
|
20
31
|
| `validate-app-dependencies.sh` | `orchestration/scripts/validate-app-dependencies.sh` | `db4cb25` |
|
|
21
32
|
| `validate-integration-docs.sh` | `orchestration/scripts/validate-integration-docs.sh` | `db4cb25` |
|
|
@@ -34,14 +45,29 @@ cp ../sdd-method/orchestration/scripts/<script>.sh scripts/
|
|
|
34
45
|
# Then update the SHA in this README and commit both together.
|
|
35
46
|
```
|
|
36
47
|
|
|
37
|
-
|
|
38
|
-
|
|
48
|
+
⚠ **Manual-and-visible turned out to be manual-and-invisible**: this README mandated byte-identity ("do not edit here …
|
|
49
|
+
byte-identity keeps `diff` usable as a drift-detection signal") while its Lifecycle
|
|
50
|
+
section simultaneously described the sync oracle as "frozen at 4a8cd05" — and the
|
|
51
|
+
provenance table pinned a *different* commit. Either reading was defensible, so a
|
|
52
|
+
386-line drift looked intentional to anyone who checked. The parity oracle now has
|
|
53
|
+
the automation this section called "a later milestone":
|
|
54
|
+
`scripts/sync-vendored-script.sh` plus `tests/parity/vendored-script-drift.test.ts`,
|
|
55
|
+
which fails with a re-sync instruction when any copy differs.
|
|
56
|
+
|
|
57
|
+
**All four files are now covered**, which mattered: when the guard was generalised it
|
|
58
|
+
immediately found that two of the three validators had ALSO drifted —
|
|
59
|
+
`validate-app-dependencies.sh` by 35 lines and `validate-integration-docs.sh` by 182,
|
|
60
|
+
the latter missing an entire check family. Those files are executed by
|
|
61
|
+
`sdd-cli validate`, so adopters had been running weaker validation than the bundle
|
|
62
|
+
provides. Re-synced.
|
|
39
63
|
|
|
40
64
|
## Lifecycle
|
|
41
65
|
|
|
42
|
-
The `sync-method-baseline.sh` oracle
|
|
43
|
-
|
|
44
|
-
|
|
66
|
+
The `sync-method-baseline.sh` oracle stays for regression testing — the native TS
|
|
67
|
+
sync is compared against it in `tests/parity/` — but it is tracked at upstream HEAD,
|
|
68
|
+
not frozen. Frozen was the wrong model: parity against a frozen oracle stops testing
|
|
69
|
+
the CLI against what adopters run and starts testing it against history, which is how
|
|
70
|
+
a real functional gap (the CLI not applying ADR transitions) stayed invisible.
|
|
45
71
|
|
|
46
72
|
The `validate-*.sh` scripts are what `sdd-cli validate` spawns. Native TS
|
|
47
73
|
reimplementation of individual validators is v0.3.0+, smallest-first
|
|
@@ -1,43 +1,83 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Sync
|
|
3
|
-
#
|
|
4
|
-
#
|
|
2
|
+
# Sync every vendored bash script from sdd-method into this repo. Mirrors
|
|
3
|
+
# scripts/sync-canonical-schema.sh: direction of truth is one-way,
|
|
4
|
+
# sdd-method -> vendored, and a vendored copy is never hand-edited.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
6
|
+
# FOUR FILES, TWO DESTINATIONS, and the distinction matters:
|
|
7
|
+
#
|
|
8
|
+
# scripts/validate-*.sh SHIP in the npm package, and the CLI shells out to
|
|
9
|
+
# them at runtime via the `sdd-cli validate`
|
|
10
|
+
# dispatcher. A stale copy here means adopters run
|
|
11
|
+
# weaker validation than the bundle provides — and
|
|
12
|
+
# two of the three were found 35 and 182 lines behind,
|
|
13
|
+
# the latter missing an entire check family.
|
|
14
|
+
#
|
|
15
|
+
# tests/parity/fixtures/ the sync ORACLE, kept only so tests/parity can
|
|
16
|
+
# sync-method-baseline.sh compare the TS implementation against the reference.
|
|
17
|
+
# Deliberately NOT in scripts/, because that directory
|
|
18
|
+
# ships: while it lived there it was 386 lines stale
|
|
19
|
+
# and would have written a single-bundle lock,
|
|
20
|
+
# dropping entries from the 97-of-105 adopter locks
|
|
21
|
+
# that are multi-bundle. Adopters receive the
|
|
22
|
+
# authoritative script in platform-baseline.
|
|
23
|
+
#
|
|
24
|
+
# `scripts/README.md` pinned each file to an upstream SHA by hand and called refresh
|
|
25
|
+
# "manual and visible". It was manual and INVISIBLE: three of four copies had drifted.
|
|
26
|
+
# This script plus tests/parity/vendored-script-drift.test.ts replace that.
|
|
12
27
|
#
|
|
13
28
|
# Usage:
|
|
14
29
|
# scripts/sync-vendored-script.sh # from $SDD_METHOD_PATH or ~/Projects/method/sdd-method
|
|
15
30
|
# scripts/sync-vendored-script.sh /path/to/sdd-method # explicit path
|
|
16
31
|
#
|
|
17
|
-
# After syncing
|
|
18
|
-
# the
|
|
32
|
+
# After syncing run `npx vitest run tests/parity` — a diff there is a real finding
|
|
33
|
+
# about the TS implementation, not a fixture to adjust.
|
|
19
34
|
|
|
20
35
|
set -euo pipefail
|
|
21
36
|
|
|
22
37
|
SDD_METHOD_PATH="${1:-${SDD_METHOD_PATH:-$HOME/Projects/method/sdd-method}}"
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
SRC_DIR="$SDD_METHOD_PATH/orchestration/scripts"
|
|
39
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
25
40
|
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
# name:destination-relative-to-repo-root
|
|
42
|
+
VENDORED=(
|
|
43
|
+
"sync-method-baseline.sh:tests/parity/fixtures"
|
|
44
|
+
"validate-app-docs.sh:scripts"
|
|
45
|
+
"validate-app-dependencies.sh:scripts"
|
|
46
|
+
"validate-integration-docs.sh:scripts"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if [[ ! -d "$SRC_DIR" ]]; then
|
|
50
|
+
echo "ERROR: sdd-method scripts not found at $SRC_DIR" >&2
|
|
28
51
|
echo " Pass the sdd-method repo path as the first argument." >&2
|
|
29
52
|
exit 1
|
|
30
53
|
fi
|
|
31
54
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
55
|
+
changed=0
|
|
56
|
+
for entry in "${VENDORED[@]}"; do
|
|
57
|
+
name="${entry%%:*}"
|
|
58
|
+
dest_dir="${entry#*:}"
|
|
59
|
+
src="$SRC_DIR/$name"
|
|
60
|
+
dest="$HERE/../$dest_dir/$name"
|
|
36
61
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
62
|
+
if [[ ! -f "$src" ]]; then
|
|
63
|
+
echo "ERROR: authoritative script missing: $src" >&2
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
if cmp -s "$src" "$dest"; then
|
|
67
|
+
printf ' ok %-32s (%s lines)\n' "$name" "$(wc -l < "$dest" | tr -d ' ')"
|
|
68
|
+
continue
|
|
69
|
+
fi
|
|
70
|
+
old=0
|
|
71
|
+
[[ -f "$dest" ]] && old=$(wc -l < "$dest" | tr -d ' ')
|
|
72
|
+
mkdir -p "$(dirname "$dest")"
|
|
73
|
+
cp "$src" "$dest"
|
|
74
|
+
printf ' SYNCED %-32s %s -> %s lines\n' "$name" "$old" "$(wc -l < "$dest" | tr -d ' ')"
|
|
75
|
+
changed=$((changed + 1))
|
|
76
|
+
done
|
|
41
77
|
|
|
42
|
-
echo
|
|
43
|
-
|
|
78
|
+
echo
|
|
79
|
+
if [[ "$changed" -gt 0 ]]; then
|
|
80
|
+
echo "$changed file(s) re-synced. Run: npx vitest run tests/parity"
|
|
81
|
+
else
|
|
82
|
+
echo "All vendored scripts already match sdd-method."
|
|
83
|
+
fi
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
#
|
|
8
8
|
# Checks (for each entry in app-platform-dependencies.yaml):
|
|
9
9
|
# 1. required_stability is a valid value (provisional | locked)
|
|
10
|
-
# 2. service_id
|
|
10
|
+
# 2. service_id resolves against the platform source of truth:
|
|
11
|
+
# catalogues/services.csv (exact id match) when the catalogue exists,
|
|
12
|
+
# else docs/platform/service-architecture.md (word-anchored match)
|
|
11
13
|
# 3. contract_path exists (as a file) when required_stability is "locked"
|
|
12
14
|
#
|
|
13
15
|
# Outputs a dependency status table.
|
|
@@ -43,7 +45,8 @@ Usage: $(basename "$0") --app-root <path> --platform-root <path>
|
|
|
43
45
|
|
|
44
46
|
Options:
|
|
45
47
|
--app-root Path to the app repository root (must contain app-platform-dependencies.yaml)
|
|
46
|
-
--platform-root Path to the platform repository root (
|
|
48
|
+
--platform-root Path to the platform repository root (service_id checks use
|
|
49
|
+
catalogues/services.csv when present, else docs/platform/service-architecture.md)
|
|
47
50
|
USAGE
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -93,6 +96,7 @@ fi
|
|
|
93
96
|
|
|
94
97
|
APP_DEPS_FILE="$APP_ROOT/app-platform-dependencies.yaml"
|
|
95
98
|
PLATFORM_SERVICE_ARCH="$PLATFORM_ROOT/docs/platform/service-architecture.md"
|
|
99
|
+
PLATFORM_SERVICES_CSV="$PLATFORM_ROOT/catalogues/services.csv"
|
|
96
100
|
|
|
97
101
|
VALID_STABILITY_VALUES=("provisional" "locked")
|
|
98
102
|
|
|
@@ -117,13 +121,33 @@ check_prerequisites() {
|
|
|
117
121
|
fi
|
|
118
122
|
log_pass "yq available"
|
|
119
123
|
|
|
124
|
+
if [[ -f "$PLATFORM_SERVICES_CSV" ]]; then
|
|
125
|
+
log_pass "Platform services catalogue found (canonical service_id source)"
|
|
126
|
+
else
|
|
127
|
+
PLATFORM_SERVICES_CSV=""
|
|
128
|
+
fi
|
|
129
|
+
|
|
120
130
|
if [[ ! -f "$PLATFORM_SERVICE_ARCH" ]]; then
|
|
121
131
|
log_warn "Platform service-architecture.md not found: $PLATFORM_SERVICE_ARCH"
|
|
122
|
-
log_warn "service_id validation will be skipped"
|
|
123
132
|
PLATFORM_SERVICE_ARCH=""
|
|
124
133
|
else
|
|
125
134
|
log_pass "Platform service-architecture.md found"
|
|
126
135
|
fi
|
|
136
|
+
|
|
137
|
+
if [[ -z "$PLATFORM_SERVICES_CSV" && -z "$PLATFORM_SERVICE_ARCH" ]]; then
|
|
138
|
+
log_warn "Neither services catalogue nor service-architecture.md found — service_id validation will be skipped"
|
|
139
|
+
fi
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Word-anchored match: the id must not be a substring of a longer slug token.
|
|
143
|
+
service_id_in_doc() {
|
|
144
|
+
local service_id="$1"
|
|
145
|
+
grep -qE "(^|[^a-zA-Z0-9-])${service_id}([^a-zA-Z0-9-]|$)" "$PLATFORM_SERVICE_ARCH" 2>/dev/null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
service_id_in_catalogue() {
|
|
149
|
+
local service_id="$1"
|
|
150
|
+
awk -F',' -v id="$service_id" 'NR>1 && $1==id {found=1} END {exit !found}' "$PLATFORM_SERVICES_CSV" 2>/dev/null
|
|
127
151
|
}
|
|
128
152
|
|
|
129
153
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -158,12 +182,23 @@ validate_dependency() {
|
|
|
158
182
|
((dep_errors++))
|
|
159
183
|
fi
|
|
160
184
|
|
|
161
|
-
# Check 2: service_id
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
185
|
+
# Check 2: service_id resolves against the platform source of truth.
|
|
186
|
+
# The services catalogue (when present) is canonical — it is the same source
|
|
187
|
+
# the aggregator FK-validates dependency edges against. The prose
|
|
188
|
+
# service-architecture.md is the fallback for pre-catalogue platforms.
|
|
189
|
+
if [[ -z "$service_id" || "$service_id" == "null" ]]; then
|
|
190
|
+
log_fail "$dep_label: service_id is empty or missing"
|
|
191
|
+
((dep_errors++))
|
|
192
|
+
elif [[ -n "$PLATFORM_SERVICES_CSV" ]]; then
|
|
193
|
+
if ! service_id_in_catalogue "$service_id"; then
|
|
194
|
+
log_fail "$dep_label: service_id '$service_id' not found in platform services catalogue (catalogues/services.csv)"
|
|
165
195
|
((dep_errors++))
|
|
166
|
-
elif
|
|
196
|
+
elif [[ -n "$PLATFORM_SERVICE_ARCH" ]] && ! service_id_in_doc "$service_id"; then
|
|
197
|
+
log_warn "$dep_label: service_id '$service_id' is in the services catalogue but not mentioned in service-architecture.md (doc drift)"
|
|
198
|
+
((WARNINGS++))
|
|
199
|
+
fi
|
|
200
|
+
elif [[ -n "$PLATFORM_SERVICE_ARCH" ]]; then
|
|
201
|
+
if ! service_id_in_doc "$service_id"; then
|
|
167
202
|
log_fail "$dep_label: service_id '$service_id' not found in platform service-architecture.md"
|
|
168
203
|
((dep_errors++))
|
|
169
204
|
fi
|
|
@@ -159,6 +159,181 @@ check_manifest() {
|
|
|
159
159
|
*)
|
|
160
160
|
;;
|
|
161
161
|
esac
|
|
162
|
+
|
|
163
|
+
# Interaction patterns (ADR 0171) — optional fields, validated only when present.
|
|
164
|
+
# Existing manifests predate the field; absence is not a failure. Directionality
|
|
165
|
+
# mismatch (pattern present but the matching capability flag is false) is a warning.
|
|
166
|
+
local ingestion invocation
|
|
167
|
+
ingestion="$(yq e '.ingestionPattern // ""' "$manifest")"
|
|
168
|
+
invocation="$(yq e '.invocationPattern // ""' "$manifest")"
|
|
169
|
+
|
|
170
|
+
if [[ -n "$ingestion" && "$ingestion" != "null" ]]; then
|
|
171
|
+
case "$ingestion" in
|
|
172
|
+
event-stream-consume|webhook|socket-listen|poll)
|
|
173
|
+
log_pass "integration-manifest.yaml: ingestionPattern '$ingestion' valid"
|
|
174
|
+
# if/fi for the same reason as the invocationPattern warn below:
|
|
175
|
+
# safe only because more code follows, which is not a property
|
|
176
|
+
# worth relying on.
|
|
177
|
+
if [[ "$inbound" != "true" ]]; then
|
|
178
|
+
log_warn "integration-manifest.yaml: ingestionPattern set but capabilityFlags.inbound is not true"
|
|
179
|
+
fi
|
|
180
|
+
;;
|
|
181
|
+
*)
|
|
182
|
+
log_fail "integration-manifest.yaml: ingestionPattern '$ingestion' not in {event-stream-consume, webhook, socket-listen, poll}"
|
|
183
|
+
FAILURES=$((FAILURES + 1))
|
|
184
|
+
;;
|
|
185
|
+
esac
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
if [[ -n "$invocation" && "$invocation" != "null" ]]; then
|
|
189
|
+
case "$invocation" in
|
|
190
|
+
request-response|async-request|publish)
|
|
191
|
+
log_pass "integration-manifest.yaml: invocationPattern '$invocation' valid"
|
|
192
|
+
# Explicit if/fi, NOT `[[ ... ]] && log_warn`. As the last
|
|
193
|
+
# statement in this function, a false test made the function
|
|
194
|
+
# return 1, and under `set -e` that aborted the whole run before
|
|
195
|
+
# Group 4 — so the check fired on the CORRECT case (a manifest
|
|
196
|
+
# that declares invocationPattern and properly sets
|
|
197
|
+
# outbound: true), and the better-formed the manifest, the more
|
|
198
|
+
# certainly its grounding evidence went unchecked.
|
|
199
|
+
if [[ "$outbound" != "true" ]]; then
|
|
200
|
+
log_warn "integration-manifest.yaml: invocationPattern set but capabilityFlags.outbound is not true"
|
|
201
|
+
fi
|
|
202
|
+
;;
|
|
203
|
+
*)
|
|
204
|
+
log_fail "integration-manifest.yaml: invocationPattern '$invocation' not in {request-response, async-request, publish}"
|
|
205
|
+
FAILURES=$((FAILURES + 1))
|
|
206
|
+
;;
|
|
207
|
+
esac
|
|
208
|
+
fi
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# ---------------------------------------------------------------------------
|
|
212
|
+
# Unedited-scaffold-residue lint (WARN-only).
|
|
213
|
+
#
|
|
214
|
+
# WHAT THIS MEASURES, and what it deliberately does not: residue measures
|
|
215
|
+
# **authoring completeness** — how much of the scaffold is still untouched. It is
|
|
216
|
+
# NOT a signal for whether an SDD models a real provider. Those are independent
|
|
217
|
+
# properties, measured to be so: on one estate 5 SDDs are demonstrations and 11
|
|
218
|
+
# carry residue, because six REAL integrations are simply not authored yet. A
|
|
219
|
+
# residue list is therefore an authoring WORK QUEUE, never a census of
|
|
220
|
+
# demonstrations. Do not re-couple it to the `exemplar` trait.
|
|
221
|
+
#
|
|
222
|
+
# THE TEMPLATE IS THE ORACLE. Residue is defined as template text that survived
|
|
223
|
+
# into the authored document, so the shipped template is read at runtime and the
|
|
224
|
+
# comparison is exact. This matters for two reasons:
|
|
225
|
+
# 1. It is self-maintaining. An earlier attempt at this check hardcoded a list
|
|
226
|
+
# of placeholder strings; the list silently went stale, so tokens from other
|
|
227
|
+
# templates read as real content.
|
|
228
|
+
# 2. It separates residue from a brace that is legitimately authored. A JSON
|
|
229
|
+
# example containing `{` is not residue; the exact string `{provider field}`
|
|
230
|
+
# is, because the template is where it comes from.
|
|
231
|
+
#
|
|
232
|
+
# WHAT IS LEGITIMATELY RETAINED — state this half or the rule flags correct
|
|
233
|
+
# authoring. Template text kept as STRUCTURE is right and must never be reported:
|
|
234
|
+
# - section headings (`^#`) — a cohort of SDDs sharing one standard deliberately
|
|
235
|
+
# keeps the method's section shape while filling it with real content, and that
|
|
236
|
+
# is exactly correct authoring;
|
|
237
|
+
# - table header and separator rows (`^|`);
|
|
238
|
+
# - short shared lines — a bare path, a field label — dropped by a length floor.
|
|
239
|
+
# Residue is template text left IN THE SLOTS, never template text AS STRUCTURE.
|
|
240
|
+
#
|
|
241
|
+
# THREE SHAPES OF RESIDUE, and only two are detectable:
|
|
242
|
+
# 1. Unfilled placeholder tokens — detected (exact token match).
|
|
243
|
+
# 2. Leftover instruction prose — detected (exact line match).
|
|
244
|
+
# 3. Unmodified example table rows — NOT ATTEMPTED, on evidence rather than
|
|
245
|
+
# difficulty: every placeholder-free table row in the shipped templates is a
|
|
246
|
+
# table HEADER row, which an authored document is supposed to retain. Rows
|
|
247
|
+
# that do carry placeholders are already caught by shape 1. So verbatim-row
|
|
248
|
+
# matching would flag precisely the lines that should survive and add no
|
|
249
|
+
# signal that shape 1 misses.
|
|
250
|
+
# ---------------------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
# Templates that may reach an integration SDD (forward via integration-baseline,
|
|
253
|
+
# reverse via reverse-extension-baseline). Whichever are present are used.
|
|
254
|
+
GROUNDING_TEMPLATES=(
|
|
255
|
+
"docs/method/methods/templates/feature-grounding-integration-template.md"
|
|
256
|
+
"docs/method/methods/reverse/templates/integration-product/docs/design/integration/feature-grounding.md"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Cap on reported items per class — an untouched scaffold would otherwise bury
|
|
260
|
+
# the rest of the run. The count is always reported in full, per no-silent-caps.
|
|
261
|
+
RESIDUE_REPORT_CAP=5
|
|
262
|
+
|
|
263
|
+
check_grounding_residue() {
|
|
264
|
+
local grounding="$1"
|
|
265
|
+
local rel="docs/design/integration/feature-grounding.md"
|
|
266
|
+
|
|
267
|
+
local -a templates=()
|
|
268
|
+
local t
|
|
269
|
+
for t in "${GROUNDING_TEMPLATES[@]}"; do
|
|
270
|
+
[[ -f "$REPO_ROOT/$t" ]] && templates+=("$REPO_ROOT/$t")
|
|
271
|
+
done
|
|
272
|
+
|
|
273
|
+
# Never pass silently for want of an oracle: "no residue found" and "could
|
|
274
|
+
# not look" must not be the same output.
|
|
275
|
+
if [[ ${#templates[@]} -eq 0 ]]; then
|
|
276
|
+
log_warn "$rel: cannot check scaffold residue — no grounding template present in this repo (expected one of: ${GROUNDING_TEMPLATES[*]})"
|
|
277
|
+
return
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
# --- Shape 1: unfilled placeholder tokens -------------------------------
|
|
281
|
+
# `|| true` on every match pipeline below: grep exits 1 on no-match, and
|
|
282
|
+
# under `set -euo pipefail` that aborts the run at the assignment. "Found
|
|
283
|
+
# nothing" is the expected result for a well-authored document, so it must not
|
|
284
|
+
# be fatal — this is the same trap that was silently killing Group 4.
|
|
285
|
+
local tokens
|
|
286
|
+
tokens="$(grep -ohE '\{[^}]{1,80}\}' "${templates[@]}" 2>/dev/null | sort -u || true)"
|
|
287
|
+
|
|
288
|
+
local -a hits=()
|
|
289
|
+
local token
|
|
290
|
+
while IFS= read -r token; do
|
|
291
|
+
[[ -z "$token" ]] && continue
|
|
292
|
+
local lines
|
|
293
|
+
lines="$(grep -Fn -- "$token" "$grounding" 2>/dev/null | cut -d: -f1 | paste -sd, - || true)"
|
|
294
|
+
[[ -n "$lines" ]] && hits+=("line ${lines}: ${token}")
|
|
295
|
+
done <<< "$tokens"
|
|
296
|
+
|
|
297
|
+
if [[ ${#hits[@]} -gt 0 ]]; then
|
|
298
|
+
log_warn "$rel: ${#hits[@]} unfilled placeholder token(s) from the shipped template — authoring incomplete (not a demonstration marker)"
|
|
299
|
+
local i
|
|
300
|
+
for ((i = 0; i < ${#hits[@]} && i < RESIDUE_REPORT_CAP; i++)); do
|
|
301
|
+
echo " - ${hits[$i]}"
|
|
302
|
+
done
|
|
303
|
+
if [[ ${#hits[@]} -gt $RESIDUE_REPORT_CAP ]]; then
|
|
304
|
+
echo " - … and $(( ${#hits[@]} - RESIDUE_REPORT_CAP )) more"
|
|
305
|
+
fi
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
# --- Shape 2: leftover template instruction prose -----------------------
|
|
309
|
+
# Prose only: headings and table rows are structure an authored document
|
|
310
|
+
# legitimately keeps. The length floor drops short shared lines (a bare path
|
|
311
|
+
# or a field label) that carry no instruction and would false-positive.
|
|
312
|
+
local -a prose_hits=()
|
|
313
|
+
local line
|
|
314
|
+
while IFS= read -r line; do
|
|
315
|
+
[[ ${#line} -lt 60 ]] && continue
|
|
316
|
+
if grep -Fxq -- "$line" "$grounding" 2>/dev/null; then
|
|
317
|
+
prose_hits+=("$line")
|
|
318
|
+
fi
|
|
319
|
+
done < <(grep -hvE '^#|^\||^$|^[[:space:]]*$' "${templates[@]}" 2>/dev/null | sort -u || true)
|
|
320
|
+
|
|
321
|
+
if [[ ${#prose_hits[@]} -gt 0 ]]; then
|
|
322
|
+
log_warn "$rel: ${#prose_hits[@]} line(s) of template instruction prose retained verbatim"
|
|
323
|
+
local j
|
|
324
|
+
for ((j = 0; j < ${#prose_hits[@]} && j < RESIDUE_REPORT_CAP; j++)); do
|
|
325
|
+
local shown="${prose_hits[$j]}"
|
|
326
|
+
[[ ${#shown} -gt 90 ]] && shown="${shown:0:90}…"
|
|
327
|
+
echo " - ${shown}"
|
|
328
|
+
done
|
|
329
|
+
if [[ ${#prose_hits[@]} -gt $RESIDUE_REPORT_CAP ]]; then
|
|
330
|
+
echo " - … and $(( ${#prose_hits[@]} - RESIDUE_REPORT_CAP )) more"
|
|
331
|
+
fi
|
|
332
|
+
fi
|
|
333
|
+
|
|
334
|
+
if [[ ${#hits[@]} -eq 0 && ${#prose_hits[@]} -eq 0 ]]; then
|
|
335
|
+
log_pass "$rel: no unedited scaffold residue detected"
|
|
336
|
+
fi
|
|
162
337
|
}
|
|
163
338
|
|
|
164
339
|
check_grounding_doc() {
|
|
@@ -197,6 +372,13 @@ check_grounding_doc() {
|
|
|
197
372
|
FAILURES=$((FAILURES + 1))
|
|
198
373
|
fi
|
|
199
374
|
fi
|
|
375
|
+
|
|
376
|
+
# Substance, not just presence. The checks above establish that the required
|
|
377
|
+
# sections EXIST; this one asks whether they were filled in. WARN-only and
|
|
378
|
+
# never incrementing FAILURES: an estate must not turn red for being honest
|
|
379
|
+
# about its authoring state, and an exploration-stage integration is a
|
|
380
|
+
# legitimate thing to have.
|
|
381
|
+
check_grounding_residue "$grounding"
|
|
200
382
|
}
|
|
201
383
|
|
|
202
384
|
main() {
|