@tachikomagundam/abathur 0.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/.github/workflows/ci.yml +29 -0
- package/.github/workflows/publish.yml +74 -0
- package/LICENSE +21 -0
- package/README.md +461 -0
- package/config/abathur.jsonc +17 -0
- package/config/genomes/historian.example.jsonc +124 -0
- package/dist/bench/adapter.js +201 -0
- package/dist/bench/fixture-probe.js +92 -0
- package/dist/bench/fixture-support.js +173 -0
- package/dist/bench/fixture.js +236 -0
- package/dist/bench/toy.js +152 -0
- package/dist/cli.js +110 -0
- package/dist/commands/bundle.js +79 -0
- package/dist/commands/genome.js +94 -0
- package/dist/commands/graft.js +71 -0
- package/dist/commands/kernel.js +47 -0
- package/dist/commands/promote.js +25 -0
- package/dist/commands/run.js +145 -0
- package/dist/commands/self-eval.js +240 -0
- package/dist/commands/status.js +186 -0
- package/dist/commands/tombstone.js +72 -0
- package/dist/config.js +161 -0
- package/dist/core/bundle-common.js +119 -0
- package/dist/core/bundle-export.js +212 -0
- package/dist/core/bundle-inspect.js +143 -0
- package/dist/core/bundle-manifest.js +105 -0
- package/dist/core/bundle-mask.js +75 -0
- package/dist/core/bundle-tar.js +240 -0
- package/dist/core/bundle.js +9 -0
- package/dist/core/evolve/brief.js +45 -0
- package/dist/core/evolve/candidate.js +140 -0
- package/dist/core/evolve/child-track.js +197 -0
- package/dist/core/evolve/friction.js +150 -0
- package/dist/core/evolve/reflect.js +191 -0
- package/dist/core/evolve/run-bench.js +170 -0
- package/dist/core/evolve/run-friction.js +63 -0
- package/dist/core/evolve/run-loop.js +282 -0
- package/dist/core/evolve/run-plan.js +39 -0
- package/dist/core/evolve/run-rows.js +145 -0
- package/dist/core/evolve/self-overlay.js +213 -0
- package/dist/core/evolve/self-snapshot.js +170 -0
- package/dist/core/evolve/stub-mutators.mjs +105 -0
- package/dist/core/evolve/udiff.js +189 -0
- package/dist/core/genome-paths.js +76 -0
- package/dist/core/genome.js +176 -0
- package/dist/core/glob.js +106 -0
- package/dist/core/graft-gates.js +184 -0
- package/dist/core/graft-rebench.js +187 -0
- package/dist/core/graft-support.js +181 -0
- package/dist/core/graft.js +218 -0
- package/dist/core/ids.js +154 -0
- package/dist/core/incumbent.js +46 -0
- package/dist/core/kernel.js +112 -0
- package/dist/core/ledger.js +198 -0
- package/dist/core/locks.js +172 -0
- package/dist/core/promote.js +119 -0
- package/dist/core/snapshot.js +61 -0
- package/dist/core/spec.js +178 -0
- package/dist/core/stats-math.js +102 -0
- package/dist/core/stats-pareto.js +57 -0
- package/dist/core/stats.js +184 -0
- package/dist/core/worktree.js +190 -0
- package/dist/exit.js +32 -0
- package/dist/genomes/toy-smoke/genome.jsonc +30 -0
- package/dist/genomes/toy-smoke/grader.mjs +61 -0
- package/dist/genomes/toy-smoke/init.mjs +63 -0
- package/dist/genomes/toy-smoke/units/add.mjs +17 -0
- package/dist/genomes/toy-smoke/units/explode.mjs +4 -0
- package/dist/genomes/toy-smoke/units/hang.mjs +16 -0
- package/dist/genomes/toy-smoke/units/mul.mjs +16 -0
- package/dist/genomes/toy-smoke/units/mutate.mjs +18 -0
- package/dist/genomes/toy-smoke/units/sub.mjs +16 -0
- package/dist/jsonc.js +77 -0
- package/dist/out.js +5 -0
- package/dist/test/bench-adapter.test.js +33 -0
- package/dist/test/bench-fixture.test.js +407 -0
- package/dist/test/bench-toy.test.js +251 -0
- package/dist/test/bundle.test.js +659 -0
- package/dist/test/config.test.js +185 -0
- package/dist/test/d7-gate.test.js +56 -0
- package/dist/test/fixture-loop.test.js +267 -0
- package/dist/test/fixtures/friction-writer.js +16 -0
- package/dist/test/fixtures-historian.js +82 -0
- package/dist/test/fixtures-self.js +143 -0
- package/dist/test/fixtures-wt.js +64 -0
- package/dist/test/friction.test.js +398 -0
- package/dist/test/genome.test.js +453 -0
- package/dist/test/git.test.js +69 -0
- package/dist/test/graft.test.js +567 -0
- package/dist/test/historian-genome.test.js +134 -0
- package/dist/test/historian-grader-io.test.js +148 -0
- package/dist/test/historian-grader.test.js +209 -0
- package/dist/test/ids.test.js +116 -0
- package/dist/test/include-val.test.js +120 -0
- package/dist/test/ledger-lock.test.js +99 -0
- package/dist/test/ledger.test.js +102 -0
- package/dist/test/promote.test.js +394 -0
- package/dist/test/reflect.test.js +410 -0
- package/dist/test/run-loop.test.js +433 -0
- package/dist/test/self-snapshot.test.js +328 -0
- package/dist/test/snapshot.test.js +86 -0
- package/dist/test/stats.test.js +423 -0
- package/dist/test/stub-mutators.test.js +17 -0
- package/dist/test/testutil.js +30 -0
- package/dist/test/worktree.test.js +198 -0
- package/dist/util/freeze.js +30 -0
- package/dist/util/git.js +85 -0
- package/docs/federation.md +184 -0
- package/docs/immutable-kernel.md +87 -0
- package/graders/historian/grader-core.d.mts +53 -0
- package/graders/historian/grader-core.mjs +276 -0
- package/graders/historian/grader-support.d.mts +57 -0
- package/graders/historian/grader-support.mjs +137 -0
- package/graders/historian/grader.mjs +113 -0
- package/graders/historian/mutate.sh +114 -0
- package/graders/historian/reset-sandbox.sh +60 -0
- package/graders/historian/run-scenario.sh +49 -0
- package/graders/historian/seed-wrapped.sh +32 -0
- package/package.json +42 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build-test:
|
|
14
|
+
name: Build & test (Node 22)
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: 22
|
|
22
|
+
cache: npm
|
|
23
|
+
# NOTE: deliberately NO registry-url here. Setting registry-url makes
|
|
24
|
+
# setup-node inject NODE_AUTH_TOKEN, which switches npm to authenticated
|
|
25
|
+
# legacy auth and defeats Trusted Publishing (OIDC) downstream.
|
|
26
|
+
|
|
27
|
+
- run: npm ci
|
|
28
|
+
- run: npm run build
|
|
29
|
+
- run: npm test
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# npm Trusted Publishing (OIDC) — zero long-lived credentials on GitHub.
|
|
2
|
+
#
|
|
3
|
+
# One-time setup required in the npmjs.com UI (Publishing access →
|
|
4
|
+
# "Trusted publisher" → Add publisher → GitHub Actions):
|
|
5
|
+
# Owner / user: TachikomaGundam
|
|
6
|
+
# Package name: @tachikomagundam/abathur
|
|
7
|
+
# Repository: Abathur
|
|
8
|
+
# Workflow name: publish.yml
|
|
9
|
+
# Environment: (leave empty unless you gate it)
|
|
10
|
+
# Release type: progressive
|
|
11
|
+
# The event this workflow publishes on is `push` (of a v* tag);
|
|
12
|
+
# the action is `npm publish --provenance --access public`.
|
|
13
|
+
#
|
|
14
|
+
# If the trusted publisher is not registered, `npm publish` fails with
|
|
15
|
+
# a 401/EPUBLISHCONFLICT — that is expected until the UI step is done.
|
|
16
|
+
name: Publish
|
|
17
|
+
|
|
18
|
+
on:
|
|
19
|
+
push:
|
|
20
|
+
tags:
|
|
21
|
+
- 'v*'
|
|
22
|
+
workflow_dispatch:
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
id-token: write # required for npm Trusted Publishing (OIDC provenance)
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
publish:
|
|
30
|
+
name: Publish to npm
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
|
|
35
|
+
- uses: actions/setup-node@v4
|
|
36
|
+
with:
|
|
37
|
+
# Node 24 ships npm >= 11.5.1, the minimum version that supports
|
|
38
|
+
# Trusted Publishing. Older npm silently falls back to token auth.
|
|
39
|
+
# NOTE: deliberately NO registry-url here — setting it injects
|
|
40
|
+
# NODE_AUTH_TOKEN and defeats OIDC (documented pitfall).
|
|
41
|
+
node-version: 24
|
|
42
|
+
cache: npm
|
|
43
|
+
|
|
44
|
+
- name: Enforce tag == package.json version
|
|
45
|
+
env:
|
|
46
|
+
GH_REF: ${{ github.ref }}
|
|
47
|
+
GH_EVENT: ${{ github.event_name }}
|
|
48
|
+
run: |
|
|
49
|
+
set -euo pipefail
|
|
50
|
+
if [ "${GH_EVENT}" = "workflow_dispatch" ]; then
|
|
51
|
+
# A dispatch lands on the default-branch tip, which carries no tag,
|
|
52
|
+
# so the describe-based check below would ALWAYS fail there. Skip it:
|
|
53
|
+
# the npm-side trusted-publisher event config remains the real gate.
|
|
54
|
+
echo "::warning::manual dispatch publish — npm Trusted Publishing event config will reject this unless a workflow_dispatch publisher is registered"
|
|
55
|
+
elif [ "${GH_REF#refs/tags/}" != "${GH_REF}" ]; then
|
|
56
|
+
# push of a v* tag: HEAD must sit exactly on that tag and the tag
|
|
57
|
+
# (minus the leading v) must equal package.json version.
|
|
58
|
+
TAG="$(git describe --tags --exact-match HEAD)"
|
|
59
|
+
VERSION="$(node -p "require('./package.json').version")"
|
|
60
|
+
if [ "${TAG#v}" != "${VERSION}" ]; then
|
|
61
|
+
echo "::error::git tag '${TAG}' does not match package.json version '${VERSION}'"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
echo "Publishing ${VERSION} (tag ${TAG})"
|
|
65
|
+
else
|
|
66
|
+
echo "::error::unexpected event '${GH_EVENT}' on ref '${GH_REF}' — refusing to publish"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
- run: npm ci
|
|
71
|
+
|
|
72
|
+
- run: npm run build
|
|
73
|
+
|
|
74
|
+
- run: npm publish --provenance --access public
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abathur contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
# abathur
|
|
2
|
+
|
|
3
|
+
An evolution harness for OpenCode agents: observe failures, mutate, re-bench,
|
|
4
|
+
select — with promotion held by a human gate. Offline lineage bundles let one
|
|
5
|
+
instance learn from another's evidence; nothing in v1 talks to a network.
|
|
6
|
+
|
|
7
|
+
一个面向 OpenCode 智能体的进化工装:观察失败、变异、重测、筛选——晋升决定始终由人把关。
|
|
8
|
+
离线血缘包(bundle)让一个实例基于另一个实例的证据学习;v1 不含任何联网传输。
|
|
9
|
+
|
|
10
|
+
## English
|
|
11
|
+
|
|
12
|
+
### What it is
|
|
13
|
+
|
|
14
|
+
- **genome** — a JSONC contract for one thing that can evolve: which bench
|
|
15
|
+
units measure it (train/val split), how (commands, adapter type, timeout,
|
|
16
|
+
statistics gates), what its evolution budget is, and which files are
|
|
17
|
+
kernel-immutable. Identity is the sha256 content fingerprint of the parsed
|
|
18
|
+
spec, never its label or filename.
|
|
19
|
+
- **bench** — an adapter that turns one genome tree into per-unit scores.
|
|
20
|
+
v1 ships two types: `toy` (pure-node fixtures, zero model calls) and
|
|
21
|
+
`opencode-fixture-scenarios` (real agent sessions against a live fixture
|
|
22
|
+
service, scored by script-first graders). Every candidate is re-benched
|
|
23
|
+
against the incumbent; todo-7 statistics gates decide.
|
|
24
|
+
- **bundle** — an offline lineage export (`.bundle.tgz`): generation trees,
|
|
25
|
+
patch, ledger summaries, redacted train-only evidence, and a v1 manifest
|
|
26
|
+
that `bundle inspect` re-verifies from the contained bytes. See
|
|
27
|
+
[docs/federation.md](docs/federation.md).
|
|
28
|
+
- **graft** — importing a peer's bundle: four byte-exact gates, then a local
|
|
29
|
+
re-bench at local reps and thresholds. The bundle's scores are recorded as
|
|
30
|
+
`peerClaim` metadata and never feed nomination math.
|
|
31
|
+
- **run loop** — incumbent → brief (failure observations) → mutator session →
|
|
32
|
+
candidate diffs → seal → re-bench → verdicts. Nominated generations sit in
|
|
33
|
+
the quarantine queue until a human runs `promote`; `tombstone` buries them.
|
|
34
|
+
Both are append-only ledger decisions; nothing is ever deleted.
|
|
35
|
+
|
|
36
|
+
### Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm i -g @tachikomagundam/abathur # from the npm registry
|
|
40
|
+
npm pack # in a checkout; prepack runs the build
|
|
41
|
+
npm i -g ./abathur-0.1.0.tgz
|
|
42
|
+
abathur --help
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Requires Node >= 22 and `git` on `PATH`. Only `zod` is a runtime dependency.
|
|
46
|
+
Real benches additionally need the `opencode` CLI (path via `opencodeBin` in
|
|
47
|
+
config or `PATH` lookup) and whatever a genome's `requires[]` probes name.
|
|
48
|
+
|
|
49
|
+
Config resolution is fail-closed: `$ABATHUR_CONFIG` (must exist when set) >
|
|
50
|
+
`~/.config/abathur/config.jsonc` > `<package>/config/abathur.jsonc`, with a
|
|
51
|
+
gitignored sibling `*.local.jsonc` deep-merged on top. Unknown keys are exit 2.
|
|
52
|
+
All writable state lives under the config dir — genome registry
|
|
53
|
+
`<configDir>/genomes/<fp16>.jsonc`, kernel manifests `<configDir>/kernels/`,
|
|
54
|
+
friction queue `<configDir>/friction.jsonl`, pending-graft queue
|
|
55
|
+
`<configDir>/graft-queue/` — never inside the package; each genome's ledger
|
|
56
|
+
rides its own repo at `<repoPath>/.state/abathur/ledger.jsonl` (gitignored).
|
|
57
|
+
|
|
58
|
+
Two setups need more than the tarball:
|
|
59
|
+
|
|
60
|
+
- **abathur-self** (self-evolution): `export ABATHUR_SELF_REPO=<path>` pointing
|
|
61
|
+
at a **git checkout** of this harness with its `node_modules` installed
|
|
62
|
+
(`npm ci`). The seed spec `genomes/abathur-self.jsonc` is registered from
|
|
63
|
+
that checkout — the self-bench builds candidate trees with the checkout's
|
|
64
|
+
pinned toolchain, so this genome does not run from the npm package alone.
|
|
65
|
+
- **historian**: the operator workflow is its own section below.
|
|
66
|
+
|
|
67
|
+
### Quick start: the zero-model toy loop
|
|
68
|
+
|
|
69
|
+
No models, no network: the `toy-smoke` genome ships inside the package
|
|
70
|
+
(`dist/genomes/toy-smoke/`) and carries a seeded bug — `add()` subtracts.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
mkdir -p ~/abathur-demo && cd ~/abathur-demo
|
|
74
|
+
export ABATHUR_PKG="$(npm root -g)/abathur"
|
|
75
|
+
|
|
76
|
+
# 1. materialize an independent toy genome repo (git init + repoPath rewritten)
|
|
77
|
+
node "$ABATHUR_PKG/dist/genomes/toy-smoke/init.mjs" ./genome
|
|
78
|
+
abathur genome add ./genome/genome.jsonc
|
|
79
|
+
|
|
80
|
+
# 2. a mutator is a command template, not a flag you can skip: `run` exits 2
|
|
81
|
+
# without --mutator. Write the smallest legal one — it must print
|
|
82
|
+
# {"candidates":[{"id","rationale","diffs":[unified-diff]}]} to stdout.
|
|
83
|
+
cat > toy-stub.mjs <<'EOF'
|
|
84
|
+
// toy-stub.mjs — minimal mutator driver: reads the scripted-patch table
|
|
85
|
+
// shipped inside the abathur package and emits it as candidate JSON; the
|
|
86
|
+
// harness applies and benches every candidate itself.
|
|
87
|
+
import { readFileSync } from "node:fs";
|
|
88
|
+
import { pathToFileURL } from "node:url";
|
|
89
|
+
|
|
90
|
+
const args = process.argv.slice(2);
|
|
91
|
+
const opt = (n) => args[args.indexOf(n) + 1];
|
|
92
|
+
const { scriptedPatches } = await import(pathToFileURL(opt("--lib")).href);
|
|
93
|
+
|
|
94
|
+
const candidates = [];
|
|
95
|
+
for (const patch of scriptedPatches()) {
|
|
96
|
+
const lines = readFileSync(`${opt("--dir")}/${patch.file}`, "utf8").split("\n");
|
|
97
|
+
const i = lines.findIndex((line) => line.includes(patch.from));
|
|
98
|
+
if (i < 0) continue; // anchor already consumed — nothing to propose here
|
|
99
|
+
candidates.push({
|
|
100
|
+
id: patch.id,
|
|
101
|
+
rationale: patch.description,
|
|
102
|
+
diffs: [
|
|
103
|
+
`--- a/${patch.file}\n+++ b/${patch.file}\n@@ -${i + 1},1 +${i + 1},1 @@\n` +
|
|
104
|
+
`-${lines[i]}\n+${lines[i].replace(patch.from, patch.to)}\n`,
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
process.stdout.write(`${JSON.stringify({ candidates })}\n`);
|
|
109
|
+
EOF
|
|
110
|
+
|
|
111
|
+
# 3. evolve: {worktree} and {brief} are rendered by the harness; the template
|
|
112
|
+
# is spawned as argv, never through a shell.
|
|
113
|
+
abathur run --genome toy-smoke \
|
|
114
|
+
--mutator "node $PWD/toy-stub.mjs --lib $ABATHUR_PKG/dist/core/evolve/stub-mutators.mjs --dir {worktree} --brief {brief}"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Typical output (toy budgets make this take seconds):
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
incumbent baseline: 4 units x 2 reps
|
|
121
|
+
candidate fix-add [tree cbc27a991cf9]: nominated (gain 0.5000, reps 2)
|
|
122
|
+
candidate break-mul [tree 52b89d61da91]: culled (gain -0.3333, reps 2)
|
|
123
|
+
gate: gain -0.3333 < minEffect 0.5000
|
|
124
|
+
...
|
|
125
|
+
budget spent: candidates=4 tokens=4338 wallS=7.542 (caps candidates=4 tokens=100000 wallS=300)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Then read, decide, and hand the winner around:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
abathur status toy-smoke # read-only ledger view
|
|
132
|
+
abathur promote toy-smoke g-... # human gate — the ONLY path to a new incumbent
|
|
133
|
+
abathur bundle export toy-smoke --gen g-... --out ./bundles # offline lineage
|
|
134
|
+
abathur bundle inspect ./bundles/*.bundle.tgz
|
|
135
|
+
abathur graft ./bundles/*.bundle.tgz --genome toy-smoke # local re-bench of a peer bundle
|
|
136
|
+
abathur run --genome toy-smoke --dry-run # plan + requires[] probes, zero spawns
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### The historian genome and the operator workflow
|
|
140
|
+
|
|
141
|
+
`historian` is the first real genome: OpenCode agents work wiki scenarios
|
|
142
|
+
against a live Wiki.js fixture; script-first graders score dimensions A–J with
|
|
143
|
+
a hard G gate. `config/genomes/historian.example.jsonc` is a **template** —
|
|
144
|
+
every machine-specific value is a `${VAR}` placeholder, because repo-tracked
|
|
145
|
+
files carry zero machine literals. Only `repoPath` has built-in env resolution
|
|
146
|
+
(`effectiveRepoPath`, `src/core/spec.ts`); `requires[]` probes and every
|
|
147
|
+
command string are spawned as argv, never through a shell, so `${VAR}` inside
|
|
148
|
+
them does **not** expand. The operator resolves the template:
|
|
149
|
+
|
|
150
|
+
1. Keep a historian bench repo checkout (`scenarios/`, `rubric.md`,
|
|
151
|
+
`seed_sandbox.sh`, `baseline/**`), a running wiki, and the wiki-ops CLI.
|
|
152
|
+
2. Copy the example to a private `*.local.jsonc` file (the gitignore pattern
|
|
153
|
+
covers it anywhere in the tree) and substitute every placeholder with the
|
|
154
|
+
real absolute paths / URLs; then `abathur genome add` **that** file.
|
|
155
|
+
The printed fingerprint is deterministic per resolved path set (on the
|
|
156
|
+
orchestrator's machine: `2b2456be6cddcb91` — yours will differ with your
|
|
157
|
+
paths, and stay stable for them).
|
|
158
|
+
3. Live-run environment the operator must keep: `~/.wikijs-api-key` in the
|
|
159
|
+
**parent** HOME (the `requires[]` probe child inherits the parent env and
|
|
160
|
+
reads it directly), and `ABATHUR_WIKI_KEY_FILE` — the sandbox-side scripts
|
|
161
|
+
reinstall the key into each mirrored sandbox home, because `resetCommand`
|
|
162
|
+
wipes the sandbox HOME mirror before every unit (hook order: reset runs
|
|
163
|
+
before the mirror, so anything hooks need must be re-installed per unit).
|
|
164
|
+
4. `export ABATHUR_JUDGE_MODEL=...` is mandatory even when empty: the
|
|
165
|
+
grader-side `opencode` invocations error out on an unset variable, not on
|
|
166
|
+
an empty one.
|
|
167
|
+
5. Prove setup without model spend: `abathur run --genome historian --dry-run`
|
|
168
|
+
runs every `requires[]` probe and prints the plan; zero engines spawn.
|
|
169
|
+
|
|
170
|
+
Real runs launch `opencode` headless and cost tokens. Budgets
|
|
171
|
+
(`budget.maxCandidates/maxModelCalls/maxTokens/maxWallS`) are hard stops —
|
|
172
|
+
crossing one truncates to `inconclusive` (exit 2), never to a fake pass.
|
|
173
|
+
|
|
174
|
+
### D7 decoupling guarantees
|
|
175
|
+
|
|
176
|
+
- The genome registry and every writable artifact live **out-of-tree** under
|
|
177
|
+
the config dir; the npm package directory is read-only at runtime.
|
|
178
|
+
- Repo-tracked files contain **zero** machine or product literals. The CI
|
|
179
|
+
gate: `grep -rnE '/home/lab|historian' src/` yields nothing outside
|
|
180
|
+
`src/test/**` fixtures. Config templates spell out placeholders instead.
|
|
181
|
+
- Machine paths enter only through config and env: a `${VAR}`-literal
|
|
182
|
+
`repoPath` fingerprints over the **unresolved** bytes, so spec identity is
|
|
183
|
+
machine-independent (abathur-self: `1913fcec…` everywhere) and resolves
|
|
184
|
+
only at filesystem seams; unset env exits 2 naming the variable.
|
|
185
|
+
- Identity is always the content fingerprint, never a label: two byte-equal
|
|
186
|
+
specs are the same genome on any machine; labels are free and may repeat.
|
|
187
|
+
- Adapters are a closed `bench.type` registry; a genome without a known
|
|
188
|
+
adapter type is exit 2, never a guess.
|
|
189
|
+
|
|
190
|
+
### Kernel seal and self-evolution
|
|
191
|
+
|
|
192
|
+
Every registered genome's `kernel.immutableGlobs` are hashed into a sealed
|
|
193
|
+
manifest under `<configDir>/kernels/`; benches and self-eval refuse to run on
|
|
194
|
+
a drifted tree, mutator candidates touching sealed paths are rejected at the
|
|
195
|
+
path stage, and only human `promote` reseals — from the promoted tree.
|
|
196
|
+
`abathur kernel audit <label>` verifies the seal against the working tree.
|
|
197
|
+
The full contract (what `abathur-self` may and may not touch, the
|
|
198
|
+
snapshot-overlay bench, why v1 self-evolution cannot change dependencies or
|
|
199
|
+
build configuration) is [docs/immutable-kernel.md](docs/immutable-kernel.md).
|
|
200
|
+
|
|
201
|
+
### Federation: bundles now, transport later
|
|
202
|
+
|
|
203
|
+
v1 ships the **format and the local tools**, not a network. A bundle is a
|
|
204
|
+
file you carry by any means you like; `bundle export` is deterministic —
|
|
205
|
+
re-exporting an unchanged ledger produces byte-identical tarballs, and the
|
|
206
|
+
byte-equality rule extends to every graft gate. Graft admits a peer lineage
|
|
207
|
+
only when genome fingerprint, benchDigest, scoring provenance, and
|
|
208
|
+
`requires[]` probes all recompute byte-equal **locally**; anything else
|
|
209
|
+
quarantines, queues as pending-bench, or refuses, with the decision booked to
|
|
210
|
+
the ledger. No noise-tolerance band, no `--force`, no bypass. Discovery,
|
|
211
|
+
transport, auto-merge, and auto-promotion are explicitly **out of scope**.
|
|
212
|
+
The schema field-for-field, the inspect gate order, and the graft decision
|
|
213
|
+
table are [docs/federation.md](docs/federation.md).
|
|
214
|
+
|
|
215
|
+
### Operator duties
|
|
216
|
+
|
|
217
|
+
- **Single-flight contract**: at most one live bench per genome fingerprint
|
|
218
|
+
per shared fixture service. The genome lock `run`/`graft` take is a
|
|
219
|
+
**machine-local** file lock (it serializes worktrees, sandboxes, and the
|
|
220
|
+
ledger on one box). Historian benches share a wiki through `resetCommand`,
|
|
221
|
+
which purges the entire `_sandbox/*` namespace — two machines benching the
|
|
222
|
+
same historian genome against one wiki will clobber each other mid-run.
|
|
223
|
+
Cross-machine serialization of historian benches is the operator's
|
|
224
|
+
responsibility (schedule, lockbox, one-wiki-one-bench rule — your call).
|
|
225
|
+
- Promotion and burial are yours alone: `promote`/`tombstone` carry no
|
|
226
|
+
`--confirm` because the CLI invocation **is** the gate; there is no
|
|
227
|
+
`--force` anywhere in the binary. Review `abathur status` quarantine depth
|
|
228
|
+
and the `graft decisions` rows before promoting.
|
|
229
|
+
- Stale pending-bench queue entries (`<configDir>/graft-queue/`) mean a
|
|
230
|
+
bundle waits on setup — register the genome or repair a probe, then re-run
|
|
231
|
+
`graft`; terminal decisions retire the entry automatically.
|
|
232
|
+
- Keep `~/.wikijs-api-key` valid and `ABATHUR_JUDGE_MODEL` exported for
|
|
233
|
+
historian work; budget caps are per-run hard stops, so raise them
|
|
234
|
+
deliberately in the spec, never by re-running off-book.
|
|
235
|
+
|
|
236
|
+
### Exit codes
|
|
237
|
+
|
|
238
|
+
The contract, every command, no exceptions:
|
|
239
|
+
|
|
240
|
+
- `0` — ok / pass / nominated.
|
|
241
|
+
- `1` — blocked: a recorded **decision** (culled, indeterminate, quarantined,
|
|
242
|
+
promote refusal, pending unregistered genome).
|
|
243
|
+
- `2` — cannot-answer: config error, malformed input, missing engine, probe
|
|
244
|
+
pending, budget-truncated (inconclusive). Never a silent skip.
|
|
245
|
+
|
|
246
|
+
### License
|
|
247
|
+
|
|
248
|
+
MIT, see [LICENSE](LICENSE).
|
|
249
|
+
|
|
250
|
+
## 中文
|
|
251
|
+
|
|
252
|
+
### 它是什么
|
|
253
|
+
|
|
254
|
+
- **genome(基因组)**——一份 JSONC 契约,描述一个可进化的对象:用哪些 bench
|
|
255
|
+
单元(train/val 划分)度量它、怎么度量(命令、适配器类型、超时、统计闸门)、
|
|
256
|
+
进化预算是多少、哪些文件属于内核不可变区。身份是解析后规格内容指纹(sha256),
|
|
257
|
+
绝不是标签或文件名。
|
|
258
|
+
- **bench(基准)**——把一棵基因组树变成逐单元分数的适配器。v1 内置两种类型:
|
|
259
|
+
`toy`(纯 node 夹具,零模型调用)与 `opencode-fixture-scenarios`(真实智能体
|
|
260
|
+
会话对着在线夹具服务运行,由 script-first 评分器打分)。每个候选都与在位者
|
|
261
|
+
(incumbent)重测对比;统计闸门出裁决。
|
|
262
|
+
- **bundle(血缘包)**——离线血缘导出(`.bundle.tgz`):各代树内容、补丁、台账
|
|
263
|
+
摘要、经脱敏的仅 train 证据,以及一份 v1 manifest;`bundle inspect` 会从包内
|
|
264
|
+
字节重新验证一切。详见 [docs/federation.md](docs/federation.md)。
|
|
265
|
+
- **graft(嫁接)**——导入对端的血缘包:四道字节级闸门,然后按本地 reps 与本地
|
|
266
|
+
阈值做本地重测。包里的分数只作为 `peerClaim` 元数据记录,永远不参与提名数学。
|
|
267
|
+
- **run 循环**——在位者 → brief(失败观察)→ 变异会话 → 候选 diff → 封印 →
|
|
268
|
+
重测 → 裁决。被提名的一代会停留在隔离队列里,直到人类执行 `promote`;
|
|
269
|
+
`tombstone` 则埋葬它。两者都是只追加的台账决定;任何数据都不删除。
|
|
270
|
+
|
|
271
|
+
### 安装
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
npm i -g @tachikomagundam/abathur # 从 npm registry 安装
|
|
275
|
+
npm pack # 在 checkout 里执行;prepack 会先构建
|
|
276
|
+
npm i -g ./abathur-0.1.0.tgz
|
|
277
|
+
abathur --help
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
需要 Node >= 22 与 `PATH` 上的 `git`。运行时依赖只有 `zod`。真实基准还需要
|
|
281
|
+
`opencode` CLI(通过配置里的 `opencodeBin` 或 `PATH` 解析),以及基因组
|
|
282
|
+
`requires[]` 探针点名的那些工具。
|
|
283
|
+
|
|
284
|
+
配置解析是 fail-closed:`$ABATHUR_CONFIG`(设置就必须存在)>
|
|
285
|
+
`~/.config/abathur/config.jsonc` > `<package>/config/abathur.jsonc`,同级
|
|
286
|
+
gitignored 的 `*.local.jsonc` 会深合并覆盖其上。未知键直接 exit 2。
|
|
287
|
+
所有可写状态都在配置目录下——基因组注册表 `<configDir>/genomes/<fp16>.jsonc`、
|
|
288
|
+
内核清单 `<configDir>/kernels/`、摩擦队列 `<configDir>/friction.jsonl`、
|
|
289
|
+
待嫁接队列 `<configDir>/graft-queue/`——绝不写进包目录;每个基因组的台账跟随
|
|
290
|
+
其自身仓库位于 `<repoPath>/.state/abathur/ledger.jsonl`(gitignored)。
|
|
291
|
+
|
|
292
|
+
两种场景不是装个 tarball 就够:
|
|
293
|
+
|
|
294
|
+
- **abathur-self**(自我演化):`export ABATHUR_SELF_REPO=<路径>` 指向本工装
|
|
295
|
+
的一个 **git checkout**,且其中 `node_modules` 已安装(`npm ci`)。种子规格
|
|
296
|
+
`genomes/abathur-self.jsonc` 从那个 checkout 注册——self-bench 用该 checkout
|
|
297
|
+
钉住的工具链构建候选树,所以这个基因组不能只靠 npm 包运行。
|
|
298
|
+
- **historian**:操作员工作流见下文独立小节。
|
|
299
|
+
|
|
300
|
+
### 快速上手:零模型玩具环
|
|
301
|
+
|
|
302
|
+
不调模型、不联网:`toy-smoke` 基因组就随包发布(`dist/genomes/toy-smoke/`),
|
|
303
|
+
且预埋了一个 bug——`add()` 做的是减法。
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
mkdir -p ~/abathur-demo && cd ~/abathur-demo
|
|
307
|
+
export ABATHUR_PKG="$(npm root -g)/abathur"
|
|
308
|
+
|
|
309
|
+
# 1. 物化一个独立的玩具基因组仓库(git init + 重写 repoPath)
|
|
310
|
+
node "$ABATHUR_PKG/dist/genomes/toy-smoke/init.mjs" ./genome
|
|
311
|
+
abathur genome add ./genome/genome.jsonc
|
|
312
|
+
|
|
313
|
+
# 2. 变异器是一条命令模板,不是可跳过的可选项:没有 --mutator,run 直接 exit 2。
|
|
314
|
+
# 写一个最小合法实现——它必须向 stdout 打印
|
|
315
|
+
# {"candidates":[{"id","rationale","diffs":[unified-diff]}]}。
|
|
316
|
+
cat > toy-stub.mjs <<'EOF'
|
|
317
|
+
// toy-stub.mjs — minimal mutator driver: reads the scripted-patch table
|
|
318
|
+
// shipped inside the abathur package and emits it as candidate JSON; the
|
|
319
|
+
// harness applies and benches every candidate itself.
|
|
320
|
+
import { readFileSync } from "node:fs";
|
|
321
|
+
import { pathToFileURL } from "node:url";
|
|
322
|
+
|
|
323
|
+
const args = process.argv.slice(2);
|
|
324
|
+
const opt = (n) => args[args.indexOf(n) + 1];
|
|
325
|
+
const { scriptedPatches } = await import(pathToFileURL(opt("--lib")).href);
|
|
326
|
+
|
|
327
|
+
const candidates = [];
|
|
328
|
+
for (const patch of scriptedPatches()) {
|
|
329
|
+
const lines = readFileSync(`${opt("--dir")}/${patch.file}`, "utf8").split("\n");
|
|
330
|
+
const i = lines.findIndex((line) => line.includes(patch.from));
|
|
331
|
+
if (i < 0) continue; // anchor already consumed — nothing to propose here
|
|
332
|
+
candidates.push({
|
|
333
|
+
id: patch.id,
|
|
334
|
+
rationale: patch.description,
|
|
335
|
+
diffs: [
|
|
336
|
+
`--- a/${patch.file}\n+++ b/${patch.file}\n@@ -${i + 1},1 +${i + 1},1 @@\n` +
|
|
337
|
+
`-${lines[i]}\n+${lines[i].replace(patch.from, patch.to)}\n`,
|
|
338
|
+
],
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
process.stdout.write(`${JSON.stringify({ candidates })}\n`);
|
|
342
|
+
EOF
|
|
343
|
+
|
|
344
|
+
# 3. 进化:{worktree} 和 {brief} 由工装渲染;模板按 argv 直接 spawn,绝不经过 shell。
|
|
345
|
+
abathur run --genome toy-smoke \
|
|
346
|
+
--mutator "node $PWD/toy-stub.mjs --lib $ABATHUR_PKG/dist/core/evolve/stub-mutators.mjs --dir {worktree} --brief {brief}"
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
典型输出(玩具预算下几秒跑完):
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
incumbent baseline: 4 units x 2 reps
|
|
353
|
+
candidate fix-add [tree cbc27a991cf9]: nominated (gain 0.5000, reps 2)
|
|
354
|
+
candidate break-mul [tree 52b89d61da91]: culled (gain -0.3333, reps 2)
|
|
355
|
+
gate: gain -0.3333 < minEffect 0.5000
|
|
356
|
+
...
|
|
357
|
+
budget spent: candidates=4 tokens=4338 wallS=7.542 (caps candidates=4 tokens=100000 wallS=300)
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
随后查看、拍板、把优胜者传出去:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
abathur status toy-smoke # 只读台账视图
|
|
364
|
+
abathur promote toy-smoke g-... # 人类闸门——通往新在位者的唯一路径
|
|
365
|
+
abathur bundle export toy-smoke --gen g-... --out ./bundles # 离线血缘
|
|
366
|
+
abathur bundle inspect ./bundles/*.bundle.tgz
|
|
367
|
+
abathur graft ./bundles/*.bundle.tgz --genome toy-smoke # 对端血缘包的本地重测
|
|
368
|
+
abathur run --genome toy-smoke --dry-run # 计划 + requires[] 探针,零 spawn
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### 历史学家基因组与操作员工作流
|
|
372
|
+
|
|
373
|
+
`historian` 是第一个真实基因组:OpenCode 智能体对着在线 Wiki.js 夹具完成 wiki
|
|
374
|
+
场景;script-first 评分器按 A–J 维度打分并有硬性 G 闸门。
|
|
375
|
+
`config/genomes/historian.example.jsonc` 是一份**模板**——每个机器相关值都是
|
|
376
|
+
`${VAR}` 占位符,因为仓库跟踪的文件里机器字面量为零。只有 `repoPath` 有内置的
|
|
377
|
+
环境变量解析(`effectiveRepoPath`,`src/core/spec.ts`);`requires[]` 探针与所有
|
|
378
|
+
命令字符串都是 argv 直接 spawn、绝不经过 shell,所以其中的 `${VAR}` **不会**展开。
|
|
379
|
+
操作员需要解析模板:
|
|
380
|
+
|
|
381
|
+
1. 备好 historian 基准仓库的 checkout(`scenarios/`、`rubric.md`、
|
|
382
|
+
`seed_sandbox.sh`、`baseline/**`)、一个运行中的 wiki,以及 wiki-ops CLI。
|
|
383
|
+
2. 把模板复制成私有的 `*.local.jsonc` 文件(gitignore 规则在树的任何位置都覆盖
|
|
384
|
+
它),把每个占位符替换为真实的绝对路径 / URL;然后对**那份文件**执行
|
|
385
|
+
`abathur genome add`。打印出的指纹对每组解析后的路径是确定性的(编排者机器上
|
|
386
|
+
是 `2b2456be6cddcb91`——你的路径会得到不同的值,但对那些路径保持稳定)。
|
|
387
|
+
3. 操作员必须维护的实跑环境:**父级** HOME 里的 `~/.wikijs-api-key`(`requires[]`
|
|
388
|
+
探针子进程继承父环境并直接读取它),以及 `ABATHUR_WIKI_KEY_FILE`——沙箱侧脚本
|
|
389
|
+
会把密钥重装进每个镜像沙箱 HOME,因为 `resetCommand` 在每个单元运行前会清空
|
|
390
|
+
沙箱 HOME 镜像(钩子顺序:reset 先于 mirror 执行,钩子需要的东西必须逐单元重装)。
|
|
391
|
+
4. `export ABATHUR_JUDGE_MODEL=...` 是强制的,即使为空:评分侧的 `opencode` 调用
|
|
392
|
+
遇到**未设置**的变量会报错,遇到空值不会。
|
|
393
|
+
5. 不花模型费用验证环境:`abathur run --genome historian --dry-run` 会跑完所有
|
|
394
|
+
`requires[]` 探针并打印计划;零引擎 spawn。
|
|
395
|
+
|
|
396
|
+
真实 run 会启动 `opencode` headless 并消耗 token。预算
|
|
397
|
+
(`budget.maxCandidates/maxModelCalls/maxTokens/maxWallS`)是硬停——越线截断为
|
|
398
|
+
`inconclusive`(exit 2),绝不会伪装成通过。
|
|
399
|
+
|
|
400
|
+
### D7 解耦保证
|
|
401
|
+
|
|
402
|
+
- 基因组注册表与一切可写产物都在**树外**、配置目录下;运行时 npm 包目录只读。
|
|
403
|
+
- 仓库跟踪的文件里**零**机器 / 产品字面量。CI 闸门:`grep -rnE '/home/lab|historian' src/`
|
|
404
|
+
在 `src/test/**` 夹具之外一无所获。配置模板用占位符表达。
|
|
405
|
+
- 机器路径只从配置与环境变量进入:`${VAR}` 字面量形式的 `repoPath` 按**未解析**
|
|
406
|
+
字节参与指纹计算,因此规格身份与机器无关(abathur-self 在任何机器都是
|
|
407
|
+
`1913fcec…`),只在文件系统接缝处解析;变量未设置就 exit 2 并点名该变量。
|
|
408
|
+
- 身份永远是内容指纹、不是标签:两份字节相同的规格在任何机器上都是同一个基因组;
|
|
409
|
+
标签自由、可重复。
|
|
410
|
+
- 适配器是封闭的 `bench.type` 注册表;遇到未知适配器类型的基因组是 exit 2,
|
|
411
|
+
绝不猜测。
|
|
412
|
+
|
|
413
|
+
### 内核封印与自我演化
|
|
414
|
+
|
|
415
|
+
每个注册基因组的 `kernel.immutableGlobs` 都会被哈希成 `<configDir>/kernels/`
|
|
416
|
+
下的封印清单;工作树漂移时基准与 self-eval 拒绝运行;触碰封印路径的变异候选在
|
|
417
|
+
路径阶段即被拒绝;只有人类 `promote` 会重新封印——且从被晋升的树封印。
|
|
418
|
+
`abathur kernel audit <label>` 对照工作树验证封印。完整契约(`abathur-self`
|
|
419
|
+
能碰什么、不能碰什么,快照覆盖式基准,为什么 v1 自我演化不能改依赖和构建配置)
|
|
420
|
+
在 [docs/immutable-kernel.md](docs/immutable-kernel.md)。
|
|
421
|
+
|
|
422
|
+
### 联邦:先有捆绑包,后有传输
|
|
423
|
+
|
|
424
|
+
v1 交付的是**格式和本地工具**,不是网络。bundle 是一个你用任何方式搬运的文件;
|
|
425
|
+
`bundle export` 是确定性的——账本未变时重复导出产出字节相同的 tarball,
|
|
426
|
+
字节相等规则贯穿所有嫁接闸门。只有当基因组指纹、benchDigest、评分溯源与
|
|
427
|
+
`requires[]` 探针全部在**本地**重算为字节相等时,graft 才接纳对端血缘;其余情况
|
|
428
|
+
一律隔离(quarantine)、排队(pending-bench)或拒绝,并把决定记入台账。
|
|
429
|
+
没有噪声容忍带,没有 `--force`,没有绕行。发现、传输、自动合并、自动晋升全部
|
|
430
|
+
明确**不在范围内**。逐字段的 schema、inspect 闸门顺序、graft 决策表见
|
|
431
|
+
[docs/federation.md](docs/federation.md)。
|
|
432
|
+
|
|
433
|
+
### 操作员职责
|
|
434
|
+
|
|
435
|
+
- **单飞契约**:每个基因组指纹、每个共享夹具服务,最多只有一个在跑的基准。
|
|
436
|
+
`run`/`graft` 获取的基因组锁是**机器本地**的文件锁(它只在单机内串行化
|
|
437
|
+
worktree、沙箱与台账)。historian 基准通过 `resetCommand` 共享 wiki,而它会
|
|
438
|
+
清空整个 `_sandbox/*` 命名空间——两台机器拿同一个 historian 基因组对着同一个
|
|
439
|
+
wiki 跑基准,会在运行中互相踩掉对方。historian 基准的跨机串行化是操作员的
|
|
440
|
+
职责(排班、锁柜、一 wiki 一基准——随你选哪种)。
|
|
441
|
+
- 晋升与埋葬只属于你:`promote`/`tombstone` 不带 `--confirm`,因为 CLI 调用本身
|
|
442
|
+
**就是**闸门;整个二进制里没有 `--force`。晋升前先查看 `abathur status` 的
|
|
443
|
+
隔离深度与 `graft decisions` 行。
|
|
444
|
+
- 待嫁接队列(`<configDir>/graft-queue/`)里的陈旧条目表示某个 bundle 在等环境
|
|
445
|
+
就绪——注册好基因组或修好探针,再跑一次 `graft`;终态决定会自动移除队列条目。
|
|
446
|
+
- 做 historian 工作时保持 `~/.wikijs-api-key` 有效、`ABATHUR_JUDGE_MODEL` 已导出;
|
|
447
|
+
预算上限是每次运行的硬停,要提就在规格里郑重地提,不要在账外反复重跑。
|
|
448
|
+
|
|
449
|
+
### 退出码
|
|
450
|
+
|
|
451
|
+
对每个命令、无一例外的契约:
|
|
452
|
+
|
|
453
|
+
- `0`——ok / pass / nominated。
|
|
454
|
+
- `1`——blocked:已记录的**决定**(culled、indeterminate、quarantined、
|
|
455
|
+
晋升被拒、未注册基因组的 pending)。
|
|
456
|
+
- `2`——cannot-answer:配置错误、输入畸形、引擎缺失、探针未就绪、预算截断
|
|
457
|
+
(inconclusive)。绝不会静默跳过。
|
|
458
|
+
|
|
459
|
+
### 许可
|
|
460
|
+
|
|
461
|
+
MIT,见 [LICENSE](LICENSE)。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Abathur default (repo) configuration — shipped in the npm tarball.
|
|
2
|
+
// Resolution order for the active config file (src/config.ts):
|
|
3
|
+
// 1. $ABATHUR_CONFIG explicit override; must exist, fail-closed otherwise
|
|
4
|
+
// 2. ~/.config/abathur/config.jsonc
|
|
5
|
+
// 3. <repo>/config/abathur.jsonc (this file)
|
|
6
|
+
// Machine-local absolute paths (opencodeBin, genome repoPath, ...) belong in a
|
|
7
|
+
// gitignored `config.local.jsonc` overlay next to whichever base file wins —
|
|
8
|
+
// NEVER in this file and never hardcoded in src/** (todo 15 grep-gate).
|
|
9
|
+
// Unknown keys here are a hard error (exit 2), by design.
|
|
10
|
+
{
|
|
11
|
+
// Absolute path to the `opencode` CLI binary. Default: resolve from PATH at
|
|
12
|
+
// call time (todo 6+); set in *.local.jsonc when PATH lookup is ambiguous.
|
|
13
|
+
"opencodeBin": null,
|
|
14
|
+
// Data home: ledger, genome registry, sandbox worktrees. null = ABATHUR_HOME
|
|
15
|
+
// env, else ~/.local/share/abathur. Overlays may point it at a scratch dir.
|
|
16
|
+
"stateDir": null
|
|
17
|
+
}
|