@renzrollon/interlock 1.0.2 → 1.1.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +23 -1
- package/README.md +70 -301
- package/bin/interlock +16 -3
- package/lib/evals-triage.mjs +38 -2
- package/lib/limits.mjs +13 -2
- package/lib/merge-lanes.mjs +45 -2
- package/lib/receipt.mjs +14 -4
- package/lib/resume-card.mjs +319 -0
- package/lib/run.mjs +193 -23
- package/package.json +1 -1
- package/shared/TOOL-ECONOMY.md +8 -1
package/lib/run.mjs
CHANGED
|
@@ -52,6 +52,7 @@ import { planRemediation } from './remediate.mjs'
|
|
|
52
52
|
import { classifySurface } from './surface.mjs'
|
|
53
53
|
import { buildChecklist } from './conformance.mjs'
|
|
54
54
|
import { record as recordAutonomy } from './autonomy.mjs'
|
|
55
|
+
import { writeResumeCard } from './resume-card.mjs'
|
|
55
56
|
import {
|
|
56
57
|
applyReplan,
|
|
57
58
|
batchOutcomes,
|
|
@@ -1449,7 +1450,8 @@ function logSpawns(ctx, manifest, step) {
|
|
|
1449
1450
|
// No run identity yet — the classify step runs before there is a wave state,
|
|
1450
1451
|
// so nothing is owed. The same rule `logVerifyJudgement` follows: a call with
|
|
1451
1452
|
// no runId is not part of a logged run, and logging is not what should fail it.
|
|
1452
|
-
if (!manifest.runId) return
|
|
1453
|
+
if (!manifest.runId) return null
|
|
1454
|
+
let failure = null
|
|
1453
1455
|
for (const s of step.spawns || []) {
|
|
1454
1456
|
if (s.kind === 'implementer') continue // logged by wave-state, at the lane
|
|
1455
1457
|
const written = appendRunLogEvent(ctx.root, {
|
|
@@ -1460,13 +1462,18 @@ function logSpawns(ctx, manifest, step) {
|
|
|
1460
1462
|
model: s.model || undefined,
|
|
1461
1463
|
kind: s.kind
|
|
1462
1464
|
})
|
|
1463
|
-
if (!written.written)
|
|
1465
|
+
if (!written.written) {
|
|
1466
|
+
ctx.warn(`agent-spawn not recorded: ${written.reason}`)
|
|
1467
|
+
// The first failure is the one that opened the gap; the rest of the loop
|
|
1468
|
+
// still runs so the warnings a reader gets are unchanged.
|
|
1469
|
+
if (!failure) failure = `agent-spawn ${s.label}: ${written.reason}`
|
|
1470
|
+
}
|
|
1464
1471
|
}
|
|
1465
|
-
return
|
|
1472
|
+
return failure
|
|
1466
1473
|
}
|
|
1467
1474
|
|
|
1468
1475
|
function logCliExit(ctx, manifest, command, exitCode) {
|
|
1469
|
-
if (!manifest.runId) return
|
|
1476
|
+
if (!manifest.runId) return null
|
|
1470
1477
|
const written = appendRunLogEvent(ctx.root, {
|
|
1471
1478
|
runId: manifest.runId,
|
|
1472
1479
|
change: manifest.change,
|
|
@@ -1474,9 +1481,46 @@ function logCliExit(ctx, manifest, command, exitCode) {
|
|
|
1474
1481
|
command,
|
|
1475
1482
|
exitCode
|
|
1476
1483
|
})
|
|
1477
|
-
if (
|
|
1484
|
+
if (written.written) return null
|
|
1485
|
+
ctx.warn(`cli-exit not recorded: ${written.reason}`)
|
|
1486
|
+
return `cli-exit ${command}: ${written.reason}`
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
/**
|
|
1490
|
+
* The halt a failed run-trajectory append owes the run.
|
|
1491
|
+
*
|
|
1492
|
+
* `.claude/ship/runs` is `fatal: true` in `lib/doctor.mjs`'s STATE_DIRS, and
|
|
1493
|
+
* `wave-state` / `verify judge` in `bin/interlock` already `process.exit(1)`
|
|
1494
|
+
* when their append does not land. The live `interlock run` path used to warn
|
|
1495
|
+
* and walk on — into the task tick and the commit — so a run nobody could
|
|
1496
|
+
* reconstruct still ticked boxes and shipped. A warning is not a gate.
|
|
1497
|
+
*
|
|
1498
|
+
* Caller-side by construction: fatality is a policy of the SITE, not of the
|
|
1499
|
+
* shared writer, which is why `appendRunLogEvent` still just reports. The
|
|
1500
|
+
* outcome corpus and review metrics keep their deliberately different
|
|
1501
|
+
* semantics — a failed write there is reported and never touches the exit code.
|
|
1502
|
+
*
|
|
1503
|
+
* `site` names where the gap is, so the halt reason is enough to find it.
|
|
1504
|
+
*/
|
|
1505
|
+
function trajectoryHaltStep(site, reason, extra = {}) {
|
|
1506
|
+
return haltStep(`trajectory append failed: ${site}: ${reason}`, extra)
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* The reason to halt with when `logWaveMutation` failed.
|
|
1511
|
+
*
|
|
1512
|
+
* It returns the filesystem's own reason where it has one. The fallback covers
|
|
1513
|
+
* an injected or older dep that reports only the boolean — better a named site
|
|
1514
|
+
* with a vague cause than a halt that says nothing at all.
|
|
1515
|
+
*/
|
|
1516
|
+
function waveAppendReason(outcome) {
|
|
1517
|
+
const reason = outcome && typeof outcome.reason === 'string' ? outcome.reason.trim() : ''
|
|
1518
|
+
return reason || 'the wave-action or its paired cli-exit did not land'
|
|
1478
1519
|
}
|
|
1479
1520
|
|
|
1521
|
+
/** Same, for `logAgentSpawns` — it returns a boolean and reports its reason to stderr. */
|
|
1522
|
+
const LANE_SPAWN_APPEND_FAILED = 'an implementer lane spawn did not land'
|
|
1523
|
+
|
|
1480
1524
|
/** The shared tree's HEAD. Never throws: an unreadable base is null, never a guess. */
|
|
1481
1525
|
function headCommit(ctx) {
|
|
1482
1526
|
return ctx.deps.headCommit(ctx.root)
|
|
@@ -1771,15 +1815,29 @@ function adoptPlan(ctx, manifest, plan) {
|
|
|
1771
1815
|
// anything, and `run-log check` does not require it.
|
|
1772
1816
|
sessionId: hostSessionId(manifest, ctx.env) || undefined
|
|
1773
1817
|
})
|
|
1774
|
-
if (!started.written)
|
|
1818
|
+
if (!started.written) {
|
|
1819
|
+
ctx.warn(`run-start not recorded: ${started.reason}`)
|
|
1820
|
+
return trajectoryHaltStep('run-start', started.reason)
|
|
1821
|
+
}
|
|
1775
1822
|
|
|
1776
|
-
const
|
|
1823
|
+
const created = ctx.deps.logWaveMutation(root, {
|
|
1777
1824
|
source: 'create',
|
|
1778
1825
|
change: manifest.change,
|
|
1779
1826
|
state
|
|
1780
1827
|
})
|
|
1781
|
-
|
|
1782
|
-
|
|
1828
|
+
const { step, ok } = created
|
|
1829
|
+
if (!ok) {
|
|
1830
|
+
ctx.warn('the wave-state trajectory append failed on run start')
|
|
1831
|
+
return trajectoryHaltStep('wave-state create', waveAppendReason(created))
|
|
1832
|
+
}
|
|
1833
|
+
// The lane spawns, which `logSpawns` deliberately skips (they are logged at
|
|
1834
|
+
// the lane, by wave-state). Its verdict is read for the same reason every
|
|
1835
|
+
// other append's is: `wave-state next` exits 1 on this exact boolean, and the
|
|
1836
|
+
// live path owes the trajectory the same fatality.
|
|
1837
|
+
if (ctx.deps.logAgentSpawns(root, { state, change: manifest.change, step, source: 'next' }) === false) {
|
|
1838
|
+
ctx.warn('an implementer agent-spawn was not recorded on run start')
|
|
1839
|
+
return trajectoryHaltStep('agent-spawn', LANE_SPAWN_APPEND_FAILED)
|
|
1840
|
+
}
|
|
1783
1841
|
return decorate(ctx, step, manifest)
|
|
1784
1842
|
}
|
|
1785
1843
|
|
|
@@ -2103,18 +2161,43 @@ export function runRecordBatch(ctx, { results }) {
|
|
|
2103
2161
|
changedPaths: ctx.deps.observedChangedPaths(root)
|
|
2104
2162
|
})
|
|
2105
2163
|
writeState(root, after)
|
|
2106
|
-
const
|
|
2164
|
+
const recorded = ctx.deps.logWaveMutation(root, {
|
|
2107
2165
|
source: 'record-batch',
|
|
2108
2166
|
change: manifest.change,
|
|
2109
2167
|
state: after
|
|
2110
2168
|
})
|
|
2111
|
-
|
|
2112
|
-
|
|
2169
|
+
const { step: nextRaw, ok: logOk } = recorded
|
|
2170
|
+
if (!logOk) {
|
|
2171
|
+
// Before the tick and before the commit. The batch result is already on
|
|
2172
|
+
// disk (`writeState` above) and there is no undo API for it, so the halt
|
|
2173
|
+
// names the gap instead of pretending the state never advanced — close's
|
|
2174
|
+
// reconstructability check reads the same trajectory and agrees.
|
|
2175
|
+
ctx.warn('the wave-state trajectory append failed on record-batch')
|
|
2176
|
+
manifest.banners.push(...banners)
|
|
2177
|
+
return finishStep(
|
|
2178
|
+
ctx,
|
|
2179
|
+
manifest,
|
|
2180
|
+
trajectoryHaltStep('wave-state record-batch', waveAppendReason(recorded), { banners })
|
|
2181
|
+
)
|
|
2182
|
+
}
|
|
2183
|
+
// Before the tick and before the commit, exactly like the wave-action above:
|
|
2184
|
+
// a batch whose implementer spawns went unrecorded is a batch nobody can
|
|
2185
|
+
// attribute, and `emitRecordedState` already exits 1 on this same boolean.
|
|
2186
|
+
const laneSpawnsLogged = ctx.deps.logAgentSpawns(root, {
|
|
2113
2187
|
state: after,
|
|
2114
2188
|
change: manifest.change,
|
|
2115
2189
|
step: nextRaw,
|
|
2116
2190
|
source: 'record-batch'
|
|
2117
2191
|
})
|
|
2192
|
+
if (laneSpawnsLogged === false) {
|
|
2193
|
+
ctx.warn('an implementer agent-spawn was not recorded on record-batch')
|
|
2194
|
+
manifest.banners.push(...banners)
|
|
2195
|
+
return finishStep(
|
|
2196
|
+
ctx,
|
|
2197
|
+
manifest,
|
|
2198
|
+
trajectoryHaltStep('agent-spawn', LANE_SPAWN_APPEND_FAILED, { banners })
|
|
2199
|
+
)
|
|
2200
|
+
}
|
|
2118
2201
|
|
|
2119
2202
|
// One authority, read once, for the tick and for the tallies alike.
|
|
2120
2203
|
const verdict = adjudicateBatches([reported], recordedOutcomes(batchOutcomes(state, after)))
|
|
@@ -2227,7 +2310,26 @@ export function runJudge(ctx, { context, results }) {
|
|
|
2227
2310
|
}
|
|
2228
2311
|
verdict = judgeVerification(plan, reportedSteps, { context })
|
|
2229
2312
|
for (const banner of verdict.banners || []) banners.push(banner)
|
|
2230
|
-
logCliExit(ctx, manifest, 'verify judge', verdict.halt ? 1 : 0)
|
|
2313
|
+
const exitFailure = logCliExit(ctx, manifest, 'verify judge', verdict.halt ? 1 : 0)
|
|
2314
|
+
if (exitFailure) {
|
|
2315
|
+
// The judgement happened; the record of it did not. Halting here is what
|
|
2316
|
+
// `verify judge` in bin/interlock already does on the same failure —
|
|
2317
|
+
// before the branch into record-verify, the handoff or the commit.
|
|
2318
|
+
//
|
|
2319
|
+
// A verdict that was ALREADY halting keeps its own reason, the same rule
|
|
2320
|
+
// `finishStep` applies below: this run stopped because verification
|
|
2321
|
+
// failed, and that reason is what reaches the summary, the push and the
|
|
2322
|
+
// resume card. The lost record is a banner beside it, never instead of it.
|
|
2323
|
+
banners.push(`TRAJECTORY APPEND FAILED: ${exitFailure}`)
|
|
2324
|
+
manifest.banners.push(...banners)
|
|
2325
|
+
return finishStep(
|
|
2326
|
+
ctx,
|
|
2327
|
+
manifest,
|
|
2328
|
+
verdict.halt
|
|
2329
|
+
? haltStep(verdict.reason || 'verification halted the run', { banners })
|
|
2330
|
+
: trajectoryHaltStep('cli-exit', exitFailure.replace(/^cli-exit /, ''), { banners })
|
|
2331
|
+
)
|
|
2332
|
+
}
|
|
2231
2333
|
} else {
|
|
2232
2334
|
banners.push(`VERIFICATION SKIPPED: reason=${reason}`)
|
|
2233
2335
|
}
|
|
@@ -2243,13 +2345,21 @@ export function runJudge(ctx, { context, results }) {
|
|
|
2243
2345
|
}
|
|
2244
2346
|
const after = recordVerifyResult(state, record)
|
|
2245
2347
|
writeState(root, after)
|
|
2246
|
-
const
|
|
2348
|
+
const verified = ctx.deps.logWaveMutation(root, {
|
|
2247
2349
|
source: 'record-verify',
|
|
2248
2350
|
change: manifest.change,
|
|
2249
2351
|
state: after
|
|
2250
2352
|
})
|
|
2251
|
-
|
|
2353
|
+
const { step: nextRaw, ok } = verified
|
|
2252
2354
|
manifest.banners.push(...banners)
|
|
2355
|
+
if (!ok) {
|
|
2356
|
+
ctx.warn('the wave-state trajectory append failed on record-verify')
|
|
2357
|
+
return finishStep(
|
|
2358
|
+
ctx,
|
|
2359
|
+
manifest,
|
|
2360
|
+
trajectoryHaltStep('wave-state record-verify', waveAppendReason(verified), { banners })
|
|
2361
|
+
)
|
|
2362
|
+
}
|
|
2253
2363
|
return finishStep(ctx, manifest, decorate(ctx, nextRaw, manifest, { banners }))
|
|
2254
2364
|
}
|
|
2255
2365
|
|
|
@@ -2481,12 +2591,16 @@ export function runReplan(ctx, { results }) {
|
|
|
2481
2591
|
return finishStep(ctx, manifest, haltStep(`replan rejected: ${(err && err.message) || String(err)}`))
|
|
2482
2592
|
}
|
|
2483
2593
|
writeState(root, after)
|
|
2484
|
-
const
|
|
2594
|
+
const replanned = ctx.deps.logWaveMutation(root, {
|
|
2485
2595
|
source: 'replan',
|
|
2486
2596
|
change: manifest.change,
|
|
2487
2597
|
state: after
|
|
2488
2598
|
})
|
|
2489
|
-
|
|
2599
|
+
const { step: nextRaw, ok } = replanned
|
|
2600
|
+
if (!ok) {
|
|
2601
|
+
ctx.warn('the wave-state trajectory append failed on replan')
|
|
2602
|
+
return finishStep(ctx, manifest, trajectoryHaltStep('wave-state replan', waveAppendReason(replanned)))
|
|
2603
|
+
}
|
|
2490
2604
|
return finishStep(ctx, manifest, decorate(ctx, nextRaw, manifest))
|
|
2491
2605
|
}
|
|
2492
2606
|
|
|
@@ -2520,8 +2634,18 @@ function noManifest() {
|
|
|
2520
2634
|
* process — each `run` subcommand is its own.
|
|
2521
2635
|
*/
|
|
2522
2636
|
function finishStep(ctx, manifest, step) {
|
|
2523
|
-
logSpawns(ctx, manifest, step)
|
|
2524
|
-
|
|
2637
|
+
const failure = logSpawns(ctx, manifest, step)
|
|
2638
|
+
// An agent-spawn nobody recorded is a gap in the trajectory, and the step it
|
|
2639
|
+
// belongs to must not run. A step that was ALREADY halting keeps its own
|
|
2640
|
+
// reason: replacing it would trade why the run stopped for the bookkeeping
|
|
2641
|
+
// failure that followed, and it halts either way.
|
|
2642
|
+
const final =
|
|
2643
|
+
failure && step.action !== 'halt'
|
|
2644
|
+
? trajectoryHaltStep('agent-spawn', failure.replace(/^agent-spawn /, ''), {
|
|
2645
|
+
...(Array.isArray(step.banners) ? { banners: step.banners } : {})
|
|
2646
|
+
})
|
|
2647
|
+
: step
|
|
2648
|
+
return recordThen(ctx.root, manifest, final)
|
|
2525
2649
|
}
|
|
2526
2650
|
|
|
2527
2651
|
// --- run close (design D10) -------------------------------------------------
|
|
@@ -2758,13 +2882,23 @@ export async function runClose(
|
|
|
2758
2882
|
: undefined
|
|
2759
2883
|
})
|
|
2760
2884
|
|
|
2885
|
+
// The trajectory writes this close owes. A failure here is the same fatal
|
|
2886
|
+
// class as every other `.claude/ship/runs` append — the close has no tick or
|
|
2887
|
+
// commit left to stop, so the halt it can still perform is a spoken
|
|
2888
|
+
// degradation and a non-zero exit, exactly what an unreconstructable run gets
|
|
2889
|
+
// below. Silence would leave a run that reads complete and cannot be replayed.
|
|
2890
|
+
let trajectoryComplete = true
|
|
2761
2891
|
if (manifest.runId) {
|
|
2762
2892
|
const written = appendRunLogEvent(root, {
|
|
2763
2893
|
...receipt,
|
|
2764
2894
|
runId: manifest.runId,
|
|
2765
2895
|
change: manifest.change
|
|
2766
2896
|
})
|
|
2767
|
-
if (!written.written)
|
|
2897
|
+
if (!written.written) {
|
|
2898
|
+
ctx.warn(`run-receipt not recorded: ${written.reason}`)
|
|
2899
|
+
trajectoryComplete = false
|
|
2900
|
+
degradations.push(`TRAJECTORY APPEND FAILED: run-receipt: ${written.reason}`)
|
|
2901
|
+
}
|
|
2768
2902
|
|
|
2769
2903
|
const terminal = appendRunLogEvent(root, {
|
|
2770
2904
|
runId: manifest.runId,
|
|
@@ -2772,7 +2906,12 @@ export async function runClose(
|
|
|
2772
2906
|
type: haltReason ? 'run-halt' : 'run-complete',
|
|
2773
2907
|
reason: haltReason || undefined
|
|
2774
2908
|
})
|
|
2775
|
-
if (!terminal.written)
|
|
2909
|
+
if (!terminal.written) {
|
|
2910
|
+
const type = haltReason ? 'run-halt' : 'run-complete'
|
|
2911
|
+
ctx.warn(`${type} not recorded: ${terminal.reason}`)
|
|
2912
|
+
trajectoryComplete = false
|
|
2913
|
+
degradations.push(`TRAJECTORY APPEND FAILED: ${type}: ${terminal.reason}`)
|
|
2914
|
+
}
|
|
2776
2915
|
}
|
|
2777
2916
|
|
|
2778
2917
|
// The outcome corpus. Its own write failure is reported and never touches the
|
|
@@ -2821,6 +2960,36 @@ export async function runClose(
|
|
|
2821
2960
|
}
|
|
2822
2961
|
}
|
|
2823
2962
|
|
|
2963
|
+
// The halt resume card. Only on a halt: a clean close has nothing to resume,
|
|
2964
|
+
// and its summary already ends in the archive reminder.
|
|
2965
|
+
//
|
|
2966
|
+
// Written last, so the card carries every banner this close raised — the
|
|
2967
|
+
// reconstructability failure and the autonomy one included. Its own failure is
|
|
2968
|
+
// a banner and never an exit code: it is a pointer to records that were
|
|
2969
|
+
// already written, so losing it costs a reader convenience, not evidence. That
|
|
2970
|
+
// is the outcome-corpus class, not the trajectory's.
|
|
2971
|
+
//
|
|
2972
|
+
// Nothing reads it back. The next ship still decides whether to skip the
|
|
2973
|
+
// classifier from the plan fingerprint, so a card that was never written, or
|
|
2974
|
+
// was hand-edited, cannot change what a later run does.
|
|
2975
|
+
let resumeCard = null
|
|
2976
|
+
if (haltReason) {
|
|
2977
|
+
const written = writeResumeCard(root, {
|
|
2978
|
+
change: manifest.change,
|
|
2979
|
+
runId: manifest.runId,
|
|
2980
|
+
halted: haltReason,
|
|
2981
|
+
leftoverTaskIds,
|
|
2982
|
+
plan: manifest.plan,
|
|
2983
|
+
planStored: Boolean(planFingerprint) && existsSync(join(root, PLAN_FILE_PATH)),
|
|
2984
|
+
waves: manifest.waves || [],
|
|
2985
|
+
degradations,
|
|
2986
|
+
projectSlug: slug,
|
|
2987
|
+
cwd: absRoot
|
|
2988
|
+
})
|
|
2989
|
+
if (written.written) resumeCard = written.path
|
|
2990
|
+
else degradations.push(`RESUME CARD NOT WRITTEN: ${written.reason}`)
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2824
2993
|
manifest.lastThen = null
|
|
2825
2994
|
writeManifest(root, manifest)
|
|
2826
2995
|
|
|
@@ -2829,7 +2998,7 @@ export async function runClose(
|
|
|
2829
2998
|
action: haltReason ? 'halt' : 'complete',
|
|
2830
2999
|
then: null,
|
|
2831
3000
|
spawns: [],
|
|
2832
|
-
exitCode: haltReason || !reconstructable ? 1 : 0,
|
|
3001
|
+
exitCode: haltReason || !reconstructable || !trajectoryComplete ? 1 : 0,
|
|
2833
3002
|
banners: degradations,
|
|
2834
3003
|
summary: formatRunSummary({
|
|
2835
3004
|
change: manifest.change,
|
|
@@ -2841,7 +3010,8 @@ export async function runClose(
|
|
|
2841
3010
|
root: absRoot,
|
|
2842
3011
|
projectSlug: slug,
|
|
2843
3012
|
push,
|
|
2844
|
-
unarchived
|
|
3013
|
+
unarchived,
|
|
3014
|
+
resumeCard
|
|
2845
3015
|
})
|
|
2846
3016
|
}
|
|
2847
3017
|
}
|
package/package.json
CHANGED
package/shared/TOOL-ECONOMY.md
CHANGED
|
@@ -110,10 +110,17 @@ one you need. When Rule 0 applies, graph query/consumers counts as “locate.”
|
|
|
110
110
|
|
|
111
111
|
At session start, current-task context comes from a few cheap, authoritative
|
|
112
112
|
sources — `git log --oneline`, `git status --short`, `openspec status --json`,
|
|
113
|
-
an open-task grep over `openspec/changes/*/tasks.md`, and the latest
|
|
113
|
+
an open-task grep over `openspec/changes/*/tasks.md`, and the latest explore
|
|
114
|
+
brief (`.claude/handoff/explore-*.md`).
|
|
114
115
|
Front-load these in one pass. Read them,
|
|
115
116
|
then act. Do not re-traverse the tree to reconstruct what they already tell you.
|
|
116
117
|
|
|
118
|
+
The glob is load-bearing, not tidiness. `.claude/handoff/` also holds
|
|
119
|
+
`ship-*.md` halt resume cards, which are records of runs that already stopped —
|
|
120
|
+
nothing reads them, and finding one is not permission to start anything. Reading
|
|
121
|
+
"the latest handoff" unqualified after a halt would hand you one as if it were
|
|
122
|
+
session context.
|
|
123
|
+
|
|
117
124
|
**Explore brief as Rule-3 state:** when `/interlock:spec` runs after explore
|
|
118
125
|
(especially after `/clear` or prompt-cache expiry), read the latest matching
|
|
119
126
|
`.claude/handoff/explore-*.md` first (see `EXPLORE-BRIEF.md`).
|