@dzhechkov/harness-cli 0.3.253 → 0.3.256
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/.dz-manifest.json +15 -7
- package/README.md +177 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +768 -28
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/sbom.json +26 -6
- package/src/cli.ts +747 -24
package/.dz-manifest.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "README.md",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "5f73e2c6d9c0ea38d5324f058cbd921bdb28dab60aa97ded97979ed994426731"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "coverage/coverage-final.json",
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"path": "dist/cli.d.ts.map",
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "18d5f55ed3c56718e470fef8bd677c55de08f4505e188b1dd29a0f0bbec09d4b"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"path": "dist/cli.js",
|
|
48
|
-
"sha256": "
|
|
48
|
+
"sha256": "c47d11fd3ed7e6049b1080cd9292ed3498e113d8bc74a9af65a027d70fa96435"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
"path": "dist/cli.js.map",
|
|
52
|
-
"sha256": "
|
|
52
|
+
"sha256": "684ea0c6fa4edbbbea1ffcd943167cd374166eb196100433d4e8d02d70560794"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"path": "dist/index.d.ts",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"path": "package.json",
|
|
76
|
-
"sha256": "
|
|
76
|
+
"sha256": "154755702a4652597bf8ca1410f9a3303b76c6a8dae8dacd867921a88c7b3626"
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
"path": "src/bin.ts",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"path": "src/cli.ts",
|
|
84
|
-
"sha256": "
|
|
84
|
+
"sha256": "0dce827134ab5f7aaf55bd76325b5e1e17881770310e64e0d66bafbedc91687e"
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
"path": "src/index.ts",
|
|
@@ -91,6 +91,14 @@
|
|
|
91
91
|
"path": "test/cli.test.ts",
|
|
92
92
|
"sha256": "7d6ba26ec1188db3b3ac89f50b015ba1f0c6117b5c30d99b22da41da10c96c12"
|
|
93
93
|
},
|
|
94
|
+
{
|
|
95
|
+
"path": "test/epoch-replay-cli.test.ts",
|
|
96
|
+
"sha256": "a5a48585b565288ca1ea96030b02b2ccb37a621ef7b3cfe54cd7aaa7164d6904"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"path": "test/guard-promote-cli.test.ts",
|
|
100
|
+
"sha256": "f8e35085106a98be239104394539c3aec350123eed4a115abf14acc2cb6b5bb9"
|
|
101
|
+
},
|
|
94
102
|
{
|
|
95
103
|
"path": "tsconfig.json",
|
|
96
104
|
"sha256": "e80ef2911c3347a713dc3dca6a5f6034d91e938e58e71345ea920dd9833bcdbb"
|
|
@@ -101,5 +109,5 @@
|
|
|
101
109
|
}
|
|
102
110
|
]
|
|
103
111
|
},
|
|
104
|
-
"signature": "
|
|
112
|
+
"signature": "5nXiD5ii68CSKw5EWFT2WXxbELjZYvHBup3O5E4oLDcTszEC2kyuHXBy7VBzcrkgzNtICsxp8+hJJMcGXa2+Cw=="
|
|
105
113
|
}
|
package/README.md
CHANGED
|
@@ -132,7 +132,7 @@ point any tool at (a skill is just Markdown). Dedicated targets are on the [road
|
|
|
132
132
|
|
|
133
133
|
## User Journey — from install to mastery
|
|
134
134
|
|
|
135
|
-
All
|
|
135
|
+
All 62 commands mapped to a real workflow:
|
|
136
136
|
|
|
137
137
|
```
|
|
138
138
|
DISCOVER → INSTALL → USE → CREATE → MAINTAIN → SHARE
|
|
@@ -236,6 +236,11 @@ dz workflow --task security-audit # adversarial security scan
|
|
|
236
236
|
|
|
237
237
|
# Cross-host state sync:
|
|
238
238
|
dz roam --apply # sync agent state across machines
|
|
239
|
+
|
|
240
|
+
# Is the self-learning loop actually paying off?
|
|
241
|
+
dz compounding # readiness + payoff report (INSUFFICIENT_DATA is a real answer)
|
|
242
|
+
dz epoch-replay --mock --n 24 --effect 0.9 # $0 dry run of the cold-vs-warm verdict math
|
|
243
|
+
dz epoch-replay --emit # ready? emit the real cold-vs-warm work order
|
|
239
244
|
```
|
|
240
245
|
|
|
241
246
|
### Phase 6: Share (publish to the world)
|
|
@@ -464,6 +469,98 @@ The prompt queries that make replay possible stay on your machine: `.dz/recall-u
|
|
|
464
469
|
git-ignored, entries are truncated at 200 chars and flagged when truncated (a prefix is not a prompt,
|
|
465
470
|
so flagged rows never count as replayable).
|
|
466
471
|
|
|
472
|
+
### Did the lessons actually change outcomes? — `dz epoch-replay`
|
|
473
|
+
|
|
474
|
+
`dz compounding` says a cold-vs-warm replay **can** be run. `dz epoch-replay` **runs** it and reports
|
|
475
|
+
what it found: Epoch 0 (cold — the prompt alone) vs Epoch 1 (warm — the same prompt plus exactly the
|
|
476
|
+
lessons the apply leg injected), on the same instances, scored into a three-valued verdict.
|
|
477
|
+
|
|
478
|
+
**The test is PAIRED.** Each instance is one judgment on one prompt, so the statistic is a single
|
|
479
|
+
binomial over *decisive* pairs: `p̂ = warm wins / (warm wins + cold wins)`, with a `95%` Wilson
|
|
480
|
+
interval reported on the **lift** scale `2p̂ − 1`. Ties carry no direction — they are excluded from
|
|
481
|
+
the denominator and reported separately.
|
|
482
|
+
|
|
483
|
+
**`SUPPORTED` only when the lift interval lies entirely above zero.** `FALSIFIED` only on **harm**
|
|
484
|
+
(entirely below zero) or on a passed **non-superiority** test — the lift upper bound below a
|
|
485
|
+
pre-registered margin, default `0.05`, at 10+ decisive pairs. Everything else is `INCONCLUSIVE`, a
|
|
486
|
+
first-class honest outcome; below 5 decisive pairs there is no verdict at all.
|
|
487
|
+
|
|
488
|
+
**A tie is under-powered, not refuted.** `6/12` gives a lift interval of `[-0.492, 0.492]` — that
|
|
489
|
+
excludes nothing, so it reads `INCONCLUSIVE`. So does a *perfectly* even 500/500 over 1000 pairs
|
|
490
|
+
(lift upper `0.0619`): the most uninformative result the protocol can produce must not be reported
|
|
491
|
+
as a refutation. A refutation has to clear a bar too.
|
|
492
|
+
|
|
493
|
+
**Try it at $0 first — `--mock` generates seeded synthetic outcomes at a TRUE effect you choose, and
|
|
494
|
+
runs them through the real verdict math:**
|
|
495
|
+
|
|
496
|
+
```
|
|
497
|
+
$ dz epoch-replay --mock --n 24 --effect 0.9 --seed 20260729
|
|
498
|
+
COLD (epoch 0, no injected lessons): 1/24 CI95 [0.007, 0.202]
|
|
499
|
+
WARM (epoch 1, apply-leg lessons): 23/24 CI95 [0.798, 0.993]
|
|
500
|
+
VERDICT: SUPPORTED
|
|
501
|
+
warm 23/24 CI [0.798, 0.993] is DISJOINT above cold 1/24 CI [0.007, 0.202]
|
|
502
|
+
|
|
503
|
+
$ dz epoch-replay --mock --n 24 --effect -0.9 --seed 20260729 # → FALSIFIED (harm)
|
|
504
|
+
$ dz epoch-replay --mock --n 24 --effect 0.4 --seed 20260729 # → INCONCLUSIVE (neither separates nor excludes)
|
|
505
|
+
$ dz epoch-replay --mock --n 12 --effect 0 --seed 7 # → INCONCLUSIVE (6/6 tie: under-powered, NOT refuted)
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
```
|
|
509
|
+
$ dz epoch-replay --mock --n 24 --effect 0.9 --seed 20260729
|
|
510
|
+
SLICE: all · 24 scored instance(s) · 24 DECISIVE pair(s)
|
|
511
|
+
COLD (epoch 0, no injected lessons): 1/24 decisive CI95 [0.007, 0.202]
|
|
512
|
+
WARM (epoch 1, apply-leg lessons): 23/24 decisive CI95 [0.798, 0.993]
|
|
513
|
+
LIFT (paired, 2p−1 over decisive pairs): +0.917 [0.595, 0.985]
|
|
514
|
+
VERDICT: SUPPORTED — the lift interval lies ENTIRELY above zero
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Every mock run prints `SYNTHETIC … it is NOT evidence about the learning loop`, and the same seed
|
|
518
|
+
gives byte-identical output — a demo is a reproducer.
|
|
519
|
+
|
|
520
|
+
**Real mode: the runner ORCHESTRATES and SCORES — it never calls a model.** Generation and judging
|
|
521
|
+
happen out of band, so the core stays pure, offline and reproducible:
|
|
522
|
+
|
|
523
|
+
```
|
|
524
|
+
$ dz epoch-replay --emit # → .dz/epoch-replay/work-order.json (git-ignored: raw prompts)
|
|
525
|
+
25 instance(s) · seed 20260729 · blind A/B assignment PRE-REGISTERED
|
|
526
|
+
# 1. have an agent fill coldPlan / warmPlan for each item (symmetric length, same task)
|
|
527
|
+
$ dz epoch-replay --judge .dz/epoch-replay/work-order.json # → blind judge prompts
|
|
528
|
+
# 2. have an EXTERNAL, cross-model judge answer them → [{ "id": …, "winner": "A|B|TIE" }]
|
|
529
|
+
$ dz epoch-replay --score judgments.json --work-order .dz/epoch-replay/work-order.json --slice task
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
Three things make that blind real rather than decorative:
|
|
533
|
+
|
|
534
|
+
- **The judge sees `{id, prompt}` and nothing else.** No assignment, no arm names, no slice label,
|
|
535
|
+
not even a path back to the work order. The judge file is byte-identical whichever way the
|
|
536
|
+
assignment fell, so it cannot be decoded. (Skipped items are reported on stdout, not in the file —
|
|
537
|
+
their reasons name arms.)
|
|
538
|
+
- **The work order is integrity-checked.** It carries a sha256 digest over its pre-registered core
|
|
539
|
+
(version, seed, margin, corpus fingerprint, every `[id, warmIsA]`); `--judge` and `--score`
|
|
540
|
+
recompute it *and* re-derive every assignment from the stated seed, refusing on any mismatch. A
|
|
541
|
+
hand-written order does not buy a verdict.
|
|
542
|
+
> **Honest scope:** this is an integrity check against accidental corruption and mismatch — **not
|
|
543
|
+
> a cryptographic commitment.** The hash is self-contained, so a determined operator can re-forge
|
|
544
|
+
> it (seed-searching a matching assignment at n=12 takes a few thousand tries). The threat model
|
|
545
|
+
> is *you making a mistake*: a hand-edited file, a stale order paired with fresh judgments. The
|
|
546
|
+
> honest-use contract is procedural — emit once, then judge, and keep the emitted file. `--emit`
|
|
547
|
+
> records `emittedAt` + a corpus fingerprint and `--score` prints the digest, seed and margin, so
|
|
548
|
+
> a reviewer can ask for the original and compare three numbers.
|
|
549
|
+
- **The non-superiority margin is pre-registered.** `--margin` is accepted only at `--emit`, is
|
|
550
|
+
stored in the work order and covered by the digest, and `--score` **refuses** a `--margin` flag —
|
|
551
|
+
a margin chosen once you can see the counts is not a pre-registration, and `--margin 99` at
|
|
552
|
+
scoring time would simply buy `FALSIFIED`. Out of range (outside `(0, 0.5]`) is refused, not
|
|
553
|
+
clamped.
|
|
554
|
+
- **Corrupt input is refused, not measured.** Duplicate judgement ids exit 1 rather than counting
|
|
555
|
+
one opinion N times. Unknown ids and unparseable winners are skipped **with a reason**, never
|
|
556
|
+
guessed.
|
|
557
|
+
|
|
558
|
+
The warm arm's only delta is the injected lessons: no gold answers, no verdicts, no outcome labels
|
|
559
|
+
ever enter a work order.
|
|
560
|
+
|
|
561
|
+
**When to use:** after `dz compounding` reports the replay as READY; before claiming that recall
|
|
562
|
+
"works"; and any time you want the claim re-checked as the corpus grows.
|
|
563
|
+
|
|
467
564
|
### Usage estimates you can act on — `dz usage`
|
|
468
565
|
|
|
469
566
|
`dz usage` estimates how much of your Claude session (5h block) and week you have spent, from local
|
|
@@ -726,7 +823,7 @@ Get the whole set with `dz init --target claude-code --preset meta`, or pick one
|
|
|
726
823
|
|
|
727
824
|
> **A skill and its npx toolkit are not duplicates — they're a graduation.** Several skills (e.g. `feature-adr`, `design-thinking`) exist BOTH as a skill inside a `dz` preset AND as a standalone `npx` package. The preset's SKILL.md is **fully functional on its own** (the whole methodology — modules + references — travels with it, and it auto-activates by description), and it's the only way to compile that capability to the **non-Claude platforms** (Codex/OpenCode/Hermes/OpenClaude) via `dz`. The npx package adds **project-level runtime governance** around the same skill: a slash command, governance rules, a context shard, and (for feature-adr) reward-learning + `/harvest`. So: pick the **skill/preset** for a working capability across platforms; pick the **npx toolkit** when you want it as a governed, command-driven fixture of one project.
|
|
728
825
|
|
|
729
|
-
## All Commands (
|
|
826
|
+
## All Commands (62)
|
|
730
827
|
|
|
731
828
|
```
|
|
732
829
|
dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force]
|
|
@@ -784,11 +881,16 @@ dz discrimination-check --test <f[,f]> [--base <ref>] [--name <filter>] [--runne
|
|
|
784
881
|
dz delivery-check --slug <slug> [--context-only] [--findings <f.json>] [--strict] [--author <model>] [--json] # portable Step-10 Delivery Gate: the `manual` form that travels to every shell target — prints the 4-plane review brief (regressions ‖ security ‖ code-quality ‖ product-honesty) + artifact probes; --findings classifies a fed-back review into a fail-closed ready|blocked hand-off (only cross-validated BLOCKER/HIGH count) and writes features/<slug>/10_delivery_review.md; --strict exits 1 on blocked
|
|
785
882
|
dz skills-verify [--dir <project>] [--expect a,b] [--static] [--strict] [--timeout <s>] [--json] # does .claude/skills/ actually REGISTER? --static = instant layout scan (CI-safe, no session): flags dirs that can never register; default also starts a real session and reads the authoritative system/init listing. exit 0 pass / 1 fail / 2 inconclusive — an unobservable registration is NEVER a pass
|
|
786
883
|
dz compounding [--project <dir>] [--json] # honest learning-loop payoff report: pool write-only ratio, guard repeat-violation trajectory (rate per audit-half), cold-vs-warm replay readiness over unique untruncated prompt events, apply-leg staleness — a gate without enough data says INSUFFICIENT_DATA, never a verdict
|
|
884
|
+
dz epoch-replay --mock [--n <N>] [--effect <-1..1>] [--tie-rate <0..1>] [--seed <N>] [--slice <name>] [--margin <0..0.5>] [--json] # $0 synthetic run through the REAL verdict math; labelled SYNTHETIC, same seed = byte-identical
|
|
885
|
+
dz epoch-replay --emit [--project <dir>] [--limit <N>] [--seed <N>] [--margin <0..0.5>] [--out <file>] [--json] # cold-vs-warm work order: replayable instances + PRE-REGISTERED blind A/B assignment + the PRE-REGISTERED non-superiority margin + a sha256 integrity digest + emittedAt/corpus fingerprint (raw prompts — defaults into the git-ignored .dz/epoch-replay/)
|
|
886
|
+
dz epoch-replay --judge <filled-work-order.json> [--out <file>] [--json] # blind judge prompts from the filled plans — the file carries {id, prompt} ONLY (no assignment, no arm names); refuses an order whose digest or seed-derived assignment does not check out; half-pairs skipped WITH A REASON, on stdout
|
|
887
|
+
dz epoch-replay --score <judgments.json> --work-order <file> [--slice <name>] [--json] # un-blind against the VERIFIED pre-registered assignment; ONE paired binomial over DECISIVE pairs (ties excluded, reported) → SUPPORTED only when the lift interval (2p−1) lies entirely above zero; FALSIFIED only on harm or a passed non-superiority test (lift upper bound below the margin PRE-REGISTERED in the work order, default 0.05, at 10+ decisive pairs); else INCONCLUSIVE (min 5 decisive pairs). Refuses a forged work order, a --margin flag, or duplicate judgement ids; the verdict is data, not an exit code
|
|
787
888
|
dz score --slug <feature> [--project <dir>] [--json] # process scorecard for ONE feature-adr run, from its artifacts: ADR confirmation, discrimination proof, cross-model QE grade, live verification, README-first, learning loop, amendments — DESCRIPTIVE-ONLY (a low score exits 0); evidence lines are shown so the reader judges the heuristics
|
|
788
889
|
dz backlog <sub> add "<idea>" | list | show <id> | goals [--validate] | roulette [--seed n] [--commit <id>] | enrich <id> | jira <id> | harmonize [--apply] # brain-backed idea backlog: capture an idea → semantic dedup against past ideas/features via the REUSED agentdb vector engine (DUPLICATE≥0.92 / RELATED / NEW) + GoalMap alignment ("map+compass") → weighted seeded roulette picks one to work on → enrich STAGES an idea2prd hand-off → jira writes an auditable outbox via a configurable MCP adapter seam (jira-mcp|copilot-mcp|none). No 2nd vector store; without agentdb it degrades to exact-text dedup (honest)
|
|
789
890
|
dz sign --init --out <path> | --pack <dir> --key <path> # --init: generate the Ed25519 keypair (private OUTSIDE the repo, prints the public key for keys/dz.pub); else sign a pack's manifest + CycloneDX SBOM
|
|
790
891
|
dz sbom --pack <dir> [--out <file>] # emit the CycloneDX 1.5 SBOM for a pack standalone (file-level bill of materials); print to stdout or write to a file
|
|
791
892
|
dz guard check --op <publish|teach|consolidate|reindex> [--text <s>] [--json] [--force <reason>] # declarative constraint layer before self-mutating ops: HARD violation → block (exit 1), SOFT → warn; zero-config defaults, .dz/guard.json to customise; dz guard --init | dz guard log (append-only audit). dz publish runs it automatically (--no-guard "<reason>" = logged escape hatch)
|
|
893
|
+
dz guard promote [--dry-run | --apply] [--window-days <N>] [--periods <N>] [--json] # lesson → guard-rule promotion: ranks lessons by firings × cost, SHADOW-replays each candidate over real commits, and proposes a rule only after TWO consecutive wins AND two window-lengths of REAL elapsed time since first observation (a window with zero firings resets the counter; a thin window is skipped, never a loss; commit dates are author-supplied, so elapsed time is measured by the local clock in .dz/promotion-state.json). Proposes by default (--dry-run writes nothing — and so never starts that clock; --apply installs it SOFT — a promoted rule can never block, and a same-id/different-body clash is a CONFLICT that exits 1 rather than claiming success); promotions AND refusals are recorded in features/guard-promotion/promotions/
|
|
792
894
|
dz feature-adr-setup --guards [--loc-cap <n>] [--apply] # P3: scaffold DETERMINISTIC guard tests into the project — guards.config.json + a zero-dependency check.mjs runner (LOC cap, secret scan, frozen-file sha256 pins, waivers-with-reasons); wire `node architecture/guards/check.mjs` into CI
|
|
793
895
|
dz publish [--filter <name>] [--bump-only] [--claim-check <off|warn|error>] (dry-run by default; pass --yes/--confirm to go live; claim-check gate defaults to warn — surfaces README claim findings, never blocks)
|
|
794
896
|
dz parity [--target <name>] [--json] # honest feature×target map COMPUTED from the capability model — full / manual (via which form) / absent, per target
|
|
@@ -1984,6 +2086,79 @@ zero specifiers, and then report every real dependency as "not recorded". The ru
|
|
|
1984
2086
|
`.dz/guard.json` cannot promote it to HARD, since a parser designed to admit "I may not understand this
|
|
1985
2087
|
file" must never be able to block a publish.
|
|
1986
2088
|
|
|
2089
|
+
#### `dz guard promote` — when a LESSON has earned the right to become a rule
|
|
2090
|
+
|
|
2091
|
+
`dz compounding` MEASURED the uncomfortable half of the learning loop (reproducer: `dz compounding
|
|
2092
|
+
--json` on this repo, 2026-07-29): 27 of 154 lessons had ever been injected — ~82 % write-only —
|
|
2093
|
+
while the lessons that *did* become guard rules collapsed their own violation rate
|
|
2094
|
+
(`no-workspace-star` 31 → 0, `readme-first` 49 → 4). Rules compound; notes-to-self do not.
|
|
2095
|
+
`dz guard promote` is the elevator between them — the cost-of-detection ladder made executable.
|
|
2096
|
+
|
|
2097
|
+
```bash
|
|
2098
|
+
dz guard promote --dry-run # inspect only: computes and prints, writes NOTHING
|
|
2099
|
+
dz guard promote # writes the proposal + refusal documents (never .dz/guard.json)
|
|
2100
|
+
dz guard promote --apply # installs the promoted rule(s) — SOFT severity, always
|
|
2101
|
+
dz guard promote --window-days 30 --periods 4 # widen the evidence window for a rarer violation
|
|
2102
|
+
```
|
|
2103
|
+
```
|
|
2104
|
+
dz guard promote — lesson → guard-rule promotion (two consecutive shadow wins required)
|
|
2105
|
+
|
|
2106
|
+
corpus: 1 lesson(s) · 0 quarantined · 13 change(s) over 4 × 7d window(s)
|
|
2107
|
+
|
|
2108
|
+
RANKED CANDIDATES (score = firings × cost, cost = 1 + lesson uses — cost is a PROXY, not a token figure):
|
|
2109
|
+
★ [ 12] promoted-pairing-package-json-changelog-md PROMOTE
|
|
2110
|
+
promote: 2 consecutive shadow win(s) over 2 evaluated period(s), 12 real firing(s)
|
|
2111
|
+
periods (oldest→newest): –0/0 –0/0 W7/7 W5/6
|
|
2112
|
+
evidence: 24f0d72ab2be: package.json changed without any **/CHANGELOG.md
|
|
2113
|
+
|
|
2114
|
+
WROTE: features/guard-promotion/promotions/001-promoted-pairing-package-json-changelog-md.md
|
|
2115
|
+
Nothing was written to .dz/guard.json — re-run with --apply to install the promoted rule(s).
|
|
2116
|
+
```
|
|
2117
|
+
|
|
2118
|
+
**A candidate must win twice, on real history.** Each lesson is reduced to a fixed *rule template*
|
|
2119
|
+
(`pairing-check` = X changed without Y · `absence-check` · `format-match`), then **shadow-replayed
|
|
2120
|
+
over actual commits** in consecutive time windows. A window in which the check would have fired on a
|
|
2121
|
+
real commit is a *win*; a window with zero firings is a *loss* and **resets the counter**; a window
|
|
2122
|
+
with fewer than 5 commits is *skipped*, never counted against it. Two consecutive wins are required,
|
|
2123
|
+
and every win cites a commit sha you can `git show`. Windows are wall-clock, so re-running the
|
|
2124
|
+
command can never manufacture a win — the counter is recomputed from history, never accumulated.
|
|
2125
|
+
|
|
2126
|
+
**And it must have been watched for two real window-lengths.** Commit timestamps are author-supplied
|
|
2127
|
+
(`GIT_COMMITTER_DATE`, a rebase, a repo import, clock skew), so history that *looks* like two windows
|
|
2128
|
+
can be laid down in one afternoon. A second clock closes that: promotion also requires
|
|
2129
|
+
`now − firstSeen ≥ 2 × --window-days` of real elapsed time, measured by the local clock and
|
|
2130
|
+
journalled in `.dz/promotion-state.json` the first time the candidate is recorded. Consequences worth
|
|
2131
|
+
knowing up front: a brand-new repo waits 14 days for its first promotion however strong the evidence,
|
|
2132
|
+
and a `--dry-run`-only workflow never promotes, because the run that would start the clock is exactly
|
|
2133
|
+
the one that writes nothing. (The `wait` reason spells both out.) This is a guard against accidental
|
|
2134
|
+
self-gaming, not a cryptographic one — the state file is local and editable.
|
|
2135
|
+
|
|
2136
|
+
**It refuses out loud.** Rule code is never synthesised from lesson text — a lesson that fits no
|
|
2137
|
+
template is listed as not-promotable *with the reason*, and the refusals are recorded in
|
|
2138
|
+
`features/guard-promotion/promotions/` alongside the promotions. A candidate already covered by an
|
|
2139
|
+
existing rule is refused as a duplicate naming that rule; a quarantined lesson (an unproven
|
|
2140
|
+
hypothesis) is refused but still told what it *would* become, so the list reads as a roadmap:
|
|
2141
|
+
```
|
|
2142
|
+
✗ [ 0] promoted-pairing-package-json-pnpm-lock-yaml NOT-PROMOTABLE
|
|
2143
|
+
not-promotable: quarantined (an unproven hypothesis must not become an enforced rule) — it WOULD
|
|
2144
|
+
classify as pairing-check, and is already covered by 'lockfile-in-sync'; confirm it with
|
|
2145
|
+
`dz teach --reinforce` to make it eligible
|
|
2146
|
+
```
|
|
2147
|
+
|
|
2148
|
+
**A promoted rule can never block.** It is written `soft`, and `resolveRules` forces SOFT for every
|
|
2149
|
+
template-backed rule — a hand-edited `"severity": "hard"` in `.dz/guard.json` is ignored. It is a real
|
|
2150
|
+
rule, though, not an inert config entry: it fires in `dz guard check --op publish` from the next run.
|
|
2151
|
+
|
|
2152
|
+
**`--apply` compares rule BODIES, not ids.** Re-applying an identical rule is a no-op and exits 0.
|
|
2153
|
+
An existing `.dz/guard.json` rule that shares the derived id but has a different body is a
|
|
2154
|
+
**conflict**: nothing is written, the clash is printed on a `✗ CONFLICT` line (and in the JSON
|
|
2155
|
+
`conflicts[]`), and the command exits **1** — a zero exit for work that was deliberately not done
|
|
2156
|
+
would be the same kind of lie the feature exists to remove.
|
|
2157
|
+
|
|
2158
|
+
*When to use it:* after a stretch of work that taught you something mechanical ("X must change with
|
|
2159
|
+
Y"), run `dz guard promote --dry-run` at session end. If a candidate has two wins, `--apply` it and
|
|
2160
|
+
that class of mistake becomes a layer-1 check instead of something you have to remember.
|
|
2161
|
+
|
|
1987
2162
|
### `dz feature-adr-setup --guards` — when a project rule should be a TEST, not a reviewer's memory
|
|
1988
2163
|
|
|
1989
2164
|
A 700-line cap enforced by reviewer judgment catches one file and misses its sibling in the same change;
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAuWH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACvD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA4gO9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA4J5E"}
|