@orkestrel/scaffold 0.0.26 → 0.0.28
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/dist/bin/main.js +5 -1
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +183 -54
- package/dist/host/claude/agents/orkestrel.md +65 -48
- package/dist/host/claude/agents/researcher.md +1 -0
- package/dist/host/claude/agents/scout.md +1 -0
- package/dist/host/claude/rules/architecture.md +19 -0
- package/dist/host/claude/rules/documentation.md +2 -0
- package/dist/host/claude/rules/quality.md +3 -0
- package/dist/host/claude/rules/tests.md +25 -8
- package/dist/host/claude/rules/workspace.md +20 -9
- package/dist/host/guides/scaffold.md +98 -14
- package/dist/host/scripts/codex.sh +0 -0
- package/dist/host/scripts/cursor.sh +0 -0
- package/dist/host/scripts/deps.sh +0 -0
- package/dist/host/scripts/ollama.sh +0 -0
- package/dist/host/tests/config.test.ts +56 -1
- package/dist/src/core/index.cjs +167 -32
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +91 -16
- package/dist/src/core/index.d.ts +91 -16
- package/dist/src/core/index.js +164 -33
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +35 -4
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +36 -5
- package/dist/src/server/index.js.map +1 -1
- package/package.json +8 -8
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Orchestration
|
|
2
2
|
|
|
3
|
-
How agents are dispatched, supervised, and accepted. Every
|
|
3
|
+
How agents are dispatched, how long-running work is supervised, and how both are accepted. Every
|
|
4
|
+
harness follows this file.
|
|
4
5
|
|
|
5
6
|
## Authority
|
|
6
7
|
|
|
@@ -216,6 +217,13 @@ clobbered edits, formatter and build races, cache phantoms, and validation cross
|
|
|
216
217
|
sharing one working tree contaminate each other's readings in both directions.
|
|
217
218
|
6. After integration, clear shared caches if needed, then have one independent `verifier` run the
|
|
218
219
|
authoritative tree-wide sweep. A writer's self-report never establishes green.
|
|
220
|
+
7. The Orchestrator's own sweep is a writing dispatch and queues behind the units that own those
|
|
221
|
+
files. A script that fixes one thing across every target is the easiest way to break rule 1,
|
|
222
|
+
because it does not feel like a dispatch — nobody was named, no brief was written, and it
|
|
223
|
+
finishes in seconds. It still writes into trees a live unit owns, and a unit whose brief it
|
|
224
|
+
invalidates will repair the same drift the other way and report a state that is already false.
|
|
225
|
+
Run it before the units, or after them, or send the decision to every unit in flight per the
|
|
226
|
+
mid-campaign rule under **Dispatch anatomy**. Never beside them.
|
|
219
227
|
|
|
220
228
|
## Execution loop
|
|
221
229
|
|
|
@@ -347,6 +355,24 @@ The harness bridge names the concrete mechanism for each of these.
|
|
|
347
355
|
- Promote anything that must outlive the campaign into a durable artifact before the sweep — a
|
|
348
356
|
commit message, a guide, a rule, a retrospective. What is only in a swept file did not survive.
|
|
349
357
|
|
|
358
|
+
### Where campaign artifacts live
|
|
359
|
+
|
|
360
|
+
- Put every campaign artifact in the **orchestrator's** repository under `.orkestrel/<package>/`,
|
|
361
|
+
named for the package the campaign is about.
|
|
362
|
+
- Never put them in the package they are about. A published package's tree is its product.
|
|
363
|
+
- Claim nothing outside `.orkestrel/` unless Orkestrel scaffold mandates it. Everything Orkestrel
|
|
364
|
+
owns in a consumer's tree lives beneath that folder, so a convention can be settled there without
|
|
365
|
+
colliding with a convention that is not Orkestrel's.
|
|
366
|
+
- Keep the campaign narrative and every ruling in the durable artifact that owns it — the guide for
|
|
367
|
+
product truth, a rule or role file for process truth, the commit message for the decision itself.
|
|
368
|
+
Use `ROADMAP.md` only where the repository already keeps one.
|
|
369
|
+
- Prefer a mechanism that recomputes a fact over a document that records it. A ledger of live state
|
|
370
|
+
is stale from the moment it is written, and the next campaign reads it as current. Where the fact
|
|
371
|
+
can be derived, derive it: the fleet's publish order lives in the catalog table `scaffold catalog`
|
|
372
|
+
regenerates, not in a written order anyone has to remember to update.
|
|
373
|
+
- Prune the campaign folder in a commit at acceptance. The tree ends clean and the record stays
|
|
374
|
+
recoverable by hash. Git history is the archive; the working tree is the workspace.
|
|
375
|
+
|
|
350
376
|
### Required sections
|
|
351
377
|
|
|
352
378
|
- **Role and engine.** The named role and its explicit engine.
|
|
@@ -416,6 +442,70 @@ wrong is right to stop.
|
|
|
416
442
|
After reconciling findings into briefs, walk the retained finding list once. Every finding names
|
|
417
443
|
the brief item that carries it. A finding with no carrier is a dropped finding.
|
|
418
444
|
|
|
445
|
+
## Long-running commands
|
|
446
|
+
|
|
447
|
+
A bench exec, a Workflow, an install, a build, and a publish chain are one class of thing: a
|
|
448
|
+
command that outlives the turn that started it. Every law here binds all of them.
|
|
449
|
+
|
|
450
|
+
### Launching
|
|
451
|
+
|
|
452
|
+
- The Orchestrator launches every long command as a harness-tracked background command under a hard
|
|
453
|
+
time cap. Never detach one from inside a dispatched agent. The harness owns the lifecycle,
|
|
454
|
+
completion re-invokes the session, and the cap kills a wedged command loudly instead of trusting
|
|
455
|
+
the agent to report its own failure. A wedged bridge is silent, and silence must never read as
|
|
456
|
+
progress.
|
|
457
|
+
- Write a multi-step chain to a script file and run the file. A chain composed inside one shell
|
|
458
|
+
argument cannot be read back, corrected, or re-run, and the record of what actually ran is the
|
|
459
|
+
argument text in a transcript rather than a file on disk.
|
|
460
|
+
- Detach anything that must survive its launching shell with `setsid`. A backgrounded flow the
|
|
461
|
+
harness reaps mid-step leaves the work half done and the exit status missing, and the reap looks
|
|
462
|
+
identical to the step failing.
|
|
463
|
+
- Size the cap from the observed high mark of comparable commands, plus an independently budgeted
|
|
464
|
+
gate allowance, plus explicit slack. Never size it from the estimate alone.
|
|
465
|
+
- Run the first use of any CLI flag, subcommand, quoting form, or stdin combination in a throwaway
|
|
466
|
+
probe. Never inside a dispatched unit or a publish chain.
|
|
467
|
+
- A launch is not a launch until its record grows past its header. Confirm the log advanced beyond
|
|
468
|
+
the head before recording that the command started, and treat an instantly-dead log as a failed
|
|
469
|
+
launch whose tail is the evidence.
|
|
470
|
+
- Keep network-dependent work out of sandboxed bench execs. Bench sandboxes deny network, so
|
|
471
|
+
lockfile generation, real installs, and live fetches belong to the Orchestrator's own tracked
|
|
472
|
+
commands or a network-capable native agent. A bench exec hanging on `npm` until its cap fires is
|
|
473
|
+
the signature of this misroute, not of a slow bench.
|
|
474
|
+
- A Workflow journals identically and dies identically, so give it the same watch — with one
|
|
475
|
+
correction. A workflow journal writes only at agent start and result, so its mtime goes quiet for
|
|
476
|
+
minutes during healthy work, and the liveness signal is the newest subagent transcript instead. A
|
|
477
|
+
watch that reports only new events cannot report a death, because silence and progress look the
|
|
478
|
+
same; the filter must fire on absence. Recover with `resumeFromRunId`, which returns every
|
|
479
|
+
completed agent from cache and re-runs only what never finished.
|
|
480
|
+
|
|
481
|
+
### Reading liveness
|
|
482
|
+
|
|
483
|
+
Read liveness from the artifact the work produces, never from its wrapper. A subagent's transcript
|
|
484
|
+
file can report zero bytes while the agent is working normally, so an empty or stale wrapper proves
|
|
485
|
+
nothing.
|
|
486
|
+
|
|
487
|
+
- Judge a unit by what it has changed in the tree: modification times on the files it owns, the
|
|
488
|
+
counts its suite reports, the report it was told to write.
|
|
489
|
+
- Check that before killing anything. A healthy unit killed on a false signal loses everything it
|
|
490
|
+
had not yet written down, and the loss is charged to the orchestrator, not the unit.
|
|
491
|
+
- If a unit must be stopped, say plainly that it was stopped and why, then assess the tree it left
|
|
492
|
+
rather than assuming its partial bytes are either good or worthless.
|
|
493
|
+
- Follow the deviation ladder for a stalled journal or a cap-killed exec, using the session id from
|
|
494
|
+
the journal head as the recovery handle.
|
|
495
|
+
|
|
496
|
+
### Confirm dead before relaunching
|
|
497
|
+
|
|
498
|
+
- Prove the previous run is gone before starting another. List the processes and read the list. A
|
|
499
|
+
second run started beside a live first one produces failures that read as the subject's — a
|
|
500
|
+
publish chain relaunched over a live one reports `EOTP` and `E403` that are its own two processes
|
|
501
|
+
colliding, and both readings point at the registry.
|
|
502
|
+
- Kill by process id, never by pattern. `pkill -f` matches the relaunch that is already starting, so
|
|
503
|
+
the pattern that cleans up the old run kills the new one and the cleanup reads as a launch
|
|
504
|
+
failure.
|
|
505
|
+
- Read a failure against what was running when it happened, not against what you believe was
|
|
506
|
+
running. The check costs one command and is the only thing that separates a real failure from
|
|
507
|
+
self-inflicted contention.
|
|
508
|
+
|
|
419
509
|
## Bench laws
|
|
420
510
|
|
|
421
511
|
External engines widen capacity. They never inherit authority. Treat every bench output as a
|
|
@@ -424,6 +514,9 @@ proposal or hypothesis until it is verified against source and accepted by the O
|
|
|
424
514
|
A bench is cross-provider reach only. Never send a model across a bridge when the running harness
|
|
425
515
|
hosts it natively.
|
|
426
516
|
|
|
517
|
+
A bench exec is a long-running command, so every law under **Long-running commands** binds it too.
|
|
518
|
+
This section adds what is true of a bench and nothing else.
|
|
519
|
+
|
|
427
520
|
Every bridge verifies before running that its CLI resolves and its bench is authenticated, and stops
|
|
428
521
|
with a deviation report naming the fallback when either fails. The role file owns the exact
|
|
429
522
|
invocation, flags, paths, probe, and recovery ladder; these four laws bind every bench regardless of
|
|
@@ -452,45 +545,6 @@ transport.
|
|
|
452
545
|
is dispatched and as it returns, because each encodes knowledge that costs real money to
|
|
453
546
|
re-derive and none of it is reproducible from the diff.
|
|
454
547
|
|
|
455
|
-
### Where campaign artifacts live
|
|
456
|
-
|
|
457
|
-
- Put every campaign artifact in the **orchestrator's** repository under `.orkestrel/<package>/`,
|
|
458
|
-
named for the package the campaign is about.
|
|
459
|
-
- Never put them in the package they are about. A published package's tree is its product.
|
|
460
|
-
- Claim nothing outside `.orkestrel/` unless Orkestrel scaffold mandates it. Everything Orkestrel
|
|
461
|
-
owns in a consumer's tree lives beneath that folder, so a convention can be settled there without
|
|
462
|
-
colliding with a convention that is not Orkestrel's.
|
|
463
|
-
- Keep the campaign narrative and every ruling in the durable artifact that owns it — the guide for
|
|
464
|
-
product truth, a rule or role file for process truth, the commit message for the decision itself.
|
|
465
|
-
Use `ROADMAP.md` only where the repository already keeps one.
|
|
466
|
-
- Prune the campaign folder in a commit at acceptance. The tree ends clean and the record stays
|
|
467
|
-
recoverable by hash. Git history is the archive; the working tree is the workspace.
|
|
468
|
-
|
|
469
|
-
### Launching a long exec
|
|
470
|
-
|
|
471
|
-
- The Orchestrator launches every long bench exec as a harness-tracked background command under a
|
|
472
|
-
hard time cap. Never detach one from inside a bridge agent. The harness owns the lifecycle,
|
|
473
|
-
completion re-invokes the session, and the cap kills a wedged bench loudly instead of trusting
|
|
474
|
-
the bridge to report its own failure. A wedged bridge is silent, and silence must never read as
|
|
475
|
-
progress.
|
|
476
|
-
- A Workflow journals identically and dies identically, so give it the same watch — with one
|
|
477
|
-
correction. A workflow journal writes only at agent start and result, so its mtime goes quiet for
|
|
478
|
-
minutes during healthy work, and the liveness signal is the newest subagent transcript instead. A
|
|
479
|
-
watch that reports only new events cannot report a death, because silence and progress look the
|
|
480
|
-
same; the filter must fire on absence. Recover with `resumeFromRunId`, which returns every
|
|
481
|
-
completed agent from cache and re-runs only what never finished.
|
|
482
|
-
- Size the cap from the observed high mark of comparable units, plus an independently budgeted gate
|
|
483
|
-
allowance, plus explicit slack. Never size it from the estimate alone.
|
|
484
|
-
- Run the first use of any CLI flag, subcommand, quoting form, or stdin combination in a throwaway
|
|
485
|
-
probe. Never inside a dispatched unit.
|
|
486
|
-
- A launch is not a launch until the journal grows past its header. Confirm the event stream
|
|
487
|
-
advanced beyond the session-configured head before recording that the exec started, and treat an
|
|
488
|
-
instantly-dead journal as a failed launch whose tail is the evidence.
|
|
489
|
-
- Keep network-dependent work out of sandboxed bench execs. Bench sandboxes deny network, so
|
|
490
|
-
lockfile generation, real installs, and live fetches belong to the Orchestrator's own tracked
|
|
491
|
-
commands or a network-capable native agent. A bench exec hanging on `npm` until its cap fires is
|
|
492
|
-
the signature of this misroute, not of a slow bench.
|
|
493
|
-
|
|
494
548
|
### Recovering a dark bench
|
|
495
549
|
|
|
496
550
|
- A probe that finds a bench binary present but authentication unavailable starts recovery in the
|
|
@@ -503,20 +557,95 @@ transport.
|
|
|
503
557
|
cannot complete, record the bench dark, name the fallback in the plan, and say so.
|
|
504
558
|
- The role file owns each bench's exact login command and probe.
|
|
505
559
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
560
|
+
## Publishing the fleet
|
|
561
|
+
|
|
562
|
+
Publishing is the user's decision and the user's credential. The Orchestrator prepares, surfaces
|
|
563
|
+
the approval, and runs the publishes the user asked for. It never substitutes an API key, an access
|
|
564
|
+
token, a copied auth file, or another login flow, and it never asks the user to paste a token into
|
|
565
|
+
the conversation.
|
|
566
|
+
|
|
567
|
+
A publish chain is a long-running command, so every law under **Long-running commands** binds it:
|
|
568
|
+
write the chain to a file, detach it with `setsid`, and confirm the previous one is dead before
|
|
569
|
+
starting another.
|
|
570
|
+
|
|
571
|
+
### What a bump obliges
|
|
572
|
+
|
|
573
|
+
A runtime dependency and a development dependency have different blast radius, and confusing them
|
|
574
|
+
either publishes packages nobody needed to publish or leaves a consumer pinned to an older release.
|
|
575
|
+
|
|
576
|
+
- A **runtime** `dependencies` bump reaches every consumer of the published package. Every package
|
|
577
|
+
downstream of it re-pins, re-runs its gates, bumps, and republishes, in layer order.
|
|
578
|
+
- A **development** `devDependencies` bump reaches nobody. Re-pin it, prove the gates still green,
|
|
579
|
+
and commit to `main`. Do not bump the version and do not publish.
|
|
580
|
+
- A development bump that forces a change to `src` or `app` is no longer a development bump. The
|
|
581
|
+
published types or runtime moved, so that package bumps and publishes on its own account, and
|
|
582
|
+
its own dependents follow the runtime rule above.
|
|
583
|
+
|
|
584
|
+
Every package is `0.0.x`, where a caret pins one exact release. A dependent therefore sees a new
|
|
585
|
+
version only after it re-pins and republishes, so the fleet publishes in topological layer order
|
|
586
|
+
derived from runtime `dependencies` alone. Layers exist for a reason a flat pass cannot fix: two
|
|
587
|
+
ranges that disagree install two copies of the same package, and the compiler reads them as two
|
|
588
|
+
distinct types.
|
|
589
|
+
|
|
590
|
+
Read the order from the catalog table in `.claude/agents/orkestrel.md`, which `scaffold catalog`
|
|
591
|
+
regenerates from the registry. Its `Layer` column is the publish round. Regenerate it before
|
|
592
|
+
sequencing a cascade rather than trusting the copy in the tree, and never write a second order down
|
|
593
|
+
somewhere else.
|
|
594
|
+
|
|
595
|
+
The tooling packages sit outside that order because nothing depends on them at runtime. `scaffold`
|
|
596
|
+
is a development dependency of every package, including packages it depends on itself, so a runtime
|
|
597
|
+
layering would report a cycle that does not exist. Each package builds against the already-published
|
|
598
|
+
`scaffold`, never against an unpublished one, and a `scaffold` release therefore publishes on its own
|
|
599
|
+
and propagates as files rather than as a cascade.
|
|
600
|
+
|
|
601
|
+
### Preparing
|
|
602
|
+
|
|
603
|
+
1. **Bump from what the registry serves, not from the local manifest.** A repository's `version`
|
|
604
|
+
can sit a release behind what was published from another checkout, and bumping that produces a
|
|
605
|
+
version the registry already holds, which fails on upload after the whole gate chain has run.
|
|
606
|
+
Read the registry first.
|
|
607
|
+
2. **Prepare a whole layer before authenticating.** Bump each version, re-pin every `@orkestrel`
|
|
608
|
+
range to what the registry serves now, install, and run the package's own `prepublishOnly` to
|
|
609
|
+
green. Move any self-pin in source with the manifest. Commit and push before the window opens.
|
|
610
|
+
3. **Prepare the next layer only after this one is on the registry.** A dependent's new pin cannot
|
|
611
|
+
install until the version it names exists, so preparation and publication interleave and cannot
|
|
612
|
+
be batched ahead.
|
|
613
|
+
|
|
614
|
+
The window is for uploads. Every gate, build, install, and commit happens outside it, which is what
|
|
615
|
+
makes `--ignore-scripts` the right flag at publish time: the artifact was already proved, and the
|
|
616
|
+
flag is what stops the gate chain running a second time inside the five minutes.
|
|
617
|
+
|
|
618
|
+
### Reaching the approval
|
|
619
|
+
|
|
620
|
+
- **Log in first** when the session is new or a day has passed. `npm login` and `npm publish` reach
|
|
621
|
+
the same browser approval, and a publish that has to run the login flow spends the window on it.
|
|
622
|
+
- `npm login` backgrounded with stdin at EOF falls through to a legacy `Username:` prompt and exits
|
|
623
|
+
**zero** without authenticating. Confirm with `npm whoami` rather than an exit code.
|
|
624
|
+
- npm offers its browser approval only when it sees a TTY. Without one it fails `EOTP` and there is
|
|
625
|
+
no way to answer it. Run the login, and the first publish of a layer, under
|
|
626
|
+
`script -qfc '<command>' <log>` with stdin read from a fifo a long `sleep` holds open.
|
|
627
|
+
- npm prints `Press ENTER to open in the browser` and does not begin polling until that is
|
|
628
|
+
acknowledged. Send a newline into the fifo. The browser it tries to open does not exist in a
|
|
629
|
+
headless container, which is harmless.
|
|
630
|
+
- Surface the approval URL to the user the moment it appears in the log, and say that approving it
|
|
631
|
+
opens a five-minute window covering the rest of the layer.
|
|
632
|
+
|
|
633
|
+
### Spending the window
|
|
634
|
+
|
|
635
|
+
- The window opens when the user approves, not when the first publish starts. Chain every remaining
|
|
636
|
+
publish inside the same process as the gate package, so no human turn sits inside it.
|
|
637
|
+
- Publish serially. Concurrent publishes collide on the auth handshake and fail each other.
|
|
638
|
+
- `EOTP` inside the window is intermittent contention rather than the window closing. Retry each
|
|
639
|
+
package about three times before recording it failed, and retry a failed set once the layer ends;
|
|
640
|
+
packages have landed on the third attempt and on a later pass with no new approval.
|
|
641
|
+
- Expect a large layer to outlast one window. Size batches to what uploads in five minutes and tell
|
|
642
|
+
the user how many approvals to expect, rather than discovering it mid-run.
|
|
643
|
+
- Read the result from the registry, not from an exit code: a piped `npm publish` reports the exit
|
|
644
|
+
status of the pipeline, and a CDN read straight after a publish can still serve the previous
|
|
645
|
+
version.
|
|
646
|
+
- Re-read the registry before telling the user a package failed. A chain still running, a retry that
|
|
647
|
+
landed, and CDN lag all produce a failure reading that the registry contradicts, and a false
|
|
648
|
+
failure report costs a needless approval and a needless republish.
|
|
520
649
|
|
|
521
650
|
## Acceptance laws
|
|
522
651
|
|
|
@@ -34,49 +34,49 @@ so network-controlled descriptions never enter agent instruction context.
|
|
|
34
34
|
|
|
35
35
|
<!-- orkestrel:catalog -->
|
|
36
36
|
|
|
37
|
-
| Package | Version |
|
|
38
|
-
| ----------------------- | -------- |
|
|
39
|
-
| `@orkestrel/abort` | `0.0.
|
|
40
|
-
| `@orkestrel/agent` | `0.0.
|
|
41
|
-
| `@orkestrel/browser` | `0.0.
|
|
42
|
-
| `@orkestrel/budget` | `0.0.
|
|
43
|
-
| `@orkestrel/console` | `0.0.
|
|
44
|
-
| `@orkestrel/contract` | `0.0.
|
|
45
|
-
| `@orkestrel/csv` | `0.0.
|
|
46
|
-
| `@orkestrel/database` | `0.0.
|
|
47
|
-
| `@orkestrel/emitter` | `0.0.
|
|
48
|
-
| `@orkestrel/guide` | `0.0.
|
|
49
|
-
| `@orkestrel/html` | `0.0.
|
|
50
|
-
| `@orkestrel/indexeddb` | `0.0.
|
|
51
|
-
| `@orkestrel/interpret` | `0.0.
|
|
52
|
-
| `@orkestrel/markdown` | `0.0.
|
|
53
|
-
| `@orkestrel/mcp` | `0.0.
|
|
54
|
-
| `@orkestrel/middleware` | `0.0.
|
|
55
|
-
| `@orkestrel/msg` | `0.0.
|
|
56
|
-
| `@orkestrel/ndjson` | `0.0.
|
|
57
|
-
| `@orkestrel/ollama` | `0.0.
|
|
58
|
-
| `@orkestrel/pool` | `0.0.
|
|
59
|
-
| `@orkestrel/program` | `0.0.
|
|
60
|
-
| `@orkestrel/qualifier` | `0.0.
|
|
61
|
-
| `@orkestrel/queue` | `0.0.
|
|
62
|
-
| `@orkestrel/rater` | `0.0.
|
|
63
|
-
| `@orkestrel/reason` | `0.0.
|
|
64
|
-
| `@orkestrel/relation` | `0.0.
|
|
65
|
-
| `@orkestrel/router` | `0.0.
|
|
66
|
-
| `@orkestrel/scaffold` | `0.0.
|
|
67
|
-
| `@orkestrel/sea` | `0.0.
|
|
68
|
-
| `@orkestrel/server` | `0.0.
|
|
69
|
-
| `@orkestrel/sqlite` | `0.0.
|
|
70
|
-
| `@orkestrel/sse` | `0.0.
|
|
71
|
-
| `@orkestrel/template` | `0.0.
|
|
72
|
-
| `@orkestrel/terminal` | `0.0.
|
|
73
|
-
| `@orkestrel/timeout` | `0.0.
|
|
74
|
-
| `@orkestrel/tool` | `0.0.
|
|
75
|
-
| `@orkestrel/toolbox` | `0.0.
|
|
76
|
-
| `@orkestrel/websocket` | `0.0.
|
|
77
|
-
| `@orkestrel/worker` | `0.0.
|
|
78
|
-
| `@orkestrel/workflow` | `0.0.
|
|
79
|
-
| `@orkestrel/workspace` | `0.0.
|
|
37
|
+
| Package | Version | Layer | Runtime dependencies |
|
|
38
|
+
| ----------------------- | -------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
39
|
+
| `@orkestrel/abort` | `0.0.6` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
40
|
+
| `@orkestrel/agent` | `0.0.15` | L5 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/budget` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/queue` `^0.0.8`, `@orkestrel/timeout` `^0.0.6`, `@orkestrel/tool` `^0.0.10`, `@orkestrel/workflow` `^0.0.11`, `@orkestrel/workspace` `^0.0.4` |
|
|
41
|
+
| `@orkestrel/browser` | `0.0.9` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/html` `^0.0.3`, `@orkestrel/websocket` `^0.0.8` |
|
|
42
|
+
| `@orkestrel/budget` | `0.0.6` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
43
|
+
| `@orkestrel/console` | `0.0.5` | L2 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6` |
|
|
44
|
+
| `@orkestrel/contract` | `0.0.11` | L0 | |
|
|
45
|
+
| `@orkestrel/csv` | `0.0.3` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
46
|
+
| `@orkestrel/database` | `0.0.8` | L2 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/indexeddb` `^0.0.7`, `@orkestrel/sqlite` `^0.0.7` |
|
|
47
|
+
| `@orkestrel/emitter` | `0.0.6` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
48
|
+
| `@orkestrel/guide` | `0.0.10` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/markdown` `^0.0.8` |
|
|
49
|
+
| `@orkestrel/html` | `0.0.3` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
50
|
+
| `@orkestrel/indexeddb` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
51
|
+
| `@orkestrel/interpret` | `0.0.8` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/reason` `^0.0.5`, `@orkestrel/template` `^0.0.3` |
|
|
52
|
+
| `@orkestrel/markdown` | `0.0.8` | L2 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/html` `^0.0.3` |
|
|
53
|
+
| `@orkestrel/mcp` | `0.0.14` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/sse` `^0.0.5`, `@orkestrel/tool` `^0.0.10`, `@orkestrel/websocket` `^0.0.8` |
|
|
54
|
+
| `@orkestrel/middleware` | `0.0.10` | L2 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/budget` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/timeout` `^0.0.6` |
|
|
55
|
+
| `@orkestrel/msg` | `0.0.6` | L0 | |
|
|
56
|
+
| `@orkestrel/ndjson` | `0.0.6` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
57
|
+
| `@orkestrel/ollama` | `0.0.9` | L6 | `@orkestrel/agent` `^0.0.15`, `@orkestrel/budget` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/ndjson` `^0.0.6`, `@orkestrel/timeout` `^0.0.6`, `@orkestrel/tool` `^0.0.10` |
|
|
58
|
+
| `@orkestrel/pool` | `0.0.7` | L2 | `@orkestrel/emitter` `^0.0.6` |
|
|
59
|
+
| `@orkestrel/program` | `0.0.7` | L4 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/qualifier` `^0.0.8`, `@orkestrel/rater` `^0.0.9`, `@orkestrel/reason` `^0.0.5` |
|
|
60
|
+
| `@orkestrel/qualifier` | `0.0.8` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/reason` `^0.0.5` |
|
|
61
|
+
| `@orkestrel/queue` | `0.0.8` | L3 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/timeout` `^0.0.6` |
|
|
62
|
+
| `@orkestrel/rater` | `0.0.9` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/reason` `^0.0.5` |
|
|
63
|
+
| `@orkestrel/reason` | `0.0.5` | L2 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6` |
|
|
64
|
+
| `@orkestrel/relation` | `0.0.8` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6` |
|
|
65
|
+
| `@orkestrel/router` | `0.0.9` | L2 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6` |
|
|
66
|
+
| `@orkestrel/scaffold` | `0.0.27` | L3 | `@orkestrel/console` `^0.0.5`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/markdown` `^0.0.8`, `@orkestrel/template` `^0.0.3` |
|
|
67
|
+
| `@orkestrel/sea` | `0.0.6` | L2 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6` |
|
|
68
|
+
| `@orkestrel/server` | `0.0.11` | L3 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/router` `^0.0.9`, `@orkestrel/timeout` `^0.0.6` |
|
|
69
|
+
| `@orkestrel/sqlite` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
70
|
+
| `@orkestrel/sse` | `0.0.5` | L0 | |
|
|
71
|
+
| `@orkestrel/template` | `0.0.3` | L2 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6` |
|
|
72
|
+
| `@orkestrel/terminal` | `0.0.6` | L3 | `@orkestrel/console` `^0.0.5`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/sse` `^0.0.5` |
|
|
73
|
+
| `@orkestrel/timeout` | `0.0.6` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
74
|
+
| `@orkestrel/tool` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.11` |
|
|
75
|
+
| `@orkestrel/toolbox` | `0.0.4` | L6 | `@orkestrel/agent` `^0.0.15`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/relation` `^0.0.8`, `@orkestrel/server` `^0.0.11`, `@orkestrel/terminal` `^0.0.6`, `@orkestrel/tool` `^0.0.10`, `@orkestrel/workflow` `^0.0.11`, `@orkestrel/workspace` `^0.0.4` |
|
|
76
|
+
| `@orkestrel/websocket` | `0.0.8` | L2 | `@orkestrel/emitter` `^0.0.6` |
|
|
77
|
+
| `@orkestrel/worker` | `0.0.7` | L4 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/pool` `^0.0.7`, `@orkestrel/queue` `^0.0.8` |
|
|
78
|
+
| `@orkestrel/workflow` | `0.0.11` | L4 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/budget` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/queue` `^0.0.8`, `@orkestrel/timeout` `^0.0.6` |
|
|
79
|
+
| `@orkestrel/workspace` | `0.0.4` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6` |
|
|
80
80
|
|
|
81
81
|
<!-- /orkestrel:catalog -->
|
|
82
82
|
|
|
@@ -89,11 +89,28 @@ range as a pin, never as a range. A dependent stays on its pinned version until
|
|
|
89
89
|
rewrites the dependent's own declared range and re-publishes the dependent. Publishing a
|
|
90
90
|
new version reaches no consumer on its own.
|
|
91
91
|
|
|
92
|
-
A bump therefore obliges a re-publish of every package downstream of it, in
|
|
93
|
-
order. Report that cascade whenever you sequence cross-package work or state blast
|
|
94
|
-
radius: name each downstream package, its declared range, and its
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
A **runtime** bump therefore obliges a re-publish of every package downstream of it, in
|
|
93
|
+
layer order. Report that cascade whenever you sequence cross-package work or state blast
|
|
94
|
+
radius: name each downstream package, its declared range, and its layer. A package whose
|
|
95
|
+
pin names an older version runs that older version, whatever the registry holds.
|
|
96
|
+
|
|
97
|
+
A **development** bump obliges nothing. A `devDependencies` range reaches no consumer of
|
|
98
|
+
the published package, so re-pin it, prove the gates still green, and stop. Never report a
|
|
99
|
+
development bump as a cascade. It becomes one only if it forces a change to `src` or
|
|
100
|
+
`app`, because then the published types or runtime moved and the package bumps on that
|
|
101
|
+
account rather than on the dependency's.
|
|
102
|
+
|
|
103
|
+
The `Layer` column above is the publish round, derived from the runtime edges in the same
|
|
104
|
+
row. `L0` depends on nothing else in the fleet and publishes first; each later layer
|
|
105
|
+
publishes only after every layer before it is on the registry. A row with no layer sits in
|
|
106
|
+
a cycle and cannot be placed in a round at all. Two packages in one layer are independent
|
|
107
|
+
of each other and may publish in any order within it.
|
|
108
|
+
|
|
109
|
+
Report a disagreeing pin as a defect, never as drift to tidy later. When two packages in
|
|
110
|
+
one install graph pin different versions of a third, npm installs both copies, and the
|
|
111
|
+
compiler reads the two copies as two distinct types. The symptom is a type error naming
|
|
112
|
+
one type as not assignable to itself. `npm ls @orkestrel/<name>` is the evidence: one line
|
|
113
|
+
is sound, and nesting is the finding.
|
|
97
114
|
|
|
98
115
|
## Evidence workflow
|
|
99
116
|
|
|
@@ -4,6 +4,7 @@ description: 'Read-only primary-source research: external capabilities, protocol
|
|
|
4
4
|
tools: Read, Grep, Glob, WebFetch, WebSearch
|
|
5
5
|
model: sonnet
|
|
6
6
|
effort: medium
|
|
7
|
+
permissionMode: dontAsk
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
You are the **Researcher** — the native evidence lane for the research job the
|
|
@@ -67,6 +67,25 @@ Use only the centralized files an environment needs.
|
|
|
67
67
|
creates a directory rather than an entity and `isVacant` is a predicate rather than a `Guard<T>`,
|
|
68
68
|
so both stay in `helpers.ts`. Placement follows what the function is; the name form follows
|
|
69
69
|
placement.
|
|
70
|
+
- Repair a violation of those forms by deciding what the function **is** first, then moving or
|
|
71
|
+
renaming to match. Both repairs exist and picking the wrong one does real damage.
|
|
72
|
+
- Wrong file, right name → **move it**. A `scan*` in `parsers.ts` is a pure lexical leaf that
|
|
73
|
+
belongs in `helpers.ts`. The barrel star-exports both, so the move leaves the published surface
|
|
74
|
+
identical.
|
|
75
|
+
- Right file, wrong name → **rename it in place**. A function returning a live entity is an entity
|
|
76
|
+
factory and belongs in `factories.ts` whatever it is called, so `restoreThing` there is misnamed,
|
|
77
|
+
not misplaced. Renaming moves the published surface and earns a version bump; that cost is the
|
|
78
|
+
correct one to pay, and it is smaller than the alternative.
|
|
79
|
+
- Never let the name choose. Relocating a correctly-placed function to escape a rename drags its
|
|
80
|
+
dependencies with it — an entity factory moved into `helpers.ts` makes that file import an
|
|
81
|
+
implementation class, and a leaf file that imports a class stops being a leaf for every module
|
|
82
|
+
beneath it.
|
|
83
|
+
- Keep the leaf pair class-free. `helpers.ts` and `validators.ts` sit at the bottom of a module's
|
|
84
|
+
graph: they import types, constants, errors, and each other, and they import no implementation
|
|
85
|
+
class. Every file that constructs or drives a class — `cloners.ts`, `compilers.ts`, `factories.ts`,
|
|
86
|
+
`shapers.ts` — sits above them, consumes them, and is never consumed by them. One cycle between
|
|
87
|
+
the two leaves is the shape this produces and is acceptable; an edge running downward from a
|
|
88
|
+
class-importing file into the leaf pair is not.
|
|
70
89
|
- `templates.ts` and `contracts.ts` hold data only — shipped template definitions and compiled
|
|
71
90
|
contracts. A function that builds either belongs in the kind file for what it builds.
|
|
72
91
|
- `handlers.ts` holds request handlers, which are functions. `routes.ts` holds data only: a route is
|
|
@@ -32,6 +32,8 @@ Documentation is an enforced contract, not explanatory decoration. The Writing r
|
|
|
32
32
|
- Every public export is documented.
|
|
33
33
|
- TypeScript, SCSS, Markdown, tests, and showcase remain aligned.
|
|
34
34
|
- A parity failure identifies drift; never suppress or weaken the test.
|
|
35
|
+
- Falsify a prose claim the way you falsify a code claim. The parity test proves a name exists, never that a sentence about behavior is true, so run the example and read what it returns. A `// false` beside a call that returns `true` is a defect of the same kind as a wrong return value, and it reaches every consumer who installs the package.
|
|
36
|
+
- Re-read the prose last, against what actually shipped. Where a change chose to document a limit rather than close it, the sentence was often drafted for the option that lost, or written more confidently than the code earns. Code rulings survive review because a test can break them; prose rulings survive because nothing tries.
|
|
35
37
|
|
|
36
38
|
For behavioral interfaces/classes:
|
|
37
39
|
|
|
@@ -63,9 +63,12 @@ A review that reads a diff finds what the diff shows. A review that tries to bre
|
|
|
63
63
|
- Draw the negative control from outside the population the instrument covers. Name the instrument's membership rule first, then pick a control that rule excludes. A control sampled from constructs the instrument already handles proves only that it discriminates among those constructs, and says nothing about the class it silently cannot reach.
|
|
64
64
|
- State an instrument's coverage beside its result. A conclusion inherits the instrument's scope, not the question's. An unstated coverage claim is read as complete, and it never is. A search proves something about the paths it walked, so name them.
|
|
65
65
|
- Match the instrument to the question. A text search reports on text, so a claim about declarations, call sites, or structure needs the compiler or a parser instead. A pattern written for one spelling of a construct reports on that spelling alone. A path check answers relative to the directory it runs from, so resolve the inputs against their own base before reading a miss as a finding.
|
|
66
|
+
- Report a question unanswered rather than answering it with a weaker instrument. A fallback that measures something adjacent returns a confident wrong answer, and nothing downstream can tell that answer from the real one — searching commit messages for a release when the question is where a version changed will match some release, just not the one asked about. Name the substitute and what it actually measures, or say the question is open.
|
|
66
67
|
- State what the controls established and what they did not. An instrument certified only from the inside is trusted exactly where it has never been tested.
|
|
67
68
|
- Treat a gap between what an instrument says it checks and what it actually matches as a defect in the instrument, not as a documented limit. A recorded blind spot buys trust only when everything outside it is genuinely covered.
|
|
68
69
|
- Measure the product, not the harness. A recorded baseline that counts something about its own fixture is not evidence about the shipped surface, however often a guide quotes it.
|
|
70
|
+
- Baseline a published-artifact claim against the published artifact. "Did my change move the surface" and "does this release differ from the last one" are different questions, and a diff against your own starting point answers only the first. A toolchain that re-emits declarations moves the artifact without any source edit, so every writer can correctly report an unmoved surface while the package's published contract has changed. Fetch what consumers actually have — the tarball, the deployed asset — and compare against that.
|
|
71
|
+
- Prove a module cycle by loading the built artifact, not by a green suite. Tests import through the source graph and a bundler resolves it differently, so a cycle that is fatal at module-init in the shipped form can stay invisible under every test. Import each published entry point and read an export from it.
|
|
69
72
|
- Adopt an instrument that settled a claim as a test before accepting the work it settled. The probe that proved a fix, carrying the control that proved the probe, is that fix's regression guard. A verification that runs once is a rehearsal, not a gate.
|
|
70
73
|
|
|
71
74
|
### Rounds and verdicts
|
|
@@ -22,6 +22,10 @@ paths:
|
|
|
22
22
|
- Cover happy paths, error paths, empty input, boundary values, `NaN`, positive/negative zero, cycles, and Map/Set order where relevant.
|
|
23
23
|
- Test observable behavior, not implementation details.
|
|
24
24
|
- Assert the membership a discovered or globbed set should have, not a total that a partly empty population satisfies. A glob spanning two locations passes a size check while one of them matches nothing.
|
|
25
|
+
- Never assert an implementation against itself. Compare the answer to a declaration, a fixture, or a second mechanism that could disagree with it. Re-deriving the answer the same way the source derives it produces a test that passes for every value the source ever returns, and it reads exactly like a real one.
|
|
26
|
+
- Probe a host-varying property at runtime, on the host the test is running on, and assert against what the probe returned. Filesystem case folding, path separators, permission bits, and rename semantics differ per host, so a fixture built on one host describes that host and silently measures something else on the next.
|
|
27
|
+
- Assert a runtime-chosen result as the property it must have, not as the number one run produced. Compression, timing, and buffer sizing are the runtime's choice, so pin the relationship the test depends on — that the encoded form is larger, that the second call is faster — and let the assertion fail when the input drifts out of the range where that relationship holds.
|
|
28
|
+
- Give a conditional skip the mechanism that makes it inapplicable, cited, not the platform name alone. A test skipped on a platform is a test nobody re-examines; a test skipped because a named API rejects a named case is one anybody can re-check.
|
|
25
29
|
- A regression test records the exact command and its failing count before the fix, and the same command's passing count after.
|
|
26
30
|
- Use `it.todo()` only for explicitly out-of-scope roadmap work, never to complete the current request. Every `.skip` or conditional skip has a narrow verifiable applicability reason.
|
|
27
31
|
- Do not create test files solely for `constants.ts`, barrels, error definitions, or `types.ts`.
|
|
@@ -32,12 +36,14 @@ paths:
|
|
|
32
36
|
A proof that covers the workspace instead of one module has a fixed location, so no package invents
|
|
33
37
|
its own:
|
|
34
38
|
|
|
35
|
-
| Path
|
|
36
|
-
|
|
|
37
|
-
| `tests/policy.test.ts`
|
|
38
|
-
| `tests/config.test.ts`
|
|
39
|
-
| `tests/guides.test.ts`
|
|
40
|
-
| `tests/
|
|
39
|
+
| Path | Proves |
|
|
40
|
+
| ---------------------------- | --------------------------------------------------------------- |
|
|
41
|
+
| `tests/policy.test.ts` | Every source file obeys the syntactic coding and placement law |
|
|
42
|
+
| `tests/config.test.ts` | Root configuration resolves its aliases, projects, and outputs |
|
|
43
|
+
| `tests/guides.test.ts` | Every documented API exists and every public API is documented |
|
|
44
|
+
| `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks |
|
|
45
|
+
| `tests/integration.test.ts` | The built package works when installed and driven from outside |
|
|
46
|
+
| `tests/service/**/*.test.ts` | The live external services this package drives, driven for real |
|
|
41
47
|
|
|
42
48
|
- `.claude/rules/workspace.md` names the Vitest project each location belongs to.
|
|
43
49
|
- `integration.test.ts` is a reserved filename at any level. It names a scope rather than a module,
|
|
@@ -76,9 +82,11 @@ Three rules bind every probe:
|
|
|
76
82
|
|
|
77
83
|
Live external services/models are the deliberate exception to fast hermetic defaults:
|
|
78
84
|
|
|
79
|
-
- Put them in
|
|
85
|
+
- Put them in the `service` project, under `tests/service/`, with `tests/setupService.ts` for setup
|
|
86
|
+
and a longer timeout. That module's presence is what registers the project, so a live proof with
|
|
87
|
+
no readiness setup is a project nothing configures.
|
|
80
88
|
- Keep them out of the default run.
|
|
81
|
-
- Warm and verify service readiness in
|
|
89
|
+
- Warm and verify service readiness in `tests/setupService.ts`.
|
|
82
90
|
- Hard-require readiness: throw loudly; never silently skip.
|
|
83
91
|
- Verify service-dependent logic through that service's project, not unrelated module tests or scattered conditional skips.
|
|
84
92
|
- Tune each request to the smallest input/context/output that proves one behavior without becoming brittle or expensive.
|
|
@@ -91,6 +99,7 @@ A test that spawns a process, packs, installs, or drives a real build is a proof
|
|
|
91
99
|
- Give it its own Vitest project with its own setup and timeout.
|
|
92
100
|
- Keep it out of the default run and require it in `prepublishOnly`.
|
|
93
101
|
- Slow and hermetic is reason enough to isolate a proof; it need not touch an external service.
|
|
102
|
+
- Where such a proof stays in a shared project, size its budget from a full contended run rather than from an isolated one. A budget that clears the isolated cost by a thin margin turns contention into a red gate reporting a timeout, which carries no diagnostic about the code and costs a full investigation to dismiss.
|
|
94
103
|
|
|
95
104
|
## Shared test infrastructure
|
|
96
105
|
|
|
@@ -188,6 +197,14 @@ Coverage rules:
|
|
|
188
197
|
Before acceptance:
|
|
189
198
|
|
|
190
199
|
- prove every intended test file is discovered by the correct project;
|
|
200
|
+
- prove every declared project is reachable from a gate. A project registered in the root
|
|
201
|
+
configuration with no script, or with a script no chain runs, is a proof that never executes — and
|
|
202
|
+
because it never executes it never fails, so the suite reports green while carrying it. Read the
|
|
203
|
+
chain, not the exit code: run each project directly once and compare that list against what `test`
|
|
204
|
+
actually invokes. The gate that would report this gap is the gate that is missing.
|
|
205
|
+
- prove a declared project's include resolves to a real file. An empty project is not a passing
|
|
206
|
+
project: Vitest exits non-zero on "no test files found", so a project aimed at a path that was
|
|
207
|
+
never created stays invisible until something finally runs it.
|
|
191
208
|
- inspect actual test counts and environments;
|
|
192
209
|
- audit `.todo`, `.skip`, conditional skips, retries, and inflated timeouts;
|
|
193
210
|
- confirm each assertion would fail for the defect it claims to catch, and that it fails rather than passes when its population is empty;
|