sign-off-axi 0.3.0 → 0.3.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/README.md +2 -0
- package/dist/cli.js +9 -8
- package/dist/store.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -202,6 +202,8 @@ npm run build:skill # regenerates skills/sign-off-axi/SKILL.md from sr
|
|
|
202
202
|
npm pack # builds the publishable sign-off-axi tarball
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
+
A release is a merged version-bump PR: CI publishes the new version to npm.
|
|
206
|
+
|
|
205
207
|
### Test runs never touch your board
|
|
206
208
|
|
|
207
209
|
With no board named, a bare command uses the nearest `.sign-off/` that `init` set up, looking from the working folder up to its repo root (the folder holding `.git`) or up to HOME, but never HOME's own `.sign-off/`. The working folder's own `.sign-off/` is always used. A folder above it is searched only when that climb reaches a repo root or HOME, so a `.sign-off/` above a folder with neither (outside HOME, no `.git`) is never used, and the account's own home is never used. Otherwise the default board is `~/.sign-off/decisions.json`, under `SIGN_OFF_HOME` when it is set (it takes HOME's place for the default board and for where the climb stops), else under HOME.
|
package/dist/cli.js
CHANGED
|
@@ -565,7 +565,7 @@ function post(args, cmd = 'post') {
|
|
|
565
565
|
const size = statSync(actual);
|
|
566
566
|
if (!size.isFile() || size.size > MOCK_BYTES)
|
|
567
567
|
throw fail(`--view ${values.view} must be a file of 5 MB or less`, 'VALIDATION_ERROR');
|
|
568
|
-
return storeIncoming(file, on, buildIncoming(values, { type, view: values.view }, filer), filer);
|
|
568
|
+
return storeIncoming(file, on, buildIncoming(values, { type, view: values.view }, filer, file), filer);
|
|
569
569
|
}
|
|
570
570
|
const mocks = values.mock?.map((m) => {
|
|
571
571
|
const at = m.indexOf('=');
|
|
@@ -578,7 +578,7 @@ function post(args, cmd = 'post') {
|
|
|
578
578
|
throw fail(`--mock ${path} escapes the board folder`, 'VALIDATION_ERROR', 'Keep mock files next to the board file');
|
|
579
579
|
return { value: m.slice(0, at), path };
|
|
580
580
|
});
|
|
581
|
-
return storeIncoming(file, on, buildIncoming(values, { type, tradeoff: values.tradeoff, mocks }, filer), filer);
|
|
581
|
+
return storeIncoming(file, on, buildIncoming(values, { type, tradeoff: values.tradeoff, mocks }, filer, file), filer);
|
|
582
582
|
}
|
|
583
583
|
/** For a program that feeds the board: the --from file, checked like any post, becomes the whole board, items and top-level fields. Answers stay.
|
|
584
584
|
* With a filer it replaces only that filer's items (and items with no filer it names by key); every other item and top-level field stays. */
|
|
@@ -669,7 +669,7 @@ function postMedia(file, on, values, type, filer) {
|
|
|
669
669
|
});
|
|
670
670
|
mkdirSync(dirname(file), { recursive: true, mode: 0o700 });
|
|
671
671
|
return locked(`${file}.lock`, () => {
|
|
672
|
-
const items = existsSync(file) ? readItems(file)
|
|
672
|
+
const { items, extra } = existsSync(file) ? readItems(file) : { items: [], extra: {} };
|
|
673
673
|
// Boards in one folder share evidence/, so a number is taken only by creating its folder; one in use is never written into.
|
|
674
674
|
const current = board(file), store = join(dirname(file), 'evidence'), taken = current.map((d) => Number(/^update-(\d+)$/.exec(d.key)?.[1] ?? 0));
|
|
675
675
|
const own = explainer && (values.key ?? slug(values.title));
|
|
@@ -698,7 +698,7 @@ function postMedia(file, on, values, type, filer) {
|
|
|
698
698
|
: { key, type, number, title, about, detail, close, link, media, filer, filed });
|
|
699
699
|
// The whole board is checked before it is written, so a bad post leaves the board and evidence/ as they were.
|
|
700
700
|
const check = `${file}.${process.pid}.check`;
|
|
701
|
-
writeFileSync(check, JSON.stringify({ schema: 2, items }), { mode: 0o600 });
|
|
701
|
+
writeFileSync(check, JSON.stringify({ ...extra, schema: 2, items }), { mode: 0o600 });
|
|
702
702
|
try {
|
|
703
703
|
loadDecisions(check);
|
|
704
704
|
}
|
|
@@ -810,13 +810,14 @@ function recommended(items, flags) {
|
|
|
810
810
|
if (d)
|
|
811
811
|
throw fail(`items[${items.indexOf(d)}] (key ${d.key}) lists options but no recommend_value`, 'VALIDATION_ERROR', `Add "recommend_value": "<value>", the option you would pick: one of ${values}`);
|
|
812
812
|
}
|
|
813
|
-
/** Flags are validated by the same loader as a file: write them as a one-decision file first. */
|
|
814
|
-
function buildIncoming(values, extra
|
|
813
|
+
/** Flags are validated by the same loader as a file: write them as a one-decision file first. Items are checked against the board they go to, so its answers_only or readonly holds. */
|
|
814
|
+
function buildIncoming(values, extra, filer, file) {
|
|
815
|
+
const board = existsSync(file) ? readItems(file).extra : {};
|
|
815
816
|
if (values.from !== undefined) {
|
|
816
817
|
// An item that names its own filer keeps it.
|
|
817
818
|
const source = resolve(values.from), raw = readItems(source).items;
|
|
818
819
|
contractItems(raw, values.from, file);
|
|
819
|
-
const incoming = loadDecisions(source);
|
|
820
|
+
const incoming = loadDecisions(source, board);
|
|
820
821
|
recommended(raw, false);
|
|
821
822
|
return { incoming: incoming.map((d) => ({ filer, ...d })), raw: raw.map((i) => ({ filer, ...i })) };
|
|
822
823
|
}
|
|
@@ -825,7 +826,7 @@ function buildIncoming(values, extra = {}, filer, file = '') {
|
|
|
825
826
|
try {
|
|
826
827
|
const { title, key = slug(title), about, detail, recommend: recommend_value, default: default_if_silent, close } = values;
|
|
827
828
|
writeFileSync(source, JSON.stringify({ schema: 2, items: [{ key, title, about, detail, options: optionsOf(values), recommend_value, default_if_silent, close, ...extra, filer }] }));
|
|
828
|
-
const incoming = loadDecisions(source), raw = readItems(source).items;
|
|
829
|
+
const incoming = loadDecisions(source, board), raw = readItems(source).items;
|
|
829
830
|
recommended(raw, true);
|
|
830
831
|
return { incoming, raw };
|
|
831
832
|
}
|
package/dist/store.js
CHANGED
|
@@ -56,8 +56,9 @@ export function writeItems(path, items, extra) {
|
|
|
56
56
|
writeFileSync(tmp, `${JSON.stringify({ ...(extra ?? old?.extra), schema: 2, items }, null, 2)}\n`, { mode: 0o600 });
|
|
57
57
|
renameSync(tmp, path);
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/** `board`: the top-level fields of the board the items are posted to, which decide in place of the file's own. */
|
|
60
|
+
export function loadDecisions(path, board) {
|
|
61
|
+
const name = basename(path), { schema, items, extra: own } = readItems(path), list = schema === 1 ? 'decisions' : 'items', extra = board ?? own;
|
|
61
62
|
const fed = extra.readonly === true || extra.answers_only === true;
|
|
62
63
|
const seen = new Map();
|
|
63
64
|
return items.map((d, i) => {
|