spec-controller 0.1.0-alpha.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/LICENSE +21 -0
- package/dist/cli-balance/cli.d.ts +87 -0
- package/dist/cli-balance/cli.d.ts.map +1 -0
- package/dist/cli-balance/cli.js +486 -0
- package/dist/cli-balance/cli.js.map +1 -0
- package/dist/cli-balance/emit/format.d.ts +60 -0
- package/dist/cli-balance/emit/format.d.ts.map +1 -0
- package/dist/cli-balance/emit/format.js +90 -0
- package/dist/cli-balance/emit/format.js.map +1 -0
- package/dist/cli-balance/emit/writer.d.ts +45 -0
- package/dist/cli-balance/emit/writer.d.ts.map +1 -0
- package/dist/cli-balance/emit/writer.js +48 -0
- package/dist/cli-balance/emit/writer.js.map +1 -0
- package/dist/cli-registry.d.ts +68 -0
- package/dist/cli-registry.d.ts.map +1 -0
- package/dist/cli-registry.js +181 -0
- package/dist/cli-registry.js.map +1 -0
- package/dist/cli.d.ts +22 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +82 -0
- package/dist/cli.js.map +1 -0
- package/dist/deferralTags.d.ts +16 -0
- package/dist/deferralTags.d.ts.map +1 -0
- package/dist/deferralTags.js +22 -0
- package/dist/deferralTags.js.map +1 -0
- package/dist/host.d.ts +50 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +69 -0
- package/dist/host.js.map +1 -0
- package/dist/ingest/ciSourcePaths.d.ts +46 -0
- package/dist/ingest/ciSourcePaths.d.ts.map +1 -0
- package/dist/ingest/ciSourcePaths.js +58 -0
- package/dist/ingest/ciSourcePaths.js.map +1 -0
- package/dist/ingest/gherkinValidation.d.ts +70 -0
- package/dist/ingest/gherkinValidation.d.ts.map +1 -0
- package/dist/ingest/gherkinValidation.js +85 -0
- package/dist/ingest/gherkinValidation.js.map +1 -0
- package/dist/ingest/ingestQualityChecks.d.ts +119 -0
- package/dist/ingest/ingestQualityChecks.d.ts.map +1 -0
- package/dist/ingest/ingestQualityChecks.js +331 -0
- package/dist/ingest/ingestQualityChecks.js.map +1 -0
- package/dist/ingest/ingestScenarios.d.ts +52 -0
- package/dist/ingest/ingestScenarios.d.ts.map +1 -0
- package/dist/ingest/ingestScenarios.js +119 -0
- package/dist/ingest/ingestScenarios.js.map +1 -0
- package/dist/mutation-ratchet/index.d.ts +48 -0
- package/dist/mutation-ratchet/index.d.ts.map +1 -0
- package/dist/mutation-ratchet/index.js +48 -0
- package/dist/mutation-ratchet/index.js.map +1 -0
- package/dist/mutation-ratchet/ratchet.d.ts +129 -0
- package/dist/mutation-ratchet/ratchet.d.ts.map +1 -0
- package/dist/mutation-ratchet/ratchet.js +222 -0
- package/dist/mutation-ratchet/ratchet.js.map +1 -0
- package/dist/mutation-ratchet/ratchetCli.d.ts +57 -0
- package/dist/mutation-ratchet/ratchetCli.d.ts.map +1 -0
- package/dist/mutation-ratchet/ratchetCli.js +139 -0
- package/dist/mutation-ratchet/ratchetCli.js.map +1 -0
- package/dist/mutation-ratchet/reconcile.d.ts +82 -0
- package/dist/mutation-ratchet/reconcile.d.ts.map +1 -0
- package/dist/mutation-ratchet/reconcile.js +67 -0
- package/dist/mutation-ratchet/reconcile.js.map +1 -0
- package/dist/mutation-ratchet/record.d.ts +210 -0
- package/dist/mutation-ratchet/record.d.ts.map +1 -0
- package/dist/mutation-ratchet/record.js +330 -0
- package/dist/mutation-ratchet/record.js.map +1 -0
- package/dist/mutation-ratchet/report.d.ts +83 -0
- package/dist/mutation-ratchet/report.d.ts.map +1 -0
- package/dist/mutation-ratchet/report.js +148 -0
- package/dist/mutation-ratchet/report.js.map +1 -0
- package/dist/mutation-ratchet/verdict.d.ts +177 -0
- package/dist/mutation-ratchet/verdict.d.ts.map +1 -0
- package/dist/mutation-ratchet/verdict.js +387 -0
- package/dist/mutation-ratchet/verdict.js.map +1 -0
- package/dist/run-management/keptRun.d.ts +156 -0
- package/dist/run-management/keptRun.d.ts.map +1 -0
- package/dist/run-management/keptRun.js +133 -0
- package/dist/run-management/keptRun.js.map +1 -0
- package/dist/run-management/resolveRunInputs.d.ts +70 -0
- package/dist/run-management/resolveRunInputs.d.ts.map +1 -0
- package/dist/run-management/resolveRunInputs.js +144 -0
- package/dist/run-management/resolveRunInputs.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The output PLAN — the pure app-layer parse+validate of the `--format <fmt>[:<path>]`
|
|
3
|
+
* grammar (the three-layer split). Given the raw
|
|
4
|
+
* `--format` specs (in argv order; empty when no flag was passed), `planOutput` returns
|
|
5
|
+
* either an `OutputPlan` — an ordered list of `{ fmt, dest }` outputs — or a typed error.
|
|
6
|
+
*
|
|
7
|
+
* PURE: no IO. The plan describes WHAT to emit and WHERE; the writer (`writer.ts`) is the
|
|
8
|
+
* only layer that performs the IO. This split lets `planOutput` be proven in isolation
|
|
9
|
+
* (`@unit`, no subprocess) while the writer's real stdout/file effect is the `@integration`
|
|
10
|
+
* arm — the anti-watermelon rule (each integration arm asserts the real IO, never a
|
|
11
|
+
* planOutput re-run).
|
|
12
|
+
*
|
|
13
|
+
* FMT-001 (`@SCN-FMT-001`) establishes the skeleton: no flag — and an explicit `md` —
|
|
14
|
+
* plan a single markdown output bound for stdout. Later sub-issues extend `planOutput`
|
|
15
|
+
* with json (FMT-002), file dests (`<fmt>:<path>`, FMT-002/003), the at-most-one-to-stdout
|
|
16
|
+
* invariant (FMT-005), the legacy-`--json` rejection (FMT-006), and unknown-format
|
|
17
|
+
* rejection (FMT-007) — each landing on the `OutputPlan` type + typed `PlanResult` error
|
|
18
|
+
* surface designed here.
|
|
19
|
+
*
|
|
20
|
+
* Parent: @SCN-FMT-001 — the output-dispatch rework.
|
|
21
|
+
*/
|
|
22
|
+
/** A supported output format. FMT-001 plans `md`; `json` is wired in FMT-002. */
|
|
23
|
+
export type OutputFormat = "md" | "json";
|
|
24
|
+
/** Where a planned output is written — stdout, or a caller-given file path. */
|
|
25
|
+
export type OutputDest = {
|
|
26
|
+
readonly kind: "stdout";
|
|
27
|
+
} | {
|
|
28
|
+
readonly kind: "file";
|
|
29
|
+
readonly path: string;
|
|
30
|
+
};
|
|
31
|
+
/** One planned output: a format routed to a destination. */
|
|
32
|
+
export interface PlannedOutput {
|
|
33
|
+
readonly fmt: OutputFormat;
|
|
34
|
+
readonly dest: OutputDest;
|
|
35
|
+
}
|
|
36
|
+
/** The plan: the ordered outputs a single invocation will emit. */
|
|
37
|
+
export interface OutputPlan {
|
|
38
|
+
readonly outputs: readonly PlannedOutput[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The result of planning — either the plan, or a typed error carrying the operator-facing
|
|
42
|
+
* message (later sub-issues raise these for the stdout-collision, legacy-flag, and
|
|
43
|
+
* unknown-format cases). A discriminated union so the CLI branches on `ok`.
|
|
44
|
+
*/
|
|
45
|
+
export type PlanResult = {
|
|
46
|
+
readonly ok: true;
|
|
47
|
+
readonly plan: OutputPlan;
|
|
48
|
+
} | {
|
|
49
|
+
readonly ok: false;
|
|
50
|
+
readonly error: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Parse + validate the `--format` specs into an `OutputPlan` (or a typed error).
|
|
54
|
+
*
|
|
55
|
+
* FMT-001: no specs (no `--format` flag) defaults to a single `md`; an explicit `md`
|
|
56
|
+
* plans the same. Each planned output is markdown bound for stdout — the default human
|
|
57
|
+
* report, like vitest/cucumber.
|
|
58
|
+
*/
|
|
59
|
+
export declare function planOutput(formatSpecs: readonly string[]): PlanResult;
|
|
60
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/cli-balance/emit/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,iFAAiF;AACjF,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,MAAM,CAAC;AAczC,+EAA+E;AAC/E,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B;AAED,mEAAmE;AACnE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAwDrE"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The output PLAN — the pure app-layer parse+validate of the `--format <fmt>[:<path>]`
|
|
3
|
+
* grammar (the three-layer split). Given the raw
|
|
4
|
+
* `--format` specs (in argv order; empty when no flag was passed), `planOutput` returns
|
|
5
|
+
* either an `OutputPlan` — an ordered list of `{ fmt, dest }` outputs — or a typed error.
|
|
6
|
+
*
|
|
7
|
+
* PURE: no IO. The plan describes WHAT to emit and WHERE; the writer (`writer.ts`) is the
|
|
8
|
+
* only layer that performs the IO. This split lets `planOutput` be proven in isolation
|
|
9
|
+
* (`@unit`, no subprocess) while the writer's real stdout/file effect is the `@integration`
|
|
10
|
+
* arm — the anti-watermelon rule (each integration arm asserts the real IO, never a
|
|
11
|
+
* planOutput re-run).
|
|
12
|
+
*
|
|
13
|
+
* FMT-001 (`@SCN-FMT-001`) establishes the skeleton: no flag — and an explicit `md` —
|
|
14
|
+
* plan a single markdown output bound for stdout. Later sub-issues extend `planOutput`
|
|
15
|
+
* with json (FMT-002), file dests (`<fmt>:<path>`, FMT-002/003), the at-most-one-to-stdout
|
|
16
|
+
* invariant (FMT-005), the legacy-`--json` rejection (FMT-006), and unknown-format
|
|
17
|
+
* rejection (FMT-007) — each landing on the `OutputPlan` type + typed `PlanResult` error
|
|
18
|
+
* surface designed here.
|
|
19
|
+
*
|
|
20
|
+
* Parent: @SCN-FMT-001 — the output-dispatch rework.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* The formats `planOutput` accepts; any other `--format` token is rejected (FMT-007 /
|
|
24
|
+
* @SCN-FMT-007). The single source of truth the unknown-format guard checks against AND
|
|
25
|
+
* lists in its hint, so a future format (html, sarif) is added in exactly one place.
|
|
26
|
+
*/
|
|
27
|
+
const SUPPORTED_FORMATS = ["md", "json"];
|
|
28
|
+
/** Runtime narrowing of a parsed `--format` token to a supported `OutputFormat`. */
|
|
29
|
+
function isSupportedFormat(fmt) {
|
|
30
|
+
return SUPPORTED_FORMATS.includes(fmt);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parse + validate the `--format` specs into an `OutputPlan` (or a typed error).
|
|
34
|
+
*
|
|
35
|
+
* FMT-001: no specs (no `--format` flag) defaults to a single `md`; an explicit `md`
|
|
36
|
+
* plans the same. Each planned output is markdown bound for stdout — the default human
|
|
37
|
+
* report, like vitest/cucumber.
|
|
38
|
+
*/
|
|
39
|
+
export function planOutput(formatSpecs) {
|
|
40
|
+
// No --format flag defaults to a single "md" spec — the default human report.
|
|
41
|
+
const specs = formatSpecs.length === 0 ? ["md"] : formatSpecs;
|
|
42
|
+
const outputs = specs.map((spec) => {
|
|
43
|
+
// Grammar: "<fmt>[:<path>]". Split on the FIRST colon — the fmt is everything before
|
|
44
|
+
// it; a ":<path>" tail routes that format to the file at <path> (FMT-002/003), where
|
|
45
|
+
// the path is everything after the first colon (so a path may itself contain colons).
|
|
46
|
+
// No colon keeps the bare-fmt → stdout default (FMT-001). The at-most-one-to-stdout /
|
|
47
|
+
// unknown-format validation (FMT-005/007) lands on this same parse and the typed
|
|
48
|
+
// PlanResult error surface.
|
|
49
|
+
const colon = spec.indexOf(":");
|
|
50
|
+
const fmt = (colon === -1 ? spec : spec.slice(0, colon));
|
|
51
|
+
const dest = colon === -1 ? { kind: "stdout" } : { kind: "file", path: spec.slice(colon + 1) };
|
|
52
|
+
return { fmt, dest };
|
|
53
|
+
});
|
|
54
|
+
// The unknown-format guard (FMT-007 / @SCN-FMT-007): a
|
|
55
|
+
// --format naming an unsupported format (e.g. `xml`, or a typo) is rejected fast on the
|
|
56
|
+
// shared typed PlanResult error surface (the same surface FMT-005/006 use) — rather than
|
|
57
|
+
// silently rendering as markdown (the writer's default render branch) and exiting 0. The
|
|
58
|
+
// error LISTS the supported formats so the operator knows what to use — basic public-CLI
|
|
59
|
+
// robustness. Checked BEFORE the stdout-collision count so an unroutable format is
|
|
60
|
+
// reported as such (an unknown format can't meaningfully collide on stdout).
|
|
61
|
+
const unknown = outputs.filter((o) => !isSupportedFormat(o.fmt));
|
|
62
|
+
if (unknown.length > 0) {
|
|
63
|
+
const bad = [...new Set(unknown.map((o) => o.fmt))].join(", ");
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
error: `Unknown --format ${bad}. Supported formats are ${SUPPORTED_FORMATS.join(", ")} ` +
|
|
67
|
+
`(e.g. --format md, --format json:report.json).`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// The stdout invariant (FMT-005 / @SCN-FMT-005): AT MOST
|
|
71
|
+
// ONE output may route to stdout, so a captured stdout never carries two interleaved
|
|
72
|
+
// documents. This is a COUNT check on the stdout-destined outputs (≤ 1) — NOT a
|
|
73
|
+
// "distinct formats > 1" check — so `--format md --format md` (the same format twice,
|
|
74
|
+
// both stdout) is rejected just as `--format md --format json` is; a distinct-formats
|
|
75
|
+
// check would wrongly pass the former. Rejected with a typed error on the shared
|
|
76
|
+
// PlanResult surface (the same surface FMT-006/007 raise), carrying the hint to give one
|
|
77
|
+
// of the colliding outputs a `:<path>` so it writes to a file instead.
|
|
78
|
+
const stdoutBound = outputs.filter((o) => o.dest.kind === "stdout");
|
|
79
|
+
if (stdoutBound.length > 1) {
|
|
80
|
+
const fmts = stdoutBound.map((o) => o.fmt).join(", ");
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
error: `More than one output routes to stdout (${fmts}); at most one may. ` +
|
|
84
|
+
`Give one of them a :<path> to write it to a file instead ` +
|
|
85
|
+
`(e.g. --format json:report.json).`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return { ok: true, plan: { outputs } };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../../src/cli-balance/emit/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The output WRITER — the impure app-layer that realises an `OutputPlan` (the
|
|
3
|
+
* three-layer split). For each planned output it renders
|
|
4
|
+
* the payload via the PURE renderers (`renderMarkdown` / `renderJson` in `src/render/`)
|
|
5
|
+
* and performs the IO: stdout, or a file.
|
|
6
|
+
*
|
|
7
|
+
* This is the ONLY layer that performs IO — the renderers stay pure (`model → string`)
|
|
8
|
+
* and `planOutput` stays pure (parse+validate). So the writer carries the `@integration`
|
|
9
|
+
* evidence: its real stdout/file effect is asserted directly (the anti-watermelon rule),
|
|
10
|
+
* never a re-run of the `planOutput` `@unit`.
|
|
11
|
+
*
|
|
12
|
+
* FMT-001 (`@SCN-FMT-001`) writes markdown to stdout. The `json` format (FMT-002) and the
|
|
13
|
+
* file destination (FMT-002/003) are wired on the same `render` + `dest` switch below as
|
|
14
|
+
* later sub-issues plan them.
|
|
15
|
+
*
|
|
16
|
+
* Parent: @SCN-FMT-001 — the output-dispatch rework.
|
|
17
|
+
*/
|
|
18
|
+
import type { FeatureNames } from "@3f-consulting/spec-controller-core";
|
|
19
|
+
import type { OutputPlan } from "./format.js";
|
|
20
|
+
import type { RunContext } from "@3f-consulting/spec-controller-core";
|
|
21
|
+
import type { EvidenceObligations } from "@3f-consulting/spec-controller-core";
|
|
22
|
+
import type { EvidenceReconciliation } from "@3f-consulting/spec-controller-core";
|
|
23
|
+
/**
|
|
24
|
+
* The reconciliation payloads the writer renders — the balance plus its sibling reads
|
|
25
|
+
* (ctx / corpus / guards / featureNames), exactly the arguments the pure renderers take. Threaded
|
|
26
|
+
* from `cli.ts` (built once, rendered per planned output). `withRuntimeObservations` and
|
|
27
|
+
* `withStaticChecks` are the GATES for the two observation axes (Test volume + the evidence grid;
|
|
28
|
+
* the Static checks cut): the renderers fold BOTH off the Ledger themselves (3F-2103 / 3F-2118 —
|
|
29
|
+
* the parallel censuses are retired), so the writer threads only whether each axis is in play,
|
|
30
|
+
* never a census object.
|
|
31
|
+
*/
|
|
32
|
+
export interface OutputPayloads {
|
|
33
|
+
readonly balance: EvidenceReconciliation;
|
|
34
|
+
readonly ctx: RunContext;
|
|
35
|
+
readonly evidenceObligations?: EvidenceObligations;
|
|
36
|
+
readonly withRuntimeObservations?: boolean;
|
|
37
|
+
readonly withStaticChecks?: boolean;
|
|
38
|
+
readonly featureNames?: FeatureNames;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Realise the plan: render each planned output and write it to its destination. The only
|
|
42
|
+
* IO in the pipeline. FMT-001 exercises the markdown→stdout path.
|
|
43
|
+
*/
|
|
44
|
+
export declare function writeOutputs(plan: OutputPlan, payloads: OutputPayloads): void;
|
|
45
|
+
//# sourceMappingURL=writer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../../src/cli-balance/emit/writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,KAAK,EAAgB,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAElF;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;IACzC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;CACtC;AAWD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CAc7E"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The output WRITER — the impure app-layer that realises an `OutputPlan` (the
|
|
3
|
+
* three-layer split). For each planned output it renders
|
|
4
|
+
* the payload via the PURE renderers (`renderMarkdown` / `renderJson` in `src/render/`)
|
|
5
|
+
* and performs the IO: stdout, or a file.
|
|
6
|
+
*
|
|
7
|
+
* This is the ONLY layer that performs IO — the renderers stay pure (`model → string`)
|
|
8
|
+
* and `planOutput` stays pure (parse+validate). So the writer carries the `@integration`
|
|
9
|
+
* evidence: its real stdout/file effect is asserted directly (the anti-watermelon rule),
|
|
10
|
+
* never a re-run of the `planOutput` `@unit`.
|
|
11
|
+
*
|
|
12
|
+
* FMT-001 (`@SCN-FMT-001`) writes markdown to stdout. The `json` format (FMT-002) and the
|
|
13
|
+
* file destination (FMT-002/003) are wired on the same `render` + `dest` switch below as
|
|
14
|
+
* later sub-issues plan them.
|
|
15
|
+
*
|
|
16
|
+
* Parent: @SCN-FMT-001 — the output-dispatch rework.
|
|
17
|
+
*/
|
|
18
|
+
import { writeFileSync } from "node:fs";
|
|
19
|
+
import { renderMarkdown } from "@3f-consulting/spec-controller-core";
|
|
20
|
+
import { renderJson } from "@3f-consulting/spec-controller-core";
|
|
21
|
+
/** Render one format from the payloads via the matching pure renderer. */
|
|
22
|
+
function render(fmt, p) {
|
|
23
|
+
// The axis gates moved onto the concept's factory (3F-2233) — by the time a payload reaches a
|
|
24
|
+
// renderer the totals are already folded, so there is nothing left here to switch on.
|
|
25
|
+
return fmt === "json"
|
|
26
|
+
? renderJson(p.balance, p.ctx, p.evidenceObligations, p.featureNames)
|
|
27
|
+
: renderMarkdown(p.balance, p.ctx, p.evidenceObligations, p.featureNames);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Realise the plan: render each planned output and write it to its destination. The only
|
|
31
|
+
* IO in the pipeline. FMT-001 exercises the markdown→stdout path.
|
|
32
|
+
*/
|
|
33
|
+
export function writeOutputs(plan, payloads) {
|
|
34
|
+
for (const output of plan.outputs) {
|
|
35
|
+
const rendered = render(output.fmt, payloads);
|
|
36
|
+
switch (output.dest.kind) {
|
|
37
|
+
case "stdout":
|
|
38
|
+
// Trailing newline so the report ends cleanly on a terminal — FMT-001's path.
|
|
39
|
+
process.stdout.write(rendered + "\n");
|
|
40
|
+
break;
|
|
41
|
+
case "file":
|
|
42
|
+
// The file destination (FMT-002/003) — same rendered bytes, to a caller path.
|
|
43
|
+
writeFileSync(output.dest.path, rendered + "\n", "utf8");
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../../src/cli-balance/emit/writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAyBjE,0EAA0E;AAC1E,SAAS,MAAM,CAAC,GAAiB,EAAE,CAAiB;IAClD,8FAA8F;IAC9F,sFAAsF;IACtF,OAAO,GAAG,KAAK,MAAM;QACnB,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,mBAAmB,EAAE,CAAC,CAAC,YAAY,CAAC;QACrE,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,mBAAmB,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAgB,EAAE,QAAwB;IACrE,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC9C,QAAQ,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACzB,KAAK,QAAQ;gBACX,8EAA8E;gBAC9E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;gBACtC,MAAM;YACR,KAAK,MAAM;gBACT,8EAA8E;gBAC9E,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;gBACzD,MAAM;QACV,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The data-driven command/flag registry — the single source the CLI answers `--help`
|
|
3
|
+
* from (@SCN-CLI-006). Two scopes mirror the two help surfaces:
|
|
4
|
+
*
|
|
5
|
+
* - `global` — the host modifiers recognised BEFORE the command (`--store`,
|
|
6
|
+
* `--run-id`), consumed by the top-level dispatcher; rendered by
|
|
7
|
+
* `spec-controller --help` (the sibling @SCN-CLI-007 surface).
|
|
8
|
+
* - `commands` — the per-command scopes; `balance`'s 9 flags (+ `--help`/`-h`),
|
|
9
|
+
* rendered by `balance --help`.
|
|
10
|
+
*
|
|
11
|
+
* A flag exists ⇒ a registry entry ⇒ it appears in help, by construction — the
|
|
12
|
+
* single-source discipline the completeness guard (@SCN-CLI-008) will enforce and the
|
|
13
|
+
* registry-bounded parser (@SCN-CLI-009) will reject against. Flag descriptions are
|
|
14
|
+
* migrated from doc/how_to_use.md's flag table, which the registry now supersedes as the
|
|
15
|
+
* source of truth. Registry + renderers live in the CLI/app layer, never the domain.
|
|
16
|
+
*/
|
|
17
|
+
/** One flag in a scope: its canonical name, optional aliases, value placeholder, help. */
|
|
18
|
+
export interface FlagSpec {
|
|
19
|
+
/** The canonical flag with dashes (e.g. "--features", "--help"). */
|
|
20
|
+
name: string;
|
|
21
|
+
/** Alternate spellings rendered after the name (e.g. ["-h"]); omit when none. */
|
|
22
|
+
aliases?: string[];
|
|
23
|
+
/** The value placeholder (e.g. "<dir>"); omit for boolean flags like `--help`. */
|
|
24
|
+
arg?: string;
|
|
25
|
+
/** True when the flag may be given more than once (`--format`); omit otherwise. */
|
|
26
|
+
repeatable?: boolean;
|
|
27
|
+
/** One-line help text — the description migrated from doc/how_to_use.md. */
|
|
28
|
+
description: string;
|
|
29
|
+
}
|
|
30
|
+
/** A command: its token, a one-line summary, and its per-command flag scope. */
|
|
31
|
+
export interface CommandSpec {
|
|
32
|
+
/** The command token (e.g. "balance"). */
|
|
33
|
+
name: string;
|
|
34
|
+
/** One-line summary for the top-level `spec-controller --help` (the CLI-007 surface). */
|
|
35
|
+
summary: string;
|
|
36
|
+
/** The command's per-command flag scope. */
|
|
37
|
+
flags: FlagSpec[];
|
|
38
|
+
}
|
|
39
|
+
/** The two-scope registry: the global host-modifier scope, and the per-command scopes. */
|
|
40
|
+
export interface CliRegistry {
|
|
41
|
+
/** Host modifiers recognised before the command — the global scope (CLI-007). */
|
|
42
|
+
global: FlagSpec[];
|
|
43
|
+
/** The per-command scopes. */
|
|
44
|
+
commands: CommandSpec[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The registry singleton — the single source both help surfaces render from.
|
|
48
|
+
*/
|
|
49
|
+
export declare const cliRegistry: CliRegistry;
|
|
50
|
+
/**
|
|
51
|
+
* Render the `balance` command's per-command scope as help text: one line per registered
|
|
52
|
+
* flag (name + arg-placeholder + description), driven BY the registry — so a flag that
|
|
53
|
+
* exists is a flag that appears, by construction. Returns the help block WITHOUT a
|
|
54
|
+
* trailing newline (the caller adds it, matching the writer's stdout convention).
|
|
55
|
+
*/
|
|
56
|
+
export declare function renderBalanceHelp(registry: CliRegistry): string;
|
|
57
|
+
/**
|
|
58
|
+
* Render the top-level `spec-controller --help` surface (@SCN-CLI-007) FROM the registry:
|
|
59
|
+
* a Commands section (each command's name + one-line summary) and a Host modifiers section
|
|
60
|
+
* (each global-scope flag: name + arg-placeholder + one-line description). The global-scope
|
|
61
|
+
* half of the two-scope registry — the counterpart to `renderBalanceHelp`'s per-command
|
|
62
|
+
* half — so the top-level usage no longer hand-keeps (and can no longer silently drift
|
|
63
|
+
* from) the command set or the host modifiers; both are sourced from `cliRegistry`.
|
|
64
|
+
* Summaries and descriptions render on a single line (no wrapping) so a registered string
|
|
65
|
+
* appears verbatim. Returns the block WITHOUT a trailing newline (the caller adds it).
|
|
66
|
+
*/
|
|
67
|
+
export declare function renderTopLevelHelp(registry: CliRegistry): string;
|
|
68
|
+
//# sourceMappingURL=cli-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-registry.d.ts","sourceRoot":"","sources":["../src/cli-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,0FAA0F;AAC1F,MAAM,WAAW,QAAQ;IACvB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,0FAA0F;AAC1F,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,8BAA8B;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAsFD;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,WAUzB,CAAC;AAQF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,CA2B/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,CA0BhE"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The data-driven command/flag registry — the single source the CLI answers `--help`
|
|
3
|
+
* from (@SCN-CLI-006). Two scopes mirror the two help surfaces:
|
|
4
|
+
*
|
|
5
|
+
* - `global` — the host modifiers recognised BEFORE the command (`--store`,
|
|
6
|
+
* `--run-id`), consumed by the top-level dispatcher; rendered by
|
|
7
|
+
* `spec-controller --help` (the sibling @SCN-CLI-007 surface).
|
|
8
|
+
* - `commands` — the per-command scopes; `balance`'s 9 flags (+ `--help`/`-h`),
|
|
9
|
+
* rendered by `balance --help`.
|
|
10
|
+
*
|
|
11
|
+
* A flag exists ⇒ a registry entry ⇒ it appears in help, by construction — the
|
|
12
|
+
* single-source discipline the completeness guard (@SCN-CLI-008) will enforce and the
|
|
13
|
+
* registry-bounded parser (@SCN-CLI-009) will reject against. Flag descriptions are
|
|
14
|
+
* migrated from doc/how_to_use.md's flag table, which the registry now supersedes as the
|
|
15
|
+
* source of truth. Registry + renderers live in the CLI/app layer, never the domain.
|
|
16
|
+
*/
|
|
17
|
+
import { DEFAULT_DEFERRAL_TAGS } from "./deferralTags.js";
|
|
18
|
+
/**
|
|
19
|
+
* The `balance` command's per-command scope — its 9 reconciliation flags plus `--help`/
|
|
20
|
+
* `-h`. Descriptions migrated from doc/how_to_use.md's flag table (the registry now the
|
|
21
|
+
* source of truth; the doc is reconciled against it by @SCN-CLI-007/008).
|
|
22
|
+
*/
|
|
23
|
+
const balanceFlags = [
|
|
24
|
+
{
|
|
25
|
+
name: "--features",
|
|
26
|
+
arg: "<dir>",
|
|
27
|
+
description: "Directory of Gherkin .feature files — the scenarios.",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "--vitest",
|
|
31
|
+
arg: "<report.json>",
|
|
32
|
+
description: "A Vitest JSON report — runtime test evidence.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "--cucumber",
|
|
36
|
+
arg: "<report.json>",
|
|
37
|
+
description: "A Cucumber JSON report — runtime test evidence.",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "--ci",
|
|
41
|
+
arg: "<ci.yml>",
|
|
42
|
+
description: "Your CI workflow — used to check whether @lint guards actually run over the real source.",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "--package-json",
|
|
46
|
+
arg: "<path>",
|
|
47
|
+
description: "package.json — used to check a named guard's script exists.",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "--measurements",
|
|
51
|
+
arg: "<report.json>",
|
|
52
|
+
description: "A measurement report — the number each measured check posted, evidence for the measured fitness checks.",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "--target",
|
|
56
|
+
arg: "<name>",
|
|
57
|
+
description: "A name recorded verbatim in the report's provenance (which project this run reconciled).",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "--source-sha",
|
|
61
|
+
arg: "<sha>",
|
|
62
|
+
description: "Override the git commit SHA recorded in provenance (defaults to the resolved HEAD).",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "--format",
|
|
66
|
+
arg: "<fmt>[:<path>]",
|
|
67
|
+
repeatable: true,
|
|
68
|
+
description: "Where to send output (md or json, optionally :<path> to write to a file). Repeatable.",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "--render-from-json",
|
|
72
|
+
arg: "<path>",
|
|
73
|
+
description: "Re-render Markdown from a previously saved Evidence Reconciliation JSON — no reconcile, no live inputs.",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "--strict",
|
|
77
|
+
description: "Fail the run (exit 3) when it carries Pending Items — the opt-in that escalates otherwise-run-neutral deferred obligations.",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "--help",
|
|
81
|
+
aliases: ["-h"],
|
|
82
|
+
description: "Show this help and exit.",
|
|
83
|
+
},
|
|
84
|
+
];
|
|
85
|
+
/** The global host-modifier scope — recognised before the command; the CLI-007 surface. */
|
|
86
|
+
const globalFlags = [
|
|
87
|
+
{
|
|
88
|
+
name: "--store",
|
|
89
|
+
arg: "<root>",
|
|
90
|
+
description: "Store the run: write <root>/<target>/<run-id>/ with the reconciliation outputs + a run.yaml manifest. Required to store; there is no default (a default would write inside the repo).",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "--run-id",
|
|
94
|
+
arg: "<id>",
|
|
95
|
+
description: "Name the stored run (default: an ISO-8601 UTC timestamp).",
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
/**
|
|
99
|
+
* The registry singleton — the single source both help surfaces render from.
|
|
100
|
+
*/
|
|
101
|
+
export const cliRegistry = {
|
|
102
|
+
global: globalFlags,
|
|
103
|
+
commands: [
|
|
104
|
+
{
|
|
105
|
+
name: "balance",
|
|
106
|
+
summary: "Reconcile Gherkin scenarios against test/CI evidence and emit the Evidence Reconciliation (Markdown to stdout by default).",
|
|
107
|
+
flags: balanceFlags,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
/** The display label for a flag: its name, any aliases, then its arg-placeholder. */
|
|
112
|
+
function flagLabel(flag) {
|
|
113
|
+
const names = [flag.name, ...(flag.aliases ?? [])].join(", ");
|
|
114
|
+
return flag.arg !== undefined ? `${names} ${flag.arg}` : names;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Render the `balance` command's per-command scope as help text: one line per registered
|
|
118
|
+
* flag (name + arg-placeholder + description), driven BY the registry — so a flag that
|
|
119
|
+
* exists is a flag that appears, by construction. Returns the help block WITHOUT a
|
|
120
|
+
* trailing newline (the caller adds it, matching the writer's stdout convention).
|
|
121
|
+
*/
|
|
122
|
+
export function renderBalanceHelp(registry) {
|
|
123
|
+
const command = registry.commands.find((c) => c.name === "balance");
|
|
124
|
+
const flags = command?.flags ?? [];
|
|
125
|
+
const gutter = 2;
|
|
126
|
+
const labelWidth = Math.max(0, ...flags.map((f) => flagLabel(f).length));
|
|
127
|
+
const lines = flags.map((flag) => ` ${flagLabel(flag).padEnd(labelWidth + gutter)}${flag.description}`);
|
|
128
|
+
// The `specController.deferralTags` config field, with its recognised starter set SINGLE-SOURCED
|
|
129
|
+
// from DEFAULT_DEFERRAL_TAGS (@SCN-PND-021 / the 3F-1405 completeness discipline — the render reads
|
|
130
|
+
// the constant, never a hand-kept copy, so the documented set cannot drift from the recognised one).
|
|
131
|
+
const starterSet = DEFAULT_DEFERRAL_TAGS.map((tag) => `@${tag}`).join(", ");
|
|
132
|
+
const configLines = [
|
|
133
|
+
"Configuration (in the target repo's package.json):",
|
|
134
|
+
" specController.deferralTags: string[]",
|
|
135
|
+
" Extra recognised deferral tags. A scenario carrying one defers every obligation it",
|
|
136
|
+
" names to a Pending Item, extending (never replacing) the built-in set.",
|
|
137
|
+
` Built-in deferral tags: ${starterSet}`,
|
|
138
|
+
];
|
|
139
|
+
return [
|
|
140
|
+
`spec-controller balance — ${command?.summary ?? ""}`,
|
|
141
|
+
"",
|
|
142
|
+
"Options:",
|
|
143
|
+
...lines,
|
|
144
|
+
"",
|
|
145
|
+
...configLines,
|
|
146
|
+
].join("\n");
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Render the top-level `spec-controller --help` surface (@SCN-CLI-007) FROM the registry:
|
|
150
|
+
* a Commands section (each command's name + one-line summary) and a Host modifiers section
|
|
151
|
+
* (each global-scope flag: name + arg-placeholder + one-line description). The global-scope
|
|
152
|
+
* half of the two-scope registry — the counterpart to `renderBalanceHelp`'s per-command
|
|
153
|
+
* half — so the top-level usage no longer hand-keeps (and can no longer silently drift
|
|
154
|
+
* from) the command set or the host modifiers; both are sourced from `cliRegistry`.
|
|
155
|
+
* Summaries and descriptions render on a single line (no wrapping) so a registered string
|
|
156
|
+
* appears verbatim. Returns the block WITHOUT a trailing newline (the caller adds it).
|
|
157
|
+
*/
|
|
158
|
+
export function renderTopLevelHelp(registry) {
|
|
159
|
+
const commandGutter = 4;
|
|
160
|
+
const commandWidth = Math.max(0, ...registry.commands.map((c) => c.name.length));
|
|
161
|
+
const commandLines = registry.commands.map((command) => ` ${command.name.padEnd(commandWidth + commandGutter)}${command.summary}`);
|
|
162
|
+
const modifierGutter = 3;
|
|
163
|
+
const modifierWidth = Math.max(0, ...registry.global.map((f) => flagLabel(f).length));
|
|
164
|
+
const modifierLines = registry.global.map((flag) => ` ${flagLabel(flag).padEnd(modifierWidth + modifierGutter)}${flag.description}`);
|
|
165
|
+
const modifierUsage = registry.global.map((flag) => `[${flagLabel(flag)}]`).join(" ");
|
|
166
|
+
return [
|
|
167
|
+
"spec-controller — reconcile scenarios against observed evidence",
|
|
168
|
+
"",
|
|
169
|
+
"Usage:",
|
|
170
|
+
` spec-controller ${modifierUsage} <command> [command-args]`,
|
|
171
|
+
"",
|
|
172
|
+
"Commands:",
|
|
173
|
+
...commandLines,
|
|
174
|
+
"",
|
|
175
|
+
"Host modifiers (before the command):",
|
|
176
|
+
...modifierLines,
|
|
177
|
+
"",
|
|
178
|
+
"Full flags, examples, and CI wiring: doc/how_to_use.md.",
|
|
179
|
+
].join("\n");
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=cli-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-registry.js","sourceRoot":"","sources":["../src/cli-registry.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The `spec-controller` binary — the top-level command host + dispatcher.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* spec-controller [--store <root>] [--run-id <id>] <command> [command-args]
|
|
7
|
+
*
|
|
8
|
+
* Commands: `balance` — the deterministic reconciliation of scenarios against
|
|
9
|
+
* observed evidence, standalone on the public core + app layer (no engine). The
|
|
10
|
+
* `analyse` / `migrate` capabilities are a separate, closed offering built on the same
|
|
11
|
+
* core.
|
|
12
|
+
*
|
|
13
|
+
* Storage is a CROSS-CUTTING host modifier, not a command. A leading `--store <root>`
|
|
14
|
+
* turns any command into a STORED run: the host wraps the command via the kept-run
|
|
15
|
+
* scaffold — a named `<root>/<target>/<run-id>/` directory, the command's outputs routed
|
|
16
|
+
* in, and a `run.yaml` manifest — and hands the command its own clean args, unchanged. So
|
|
17
|
+
* `balance` stays emit-only (its default, no-store output is byte-identical); the store
|
|
18
|
+
* opinion lives at the host, injected via `--store` (required — there is no default, a
|
|
19
|
+
* default would write inside the repo). `--run-id` defaults to a timestamp.
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;GAkBG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The `spec-controller` binary — the top-level command host + dispatcher.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* spec-controller [--store <root>] [--run-id <id>] <command> [command-args]
|
|
7
|
+
*
|
|
8
|
+
* Commands: `balance` — the deterministic reconciliation of scenarios against
|
|
9
|
+
* observed evidence, standalone on the public core + app layer (no engine). The
|
|
10
|
+
* `analyse` / `migrate` capabilities are a separate, closed offering built on the same
|
|
11
|
+
* core.
|
|
12
|
+
*
|
|
13
|
+
* Storage is a CROSS-CUTTING host modifier, not a command. A leading `--store <root>`
|
|
14
|
+
* turns any command into a STORED run: the host wraps the command via the kept-run
|
|
15
|
+
* scaffold — a named `<root>/<target>/<run-id>/` directory, the command's outputs routed
|
|
16
|
+
* in, and a `run.yaml` manifest — and hands the command its own clean args, unchanged. So
|
|
17
|
+
* `balance` stays emit-only (its default, no-store output is byte-identical); the store
|
|
18
|
+
* opinion lives at the host, injected via `--store` (required — there is no default, a
|
|
19
|
+
* default would write inside the repo). `--run-id` defaults to a timestamp.
|
|
20
|
+
*/
|
|
21
|
+
import { dirname } from "node:path";
|
|
22
|
+
import { runBalance } from "./cli-balance/cli.js";
|
|
23
|
+
import { resolveSourceSha, resolveToolIdentity } from "./run-management/resolveRunInputs.js";
|
|
24
|
+
import { executeKeptRun, resolveRunId, } from "./run-management/keptRun.js";
|
|
25
|
+
import { parseHostInvocation, wrapCommandArgv, peekFlag } from "./host.js";
|
|
26
|
+
import { cliRegistry, renderTopLevelHelp } from "./cli-registry.js";
|
|
27
|
+
/** Dispatch a command to its handler with the given (clean or store-wrapped) argv. */
|
|
28
|
+
function runCommand(command, argv) {
|
|
29
|
+
switch (command) {
|
|
30
|
+
case "balance":
|
|
31
|
+
runBalance(argv);
|
|
32
|
+
return;
|
|
33
|
+
default:
|
|
34
|
+
process.stderr.write(`spec-controller: unknown command "${command}".\n\n${renderTopLevelHelp(cliRegistry)}\n`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function main(hostArgv) {
|
|
39
|
+
const inv = parseHostInvocation(hostArgv);
|
|
40
|
+
const command = inv.command;
|
|
41
|
+
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
|
|
42
|
+
process.stdout.write(renderTopLevelHelp(cliRegistry) + "\n");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// No store directive → dispatch the command directly; its output is unchanged.
|
|
46
|
+
if (inv.store === undefined) {
|
|
47
|
+
runCommand(command, inv.commandArgs);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Store directive → wrap the command via keptRun. The scaffold makes the run dir, hands
|
|
51
|
+
// the injected route the two store-dir `--format` specs, and stamps run.yaml. The route
|
|
52
|
+
// dispatches the command with its own clean args PLUS those specs, so the command's own
|
|
53
|
+
// writer produces the stored artefacts (byte-identical to a bare `--format` run). The
|
|
54
|
+
// host peeks the conventional provenance flags off the command args to name the dir +
|
|
55
|
+
// stamp the manifest, without consuming them (the command still sees them).
|
|
56
|
+
const runId = resolveRunId({ runId: inv.runId, now: new Date() });
|
|
57
|
+
const target = peekFlag(inv.commandArgs, "target") ?? "unknown-target";
|
|
58
|
+
const features = peekFlag(inv.commandArgs, "features");
|
|
59
|
+
const sourceSha = resolveSourceSha({
|
|
60
|
+
sourceSha: peekFlag(inv.commandArgs, "source-sha"),
|
|
61
|
+
targetDir: features !== undefined ? dirname(features) : undefined,
|
|
62
|
+
}) ?? "unknown";
|
|
63
|
+
const spec = { storeRoot: inv.store, target, runId };
|
|
64
|
+
// `mode` is the command's run kind; only `balance` ships today (analyse/migrate are the
|
|
65
|
+
// private engine's, added when it lands).
|
|
66
|
+
// The TOOL's own identity (3F-1916) — which spec-controller produced this run, read
|
|
67
|
+
// from the tool's OWN dir (never cwd, which is the target). "unknown" for a null SHA,
|
|
68
|
+
// matching sourceSha's own fallback so run.yaml is internally consistent.
|
|
69
|
+
const toolIdentity = resolveToolIdentity();
|
|
70
|
+
const meta = {
|
|
71
|
+
sourceSha,
|
|
72
|
+
toolSha: toolIdentity.toolSha ?? "unknown",
|
|
73
|
+
toolVersion: toolIdentity.toolVersion,
|
|
74
|
+
mode: "balance",
|
|
75
|
+
date: new Date().toISOString(),
|
|
76
|
+
};
|
|
77
|
+
const plan = executeKeptRun(spec, meta, (formatSpecs) => runCommand(command, wrapCommandArgv(inv.commandArgs, [...formatSpecs])));
|
|
78
|
+
// The command's stdout was routed into the run dir, so tell the operator where it landed.
|
|
79
|
+
process.stdout.write(plan.runDir + "\n");
|
|
80
|
+
}
|
|
81
|
+
main(process.argv.slice(2));
|
|
82
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|