@norskvideo/ctl-dev-kit 0.1.50 → 0.1.52
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.
|
@@ -84,7 +84,22 @@
|
|
|
84
84
|
the doc lives and whether rules sit in one sweep suite
|
|
85
85
|
(`norsk-ctl-product-playout`, `-commentary`) or beside the code they guard
|
|
86
86
|
(`-probe`, `-turnkey-funke-pegasus`) is the repo's choice — the contract is
|
|
87
|
-
the IDs and the guard, not the layout.
|
|
87
|
+
the IDs and the guard, not the layout. A **customer-specific** workflow (a
|
|
88
|
+
reconciled `input.json` under `customers/<slug>/`) carries its **own**
|
|
89
|
+
`customers/<slug>/INVARIANTS.md`, scoped to that customer, seeded from
|
|
90
|
+
`testing/INVARIANTS.customer.template.md` and guarded by a parity test whose
|
|
91
|
+
`testDirs` is scoped to that customer's tests; assert against the composed
|
|
92
|
+
graph so resolved defaults (segment durations, resolutions, output count) are
|
|
93
|
+
guarded, not just literal config.
|
|
94
|
+
- **Invariants are a developer-managed contract — never edit them autonomously.**
|
|
95
|
+
An `INVARIANTS.md` (product or customer) records what a human, or a paying
|
|
96
|
+
customer, has decided must not regress. Claude / automated agents MUST NOT add,
|
|
97
|
+
edit, remove, or renumber invariant rows — or weaken the tests that guard them
|
|
98
|
+
— on their own initiative; those changes come only through deliberate
|
|
99
|
+
interaction with a developer. If a code change appears to conflict with an
|
|
100
|
+
invariant, STOP and surface it to a developer rather than "fixing" the
|
|
101
|
+
invariant to make a test pass. This is precisely what protects against
|
|
102
|
+
accidental regressions to the things that matter most.
|
|
88
103
|
- **Know who reads it — name the persona before writing docs or a
|
|
89
104
|
persona-facing surface.** Every Norsk ctl product writes for the same four
|
|
90
105
|
reader personas — Evaluator, Builder, Integrator, Operator (SRE) — defined in
|
package/package.json
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# <customer> workflow invariants
|
|
2
|
+
|
|
3
|
+
<!-- Template from @norskvideo/ctl-dev-kit testing/INVARIANTS.customer.template.md.
|
|
4
|
+
Copy to customers/<slug>/INVARIANTS.md, replace the example rows with the rules
|
|
5
|
+
THIS customer cares about, and add the parity-guard test below. This is the
|
|
6
|
+
customer-scoped sibling of INVARIANTS.template.md (which is product-scoped): use
|
|
7
|
+
this one for a reconciled per-customer input.json under customers/, and the
|
|
8
|
+
product template for the shipped product's own composed workflow. -->
|
|
9
|
+
|
|
10
|
+
> **CUSTOMER CONTRACT — developer-managed.** These are the load-bearing rules
|
|
11
|
+
> this customer deems important about their workflow. They change **ONLY**
|
|
12
|
+
> through deliberate interaction with a developer. Claude / automated agents MUST
|
|
13
|
+
> NOT add, edit, remove, or renumber the rows below — or weaken their guarding
|
|
14
|
+
> tests — on their own initiative. If a code change appears to conflict with an
|
|
15
|
+
> invariant, **stop and surface it to a developer** rather than "fixing" the
|
|
16
|
+
> invariant. This is what protects the customer from accidental regressions to
|
|
17
|
+
> the things they care about.
|
|
18
|
+
|
|
19
|
+
Each rule has a stable ID (`INV-<AREA>-<NNN>`; never renumber, never reuse a
|
|
20
|
+
retired ID) and at least one test that cites the ID verbatim — the parity guard
|
|
21
|
+
fails CI when a row has no citing test or a test cites an unknown ID. Prefer
|
|
22
|
+
asserting against the **composed Studio graph** (compose the customer's
|
|
23
|
+
`input.json` and inspect the emitted workflow), not just the raw config — that
|
|
24
|
+
way resolved defaults (segment durations, as-rendered resolutions, output count)
|
|
25
|
+
are guarded too, not only literal fields. When a code comment carries one of
|
|
26
|
+
these rules, cite the ID there too instead of restating the rule.
|
|
27
|
+
|
|
28
|
+
| ID | Invariant | Guarded by |
|
|
29
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------- |
|
|
30
|
+
| INV-PLAYLIST-001 | This customer serves legacy playlists only — every composed CMAF playlist is non-low-latency, never LL-HLS | `<slug>/<slug>-invariants.test.ts` |
|
|
31
|
+
| INV-SEG-001 | CMAF segment duration is 4s on every rendition (a resolved default — only guardable via the composed graph) | `<slug>/<slug>-invariants.test.ts` |
|
|
32
|
+
| INV-OUT-001 | Exactly three delivery rungs in a single named CMAF group, one playlist per rung | `<slug>/<slug>-invariants.test.ts` |
|
|
33
|
+
| INV-RES-001 | The delivery resolutions are exactly the ladder the customer signed off (e.g. 1280x720 / 640x360 / 320x180) | `<slug>/<slug>-invariants.test.ts` |
|
|
34
|
+
| INV-MOQ-001 | The low-latency return / monitor rides MoQ, never a WHIP/RTMP fallback | `<slug>/<slug>-invariants.test.ts` |
|
|
35
|
+
|
|
36
|
+
The rows above are worked examples (drawn from the datum commentary deployment);
|
|
37
|
+
delete them when seeding a new customer.
|
|
38
|
+
|
|
39
|
+
## Parity guard
|
|
40
|
+
|
|
41
|
+
Copy into `customers/<slug>/`-adjacent test dir as `<slug>-invariants-parity.test.ts`
|
|
42
|
+
(keep it beside the invariant tests so `testDirs` stays scoped to this customer's
|
|
43
|
+
IDs — other customers and the product's own invariants use their own guards):
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { expect, test } from "bun:test";
|
|
47
|
+
import { join } from "node:path";
|
|
48
|
+
import { invariantParityFromDisk } from "@norskvideo/ctl-dev-kit/testing/invariants";
|
|
49
|
+
|
|
50
|
+
// Adjust the number of "../" hops to reach the repo root from this test's dir.
|
|
51
|
+
const repoRoot = join(import.meta.dir, "..", "..", "..", "..");
|
|
52
|
+
|
|
53
|
+
test("every <slug> invariant has a citing test and every cited ID is tabled", () => {
|
|
54
|
+
const report = invariantParityFromDisk({
|
|
55
|
+
invariantsPath: join(repoRoot, "customers", "<slug>", "INVARIANTS.md"),
|
|
56
|
+
testDirs: [import.meta.dir],
|
|
57
|
+
});
|
|
58
|
+
expect(report.problems).toEqual([]);
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
A runtime-only invariant the composed graph can't show (e.g. a timing
|
|
63
|
+
guarantee) still gets a table row — declare its ID in the guard's `untestableIds`
|
|
64
|
+
option; the guard rejects unknown entries and flags the marker as stale once a
|
|
65
|
+
test does cite the ID.
|