@davidbalzan/groundwork 0.4.2 → 0.4.3
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 +2 -2
- package/src/commands/doctor.mjs +40 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davidbalzan/groundwork",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Groundwork — an installable AI development workflow (skills + doc methodology) you bolt onto any repo.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"author": "David Balzan",
|
|
30
30
|
"license": "UNLICENSED",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@davidbalzan/groundwork-seam": "0.1.
|
|
32
|
+
"@davidbalzan/groundwork-seam": "0.1.7"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"groundwork": "node src/cli.mjs",
|
package/src/commands/doctor.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { countCheckboxes, progressBar } from "../lib/progress.mjs";
|
|
|
7
7
|
import { ARTIFACTS, workDocPaths } from "../lib/artifacts.mjs";
|
|
8
8
|
import { log, bold, green, yellow, dim, cyan } from "../lib/log.mjs";
|
|
9
9
|
import { adrTripwire } from "../lib/adr-tripwire.mjs";
|
|
10
|
-
import { parseFactsDoc, parseWorkDoc, workDocIssues, workDocLegacyWriteIssues, workDocIssuesDetailed, queueItemsOf, doneEntriesOf, workstreamsV1RowsOf } from "@davidbalzan/groundwork-seam";
|
|
10
|
+
import { parseFactsDoc, parseWorkDoc, workDocIssues, workDocLegacyWriteIssues, workDocIssuesDetailed, queueItemsOf, doneEntriesOf, workstreamsV1RowsOf, sweepTagOf, sweepTagCensus } from "@davidbalzan/groundwork-seam";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* `groundwork doctor` — flag drift between the docs and reality. Offline + deterministic.
|
|
@@ -871,6 +871,45 @@ guard("version-truth", () => {
|
|
|
871
871
|
}
|
|
872
872
|
});
|
|
873
873
|
|
|
874
|
+
// A sweep census nobody has to grep for.
|
|
875
|
+
//
|
|
876
|
+
// MATCH A TAG BY ITS POSITION, NOT BY ITS OCCURRENCE. A corpus that documents its
|
|
877
|
+
// own tags guarantees the tag string appears in items that do not carry it — items
|
|
878
|
+
// cite other sweeps by name, and every `<!-- queue-prune: -->` receipt quotes the
|
|
879
|
+
// lines it removed. **The trail that makes a move checkable is the trail that makes
|
|
880
|
+
// a naive scan wrong**, and it worsens as the trail grows.
|
|
881
|
+
//
|
|
882
|
+
// Measured on the live corpus: grep 14 · parser-by-occurrence 14 · parser-by-
|
|
883
|
+
// position 13. Using the parser was NECESSARY AND NOT SUFFICIENT. This check exists
|
|
884
|
+
// so the correct number is available without anyone reaching for `grep -c`, because
|
|
885
|
+
// the reaching is the defect and it happened three times in twenty minutes.
|
|
886
|
+
//
|
|
887
|
+
// It reports rather than judges: a tag count is not a problem, and the only failure
|
|
888
|
+
// it can name is a MENTION that outnumbers the CARRIERS, which is the shape that
|
|
889
|
+
// misleads.
|
|
890
|
+
guard("sweep-census", () => {
|
|
891
|
+
if (!queueDocs.length) return;
|
|
892
|
+
const items = queueDocs.flatMap((rel) => queueItemsOf(parseWorkDoc(readText(path.join(docs, rel)))));
|
|
893
|
+
if (!items.length) return;
|
|
894
|
+
const census = sweepTagCensus(items);
|
|
895
|
+
const tags = Object.keys(census).sort();
|
|
896
|
+
if (!tags.length) {
|
|
897
|
+
push("sweep-census", "ok", [`${items.length} item(s), none carrying a sweep tag`]);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
const lines = tags.map((t) => {
|
|
901
|
+
const carried = census[t];
|
|
902
|
+
// Mentions across ALL item text, the number a grep would return.
|
|
903
|
+
const mentioned = items.filter((i) => new RegExp(`\\[SWEEP:${t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\]`, "i").test(i.text)).length;
|
|
904
|
+
const gap = mentioned - carried;
|
|
905
|
+
return `${t}: ${carried} carried` + (gap > 0 ? ` · ${mentioned} mentioned — a naive scan over-counts by ${gap}` : "");
|
|
906
|
+
});
|
|
907
|
+
push("sweep-census", "ok", [
|
|
908
|
+
`${items.length} item(s) across ${queueDocs.length} work-doc(s), tags matched at POSITION not occurrence`,
|
|
909
|
+
...lines,
|
|
910
|
+
]);
|
|
911
|
+
});
|
|
912
|
+
|
|
874
913
|
const factsFile = path.join(docs, "FACTS.md");
|
|
875
914
|
guard("facts-freshness", () => {
|
|
876
915
|
if (exists(factsFile)) {
|