Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c736b7ed2 | ||
|
|
9ad9b101cb | ||
|
|
eb76e90287 | ||
|
|
0ab9996af3 | ||
|
|
055d974ae0 | ||
|
|
6c7e5b4ea1 | ||
|
|
9090c8f91a | ||
|
|
76a5c0a92f | ||
|
|
a8868b35aa | ||
|
|
47cd6271a7 | ||
|
|
860f39130a | ||
|
|
f6f7925483 | ||
|
|
76e38ecd5f | ||
|
|
41d096efdd | ||
|
|
dd71b56d2c | ||
|
|
b6ff9df9e7 | ||
|
|
6f35978702 | ||
|
|
993fb21cfa | ||
|
|
15d6923d7a | ||
|
|
38e63273a4 | ||
|
|
07b2c86f17 | ||
|
|
dea56dad43 | ||
|
|
292e3a8a11 | ||
|
|
2aee1b7710 | ||
|
|
d260b6a4af | ||
|
|
2d12602a78 | ||
|
|
a54b9acd33 | ||
|
|
6a82141b83 | ||
|
|
6d812650b6 | ||
|
|
82b79e46a7 | ||
|
|
539141c3db | ||
|
|
2997afbe0e | ||
|
|
9ac7dce71a | ||
|
|
4390fa25a0 | ||
|
|
293613f18b | ||
|
|
4b28ce0641 | ||
|
|
168b9231b9 | ||
|
|
2510f68b05 | ||
|
|
0faee1f6b6 | ||
|
|
917f2851ad | ||
|
|
a1cc0c7721 | ||
|
|
08b1035bdd | ||
|
|
9d7930f48e | ||
|
|
251b7a105f | ||
|
|
515f2f2ab8 | ||
|
|
c5b4a61cc9 | ||
|
|
6a0cf292bc | ||
|
|
af6c56c003 | ||
|
|
d42b0b31e6 | ||
|
|
d2274725ad | ||
|
|
adc1417756 | ||
|
|
983d7d5c42 | ||
|
|
56b971b322 | ||
|
|
754e8c2d3c | ||
|
|
5b3ff5bcc0 | ||
|
|
fd6ddc4d89 | ||
|
|
65bf10c691 | ||
|
|
c1d5f15287 | ||
|
|
5db2e2492b | ||
|
|
acbcc8389e | ||
|
|
a2f0eefa35 | ||
|
|
69fc06b6a2 | ||
|
|
9d7f68fcc3 | ||
|
|
0d1236a981 | ||
|
|
af10dafeb0 | ||
|
|
5e27ff2210 | ||
|
|
fea80d8618 | ||
|
|
73a2a34251 | ||
|
|
fd6837dd9c | ||
|
|
e6cc4f0c95 | ||
|
|
64cb527428 | ||
|
|
bb2461ee27 | ||
|
|
a144b0f0da | ||
|
|
3cf274dd62 | ||
|
|
0c288f306f | ||
|
|
6207755b84 | ||
|
|
347c2b4256 | ||
|
|
7aa61a5a2f | ||
|
|
c8926ef872 | ||
|
|
74a8ffd27d | ||
|
|
dbf925bde1 | ||
|
|
52a395f55f | ||
|
|
e0beec69bd | ||
|
|
824d6fd3d2 | ||
|
|
0b78b0e039 | ||
|
|
b5ab45188d | ||
|
|
65fef8cd3b | ||
|
|
3eaa31ce0c | ||
|
|
eb5b31dc03 | ||
|
|
35662cb424 | ||
|
|
54b3ac5d70 | ||
|
|
c87b52c4e3 | ||
|
|
ca87e363d8 | ||
|
|
8f81a312ba | ||
|
|
147a51a832 | ||
|
|
1b816ea04b | ||
|
|
a5564f1461 | ||
|
|
4ad362035c | ||
|
|
933e4c6a2a | ||
|
|
1275cf98cb | ||
|
|
f8859ad330 | ||
|
|
4250cc2215 | ||
|
|
e4e0f144b1 | ||
|
|
c6d15ca9a5 | ||
|
|
7a669fd50b | ||
|
|
dc9fb3e433 | ||
|
|
86f170f79a | ||
|
|
b0c82c8c46 | ||
|
|
27280f65fe | ||
|
|
c396aed649 | ||
|
|
51b221a916 | ||
|
|
c14f0dc96c | ||
|
|
10dc78246c | ||
|
|
00d6e7b2be | ||
|
|
e6893ae769 | ||
|
|
58916fd78c | ||
|
|
16b600eed7 | ||
|
|
5a8f8f4c17 | ||
|
|
17ca4b9bec | ||
|
|
cb2f9edb45 | ||
|
|
76bb98350f | ||
|
|
0f2a919606 | ||
|
|
51adeb623a | ||
|
|
05e9cd9639 | ||
|
|
66554a1b61 | ||
|
|
795bb97465 | ||
|
|
f9399d406d | ||
|
|
b2c5a59e67 | ||
|
|
2c97b6859f | ||
|
|
318d3bbeae | ||
|
|
84e4881982 | ||
|
|
61e62f13f1 | ||
|
|
626f848397 | ||
|
|
d268b365ac | ||
|
|
a399477bff | ||
|
|
69e7c448d4 | ||
|
|
a3c36ca6d0 | ||
|
|
48b35b3e50 | ||
|
|
59efd385db | ||
|
|
141c35ec9e | ||
|
|
a5987eaa47 | ||
|
|
6c09ced19e | ||
|
|
84b89ac5da | ||
|
|
c8a85d0469 | ||
|
|
baab7256dd | ||
|
|
f8b9846aa7 | ||
|
|
29c5cb6802 | ||
|
|
6cd490287d | ||
|
|
c9c4518ef1 | ||
|
|
c671e72736 | ||
|
|
a4badbc7bc | ||
|
|
19f93200c1 | ||
|
|
5069865ef6 | ||
|
|
1035210ca9 | ||
|
|
899fd85dc3 | ||
|
|
7a73c9b1a9 | ||
|
|
30dce26be3 | ||
|
|
332a9f72b0 | ||
|
|
f7a7c57ff2 | ||
|
|
12e9584a76 | ||
|
|
1168dcdf87 | ||
|
|
eefa4dd03a | ||
|
|
3a09a2c015 | ||
|
|
0364d09ce4 | ||
|
|
b0052191d7 | ||
|
|
002c382eb8 | ||
|
|
77bb656935 | ||
|
|
25df49518f | ||
|
|
00546bad87 | ||
|
|
21222b9d1d | ||
|
|
a1c98e08c9 | ||
|
|
13b58c9a25 | ||
|
|
d90527e44a | ||
|
|
8fab15c43f | ||
|
|
a367ebd605 | ||
|
|
e779617a56 | ||
|
|
3ca5afcd91 | ||
|
|
7569482ab0 | ||
|
|
dbaa404dda | ||
|
|
0f4a3fce6f | ||
|
|
8c17723d85 | ||
|
|
b83b246788 | ||
|
|
bd3a9b7960 | ||
|
|
97fac433f7 | ||
|
|
73ac04f810 | ||
|
|
353a48bad2 | ||
|
|
bb001de685 | ||
|
|
5605a1c0d4 | ||
|
|
37c407236f | ||
|
|
bddefcba8d | ||
|
|
5591f3a921 | ||
|
|
2092134c2a | ||
|
|
db7079e994 | ||
|
|
d040b14e32 | ||
|
|
cf7a11e993 | ||
|
|
1bced23666 | ||
|
|
74fd7451aa | ||
|
|
763ae54874 | ||
|
|
599978d95b | ||
|
|
4d8e6f95a0 | ||
|
|
aed3c27c3a | ||
|
|
b1dd9eda7c | ||
|
|
5a3f501351 | ||
|
|
da6963d0c1 | ||
|
|
81262e6fb9 | ||
|
|
62ed089ef1 | ||
|
|
b920a24574 | ||
|
|
94a1fdc642 | ||
|
|
7e27f25fff | ||
|
|
62e29e2e46 | ||
|
|
07c08c341e | ||
|
|
f050ba8aca | ||
|
|
094fa2d99c | ||
|
|
62b2188abe | ||
|
|
0f7df7dbf6 | ||
|
|
349068164d | ||
|
|
9102fc2be6 | ||
|
|
7001fce0d2 | ||
|
|
b965e64107 | ||
|
|
e4ff44c011 | ||
|
|
795e429159 | ||
|
|
6fb5c4c8d2 | ||
|
|
a0537fe04a | ||
|
|
a3b6544be8 | ||
|
|
dae5f64312 | ||
|
|
a15406bc36 | ||
|
|
0fdfe0185c | ||
|
|
8f84988893 | ||
|
|
89913017fc | ||
|
|
d97228ab9c | ||
|
|
964bc6a4d6 | ||
|
|
e9417e164e | ||
|
|
91e1867a73 | ||
|
|
026394c518 | ||
|
|
29989a5b27 | ||
|
|
1e3967a9f0 | ||
|
|
653fbccf70 | ||
|
|
65ceff40b9 | ||
|
|
c5c64684b3 | ||
|
|
7f63d45d38 | ||
|
|
357a96822c | ||
|
|
8812db16a3 | ||
|
|
04120edd73 | ||
|
|
e54360d0b0 | ||
|
|
7e360d618d | ||
|
|
cc603f2c7b | ||
|
|
0cc10970af | ||
|
|
85a2b6fa29 | ||
|
|
4d329441d7 | ||
|
|
8aa21d3da0 | ||
|
|
573158e692 | ||
|
|
e3c5c62a1c | ||
|
|
a8d7867143 | ||
|
|
dba124bfce | ||
|
|
9ce798ec1d | ||
|
|
279eb5a316 | ||
|
|
7147c1a0d1 | ||
|
|
35add00493 | ||
|
|
19c062e15c | ||
|
|
e481184fff | ||
|
|
eef472cdca | ||
|
|
bd5c46f9d9 | ||
|
|
e8f1503028 | ||
|
|
c63717adfb | ||
|
|
45a7dd6a8c | ||
|
|
ee77255c65 | ||
|
|
9ea8c00701 | ||
|
|
3318c99171 | ||
|
|
de6a01fca1 | ||
|
|
a858ab1392 | ||
|
|
649febfff7 | ||
|
|
da8d705221 | ||
|
|
268aa4b332 | ||
|
|
80853ece02 | ||
|
|
f24e78826d | ||
|
|
e09bbedca0 | ||
|
|
71a7c05336 | ||
|
|
f07d8f3843 | ||
|
|
a915a3b072 | ||
|
|
86ece2180c | ||
|
|
056fa974a8 | ||
|
|
4402c4284b | ||
|
|
ea7b9a0ca3 | ||
|
|
4bbed58d2c | ||
|
|
29ba4341c2 | ||
|
|
d53569e4f8 | ||
|
|
050e142d61 | ||
|
|
94a748a4ad | ||
|
|
85f20cfe9d | ||
|
|
05ba4fdd4e | ||
|
|
e489c10067 | ||
|
|
e383331f05 | ||
|
|
224bcfe3e0 | ||
|
|
7c1d818b7a | ||
|
|
7d26a1465b | ||
|
|
37f2ae16b8 | ||
|
|
b3342289e3 | ||
|
|
f32fd62838 | ||
|
|
3c55605da3 | ||
|
|
d8093e2c5d | ||
|
|
546c11f547 | ||
|
|
998c2adf38 | ||
|
|
d4640a7f12 | ||
|
|
c0073a5dcb | ||
|
|
00839d1af7 | ||
|
|
1a1ac0b516 | ||
|
|
9ee62d87b3 | ||
|
|
558dd2cbb0 | ||
|
|
d77d1bce93 | ||
|
|
6c1c4fea97 | ||
|
|
833d7af476 | ||
|
|
c997880ffc | ||
|
|
1fee01b103 | ||
|
|
fb1fd83466 | ||
|
|
870130ccc3 | ||
|
|
662dfa8af1 | ||
|
|
84162de459 | ||
|
|
0aa2021dcc | ||
|
|
991f55e57f | ||
|
|
d005f64f53 | ||
|
|
1003e56cad | ||
|
|
1203f23d02 | ||
|
|
56d30e0536 | ||
|
|
6734bae4ea | ||
|
|
a574ff0879 | ||
|
|
35a721b4c0 | ||
|
|
796162b09c | ||
|
|
2eb91c0e81 | ||
|
|
1a35b4ce2d | ||
|
|
3c3d881125 | ||
|
|
6847638f78 | ||
|
|
4d32480348 | ||
|
|
d23000f3f1 | ||
|
|
d139560829 | ||
|
|
16b5569411 | ||
|
|
c441a536bb | ||
|
|
666e9a84a9 | ||
|
|
57eeeedd62 | ||
|
|
1099deed2f | ||
|
|
a6b29c7823 | ||
|
|
e0fd14409e | ||
|
|
f944d7df38 | ||
|
|
e9a25cd6cf | ||
|
|
802015376f | ||
|
|
6e5b69a657 | ||
|
|
85f4f3ca5e | ||
|
|
b0d7380fb5 | ||
|
|
3d21fe3dac | ||
|
|
257bf77f91 | ||
|
|
60197993e5 | ||
|
|
b9b089fa49 | ||
|
|
49ba669fa6 | ||
|
|
593cddba5d | ||
|
|
d2fd546aa0 | ||
|
|
4475fed547 | ||
|
|
3d158800c7 | ||
|
|
07096f2c59 | ||
|
|
b4315878e4 | ||
|
|
4204667f0a | ||
|
|
9acfb73645 | ||
|
|
a691a701d5 | ||
|
|
bf2f3f0a25 | ||
|
|
59ff243cc1 | ||
|
|
537c65b10d | ||
|
|
621b90236d | ||
|
|
a3045b0b75 | ||
|
|
405dcc78e0 | ||
|
|
2597b58b88 | ||
|
|
49bd788442 | ||
|
|
7d20ef9b64 | ||
|
|
bc66b33ebf | ||
|
|
885f52ee22 | ||
|
|
cfb50b7576 | ||
|
|
80249a0ac5 | ||
|
|
69083f1970 | ||
|
|
50039ed83b | ||
|
|
28f5ed5ee1 | ||
|
|
2bc95aca8d | ||
|
|
290ed7341e | ||
|
|
70f8775838 | ||
|
|
060daaa44c | ||
|
|
32533500ed | ||
|
|
e34a3fb1cd | ||
|
|
45598be00d | ||
|
|
d7412f0855 | ||
|
|
b2adffbbbd | ||
|
|
a1ecd59d10 | ||
|
|
373891985e | ||
|
|
15aeb9a890 | ||
|
|
0b7e89b4f5 | ||
|
|
32c16c544d | ||
|
|
0569a2e599 | ||
|
|
192c63b36e | ||
|
|
b433be3e33 | ||
|
|
60eec844ed | ||
|
|
8f9835fb9b | ||
|
|
11e4bd20d5 | ||
|
|
59866dc8a7 | ||
|
|
cd51159660 | ||
|
|
53cc9618cf | ||
|
|
0771ca5f86 | ||
|
|
99f87e7b53 | ||
|
|
9964e01e56 | ||
|
|
62eb2bf90e | ||
|
|
63bf1558de | ||
|
|
f2ff903e65 | ||
|
|
6bdd28521a | ||
|
|
e7236f6eb6 | ||
|
|
b56e183753 | ||
|
|
2c273574a4 | ||
|
|
aad4a7e63b | ||
|
|
788d856934 | ||
|
|
d68c72fdbc | ||
|
|
d4203f8559 | ||
|
|
9c595e7b46 | ||
|
|
e696bc366f | ||
|
|
9f0d6fe16d | ||
|
|
2612e645a4 | ||
|
|
a293959fab | ||
|
|
fe8e2e3e4e | ||
|
|
5290a62f40 | ||
|
|
e17f2c5cc6 | ||
|
|
b472eeb786 | ||
|
|
96eb8c64e3 | ||
|
|
80b768d016 | ||
|
|
0d50228620 | ||
|
|
ddbf3c082c | ||
|
|
b23169d754 | ||
|
|
f785555851 | ||
|
|
78fecfa9a1 | ||
|
|
bc2c4415a9 | ||
|
|
a69e73806f | ||
|
|
fe0b49773b | ||
|
|
9fbed27451 | ||
|
|
294eaeb16a | ||
|
|
4bbc0b21d1 | ||
|
|
28ce770c5d | ||
|
|
397ae5989f | ||
|
|
98733604ce | ||
|
|
46dfe58d89 | ||
|
|
059e9bf9a1 | ||
|
|
b93672f455 | ||
|
|
6593cb9d83 | ||
|
|
3a6765e028 | ||
|
|
60bc3e895b | ||
|
|
1e4598aa76 | ||
|
|
53287ac122 |
No files matched your search
@@ -149,7 +149,7 @@ jobs:
|
||||
\ {str(commit_sha)[:7]}\")\n\nprint(f\"AUTO_BUMPED=true \u2014 version {NEW}\
|
||||
\ live on master; TUI footer will render v{NEW}\")\nwith open(os.environ[\"\
|
||||
GITHUB_ENV\"], \"a\") as env:\n env.write(f\"RELEASE_VERSION={NEW}\\n\"\
|
||||
)\n env.write(\"AUTO_BUMPED=true\\n\")\nPYBUMP\n"
|
||||
)\n env.write(\"AUTO_BUMPED=true\\n\")\n env.write(f\"BUMP_COMMIT_SHA={commit_sha}\\n\")\nPYBUMP\n"
|
||||
- name: Create evidence-derived release (SCAFFOLD-TRANCHE-009B)
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.ROBOT_TOKEN }}
|
||||
@@ -167,6 +167,57 @@ jobs:
|
||||
\ init \\\n --version \"$RELEASE_VERSION\" \\\n --server \"$RELEASE_SERVER_URL\"\
|
||||
\ --repo \"$RELEASE_REPO\" \\\n --store store/scaffold/current.json \\\n\
|
||||
\ ${EXIT_NOTE:+--exit-note \"$EXIT_NOTE\"}"
|
||||
- name: Push tui-v<version> tag via git so the tag-push event fires tui-release (D27)
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.ROBOT_TOKEN }}
|
||||
RELEASE_SERVER_URL: ${{ gitea.server_url }}
|
||||
run: |
|
||||
# WHY git push instead of the Gitea API: API-created tags (POST /tags)
|
||||
# do NOT emit a push event, so tui-release.yml's `on: push: tags: tui-v*`
|
||||
# never fired (tui-v0.310.0 landed with no TUI release). A real
|
||||
# `git push` of the tag is the trigger Gitea actions actually honor.
|
||||
TAG="tui-v${RELEASE_VERSION}"
|
||||
SERVER="$(printf '%s' "$RELEASE_SERVER_URL" | sed 's:/*$::')"
|
||||
|
||||
# git auth: same mechanism actions/checkout persists (http extraheader),
|
||||
# but with the write-capable ROBOT_TOKEN and the token owner's real
|
||||
# login, because Gitea's git-over-HTTP basic auth wants the owner's
|
||||
# username when the password is a token.
|
||||
USER_JSON="$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" "${SERVER}/api/v1/user")" || {
|
||||
echo "::error::GET /api/v1/user failed — cannot resolve the token owner for git auth"; exit 1; }
|
||||
ROBOT_USER="$(printf '%s' "$USER_JSON" | python3 -c 'import json,sys; print(json.load(sys.stdin)["login"])')"
|
||||
ROBOT_EMAIL="$(printf '%s' "$USER_JSON" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("email") or "")')"
|
||||
[ -n "$ROBOT_EMAIL" ] || ROBOT_EMAIL="${ROBOT_USER}@users.noreply.$(printf '%s' "$SERVER" | sed 's|^https\?://||')"
|
||||
AUTH_B64="$(printf '%s:%s' "$ROBOT_USER" "$GITEA_TOKEN" | base64 | tr -d '\n')"
|
||||
GIT_HTTP_BASE="$(git remote get-url origin | sed -nE 's#^(https?://[^/]+).*$#\1/#p')"
|
||||
[ -n "$GIT_HTTP_BASE" ] || GIT_HTTP_BASE="${SERVER}/"
|
||||
git config --local "http.${GIT_HTTP_BASE}.extraheader" "AUTHORIZATION: basic ${AUTH_B64}"
|
||||
git config --local "http.${SERVER}/.extraheader" "AUTHORIZATION: basic ${AUTH_B64}"
|
||||
|
||||
# Idempotency: a tag already on the server means this convergence event
|
||||
# already covered it. If its TUI release is missing anyway (an orphan
|
||||
# from the old API-tag path), recover with the documented re-push in
|
||||
# tui/version/README.md.
|
||||
if [ -n "$(git ls-remote --tags origin "refs/tags/${TAG}")" ]; then
|
||||
echo "${TAG} already exists on the server — TUI release already covered, nothing to do"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# The auto-bump commit was created via the API AFTER this checkout, so
|
||||
# it is not in the local object store yet — fetch master first.
|
||||
git fetch --no-tags origin master
|
||||
TARGET="${BUMP_COMMIT_SHA:-$(git rev-parse HEAD)}"
|
||||
if ! git cat-file -e "${TARGET}^{commit}" 2>/dev/null; then
|
||||
echo "::error::tag target ${TARGET} absent from origin/master — refusing to tag"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git tag -d "$TAG" >/dev/null 2>&1 || true
|
||||
git -c user.name="$ROBOT_USER" -c user.email="$ROBOT_EMAIL" tag -a "$TAG" \
|
||||
-m "chore(release): ${TAG} — native TUI convergence tag (same semver as the reviewed release, single version across faces)" \
|
||||
"$TARGET"
|
||||
git push origin "refs/tags/${TAG}:refs/tags/${TAG}"
|
||||
echo "pushed ${TAG} at ${TARGET:0:7} — tag-push event fires tui-release.yml"
|
||||
concurrency:
|
||||
group: auto-version-and-release-${ github.ref }
|
||||
cancel-in-progress: true
|
||||
@@ -231,9 +231,9 @@ jobs:
|
||||
- name: curator-core candidate-decay parity gate (HARD — #2479 batch: ledger decay + journal render + cosine)
|
||||
run: |
|
||||
cd src/go/curator-core && go build -o /tmp/curator-core-${{ gitea.run_id }} . && /tmp/curator-core-${{ gitea.run_id }} replay truth_candidate_decay.json && go test -run TestCandidateDecayParity -v
|
||||
- name: cron-engine parity gate (HARD — #2479 batches 1+126: parser + lifecycle)
|
||||
- name: cron-engine parity gate (HARD — #2479 batches 1+126 + gap-close: parser + lifecycle + execution family)
|
||||
run: |
|
||||
cd src/go/cron-engine && go build -o /tmp/cron-engine-${{ gitea.run_id }} . && /tmp/cron-engine-${{ gitea.run_id }} replay truth.json && go test -run TestLifecycleParity -v
|
||||
cd src/go/cron-engine && go build -o /tmp/cron-engine-${{ gitea.run_id }} . && /tmp/cron-engine-${{ gitea.run_id }} replay truth.json && /tmp/cron-engine-${{ gitea.run_id }} replay-gap truth_gap.json && go test -run 'TestLifecycleParity|TestGapParity' -v
|
||||
- name: goal-engine parity gate (HARD — #2479 batches 2+129: verifier + lifecycle)
|
||||
run: |
|
||||
cd src/go/goal-engine && go build -o /tmp/goal-engine-${{ gitea.run_id }} . && /tmp/goal-engine-${{ gitea.run_id }} replay truth.json && go test -run TestLifecycleParity -v
|
||||
@@ -269,7 +269,7 @@ jobs:
|
||||
/tmp/model-routing-${{ gitea.run_id }} replay truth.json
|
||||
- name: work-manager parity gate (HARD — #2479 batches 9+137+156: batching + lifecycle)
|
||||
run: |
|
||||
cd src/go/work-manager && go build -o /tmp/work-manager-${{ gitea.run_id }} . && /tmp/work-manager-${{ gitea.run_id }} replay truth.json && go test -run TestLifecycleParity -v
|
||||
cd src/go/work-manager && go build -o /tmp/work-manager-${{ gitea.run_id }} . && /tmp/work-manager-${{ gitea.run_id }} replay internal/workmanager/truth.json && go test -run TestLifecycleParity -v ./...
|
||||
- name: core-server parity gate (HARD — #2479 batches 10+138: envelopes + extraction/identity)
|
||||
run: |
|
||||
cd src/go/core-server && go build -o /tmp/core-server-${{ gitea.run_id }} .
|
||||
@@ -373,7 +373,7 @@ jobs:
|
||||
cd src/go/scaffold && go build -o /tmp/scaffold-${{ gitea.run_id }} . && go test -run TestBuildersParity -v
|
||||
- name: tool-executor prepared-path parity gate (HARD — #2479 batch)
|
||||
run: |
|
||||
cd src/go/tool-executor && go build -o /tmp/tool-exec-${{ gitea.run_id }} . && go test -run TestPreparedPathParity -v
|
||||
cd src/go/tool-executor && go build -o /tmp/tool-exec-${{ gitea.run_id }} . && go test -run TestPreparedPathParity -v ./...
|
||||
- name: failure-filer parity gate (HARD — #2479 batch: classify/redact/tier/marker)
|
||||
run: |
|
||||
cd src/go/failure-filer && go build -o /tmp/failfiler-${{ gitea.run_id }} . && go test -run TestFilerParity -v
|
||||
@@ -396,7 +396,7 @@ jobs:
|
||||
# multiple FuzzX targets exits 1 ("matches more than one fuzz test")
|
||||
# and pipefail turns that into a job failure. Enumerate targets and
|
||||
# fuzz each — and fail LOUD (no pipe-mask, exit 1 propagates).
|
||||
for dir in src/go/tool-executor src/go/curator-core src/go/memory-store-deep; do
|
||||
for dir in src/go/tool-executor/internal/toolexec src/go/curator-core src/go/memory-store-deep; do
|
||||
cd "$dir"
|
||||
for target in $(go test -list '^Fuzz' . | grep '^Fuzz'); do
|
||||
echo "fuzz: $dir/$target"
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
name: TUI Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'tui-v*'
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
release:
|
||||
runs-on: rune-ci-desktop
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Read semver from the pushed tag
|
||||
run: |
|
||||
TAG="${GITEA_REF_NAME}"
|
||||
case "$TAG" in
|
||||
tui-v*) VERSION="${TAG#tui-v}" ;;
|
||||
*) echo "::error::triggered on non-tui tag $TAG"; exit 1 ;;
|
||||
esac
|
||||
echo "TUI_TAG=$TAG" >> "$GITHUB_ENV"
|
||||
echo "TUI_VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||
echo "releasing $TAG (semver $VERSION)"
|
||||
- name: Paired build with semver injection (vendored, GOPROXY=off)
|
||||
env:
|
||||
GOPROXY: "off"
|
||||
GOFLAGS: "-mod=vendor"
|
||||
run: |
|
||||
[ -f tui/vendor/modules.txt ] || { echo "::error::tui/vendor missing - not a self-contained build"; exit 1; }
|
||||
echo "vendor proof: GOPROXY=$(go env GOPROXY) flags=$(go env GOFLAGS) ($(wc -l < tui/vendor/modules.txt) vendored modules)"
|
||||
bash scripts/build_paired.sh
|
||||
BUILD_ID="$(./verification/build-id/rune-tui --build-id)"
|
||||
echo "TUI_BUILD_ID=$BUILD_ID" >> "$GITHUB_ENV"
|
||||
echo "binary stamped: build=$BUILD_ID semver=$TUI_VERSION (from $TUI_TAG on HEAD)"
|
||||
- name: Create release with commit range since last tui tag
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.ROBOT_TOKEN }}
|
||||
RELEASE_SERVER_URL: ${{ gitea.server_url }}
|
||||
RELEASE_REPO: ${{ gitea.repository }}
|
||||
run: |
|
||||
LAST_TUI_TAG="$(git tag --list 'tui-v*' --sort=-creatordate | grep -vx "$TUI_TAG" | head -1 || true)"
|
||||
{
|
||||
echo "Native TUI release $TUI_VERSION"
|
||||
echo
|
||||
echo "- semver: \`$TUI_VERSION\` (injected at build from tag \`$TUI_TAG\`)"
|
||||
echo "- paired build id: \`$TUI_BUILD_ID\`"
|
||||
echo "- binary: \`rune-tui-$TUI_VERSION\` (self-contained, vendored deps, GOPROXY=off proof)"
|
||||
echo
|
||||
if [ -n "$LAST_TUI_TAG" ]; then
|
||||
echo "## Commits since $LAST_TUI_TAG"
|
||||
echo
|
||||
git log --oneline "$LAST_TUI_TAG..HEAD"
|
||||
else
|
||||
echo "## Commits (first tui-tagged release)"
|
||||
echo
|
||||
git log --oneline -30
|
||||
fi
|
||||
} > /tmp/tui-release-body.md
|
||||
TUI_TAG="$TUI_TAG" TUI_VERSION="$TUI_VERSION" python3 - <<'PYREL'
|
||||
import json, os, sys, urllib.request
|
||||
|
||||
TOKEN = os.environ["GITEA_TOKEN"]
|
||||
BASE = os.environ["RELEASE_SERVER_URL"].rstrip("/") + "/api/v1/repos/" + os.environ["RELEASE_REPO"]
|
||||
TAG = os.environ["TUI_TAG"]
|
||||
VERSION = os.environ["TUI_VERSION"]
|
||||
|
||||
def api(path, method="GET", data=None):
|
||||
req = urllib.request.Request(
|
||||
BASE + path,
|
||||
data=json.dumps(data).encode() if data is not None else None,
|
||||
headers={"Authorization": "token " + TOKEN, "Content-Type": "application/json"},
|
||||
method=method,
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as r:
|
||||
body, status = r.read(), r.status
|
||||
except urllib.error.HTTPError as e:
|
||||
body, status = e.read(), e.code
|
||||
try:
|
||||
return status, (json.loads(body) if body else {})
|
||||
except json.JSONDecodeError:
|
||||
return status, {"_raw": body.decode("utf-8", "replace")[:200]}
|
||||
|
||||
body = open("/tmp/tui-release-body.md").read()
|
||||
status, existing = api(f"/releases/tags/{TAG}")
|
||||
if status == 200:
|
||||
release_id = existing["id"]
|
||||
status, _ = api(f"/releases/{release_id}", "PATCH", {"body": body, "name": f"Rune TUI {VERSION}"})
|
||||
if status not in (200, 201):
|
||||
print(f"release patch failed ({status})"); sys.exit(1)
|
||||
print(f"updated existing release {release_id} for {TAG}")
|
||||
else:
|
||||
status, rel = api("/releases", "POST", {
|
||||
"tag_name": TAG, "name": f"Rune TUI {VERSION}",
|
||||
"body": body, "draft": False, "prerelease": False,
|
||||
})
|
||||
if status != 201:
|
||||
print(f"release create failed ({status}): {str(rel)[:300]}"); sys.exit(1)
|
||||
release_id = rel["id"]
|
||||
print(f"created release {release_id} for {TAG}")
|
||||
with open(os.environ["GITHUB_ENV"], "a") as env:
|
||||
env.write(f"TUI_RELEASE_ID={release_id}\n")
|
||||
PYREL
|
||||
- name: Attach the single binary to the release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.ROBOT_TOKEN }}
|
||||
RELEASE_SERVER_URL: ${{ gitea.server_url }}
|
||||
RELEASE_REPO: ${{ gitea.repository }}
|
||||
run: |
|
||||
ASSET="rune-tui-$TUI_VERSION"
|
||||
# re-runs must not stack duplicate attachments (Gitea 409s on name clash)
|
||||
curl -sf -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$RELEASE_SERVER_URL/api/v1/repos/$RELEASE_REPO/releases/$TUI_RELEASE_ID/assets" \
|
||||
| python3 -c "import json,sys; [print(a['id']) for a in json.load(sys.stdin) if a['name'] == '$ASSET']" \
|
||||
| while read -r AID; do
|
||||
curl -sf -X DELETE -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$RELEASE_SERVER_URL/api/v1/repos/$RELEASE_REPO/releases/$TUI_RELEASE_ID/assets/$AID" || \
|
||||
{ echo "::error::could not clear stale asset $AID"; exit 1; }
|
||||
done
|
||||
curl -sf -X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||
-F "attachment=@verification/build-id/rune-tui" \
|
||||
"$RELEASE_SERVER_URL/api/v1/repos/$RELEASE_REPO/releases/$TUI_RELEASE_ID/assets?name=$ASSET" \
|
||||
| python3 -c "import json,sys; a=json.load(sys.stdin); print('attached:', a.get('name'), a.get('browser_download_url'))"
|
||||
concurrency:
|
||||
group: tui-release-${{ gitea.ref }}
|
||||
cancel-in-progress: false
|
||||
+20
@@ -23,4 +23,24 @@ src/go/*/[^.]*
|
||||
!src/go/*/*.json
|
||||
!src/go/*/*.mod
|
||||
!src/go/*/*.sum
|
||||
# the artifact rule also swallowed module subpackages (internal/ etc.) —
|
||||
# sources there are tracked (see internal/entry/*.go), so re-include them.
|
||||
!src/go/*/*/
|
||||
!src/go/*/*/**/*.go
|
||||
!src/go/*/*/**/*.json
|
||||
# committed fixture stores (frozen-truth construction rigs)
|
||||
!src/go/*/fixtures/
|
||||
!src/go/*/fixtures/**
|
||||
store/scaffold/current.json
|
||||
|
||||
# phase 0C task 0.14: paired-build stamps are per-build output (one build
|
||||
# id in verification/build-id/, gitignored like the binaries it stamps).
|
||||
verification/build-id/
|
||||
tui/boundary/gen/build_id_gen.go
|
||||
|
||||
# D30 wasm migration wave 1: wasm modules are reproducibly-built compiled
|
||||
# artifacts (build_paired.sh: cargo → tui/wasm/modules/), never committed —
|
||||
# same no-committed-binaries law as the Go/Rust binaries. Source of truth
|
||||
# is src/rust/token_counting; manifest.json carries the paired build id.
|
||||
tui/wasm/modules/
|
||||
store/scaffold/current.json
|
||||
@@ -0,0 +1,155 @@
|
||||
# Morning test list — 2026-10-16
|
||||
|
||||
Things you can test by hand, in order, with your own eyes and ears.
|
||||
This list gets filled in further as overnight work lands.
|
||||
|
||||
## 1. System audio (landed yesterday afternoon)
|
||||
|
||||
- Play anything (browser video, music).
|
||||
- Expect: sound through your monitors as normal.
|
||||
- If you reboot: log in, wait ~15 seconds, play something again.
|
||||
Expect: sound still there (the self-healing system rebuilds the
|
||||
System_Audio path if the startup race ever eats it).
|
||||
|
||||
## 2. Game audio via Heroic (armed, waiting on you)
|
||||
|
||||
- Close Mafia if it's still open (the fix applies itself on close).
|
||||
- Relaunch it through Heroic.
|
||||
- Expect: sound from the menus onward.
|
||||
- If still silent: tell Rune — the next fix (umu runtime rollback) is
|
||||
already planned and takes minutes.
|
||||
|
||||
## 3. The one-command whole-system check (LANDED overnight)
|
||||
|
||||
- Run, from the repo root:
|
||||
`make check`
|
||||
(same thing, spelled out: `python scripts/unified_check.py`)
|
||||
- Takes several minutes — it sweeps everything: all 103 Rust crates, the
|
||||
whole Python suite, the Go side, the boundary conversation tests, the
|
||||
code-generation gate, and the no-binaries gate.
|
||||
- Expect the final verdict **AMBER**. Plain meaning of AMBER: "only the
|
||||
old, known failures remain — nothing new." That is the correct, honest
|
||||
green for this repo right now. RED means something new broke.
|
||||
- The known-old list it tolerates: ~120 old Python failures (recorded
|
||||
baseline, being paid down over time) + one known-sick Rust crate
|
||||
(prompt_sync — found by this very runner on its first ever sweep;
|
||||
repair already queued overnight).
|
||||
|
||||
## 4. The TUI first breath (foundation — LANDED overnight)
|
||||
|
||||
- Run, from the repo root:
|
||||
`cd tui && go run ./cmd/rune-tui`
|
||||
(The `./cmd/rune-tui` part matters — the module root itself has no
|
||||
main program.)
|
||||
- Expect: a small face showing **Rune**, a version placeholder
|
||||
(0.0.0-dev), a build line, and "q to quit". Press **q** (or ctrl+c):
|
||||
it exits cleanly (exit code 0).
|
||||
- The build line says `unset (unpaired dev build)` for `go run` — that
|
||||
is honest: `go run` skips the paired-build stamping step. A
|
||||
paired-stamped build (`bash scripts/build_paired.sh`, then run
|
||||
`verification/build-id/rune-tui`) shows the real build id instead.
|
||||
- Pre-verified overnight by machine (boot, render, q, clean exit 0),
|
||||
including through a fake terminal — your run is the human confirmation.
|
||||
- It is the seed, not the tree — it proves the whole chain works end to
|
||||
end (module + vendored dependencies + stamping + tests).
|
||||
|
||||
## 5. The behavior map (LANDED overnight)
|
||||
|
||||
- What it is: the single action-model both future visual designs must
|
||||
satisfy — 144 states, 20 actions, frozen golden paths. It is the
|
||||
guarantee that every visual option behaves identically.
|
||||
- See it pass:
|
||||
`cd tui && go test ./semantic/ -v`
|
||||
- You will see the mutant tests (deliberate breakages that MUST fail the
|
||||
checker) and the key-parsing tests (the tricky alt+q case included).
|
||||
All green = the foundation of all three future looks is solid.
|
||||
|
||||
---
|
||||
|
||||
## What you do NOT need to test by hand
|
||||
|
||||
Everything below is already proven by born-failing tests before it
|
||||
landed (the five repairs, the boundary machinery, the sandbox). The
|
||||
test list above is only for things that need human eyes or ears.
|
||||
|
||||
## Overnight judgment calls made while you slept
|
||||
|
||||
(Filled in as they happen — each one gets a line here so you can
|
||||
overrule anything in the morning.)
|
||||
|
||||
1. **Substrate's first sweep found a sick Rust crate** (`prompt_sync` —
|
||||
an arithmetic overflow that crashes three of its tests; pre-existing,
|
||||
never swept before, NOT caused by tonight's work). Left unfixed by
|
||||
the substrate worker (correctly — separate concern), repair queued
|
||||
overnight.
|
||||
2. **The repo's "spine" snapshot was stale** relative to yesterday's
|
||||
landed fixes. The suite refreshes it when it runs; I left the
|
||||
refreshed version in place because restoring the stale one would arm
|
||||
a false alarm. Flagged for your call if you want spine handling
|
||||
changed.
|
||||
3. **The old failure count is 120, not 116** (the research's number was
|
||||
approximate). The runner records the honest baseline and compares
|
||||
against it by content, not by count — the edges of the old set flicker
|
||||
by one or two between runs, so exact counts lie.
|
||||
4. **The sick crate was repaired overnight** (prompt_sync — a Python-to-Rust
|
||||
port where Python's forgiving negative numbers met Rust's hard floor;
|
||||
two underflow sites fixed with exact-Python-semantics guards; all its
|
||||
tests including the frozen oracle battery now pass). The one-command
|
||||
check in section 3 still says AMBER — that remains correct.
|
||||
5. **One crate is order-flaky** (graph_projection): passes 12/12 when run
|
||||
alone, occasionally trips when the whole system sweeps in one order.
|
||||
Known flake class, tolerated in the baseline so it cannot cause false
|
||||
alarms. Real fix (test hygiene) queued for a later wave.
|
||||
6. **Bubbles v2.2.1 is pinned through a tiny internal package, not used
|
||||
yet** (tui/internal/toolkit/pins.go — one blank import). `go mod tidy`
|
||||
deletes any dependency nothing imports, and the spec pins all three
|
||||
toolkit pieces (task 5.3b), so the pin needs a real import to survive
|
||||
tidy + vendoring. It says exactly that in its comment; components
|
||||
arrive with later waves.
|
||||
7. **tui/go.mod grew its dependency list through the GENERATOR, not by
|
||||
hand** — go.mod is generated territory (the codegen gate byte-diffs
|
||||
it), so the pins went into the gen_boundary_types.py template and the
|
||||
file must stay byte-identical to what `go mod tidy` produces. The
|
||||
runner needed NO changes: Go spots tui/vendor/ automatically and
|
||||
builds offline from it.
|
||||
|
||||
6. **Wave D (lanes groundwork) is building overnight** — the two-lane
|
||||
skeleton (Chat left, Task right, focus switching) rendered over the
|
||||
behavior map above. Judgment entry when it lands.
|
||||
|
||||
7. **Wave D (two-lane skeleton) LANDED overnight** — the face now shows
|
||||
Chat left + Task right, focused pane wider, tab/1/2 to switch focus,
|
||||
q to quit. Panes say "not yet wired" — honest placeholders; the real
|
||||
content arrives in later waves.
|
||||
Run it: `cd tui && go run ./cmd/rune-tui`
|
||||
8. **Judgment call: Gitea restarted itself mid-night** (03:44) — the Wave D
|
||||
push failed once, then landed clean on retry. If the machine rebooted
|
||||
overnight, your morning `git push` context is unaffected; all commits
|
||||
are on the server.
|
||||
|
||||
9. **Wave E (activity rail) LANDED overnight** — beneath the lanes now
|
||||
sits a RAIL line (which lane is focused, status, newest event with its
|
||||
source). Press `r` to open the full-width ACTIVITY RAIL box; `x` closes
|
||||
it back to exactly the lanes you had. `q` quits (inside the box, keys
|
||||
are swallowed until you close it — it is an inspection room, not a
|
||||
door). Entries already carry source + ancestry — the thread tree (your
|
||||
SA-D7) gets its data free.
|
||||
10. **Judgment call: Wave E needed two watchdog nudges** — the worker
|
||||
stalled twice with nothing on disk, moved both times right after the
|
||||
ping. Pattern of the night: long writes, not stuck workers. The rule
|
||||
(probe the worktree, nudge, escalate to tighter re-spawn) worked
|
||||
without ever firing the trigger.
|
||||
|
||||
11. **Heroic: already up to date** (2.22.4-1, newest everywhere — nothing
|
||||
was changed). Your launcher is healthy; 40 files, none missing.
|
||||
12. **Game audio, corrected picture:** the fix already applied itself
|
||||
yesterday at 15:12 (the watcher fired on game close; registry patched
|
||||
to Audio=pulse; backup saved). BUT two things changed the equation:
|
||||
(a) the runtime on disk is Oct 7, not Oct 13 — so the "Oct 13 broke
|
||||
it" theory is wrong; (b) the next game launch may auto-download a
|
||||
newer runtime, which could change things again. So: relaunch Mafia
|
||||
through Heroic, listen from the menus. If sound is there — done, tell
|
||||
Rune and the umu update hold lifts. If silent — say so immediately;
|
||||
the rollback plan pivots to whatever runtime just downloaded.
|
||||
(Held deliberately: umu-launcher 1.4.4-1 NOT installed until your
|
||||
audio verdict.)
|
||||
@@ -0,0 +1,13 @@
|
||||
# phase 0C task 0.13 — ONE command for the whole test world (design D26:
|
||||
# no two test worlds; partial green is impossible). This is the entry the
|
||||
# landing gate (scripts/check_landing.py) demands evidence from.
|
||||
.PHONY: check boundary-regen paired-build
|
||||
|
||||
check:
|
||||
python scripts/unified_check.py
|
||||
|
||||
boundary-regen:
|
||||
python scripts/gen_boundary_types.py
|
||||
|
||||
paired-build:
|
||||
bash scripts/build_paired.sh
|
||||
@@ -1,6 +1,6 @@
|
||||
# Rune
|
||||
|
||||
**Current release: v0.279.0**
|
||||
**Current release: v0.312.0**
|
||||
|
||||
**Rune is a persistent personal agent designed to accumulate experience, develop reusable competence, and remain recognisably the same agent even when its underlying foundation model changes.**
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "c9419eadc0641ecaf40267e0543a5bdbfc548b377a4c2721f92662a641e32f10", "evidence": {"detail": "auth check observed: unreachable", "ended_at": 1792233218.9893527, "error_class": "UNREACHABLE", "input_digest": "dd027a0116f03542db66d808b90e5c9cd27a99ed290a046c4ea7f3a8a143d6f3", "output_digest": "c3c4c022a5a8e2e454665615a663a6984323d1c54df648ae4f49599a329a22fb", "started_at": 1792233218.9739888}, "kind": "provider", "observed_at": 1792233218.989386, "path": "provider:ollama", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233218-492cf12e", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "unmeasured"}
|
||||
{"digest": "ebd493b6544b8f215d2a714a998a7ac37add986d780fc1b809c0a270e8821e5d", "evidence": {"detail": "minimal call observed failing: Exception: Ollama connection failed: cannot import name '_clean_model_name' from 'model_connect' (/h", "ended_at": 1792233218.997182, "error_class": "UNREACHABLE", "input_digest": "a21f92f9ee3f2256bcbf27aa45981290981c6c1cf2655bfa50b0ba11ba20868c", "output_digest": "76b0ec3f3942928cb5232e64112f0fa5347f3a5193ca8a82a62d9e963e142a44", "started_at": 1792233218.9893873}, "kind": "provider", "observed_at": 1792233218.9972157, "path": "provider:ollama", "phase": "minimal_call", "prev_digest": "c9419eadc0641ecaf40267e0543a5bdbfc548b377a4c2721f92662a641e32f10", "receipt_id": "LP-1792233218-e0f20aa3", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "unmeasured"}
|
||||
{"digest": "422166d72546bf077ff51d2152acbd0e987e92d3d8b00748d5a14706e66944ac", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233219.006819, "error_class": null, "injected_fault": "generate against a nonexistent model id", "input_digest": "6c0df4008620e6808cc874cd54c1c9b516dd5b50e4ea1bfe0ad118b00f085587", "output_digest": "71316104b00a83a2835a7d02b2b1fc42de3cf5bc50f4135785314698032e1622", "started_at": 1792233218.997217}, "kind": "provider", "observed_at": 1792233219.0068464, "path": "provider:ollama", "phase": "failure_injection", "prev_digest": "ebd493b6544b8f215d2a714a998a7ac37add986d780fc1b809c0a270e8821e5d", "receipt_id": "LP-1792233219-bb625093", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "0aecbb624f13ee7719fd6e0679655142ec39f252a134f2181579f85150793168", "evidence": {"detail": "recovery call observed failing: Exception: Ollama connection failed: cannot import name '_clean_model_name' from 'model_connect' (/h", "ended_at": 1792233219.0125165, "error_class": "UNREACHABLE", "input_digest": "da1b19503325152474269326e22e35a3541de5be0b52f6a4d15697562276ffcf", "output_digest": "39c9e709defa1679ba1d6ab7c60e3a4be6778264bef4fbe47cb1f1e3cbf8afb7", "started_at": 1792233219.0068486}, "kind": "provider", "observed_at": 1792233219.0125406, "path": "provider:ollama", "phase": "recovery", "prev_digest": "422166d72546bf077ff51d2152acbd0e987e92d3d8b00748d5a14706e66944ac", "receipt_id": "LP-1792233219-a4e0929a", "recovery_of": "LP-1792233219-bb625093", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "unmeasured"}
|
||||
{"digest": "eb332a8c25995da32fd4c4db62a5ba910f6b3469beadb2a1e3b8155c565bd1a5", "evidence": {"detail": "auth check observed: unreachable", "ended_at": 1792234272.8529687, "error_class": "UNREACHABLE", "input_digest": "dd027a0116f03542db66d808b90e5c9cd27a99ed290a046c4ea7f3a8a143d6f3", "output_digest": "c3c4c022a5a8e2e454665615a663a6984323d1c54df648ae4f49599a329a22fb", "started_at": 1792234272.8361378}, "kind": "provider", "observed_at": 1792234272.8529952, "path": "provider:ollama", "phase": "auth_check", "prev_digest": "0aecbb624f13ee7719fd6e0679655142ec39f252a134f2181579f85150793168", "receipt_id": "LP-1792234272-79069dd1", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "unmeasured"}
|
||||
{"digest": "147fed3aca1ac49ebc86d959fbf89e1bb2c7a93343a94605f7e51890f22bdc51", "evidence": {"detail": "minimal call observed failing: Exception: Ollama connection failed: cannot import name '_clean_model_name' from 'model_connect' (/h", "ended_at": 1792234272.860741, "error_class": "UNREACHABLE", "input_digest": "a21f92f9ee3f2256bcbf27aa45981290981c6c1cf2655bfa50b0ba11ba20868c", "output_digest": "76b0ec3f3942928cb5232e64112f0fa5347f3a5193ca8a82a62d9e963e142a44", "started_at": 1792234272.8529968}, "kind": "provider", "observed_at": 1792234272.8607702, "path": "provider:ollama", "phase": "minimal_call", "prev_digest": "eb332a8c25995da32fd4c4db62a5ba910f6b3469beadb2a1e3b8155c565bd1a5", "receipt_id": "LP-1792234272-9c39fdf0", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "unmeasured"}
|
||||
{"digest": "4ff45d265c98ee4dedb981ea927efc89342c3af39288f7343def206e0c6865c1", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234272.8636794, "error_class": null, "injected_fault": "generate against a nonexistent model id", "input_digest": "6c0df4008620e6808cc874cd54c1c9b516dd5b50e4ea1bfe0ad118b00f085587", "output_digest": "71316104b00a83a2835a7d02b2b1fc42de3cf5bc50f4135785314698032e1622", "started_at": 1792234272.8607724}, "kind": "provider", "observed_at": 1792234272.8637164, "path": "provider:ollama", "phase": "failure_injection", "prev_digest": "147fed3aca1ac49ebc86d959fbf89e1bb2c7a93343a94605f7e51890f22bdc51", "receipt_id": "LP-1792234272-b76c579a", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "34793a3250f51c5b28de3405feda20e0cf6a56d456317dd85c1847966002da84", "evidence": {"detail": "recovery call observed failing: Exception: Ollama connection failed: cannot import name '_clean_model_name' from 'model_connect' (/h", "ended_at": 1792234272.8698554, "error_class": "UNREACHABLE", "input_digest": "da1b19503325152474269326e22e35a3541de5be0b52f6a4d15697562276ffcf", "output_digest": "39c9e709defa1679ba1d6ab7c60e3a4be6778264bef4fbe47cb1f1e3cbf8afb7", "started_at": 1792234272.863718}, "kind": "provider", "observed_at": 1792234272.8698883, "path": "provider:ollama", "phase": "recovery", "prev_digest": "4ff45d265c98ee4dedb981ea927efc89342c3af39288f7343def206e0c6865c1", "receipt_id": "LP-1792234272-f20f26d0", "recovery_of": "LP-1792234272-b76c579a", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "unmeasured"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "99025712c48b1307084db77b76fcb86dbda65a0e865ca76431edac2946c66663", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233218.9710536, "error_class": null, "input_digest": "effb3b5d5769d540ff9760238ef48bff2f7e776d98141067ecab0e89d02745fb", "output_digest": "a43dee11da21b5c47cd68d4a4fffd3f16b118377a18610880c4ddb8c437cff30", "started_at": 1792233218.9710517}, "kind": "tool", "observed_at": 1792233218.971072, "path": "tool:check_growth", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233218-fe7201f0", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "f1732dcec4346174f63e4261e58deac8811065f5ab8ceaea640b7e6fe380fd8e", "evidence": {"detail": "check growth status", "ended_at": 1792233218.971133, "error_class": null, "input_digest": "cb2cbff6f8bc71c36d0ee1d0a7959bc3ee7ff9aa933f06bfaa51c50076fc1bc9", "output_digest": "71a806b344d675b9a062b2e91bc5b3a9c8b56b64c83d7027f3b2001279de1c4f", "started_at": 1792233218.971073}, "kind": "tool", "observed_at": 1792233218.9711418, "path": "tool:check_growth", "phase": "minimal_call", "prev_digest": "99025712c48b1307084db77b76fcb86dbda65a0e865ca76431edac2946c66663", "receipt_id": "LP-1792233218-65eafd31", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "ed3856b44b6239e6446b77469a6c41527112ad465814cea2df4242a36bc283ff", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233218.9712937, "error_class": null, "injected_fault": "check with a disabled growth gate", "input_digest": "01fa0327088c9965436c396d3f0bb4234e19811f3466d8911f5bdb5df3b5b9b5", "output_digest": "ed530432d25645b5a47a6c6c577e89a0c9202b1596c61a6cd54054ccb516e4d2", "started_at": 1792233218.9711423}, "kind": "tool", "observed_at": 1792233218.971308, "path": "tool:check_growth", "phase": "failure_injection", "prev_digest": "f1732dcec4346174f63e4261e58deac8811065f5ab8ceaea640b7e6fe380fd8e", "receipt_id": "LP-1792233218-44814065", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "32be97e9cfe91e0466147c5357cb13df1bff9ad5b66b68887d8d99c07ff0cf0a", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233218.9713871, "error_class": null, "input_digest": "739b3f090bd90a54c54f40b518d0e5e316bda4f14ab25342445139362394e167", "output_digest": "88a7f14ac8dc1521fede6db1e4c142fba565cd1a2827424ad93e2ad6c49fe411", "started_at": 1792233218.9713087}, "kind": "tool", "observed_at": 1792233218.971402, "path": "tool:check_growth", "phase": "recovery", "prev_digest": "ed3856b44b6239e6446b77469a6c41527112ad465814cea2df4242a36bc283ff", "receipt_id": "LP-1792233218-8aec1e2c", "recovery_of": "LP-1792233218-44814065", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "e45d6ec8bc98722470b1c6bd712dc290e7cdf20cb4585ee1e11487d50ee637dc", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234272.832866, "error_class": null, "input_digest": "effb3b5d5769d540ff9760238ef48bff2f7e776d98141067ecab0e89d02745fb", "output_digest": "a43dee11da21b5c47cd68d4a4fffd3f16b118377a18610880c4ddb8c437cff30", "started_at": 1792234272.8328648}, "kind": "tool", "observed_at": 1792234272.8328886, "path": "tool:check_growth", "phase": "auth_check", "prev_digest": "32be97e9cfe91e0466147c5357cb13df1bff9ad5b66b68887d8d99c07ff0cf0a", "receipt_id": "LP-1792234272-f2be3eb8", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "17e07c39e371aaeebfa83cb873ba65f3bed126894b4f052bf514a7f8baa1ce63", "evidence": {"detail": "check growth status", "ended_at": 1792234272.8329782, "error_class": null, "input_digest": "cb2cbff6f8bc71c36d0ee1d0a7959bc3ee7ff9aa933f06bfaa51c50076fc1bc9", "output_digest": "71a806b344d675b9a062b2e91bc5b3a9c8b56b64c83d7027f3b2001279de1c4f", "started_at": 1792234272.8328893}, "kind": "tool", "observed_at": 1792234272.8329926, "path": "tool:check_growth", "phase": "minimal_call", "prev_digest": "e45d6ec8bc98722470b1c6bd712dc290e7cdf20cb4585ee1e11487d50ee637dc", "receipt_id": "LP-1792234272-bc7a7334", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "0031728bfd365ccb090ec337a9c91953975352ba48b7d3576caef2e31c9a2417", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234272.8330224, "error_class": null, "injected_fault": "check with a disabled growth gate", "input_digest": "01fa0327088c9965436c396d3f0bb4234e19811f3466d8911f5bdb5df3b5b9b5", "output_digest": "ed530432d25645b5a47a6c6c577e89a0c9202b1596c61a6cd54054ccb516e4d2", "started_at": 1792234272.8329933}, "kind": "tool", "observed_at": 1792234272.8330305, "path": "tool:check_growth", "phase": "failure_injection", "prev_digest": "17e07c39e371aaeebfa83cb873ba65f3bed126894b4f052bf514a7f8baa1ce63", "receipt_id": "LP-1792234272-3c00183b", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "74a1cb3f6452f59f43877f96d88b2a5243b78e3914443d8744f41d717eb6e568", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234272.8330655, "error_class": null, "input_digest": "739b3f090bd90a54c54f40b518d0e5e316bda4f14ab25342445139362394e167", "output_digest": "88a7f14ac8dc1521fede6db1e4c142fba565cd1a2827424ad93e2ad6c49fe411", "started_at": 1792234272.833031}, "kind": "tool", "observed_at": 1792234272.8330722, "path": "tool:check_growth", "phase": "recovery", "prev_digest": "0031728bfd365ccb090ec337a9c91953975352ba48b7d3576caef2e31c9a2417", "receipt_id": "LP-1792234272-a52f85ab", "recovery_of": "LP-1792234272-3c00183b", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "fb033f6370e01ed82a84f32ff0a54df6b77ce3765e79333f584b699620ae1085", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233218.972268, "error_class": null, "input_digest": "d6553a8acb42491e8735929dc6f91b553ff94c4e797554e46273f9acb9e30bd4", "output_digest": "c8cb4141c0ee8d220179c8acc4aae21aa0cff50326c88a13bdfdb2c9c5b2203f", "started_at": 1792233218.9722667}, "kind": "tool", "observed_at": 1792233218.9722826, "path": "tool:diagnose_behavior", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233218-dd7210f5", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "ab7b7039801bd75b33269958bd3d323425612b084b69024b9d98442734cf6920", "evidence": {"detail": "minimal call failed: Text is required", "ended_at": 1792233218.972331, "error_class": "TOOL_ERROR", "input_digest": "f9059e4bd866ddfc75fb5ccd9b89306e9cf4a65fdc82ab98c1a677c78c0d1685", "output_digest": "ead878aa4011839b17b4f7d7a75db98c83f4b3ed22140d36e4a16580cf81d671", "started_at": 1792233218.9722831}, "kind": "tool", "observed_at": 1792233218.9723408, "path": "tool:diagnose_behavior", "phase": "minimal_call", "prev_digest": "fb033f6370e01ed82a84f32ff0a54df6b77ce3765e79333f584b699620ae1085", "receipt_id": "LP-1792233218-134fe0fd", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_fail"}
|
||||
{"digest": "b858918d419ad179109da3c570351f8bf3f4c40bda784c8caffe2b1d379401f2", "evidence": {"detail": "injected fault observed failing and classified: Text is required", "ended_at": 1792233218.9723644, "error_class": "TOOL_ERROR", "injected_fault": "diagnose a nonexistent behavior", "input_digest": "8c4282e880c336c239d9ac91bd85a502b44ac6d404592d5aabfe17b8b834f849", "output_digest": "01973c0c768f536fed316470916982cef444dfc09453f9e52ae1b43fe997291c", "started_at": 1792233218.9723413}, "kind": "tool", "observed_at": 1792233218.9723866, "path": "tool:diagnose_behavior", "phase": "failure_injection", "prev_digest": "ab7b7039801bd75b33269958bd3d323425612b084b69024b9d98442734cf6920", "receipt_id": "LP-1792233218-7c223469", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "3b153deb3d2f4c4f3560095e4dff865f7f4c3974acee34124f5ea052a2aa717e", "evidence": {"detail": "recovery call failed: Text is required", "ended_at": 1792233218.972428, "error_class": "TOOL_ERROR", "input_digest": "d78b3459d5852c759f6ea1b0f50054bdea2fe9ba78aefc436739e01da74271ad", "output_digest": "42ed65c4c87cc759829b454747b386d6ed17fbc1d384a276f655671eb83e8c8b", "started_at": 1792233218.9723876}, "kind": "tool", "observed_at": 1792233218.9724379, "path": "tool:diagnose_behavior", "phase": "recovery", "prev_digest": "b858918d419ad179109da3c570351f8bf3f4c40bda784c8caffe2b1d379401f2", "receipt_id": "LP-1792233218-77b6723d", "recovery_of": "LP-1792233218-7c223469", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_fail"}
|
||||
{"digest": "c32d50db85d43518dda60996844776e0989cd908c5b9c5030c39ae9743a71946", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234272.8339787, "error_class": null, "input_digest": "d6553a8acb42491e8735929dc6f91b553ff94c4e797554e46273f9acb9e30bd4", "output_digest": "c8cb4141c0ee8d220179c8acc4aae21aa0cff50326c88a13bdfdb2c9c5b2203f", "started_at": 1792234272.8339775}, "kind": "tool", "observed_at": 1792234272.8339918, "path": "tool:diagnose_behavior", "phase": "auth_check", "prev_digest": "3b153deb3d2f4c4f3560095e4dff865f7f4c3974acee34124f5ea052a2aa717e", "receipt_id": "LP-1792234272-dfac6bb4", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "8756bc92697e9f5de328db31223bf7481f698e1ad7634e7f058d36d0dc83ddec", "evidence": {"detail": "minimal call failed: Text is required", "ended_at": 1792234272.8340354, "error_class": "TOOL_ERROR", "input_digest": "f9059e4bd866ddfc75fb5ccd9b89306e9cf4a65fdc82ab98c1a677c78c0d1685", "output_digest": "ead878aa4011839b17b4f7d7a75db98c83f4b3ed22140d36e4a16580cf81d671", "started_at": 1792234272.8339922}, "kind": "tool", "observed_at": 1792234272.8340425, "path": "tool:diagnose_behavior", "phase": "minimal_call", "prev_digest": "c32d50db85d43518dda60996844776e0989cd908c5b9c5030c39ae9743a71946", "receipt_id": "LP-1792234272-836d1042", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_fail"}
|
||||
{"digest": "9297fed4e6920a3f5762cfe2fb274a29c3c1fec4802393f2ff3f004d70b7719a", "evidence": {"detail": "injected fault observed failing and classified: Text is required", "ended_at": 1792234272.8340626, "error_class": "TOOL_ERROR", "injected_fault": "diagnose a nonexistent behavior", "input_digest": "8c4282e880c336c239d9ac91bd85a502b44ac6d404592d5aabfe17b8b834f849", "output_digest": "01973c0c768f536fed316470916982cef444dfc09453f9e52ae1b43fe997291c", "started_at": 1792234272.834043}, "kind": "tool", "observed_at": 1792234272.834069, "path": "tool:diagnose_behavior", "phase": "failure_injection", "prev_digest": "8756bc92697e9f5de328db31223bf7481f698e1ad7634e7f058d36d0dc83ddec", "receipt_id": "LP-1792234272-a9bb3919", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "8ad5aee780ccd936b60fc54fbaa1eb1eb53876090e95815abb2e4b6bf6587e2d", "evidence": {"detail": "recovery call failed: Text is required", "ended_at": 1792234272.8340976, "error_class": "TOOL_ERROR", "input_digest": "d78b3459d5852c759f6ea1b0f50054bdea2fe9ba78aefc436739e01da74271ad", "output_digest": "42ed65c4c87cc759829b454747b386d6ed17fbc1d384a276f655671eb83e8c8b", "started_at": 1792234272.8340695}, "kind": "tool", "observed_at": 1792234272.8341036, "path": "tool:diagnose_behavior", "phase": "recovery", "prev_digest": "9297fed4e6920a3f5762cfe2fb274a29c3c1fec4802393f2ff3f004d70b7719a", "receipt_id": "LP-1792234272-20e5a3a8", "recovery_of": "LP-1792234272-a9bb3919", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_fail"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "48cd2f229a78ab32c2f2070a5b47e77ef5e48db795c686649565883660ee6aa5", "evidence": {"detail": "approval gate refused to prepare (observed gate): Search string is required", "ended_at": 1792233217.9267302, "error_class": "GATE_REFUSED", "input_digest": "4eabb2182550c057b79bd5976ad49473e3084277549b998891a122dc3dc7f9db", "output_digest": "a45af98f4939d4fd8485c8d4fa7ab62a45dd55e86f998696b15442bcff3e0349", "started_at": 1792233217.9261644}, "kind": "tool", "observed_at": 1792233217.9267461, "path": "tool:edit_file", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-a68e62ef", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_fail"}
|
||||
{"digest": "a9953e071141252ad8c3b42486abcbb1cda5789b0d829cd982d7620faf59b56f", "evidence": {"detail": "minimal call failed: Search string is required", "ended_at": 1792233217.9273448, "error_class": "TOOL_ERROR", "input_digest": "6fb6fbf080b369dc2e686b32cddbc680267340fb9425e08afb7b9fa9e7e8b377", "output_digest": "f4a206c8cead20aeeb6bb307edb77a9f2d53db376d64bc1813ce588ad6053da8", "started_at": 1792233217.9267473}, "kind": "tool", "observed_at": 1792233217.9273586, "path": "tool:edit_file", "phase": "minimal_call", "prev_digest": "48cd2f229a78ab32c2f2070a5b47e77ef5e48db795c686649565883660ee6aa5", "receipt_id": "LP-1792233217-e0f92cc9", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_fail"}
|
||||
{"digest": "a45da58bf28d26c9ae75153acebf1a8c2cc9e26a3d513714bfa8697c8bdd9620", "evidence": {"detail": "injected fault observed failing and classified: Search string is required", "ended_at": 1792233217.9278736, "error_class": "TOOL_ERROR", "injected_fault": "edit a file with a missing anchor", "input_digest": "63ce71b463d1e77f081e233375c7c291c34c6f93853676b848ef68f96dc7feaa", "output_digest": "0237d8dd751db3440a726294e02d4fd8a84b00989c6119f0577f034870a3af5d", "started_at": 1792233217.92736}, "kind": "tool", "observed_at": 1792233217.9278855, "path": "tool:edit_file", "phase": "failure_injection", "prev_digest": "a9953e071141252ad8c3b42486abcbb1cda5789b0d829cd982d7620faf59b56f", "receipt_id": "LP-1792233217-3ad43eab", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "8e4d081578f175f2b37caedab52f719b56668ca311e7e8fb31e9842feee42a6c", "evidence": {"detail": "recovery call failed: Search string is required", "ended_at": 1792233217.928333, "error_class": "TOOL_ERROR", "input_digest": "a70358c28463e2f8e5eea5eb760f76644b1e20383fa4678ec4c318d190c60d11", "output_digest": "f89be8c3fddb6328acfd951900fb398c74c50f7f1708e527e688a18d016f9302", "started_at": 1792233217.9278872}, "kind": "tool", "observed_at": 1792233217.928343, "path": "tool:edit_file", "phase": "recovery", "prev_digest": "a45da58bf28d26c9ae75153acebf1a8c2cc9e26a3d513714bfa8697c8bdd9620", "receipt_id": "LP-1792233217-6ee28288", "recovery_of": "LP-1792233217-3ad43eab", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_fail"}
|
||||
{"digest": "dc5b600d7dfc0d4e381201f66c4f3e6dcb17a9d383f1dd0ea86d214f0150ab76", "evidence": {"detail": "approval gate observed: prepared + approved (single-use token, success=True)", "ended_at": 1792234271.5940187, "error_class": null, "input_digest": "4eabb2182550c057b79bd5976ad49473e3084277549b998891a122dc3dc7f9db", "output_digest": "3a5717ee3ccb85eafbb6c567c687594f4c39d771cf429b812c26155ae4177f65", "started_at": 1792234271.5907593}, "kind": "tool", "observed_at": 1792234271.5940323, "path": "tool:edit_file", "phase": "auth_check", "prev_digest": "8e4d081578f175f2b37caedab52f719b56668ca311e7e8fb31e9842feee42a6c", "receipt_id": "LP-1792234271-74f79322", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "2c779b57fe74d139b79ce64707d0290d1252b26f3ce158dce6eb3cd0a3c040f7", "evidence": {"detail": "minimal call failed: Search string not found in edit_file.txt", "ended_at": 1792234271.5949092, "error_class": "TOOL_ERROR", "input_digest": "6fb6fbf080b369dc2e686b32cddbc680267340fb9425e08afb7b9fa9e7e8b377", "output_digest": "20ed3750d32c8a881aea08cb33fb52bba8fd61fcf969c2022b721bbd8e9ba42a", "started_at": 1792234271.5940335}, "kind": "tool", "observed_at": 1792234271.594928, "path": "tool:edit_file", "phase": "minimal_call", "prev_digest": "dc5b600d7dfc0d4e381201f66c4f3e6dcb17a9d383f1dd0ea86d214f0150ab76", "receipt_id": "LP-1792234271-b30479ab", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_fail"}
|
||||
{"digest": "5d7fd346bcbb3adbc3f3952c5f592866541d5afe2bfdeb01ee98cff7c669085c", "evidence": {"detail": "injected fault observed failing and classified: Edit failed: [Errno 2] No such file or directory: '/mnt/workdrive/rune/store/live_proof/sandbox/__nonexistent_live_probe", "ended_at": 1792234271.5957453, "error_class": "ENOENT", "injected_fault": "edit a file with a missing anchor", "input_digest": "63ce71b463d1e77f081e233375c7c291c34c6f93853676b848ef68f96dc7feaa", "output_digest": "364eb58fdab4217299d3a7f3fd590815a1964f86b74f7c2fa0d38032a62150c0", "started_at": 1792234271.594929}, "kind": "tool", "observed_at": 1792234271.5957549, "path": "tool:edit_file", "phase": "failure_injection", "prev_digest": "2c779b57fe74d139b79ce64707d0290d1252b26f3ce158dce6eb3cd0a3c040f7", "receipt_id": "LP-1792234271-65f7fd20", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "d5a0fbc69149bae42bf8d556920c9052247db4d55d5c892c55c72fa4b3b9cae9", "evidence": {"detail": "recovery call failed: Search string not found in edit_file.txt", "ended_at": 1792234271.5965545, "error_class": "TOOL_ERROR", "input_digest": "a70358c28463e2f8e5eea5eb760f76644b1e20383fa4678ec4c318d190c60d11", "output_digest": "d9bf9f6f2c4f64727973bc0f636af218d074075cd1926e0bc501813278d63992", "started_at": 1792234271.595756}, "kind": "tool", "observed_at": 1792234271.5965624, "path": "tool:edit_file", "phase": "recovery", "prev_digest": "5d7fd346bcbb3adbc3f3952c5f592866541d5afe2bfdeb01ee98cff7c669085c", "receipt_id": "LP-1792234271-28bf8c0f", "recovery_of": "LP-1792234271-65f7fd20", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_fail"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "de031143b88a4d31724982cb7bc0a04d1b9953b41b4e86d282ea21849ad0e500", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233218.9731452, "error_class": null, "input_digest": "0adb83aa939f7216e7bf9ba290ca9e9a64c6e8410bbf031307b944b3fd61062f", "output_digest": "35e723bbc9593fdbb9b7b37505f216ba25bc48db956b46db0c40720f6e9c55c2", "started_at": 1792233218.973144}, "kind": "tool", "observed_at": 1792233218.9731574, "path": "tool:find_capability", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233218-ce2187d2", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "9f677dc17c92915a62e15d7f750400195b31c341555f78979c1302c88ffdfc5e", "evidence": {"detail": "minimal call failed: Query is required \u2014 describe the capability you need", "ended_at": 1792233218.9731963, "error_class": "TOOL_ERROR", "input_digest": "3b47e2eacf2a6aa514330dfcd9bee3f7cfc83bae4c0930f657f00044030ae69b", "output_digest": "62565e29fa6c5e71b931cbfd6c1684af3a471c679da69334046585eefc2fee88", "started_at": 1792233218.973158}, "kind": "tool", "observed_at": 1792233218.9732049, "path": "tool:find_capability", "phase": "minimal_call", "prev_digest": "de031143b88a4d31724982cb7bc0a04d1b9953b41b4e86d282ea21849ad0e500", "receipt_id": "LP-1792233218-3566a907", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_fail"}
|
||||
{"digest": "38176a2beaf5d78cc874cbfac22ac7845b000efdb13f2a284b053d808f0a7da6", "evidence": {"detail": "injected fault observed failing and classified: Query is required \u2014 describe the capability you need", "ended_at": 1792233218.9732258, "error_class": "TOOL_ERROR", "injected_fault": "find with an empty description", "input_digest": "0ac9aad40765fe88299b7b07e5013a81a1f2a895b8afb4dce2c3cfac64d7dd26", "output_digest": "28352af01bb36df885d64cb1b6bc26d4a61e8077629f823fb67340c0d423d174", "started_at": 1792233218.9732053}, "kind": "tool", "observed_at": 1792233218.9732332, "path": "tool:find_capability", "phase": "failure_injection", "prev_digest": "9f677dc17c92915a62e15d7f750400195b31c341555f78979c1302c88ffdfc5e", "receipt_id": "LP-1792233218-712a2227", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "50854fcc958dbce5821e72e55b730b0343f44590be10930a3b12bc727881d707", "evidence": {"detail": "recovery call failed: Query is required \u2014 describe the capability you need", "ended_at": 1792233218.9732618, "error_class": "TOOL_ERROR", "input_digest": "d151d7e8dd968e38c8d85bef653e87199364bc1414b7c97b8e1fc59a601c9a59", "output_digest": "d8491c426e55ca12008180f3da157210d2f7fb207ac428a1b90ecb4de1e924ef", "started_at": 1792233218.973234}, "kind": "tool", "observed_at": 1792233218.9732685, "path": "tool:find_capability", "phase": "recovery", "prev_digest": "38176a2beaf5d78cc874cbfac22ac7845b000efdb13f2a284b053d808f0a7da6", "receipt_id": "LP-1792233218-9a17a48f", "recovery_of": "LP-1792233218-712a2227", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_fail"}
|
||||
{"digest": "1f2cd83e04e5afda5afd551a7f84165cf4203d994ea439dcdbb2c416cdce4067", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234272.8349164, "error_class": null, "input_digest": "0adb83aa939f7216e7bf9ba290ca9e9a64c6e8410bbf031307b944b3fd61062f", "output_digest": "35e723bbc9593fdbb9b7b37505f216ba25bc48db956b46db0c40720f6e9c55c2", "started_at": 1792234272.8349156}, "kind": "tool", "observed_at": 1792234272.83493, "path": "tool:find_capability", "phase": "auth_check", "prev_digest": "50854fcc958dbce5821e72e55b730b0343f44590be10930a3b12bc727881d707", "receipt_id": "LP-1792234272-b25ebea1", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "6f8eb8865b3b3617dbe09a42bd481b06ef93e5bedab18950082231388f66cc14", "evidence": {"detail": "minimal call failed: Query is required \u2014 describe the capability you need", "ended_at": 1792234272.8349726, "error_class": "TOOL_ERROR", "input_digest": "3b47e2eacf2a6aa514330dfcd9bee3f7cfc83bae4c0930f657f00044030ae69b", "output_digest": "62565e29fa6c5e71b931cbfd6c1684af3a471c679da69334046585eefc2fee88", "started_at": 1792234272.8349307}, "kind": "tool", "observed_at": 1792234272.834981, "path": "tool:find_capability", "phase": "minimal_call", "prev_digest": "1f2cd83e04e5afda5afd551a7f84165cf4203d994ea439dcdbb2c416cdce4067", "receipt_id": "LP-1792234272-c3442c1f", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_fail"}
|
||||
{"digest": "08b43ea4362c8d56c5a006187c8c91e7da64c39056798f338505d1501606c7b3", "evidence": {"detail": "injected fault observed failing and classified: Query is required \u2014 describe the capability you need", "ended_at": 1792234272.8350027, "error_class": "TOOL_ERROR", "injected_fault": "find with an empty description", "input_digest": "0ac9aad40765fe88299b7b07e5013a81a1f2a895b8afb4dce2c3cfac64d7dd26", "output_digest": "28352af01bb36df885d64cb1b6bc26d4a61e8077629f823fb67340c0d423d174", "started_at": 1792234272.8349812}, "kind": "tool", "observed_at": 1792234272.8350093, "path": "tool:find_capability", "phase": "failure_injection", "prev_digest": "6f8eb8865b3b3617dbe09a42bd481b06ef93e5bedab18950082231388f66cc14", "receipt_id": "LP-1792234272-79fe0ee7", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "ab14d5cddb37f55e0c9adbcf00f51faf439dd10e54c6b26d2821180dd7b6606a", "evidence": {"detail": "recovery call failed: Query is required \u2014 describe the capability you need", "ended_at": 1792234272.8350449, "error_class": "TOOL_ERROR", "input_digest": "d151d7e8dd968e38c8d85bef653e87199364bc1414b7c97b8e1fc59a601c9a59", "output_digest": "d8491c426e55ca12008180f3da157210d2f7fb207ac428a1b90ecb4de1e924ef", "started_at": 1792234272.8350098}, "kind": "tool", "observed_at": 1792234272.8350532, "path": "tool:find_capability", "phase": "recovery", "prev_digest": "08b43ea4362c8d56c5a006187c8c91e7da64c39056798f338505d1501606c7b3", "receipt_id": "LP-1792234272-6766c0b0", "recovery_of": "LP-1792234272-79fe0ee7", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_fail"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "4339eac29575b6f17356e0dcd605bef2e8ae659e072cbb81416e92b88698f85c", "evidence": {"detail": "approval gate observed: prepared + approved (single-use token, success=False)", "ended_at": 1792233217.9835057, "error_class": null, "input_digest": "d496d4e824e588322f4e103d0da9bbe37a373e9ac5bc58e90ad6a4b761882322", "output_digest": "b4dacd5dcf32609a5baf5b876f42c419899d3086436c99dca34d6810663b1e21", "started_at": 1792233217.9491942}, "kind": "tool", "observed_at": 1792233217.9835477, "path": "tool:format_code", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-6f043361", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "9164fb82484dba5ae0e8c497728866716a2cb8874021b1fe6d53fb7273ada121", "evidence": {"detail": "format the sandbox file", "ended_at": 1792233217.984442, "error_class": null, "input_digest": "32d0d5c5830c69cf32c0ee758005a203837ad7fd802259c1ad21b6f3954bc63b", "output_digest": "c8c2a02139b62296fee3cc277bd826d681d35f5646811f49cdbc91afed8ab1e7", "started_at": 1792233217.983549}, "kind": "tool", "observed_at": 1792233217.984453, "path": "tool:format_code", "phase": "minimal_call", "prev_digest": "4339eac29575b6f17356e0dcd605bef2e8ae659e072cbb81416e92b88698f85c", "receipt_id": "LP-1792233217-39e45da0", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "0a929f0cdf83277119d58ac3dfe50b63e646b7a8eb078e44cf3a790f4384638f", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233217.985334, "error_class": null, "injected_fault": "format a nonexistent file", "input_digest": "699188135863c5b805a7e910e721be757d537f958ef28f1590a2f7d087732c81", "output_digest": "6ed318b6181b478b9a2bc0fff2614602fb252749895d9bd5fa8b3f03b1fe613c", "started_at": 1792233217.9844537}, "kind": "tool", "observed_at": 1792233217.9853451, "path": "tool:format_code", "phase": "failure_injection", "prev_digest": "9164fb82484dba5ae0e8c497728866716a2cb8874021b1fe6d53fb7273ada121", "receipt_id": "LP-1792233217-cdc17635", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "0c704fc95e1195ff65984ee123c847f64d714cabd312ba8b8b38ec1764d8586c", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233217.9860196, "error_class": null, "input_digest": "cfa71b658f7e5cd0bd2c68fcc3a0387dffc2997b910f2ed78a5cfb2db2c9f715", "output_digest": "ae2604e827746341571cc34b70a5fd25cfd8983942c871a97640d1012d562630", "started_at": 1792233217.9853458}, "kind": "tool", "observed_at": 1792233217.9860277, "path": "tool:format_code", "phase": "recovery", "prev_digest": "0a929f0cdf83277119d58ac3dfe50b63e646b7a8eb078e44cf3a790f4384638f", "receipt_id": "LP-1792233217-096d666c", "recovery_of": "LP-1792233217-cdc17635", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "dd1250bf123cb79d2fbf1072359e671d38772f14ca36e547320286829a3d722a", "evidence": {"detail": "approval gate observed: prepared + approved (single-use token, success=False)", "ended_at": 1792234271.6412644, "error_class": null, "input_digest": "d496d4e824e588322f4e103d0da9bbe37a373e9ac5bc58e90ad6a4b761882322", "output_digest": "b4dacd5dcf32609a5baf5b876f42c419899d3086436c99dca34d6810663b1e21", "started_at": 1792234271.6176572}, "kind": "tool", "observed_at": 1792234271.6412973, "path": "tool:format_code", "phase": "auth_check", "prev_digest": "0c704fc95e1195ff65984ee123c847f64d714cabd312ba8b8b38ec1764d8586c", "receipt_id": "LP-1792234271-bad0a4a4", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "9ec4ca7e02f555fefb59a8d9ee2e582c6b54f8b047cfeb23c6053cbeccc9f214", "evidence": {"detail": "format the sandbox file", "ended_at": 1792234271.6419256, "error_class": null, "input_digest": "32d0d5c5830c69cf32c0ee758005a203837ad7fd802259c1ad21b6f3954bc63b", "output_digest": "c8c2a02139b62296fee3cc277bd826d681d35f5646811f49cdbc91afed8ab1e7", "started_at": 1792234271.6412988}, "kind": "tool", "observed_at": 1792234271.641933, "path": "tool:format_code", "phase": "minimal_call", "prev_digest": "dd1250bf123cb79d2fbf1072359e671d38772f14ca36e547320286829a3d722a", "receipt_id": "LP-1792234271-7eee87ea", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "e45f1684037afb1cf03448b01053d8170b613851e9484de8010ef46621ad6e04", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234271.6423914, "error_class": null, "injected_fault": "format a nonexistent file", "input_digest": "699188135863c5b805a7e910e721be757d537f958ef28f1590a2f7d087732c81", "output_digest": "6ed318b6181b478b9a2bc0fff2614602fb252749895d9bd5fa8b3f03b1fe613c", "started_at": 1792234271.6419346}, "kind": "tool", "observed_at": 1792234271.6424007, "path": "tool:format_code", "phase": "failure_injection", "prev_digest": "9ec4ca7e02f555fefb59a8d9ee2e582c6b54f8b047cfeb23c6053cbeccc9f214", "receipt_id": "LP-1792234271-fdfacc31", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "60ce32130f5c17019ac7b70717e0f420fa54c8ed56a68fd674621e94275be7b2", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.6428502, "error_class": null, "input_digest": "cfa71b658f7e5cd0bd2c68fcc3a0387dffc2997b910f2ed78a5cfb2db2c9f715", "output_digest": "ae2604e827746341571cc34b70a5fd25cfd8983942c871a97640d1012d562630", "started_at": 1792234271.6424017}, "kind": "tool", "observed_at": 1792234271.6428561, "path": "tool:format_code", "phase": "recovery", "prev_digest": "e45f1684037afb1cf03448b01053d8170b613851e9484de8010ef46621ad6e04", "receipt_id": "LP-1792234271-a43376a0", "recovery_of": "LP-1792234271-fdfacc31", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "916622b0692180079be0fa14dc9868f280ae325a0ccb735aba7df355edbc013b", "evidence": {"detail": "approval gate observed: prepared + approved (single-use token, success=True)", "ended_at": 1792233218.126073, "error_class": null, "input_digest": "ed159930321ec7053a54a9f1e7a82daeadcf1554c84fc707791bfe3fed89ae36", "output_digest": "577080931c4764d94a2b67815cba86356206569ca9fc76ae2bb9f6ae150bfc06", "started_at": 1792233218.0524125}, "kind": "tool", "observed_at": 1792233218.1261241, "path": "tool:git_commit", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233218-178540f6", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "38f228c1f4e5b222b197325149efd41779a62aface45517b0a54873a0c960b27", "evidence": {"detail": "commit the sandbox repo", "ended_at": 1792233218.1262887, "error_class": null, "input_digest": "54ceaa1b4eccbee19db6a3dc8c24f692d029804bd0ae7c9f685c1c7860a3f2d0", "output_digest": "7b06fbc1d8ade52285636daec396c0d8ca75964b514e1077139302b18963279c", "started_at": 1792233218.1261256}, "kind": "tool", "observed_at": 1792233218.1262999, "path": "tool:git_commit", "phase": "minimal_call", "prev_digest": "916622b0692180079be0fa14dc9868f280ae325a0ccb735aba7df355edbc013b", "receipt_id": "LP-1792233218-b761dbbe", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "8e1595f6593a9291f038589c9e34b4d14a173b34764cb2e7644ffbd4c047ffb0", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233218.1263576, "error_class": null, "injected_fault": "commit in a nonexistent repo", "input_digest": "9bddc4cfb693552c656dc9e47584d9068b3be99daae94438e0530121cb97fc8e", "output_digest": "7f907c177d743bae750c07e27658d6879a89845424b140bed34862aacda401b7", "started_at": 1792233218.1263006}, "kind": "tool", "observed_at": 1792233218.1263666, "path": "tool:git_commit", "phase": "failure_injection", "prev_digest": "38f228c1f4e5b222b197325149efd41779a62aface45517b0a54873a0c960b27", "receipt_id": "LP-1792233218-8677fcf9", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "83fb0e9120ac471c8424758037a68d54722fc570a42b735e661daabe12631fe8", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233218.1264496, "error_class": null, "input_digest": "bc75d76baf987fe949e4bb458bfc3b5746e59531bce0b74ddc3e54054a7c6b9d", "output_digest": "026076a4884e63ffc7e67997e014186d6211e01bc0e63d77c19b6392c1836f9c", "started_at": 1792233218.1263678}, "kind": "tool", "observed_at": 1792233218.1264575, "path": "tool:git_commit", "phase": "recovery", "prev_digest": "8e1595f6593a9291f038589c9e34b4d14a173b34764cb2e7644ffbd4c047ffb0", "receipt_id": "LP-1792233218-ccff3ae0", "recovery_of": "LP-1792233218-8677fcf9", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "073f9815684222ff103e6a3e30db658ec97162fe202cd2d5c05c4ba111d88e88", "evidence": {"detail": "approval gate observed: prepared + approved (single-use token, success=True)", "ended_at": 1792234271.914318, "error_class": null, "input_digest": "ed159930321ec7053a54a9f1e7a82daeadcf1554c84fc707791bfe3fed89ae36", "output_digest": "577080931c4764d94a2b67815cba86356206569ca9fc76ae2bb9f6ae150bfc06", "started_at": 1792234271.71573}, "kind": "tool", "observed_at": 1792234271.9143634, "path": "tool:git_commit", "phase": "auth_check", "prev_digest": "83fb0e9120ac471c8424758037a68d54722fc570a42b735e661daabe12631fe8", "receipt_id": "LP-1792234271-81ad40b9", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "1a1a64773f8dfe4b3618f26d6d8c60b8c17ec71a1acc6436fbd1babf7ddd5388", "evidence": {"detail": "commit the sandbox repo", "ended_at": 1792234271.9145355, "error_class": null, "input_digest": "54ceaa1b4eccbee19db6a3dc8c24f692d029804bd0ae7c9f685c1c7860a3f2d0", "output_digest": "7b06fbc1d8ade52285636daec396c0d8ca75964b514e1077139302b18963279c", "started_at": 1792234271.9143646}, "kind": "tool", "observed_at": 1792234271.9145465, "path": "tool:git_commit", "phase": "minimal_call", "prev_digest": "073f9815684222ff103e6a3e30db658ec97162fe202cd2d5c05c4ba111d88e88", "receipt_id": "LP-1792234271-fe21eb5b", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "4ecfc38df097fa56163ae6b8501207c50ca58100ea634511978be196a1e628a8", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234271.9145935, "error_class": null, "injected_fault": "commit in a nonexistent repo", "input_digest": "9bddc4cfb693552c656dc9e47584d9068b3be99daae94438e0530121cb97fc8e", "output_digest": "7f907c177d743bae750c07e27658d6879a89845424b140bed34862aacda401b7", "started_at": 1792234271.9145477}, "kind": "tool", "observed_at": 1792234271.914602, "path": "tool:git_commit", "phase": "failure_injection", "prev_digest": "1a1a64773f8dfe4b3618f26d6d8c60b8c17ec71a1acc6436fbd1babf7ddd5388", "receipt_id": "LP-1792234271-bc6f3907", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "994380b2322699aaef85b0b64f07828650b6f06cc25a3a0c5d70c84b5fb7a610", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.9146597, "error_class": null, "input_digest": "bc75d76baf987fe949e4bb458bfc3b5746e59531bce0b74ddc3e54054a7c6b9d", "output_digest": "026076a4884e63ffc7e67997e014186d6211e01bc0e63d77c19b6392c1836f9c", "started_at": 1792234271.9146032}, "kind": "tool", "observed_at": 1792234271.9146674, "path": "tool:git_commit", "phase": "recovery", "prev_digest": "4ecfc38df097fa56163ae6b8501207c50ca58100ea634511978be196a1e628a8", "receipt_id": "LP-1792234271-a1df6340", "recovery_of": "LP-1792234271-bc6f3907", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "b8a6d2fdafc5e73d9a7ede9bd9ad93a09718d7c5aecec56f3b59828ff74093f4", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233217.9869187, "error_class": null, "input_digest": "dd109683f9eb4df87b83f1537860edf044f18fe29fb5bf9de5b570e4d42351d1", "output_digest": "600f50fcd096a6e7f256b1cb1e984795b95d9a2bca2271c14b1e555df34b0535", "started_at": 1792233217.9869177}, "kind": "tool", "observed_at": 1792233217.9869332, "path": "tool:git_diff", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-f020249f", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "4e3eed0978e2137df3ac4b4145d80dc8fe36872dbc7608de219977f684586323", "evidence": {"detail": "git diff over the sandbox repo", "ended_at": 1792233217.9911351, "error_class": null, "input_digest": "5dc9c8c0e0c1ef6aefe7169679869a6840c7df389307272714b882247d263083", "output_digest": "6ac3a850c53e3379dd847a6954b7c10317b0e655863a0c1fa67ac996102b4a9d", "started_at": 1792233217.9869337}, "kind": "tool", "observed_at": 1792233217.9911575, "path": "tool:git_diff", "phase": "minimal_call", "prev_digest": "b8a6d2fdafc5e73d9a7ede9bd9ad93a09718d7c5aecec56f3b59828ff74093f4", "receipt_id": "LP-1792233217-b056453e", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "1399ec2a6747f3bafa601cf469768b969520548b21c2104d379854cf570ed075", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233217.9927576, "error_class": null, "injected_fault": "diff a denied (blocked) repo path", "input_digest": "56cb25dd83425ae7f3d26a189d872c5fbde6dae02b7a982a013494f55e135edf", "output_digest": "48648c794f0ada1a506476a80fd26a95e9ed255acab9015aa01963b483fae14f", "started_at": 1792233217.9911582}, "kind": "tool", "observed_at": 1792233217.9927845, "path": "tool:git_diff", "phase": "failure_injection", "prev_digest": "4e3eed0978e2137df3ac4b4145d80dc8fe36872dbc7608de219977f684586323", "receipt_id": "LP-1792233217-fe7a0fc9", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "6ff4c8d31c5acdb1ee3abf1e74e9f14d308f2b493d6ce25896109a5df4447b8b", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233217.9959311, "error_class": null, "input_digest": "6133a0f98e6ba29061d08e6c51faae4dbf25538ccb39d04b85a8dee63616f948", "output_digest": "3e4609baeb5a414e794e4616e9bfc60632540142450492360ace994cce0b0777", "started_at": 1792233217.9927852}, "kind": "tool", "observed_at": 1792233217.9959495, "path": "tool:git_diff", "phase": "recovery", "prev_digest": "1399ec2a6747f3bafa601cf469768b969520548b21c2104d379854cf570ed075", "receipt_id": "LP-1792233217-b2af2209", "recovery_of": "LP-1792233217-fe7a0fc9", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "70fdd79ac35608e5c15786e35e57f54b16991f8969413bef7bd20ad1840c8ce9", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.6440454, "error_class": null, "input_digest": "dd109683f9eb4df87b83f1537860edf044f18fe29fb5bf9de5b570e4d42351d1", "output_digest": "600f50fcd096a6e7f256b1cb1e984795b95d9a2bca2271c14b1e555df34b0535", "started_at": 1792234271.6440432}, "kind": "tool", "observed_at": 1792234271.6440785, "path": "tool:git_diff", "phase": "auth_check", "prev_digest": "6ff4c8d31c5acdb1ee3abf1e74e9f14d308f2b493d6ce25896109a5df4447b8b", "receipt_id": "LP-1792234271-df5ab7a3", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "a82ddab09ba040046b1c766ee624456ce04eb9c483c498567b9f6262bdf05445", "evidence": {"detail": "git diff over the sandbox repo", "ended_at": 1792234271.6542373, "error_class": null, "input_digest": "5dc9c8c0e0c1ef6aefe7169679869a6840c7df389307272714b882247d263083", "output_digest": "6ac3a850c53e3379dd847a6954b7c10317b0e655863a0c1fa67ac996102b4a9d", "started_at": 1792234271.6440797}, "kind": "tool", "observed_at": 1792234271.6542816, "path": "tool:git_diff", "phase": "minimal_call", "prev_digest": "70fdd79ac35608e5c15786e35e57f54b16991f8969413bef7bd20ad1840c8ce9", "receipt_id": "LP-1792234271-932fb030", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "289c44ecd57249e81f024825bfc778c46b26b8e4a7073547222773a2cee293f1", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234271.6567209, "error_class": null, "injected_fault": "diff a denied (blocked) repo path", "input_digest": "56cb25dd83425ae7f3d26a189d872c5fbde6dae02b7a982a013494f55e135edf", "output_digest": "48648c794f0ada1a506476a80fd26a95e9ed255acab9015aa01963b483fae14f", "started_at": 1792234271.6542833}, "kind": "tool", "observed_at": 1792234271.6567662, "path": "tool:git_diff", "phase": "failure_injection", "prev_digest": "a82ddab09ba040046b1c766ee624456ce04eb9c483c498567b9f6262bdf05445", "receipt_id": "LP-1792234271-e5bd14bc", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "ca6edf5380c18685a683509c5919f4c89391a5c058ac32601c5d3d91a20fbaec", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.6615918, "error_class": null, "input_digest": "6133a0f98e6ba29061d08e6c51faae4dbf25538ccb39d04b85a8dee63616f948", "output_digest": "3e4609baeb5a414e794e4616e9bfc60632540142450492360ace994cce0b0777", "started_at": 1792234271.656769}, "kind": "tool", "observed_at": 1792234271.661634, "path": "tool:git_diff", "phase": "recovery", "prev_digest": "289c44ecd57249e81f024825bfc778c46b26b8e4a7073547222773a2cee293f1", "receipt_id": "LP-1792234271-7aab4e64", "recovery_of": "LP-1792234271-e5bd14bc", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "d37a399bd2e98a508d665ae770cd0fd13b392be7377ffd8a83a2cfc948df4dcf", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233217.9971395, "error_class": null, "input_digest": "649063c0ce4caeb01410f1d801f5473ca5a915b93b584979ed58df4c7d174e56", "output_digest": "474dbdd4b646a9d07ca2953c3344cfc3e70a4eaf5424262e68a8cf5bff6a2c3d", "started_at": 1792233217.997138}, "kind": "tool", "observed_at": 1792233217.9971535, "path": "tool:git_log", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-c706c568", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "dbc6f52f31665718ef4535042dce6d2117a13c928387e608b7dfb3b8c87604d2", "evidence": {"detail": "git log over the sandbox repo", "ended_at": 1792233218.026486, "error_class": null, "input_digest": "3b421cec4cca1fb5c1c61278a90d780af62162d18945951c736240001ecd785c", "output_digest": "0909012aa545ff66ddfd9e7f1a0effe86d275452a13dec63a99590b908bffdd4", "started_at": 1792233217.997154}, "kind": "tool", "observed_at": 1792233218.0265276, "path": "tool:git_log", "phase": "minimal_call", "prev_digest": "d37a399bd2e98a508d665ae770cd0fd13b392be7377ffd8a83a2cfc948df4dcf", "receipt_id": "LP-1792233218-a9755c01", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "7314b1c165f9147d420611dd332bb5acb2d7f35515033e212a6638557f92c060", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233218.0281873, "error_class": null, "injected_fault": "log a denied (blocked) repo path", "input_digest": "6975cd174ebb61d6ef0f859f9c4dc4e282b3d2190531ac3195577f10652bdaf1", "output_digest": "1e710f2b8c9eb63962f2bc36711fbc0f55f993f6f6e3f0450e95fddb0c982a02", "started_at": 1792233218.0265288}, "kind": "tool", "observed_at": 1792233218.028207, "path": "tool:git_log", "phase": "failure_injection", "prev_digest": "dbc6f52f31665718ef4535042dce6d2117a13c928387e608b7dfb3b8c87604d2", "receipt_id": "LP-1792233218-21a25935", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "8c5bdf961689cdf93e47cab74adf6189a70250d3906db4058725b7545efd3d86", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233218.0514162, "error_class": null, "input_digest": "40c3054fe56e3e132c4996bcce0ddaa321ff089a2a3cea1a86fbfd313bb3dcbd", "output_digest": "1737bb3c248263253e525f3891c664753ee30c1038127d2d9e5e806b415ad8a5", "started_at": 1792233218.0282078}, "kind": "tool", "observed_at": 1792233218.0514512, "path": "tool:git_log", "phase": "recovery", "prev_digest": "7314b1c165f9147d420611dd332bb5acb2d7f35515033e212a6638557f92c060", "receipt_id": "LP-1792233218-876244c2", "recovery_of": "LP-1792233218-21a25935", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "2602a1f6fcea47d4c440e6d577a7264c45ad834f7e9119ae8a908c96796b7824", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.6629384, "error_class": null, "input_digest": "649063c0ce4caeb01410f1d801f5473ca5a915b93b584979ed58df4c7d174e56", "output_digest": "474dbdd4b646a9d07ca2953c3344cfc3e70a4eaf5424262e68a8cf5bff6a2c3d", "started_at": 1792234271.662937}, "kind": "tool", "observed_at": 1792234271.6629672, "path": "tool:git_log", "phase": "auth_check", "prev_digest": "8c5bdf961689cdf93e47cab74adf6189a70250d3906db4058725b7545efd3d86", "receipt_id": "LP-1792234271-37ca6466", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "5ed130156aa2ab7ca5374d4f96839bfc74606f52c39c34561109ab2f48fb86c6", "evidence": {"detail": "git log over the sandbox repo", "ended_at": 1792234271.6929662, "error_class": null, "input_digest": "3b421cec4cca1fb5c1c61278a90d780af62162d18945951c736240001ecd785c", "output_digest": "0909012aa545ff66ddfd9e7f1a0effe86d275452a13dec63a99590b908bffdd4", "started_at": 1792234271.6629682}, "kind": "tool", "observed_at": 1792234271.6930032, "path": "tool:git_log", "phase": "minimal_call", "prev_digest": "2602a1f6fcea47d4c440e6d577a7264c45ad834f7e9119ae8a908c96796b7824", "receipt_id": "LP-1792234271-d90ab32f", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "5f3052d9436dd547a5b25d8ea05cbce96601d1d8746e90a54c26812f8f964e02", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234271.6946573, "error_class": null, "injected_fault": "log a denied (blocked) repo path", "input_digest": "6975cd174ebb61d6ef0f859f9c4dc4e282b3d2190531ac3195577f10652bdaf1", "output_digest": "1e710f2b8c9eb63962f2bc36711fbc0f55f993f6f6e3f0450e95fddb0c982a02", "started_at": 1792234271.6930046}, "kind": "tool", "observed_at": 1792234271.6946838, "path": "tool:git_log", "phase": "failure_injection", "prev_digest": "5ed130156aa2ab7ca5374d4f96839bfc74606f52c39c34561109ab2f48fb86c6", "receipt_id": "LP-1792234271-4e1f029c", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "c314d226a8f46b7e3ac7b071c274d7c7e6c3ca590d7806b5f14eca3cdc2c6cd3", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.714346, "error_class": null, "input_digest": "40c3054fe56e3e132c4996bcce0ddaa321ff089a2a3cea1a86fbfd313bb3dcbd", "output_digest": "1737bb3c248263253e525f3891c664753ee30c1038127d2d9e5e806b415ad8a5", "started_at": 1792234271.6946852}, "kind": "tool", "observed_at": 1792234271.7143848, "path": "tool:git_log", "phase": "recovery", "prev_digest": "5f3052d9436dd547a5b25d8ea05cbce96601d1d8746e90a54c26812f8f964e02", "receipt_id": "LP-1792234271-b05f6066", "recovery_of": "LP-1792234271-4e1f029c", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "fbd396f3b474173ddee9fa101702586dcdc0d40c54a13859febefb50cedd8278", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233217.9339151, "error_class": null, "input_digest": "4f843121fb703d9b35dfc4d14dfa2853921cda7260dbcccf319fe2a34a530748", "output_digest": "4eb22f7ea0eddae4e334f9327b3bd1b401a7e6bca7e6514cb36519196079f333", "started_at": 1792233217.933913}, "kind": "tool", "observed_at": 1792233217.933931, "path": "tool:glob_files", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-d0ea8c91", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "5a44ebf5c7aed90e1c87242450efe28bab9e915a0b56be7868c30cff5b1c3f03", "evidence": {"detail": "minimal call failed: Glob failed: Non-relative patterns are unsupported", "ended_at": 1792233217.934225, "error_class": "TOOL_ERROR", "input_digest": "6d29ab4801e6dcf7ac519cb0317756465a052e717cca11a43c6475eb2756ab13", "output_digest": "18ca7c617ecabdb08c57e16078a646adc71ecd3c7dbb989432958beaf46f5bf4", "started_at": 1792233217.9339318}, "kind": "tool", "observed_at": 1792233217.9342358, "path": "tool:glob_files", "phase": "minimal_call", "prev_digest": "fbd396f3b474173ddee9fa101702586dcdc0d40c54a13859febefb50cedd8278", "receipt_id": "LP-1792233217-e24f8da8", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_fail"}
|
||||
{"digest": "cf3da74f77b687063e6a522ba59369a6a0f0d88b982187cf2f3e6ebdc7f4936d", "evidence": {"detail": "injected fault observed failing and classified: Glob failed: Non-relative patterns are unsupported", "ended_at": 1792233217.9344637, "error_class": "TOOL_ERROR", "injected_fault": "glob a denied (blocked) directory", "input_digest": "18ecd842ba95f399ce55dad78bc94a4b2e81f2aab234b74e78a065dbd09fc8a9", "output_digest": "fc71fec9e8df2268216fa60097e52336b6a820b3912777259e4c30e2a363e3af", "started_at": 1792233217.9342365}, "kind": "tool", "observed_at": 1792233217.9344726, "path": "tool:glob_files", "phase": "failure_injection", "prev_digest": "5a44ebf5c7aed90e1c87242450efe28bab9e915a0b56be7868c30cff5b1c3f03", "receipt_id": "LP-1792233217-2e142c3a", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "e19f188c25fa4ecf7b5eb61060dbe9bd6da5e382de88b98cbb45321e0b3fe458", "evidence": {"detail": "recovery call failed: Glob failed: Non-relative patterns are unsupported", "ended_at": 1792233217.9346797, "error_class": "TOOL_ERROR", "input_digest": "c888737009165e2beefd3b8a85d12410caa374e096bc767e81c8f72504262fe5", "output_digest": "548f772b66899f037391d4b5a9bc3f78eb7db2b3ca8636b1e2803f31cdcc9fb4", "started_at": 1792233217.9344733}, "kind": "tool", "observed_at": 1792233217.9346871, "path": "tool:glob_files", "phase": "recovery", "prev_digest": "cf3da74f77b687063e6a522ba59369a6a0f0d88b982187cf2f3e6ebdc7f4936d", "receipt_id": "LP-1792233217-66d6763a", "recovery_of": "LP-1792233217-2e142c3a", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_fail"}
|
||||
{"digest": "c6e7d44d8db50d0c011d05262ace8a57f1f3249e42fd6557d8f8ed20799d54d4", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.6009638, "error_class": null, "input_digest": "4f843121fb703d9b35dfc4d14dfa2853921cda7260dbcccf319fe2a34a530748", "output_digest": "4eb22f7ea0eddae4e334f9327b3bd1b401a7e6bca7e6514cb36519196079f333", "started_at": 1792234271.6009629}, "kind": "tool", "observed_at": 1792234271.6009784, "path": "tool:glob_files", "phase": "auth_check", "prev_digest": "e19f188c25fa4ecf7b5eb61060dbe9bd6da5e382de88b98cbb45321e0b3fe458", "receipt_id": "LP-1792234271-28ce602a", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "cd592f9f5311b2b7e5ff65312869c8f18ea71869acde9bfcc5188a2e67ff5692", "evidence": {"detail": "minimal call failed: Glob failed: Non-relative patterns are unsupported", "ended_at": 1792234271.6012032, "error_class": "TOOL_ERROR", "input_digest": "6d29ab4801e6dcf7ac519cb0317756465a052e717cca11a43c6475eb2756ab13", "output_digest": "18ca7c617ecabdb08c57e16078a646adc71ecd3c7dbb989432958beaf46f5bf4", "started_at": 1792234271.600979}, "kind": "tool", "observed_at": 1792234271.6012113, "path": "tool:glob_files", "phase": "minimal_call", "prev_digest": "c6e7d44d8db50d0c011d05262ace8a57f1f3249e42fd6557d8f8ed20799d54d4", "receipt_id": "LP-1792234271-166ef69b", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_fail"}
|
||||
{"digest": "e8f61c426974e4fe988c9da8d186b85f2beab79d92b33b184df5ddd3a5a071f3", "evidence": {"detail": "injected fault observed failing and classified: Glob failed: Non-relative patterns are unsupported", "ended_at": 1792234271.6014142, "error_class": "TOOL_ERROR", "injected_fault": "glob a denied (blocked) directory", "input_digest": "18ecd842ba95f399ce55dad78bc94a4b2e81f2aab234b74e78a065dbd09fc8a9", "output_digest": "fc71fec9e8df2268216fa60097e52336b6a820b3912777259e4c30e2a363e3af", "started_at": 1792234271.6012118}, "kind": "tool", "observed_at": 1792234271.6014228, "path": "tool:glob_files", "phase": "failure_injection", "prev_digest": "cd592f9f5311b2b7e5ff65312869c8f18ea71869acde9bfcc5188a2e67ff5692", "receipt_id": "LP-1792234271-4a6974f3", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "b3ce4f9583b5fabaf26f4a125d5b71614687f2d4919249829ac39c749d3feda1", "evidence": {"detail": "recovery call failed: Glob failed: Non-relative patterns are unsupported", "ended_at": 1792234271.6016119, "error_class": "TOOL_ERROR", "input_digest": "c888737009165e2beefd3b8a85d12410caa374e096bc767e81c8f72504262fe5", "output_digest": "548f772b66899f037391d4b5a9bc3f78eb7db2b3ca8636b1e2803f31cdcc9fb4", "started_at": 1792234271.6014235}, "kind": "tool", "observed_at": 1792234271.6016188, "path": "tool:glob_files", "phase": "recovery", "prev_digest": "e8f61c426974e4fe988c9da8d186b85f2beab79d92b33b184df5ddd3a5a071f3", "receipt_id": "LP-1792234271-aace9b93", "recovery_of": "LP-1792234271-4a6974f3", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_fail"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "ca6b2d197b0f4044a4208800b15e8990a18af4aa4e3c7e165c884f725b439706", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233217.935326, "error_class": null, "input_digest": "cdd89c699a648d21d577bcfeb2b87a987f64944e82350846d139f4f1eec4d418", "output_digest": "d4766f9548002e4602da5da4f827f61294cc8b08a234cc7880f8ae42147ab278", "started_at": 1792233217.9353254}, "kind": "tool", "observed_at": 1792233217.9353373, "path": "tool:grep_files", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-17701868", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "ce86db21dd675cfb1f8e5f9f4af6bc822f65d2064ff62f12629216de55e7e112", "evidence": {"detail": "grep a sandbox file", "ended_at": 1792233217.9409065, "error_class": null, "input_digest": "38ce20c1d4bc02969671edfe6f3464b78a54a6ba9b089ddad424c5583748a6e8", "output_digest": "1955a17bb45ecc94b754b450cee352c2fe72595a1b05b703f93fd2d8acde73ba", "started_at": 1792233217.9353378}, "kind": "tool", "observed_at": 1792233217.9409366, "path": "tool:grep_files", "phase": "minimal_call", "prev_digest": "ca6b2d197b0f4044a4208800b15e8990a18af4aa4e3c7e165c884f725b439706", "receipt_id": "LP-1792233217-a7192254", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "3e3047bd15e478e012e1aedd3dbfd41e33ebf414f0a9a3b571f88d4710a07a96", "evidence": {"detail": "injected fault observed failing and classified: Access denied: /etc/__live_proof_probe__", "ended_at": 1792233217.9411662, "error_class": "PERMISSION", "injected_fault": "grep a denied (blocked) directory", "input_digest": "c65c78d2d1158e068826d995cf869a01093c6595829ef41bd2b4802ade1c0ef2", "output_digest": "e2c0b1072fac866e0ec2ea1177d20dee5a2a89439b2307e06cae94d31ea6c59b", "started_at": 1792233217.9409375}, "kind": "tool", "observed_at": 1792233217.941184, "path": "tool:grep_files", "phase": "failure_injection", "prev_digest": "ce86db21dd675cfb1f8e5f9f4af6bc822f65d2064ff62f12629216de55e7e112", "receipt_id": "LP-1792233217-f7b02ecf", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "54090879f2f24ba8696656076e14b9d392673b13205a318d696404e416581769", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233217.9442227, "error_class": null, "input_digest": "364577c4d7e3583ff668e6ab3d5c7c327ccdad96e32b554b6701e3ff16665bb4", "output_digest": "492c4575f234f3b56c2e0b3271eb87dfccf7fae373e64af1c20753f9bd40ce11", "started_at": 1792233217.9411852}, "kind": "tool", "observed_at": 1792233217.9442453, "path": "tool:grep_files", "phase": "recovery", "prev_digest": "3e3047bd15e478e012e1aedd3dbfd41e33ebf414f0a9a3b571f88d4710a07a96", "receipt_id": "LP-1792233217-120e6b01", "recovery_of": "LP-1792233217-f7b02ecf", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "725a232bd125611cfcbab7e6c0702359bedfeef5cf4215cbd4b9cab5befcbb7a", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.6025229, "error_class": null, "input_digest": "cdd89c699a648d21d577bcfeb2b87a987f64944e82350846d139f4f1eec4d418", "output_digest": "d4766f9548002e4602da5da4f827f61294cc8b08a234cc7880f8ae42147ab278", "started_at": 1792234271.6025221}, "kind": "tool", "observed_at": 1792234271.6025395, "path": "tool:grep_files", "phase": "auth_check", "prev_digest": "54090879f2f24ba8696656076e14b9d392673b13205a318d696404e416581769", "receipt_id": "LP-1792234271-37ebbcdd", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "d4579a36a0043c200661f235d2f5562ce9c1142d091d4fef7e708948f3e39e75", "evidence": {"detail": "grep a sandbox file", "ended_at": 1792234271.6092792, "error_class": null, "input_digest": "38ce20c1d4bc02969671edfe6f3464b78a54a6ba9b089ddad424c5583748a6e8", "output_digest": "1955a17bb45ecc94b754b450cee352c2fe72595a1b05b703f93fd2d8acde73ba", "started_at": 1792234271.6025403}, "kind": "tool", "observed_at": 1792234271.6093132, "path": "tool:grep_files", "phase": "minimal_call", "prev_digest": "725a232bd125611cfcbab7e6c0702359bedfeef5cf4215cbd4b9cab5befcbb7a", "receipt_id": "LP-1792234271-aadffe6a", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "4e7426470340a5eaacc4f26bdec0799758778fb2c69e7830aff9c423220565fa", "evidence": {"detail": "injected fault observed failing and classified: Access denied: /etc/__live_proof_probe__", "ended_at": 1792234271.6094868, "error_class": "PERMISSION", "injected_fault": "grep a denied (blocked) directory", "input_digest": "c65c78d2d1158e068826d995cf869a01093c6595829ef41bd2b4802ade1c0ef2", "output_digest": "e2c0b1072fac866e0ec2ea1177d20dee5a2a89439b2307e06cae94d31ea6c59b", "started_at": 1792234271.609315}, "kind": "tool", "observed_at": 1792234271.6095002, "path": "tool:grep_files", "phase": "failure_injection", "prev_digest": "d4579a36a0043c200661f235d2f5562ce9c1142d091d4fef7e708948f3e39e75", "receipt_id": "LP-1792234271-b3ea1b58", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "e043fb2c4fe25f6f1be293c3af0cc419fc09ff0823a93fe3b4bb0c2b3ea29a14", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.6120403, "error_class": null, "input_digest": "364577c4d7e3583ff668e6ab3d5c7c327ccdad96e32b554b6701e3ff16665bb4", "output_digest": "492c4575f234f3b56c2e0b3271eb87dfccf7fae373e64af1c20753f9bd40ce11", "started_at": 1792234271.6095011}, "kind": "tool", "observed_at": 1792234271.6121018, "path": "tool:grep_files", "phase": "recovery", "prev_digest": "4e7426470340a5eaacc4f26bdec0799758778fb2c69e7830aff9c423220565fa", "receipt_id": "LP-1792234271-14b06674", "recovery_of": "LP-1792234271-b3ea1b58", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "2afa23cbd5394f3b1d8a5da9bef664f881009a39743f3b79d69e79aafa39194b", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233217.9476285, "error_class": null, "input_digest": "2a7a6a240931ee72ca2019216925dacd7aba19b13999a3fb7ceb236d7a0288a7", "output_digest": "9ac538a32cfeadb31fccf607fac77e1f8a64b2053a6083c94cabc4a7c835c8f0", "started_at": 1792233217.9476275}, "kind": "tool", "observed_at": 1792233217.9476404, "path": "tool:lint_file", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-4dd5966b", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "4802525f3c9276621b2a362eb5264cb28c5d48b966dd085534e992f4d4dc5730", "evidence": {"detail": "minimal call succeeded", "ended_at": 1792233217.9476998, "error_class": null, "input_digest": "cd2bb73a5642aadf8695d8127b90b452514ce85166ab6b49b18d904eedc678cb", "output_digest": "be244c27ed1fc6de75e4aff5cc21ec8e59617b9bfabc778bf55d1d5834c94a22", "started_at": 1792233217.947641}, "kind": "tool", "observed_at": 1792233217.9477074, "path": "tool:lint_file", "phase": "minimal_call", "prev_digest": "2afa23cbd5394f3b1d8a5da9bef664f881009a39743f3b79d69e79aafa39194b", "receipt_id": "LP-1792233217-925281b0", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "8be94b51c2df782f24645930b8dfbb35585560a2da00bdd175900456a7842581", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233217.947737, "error_class": null, "injected_fault": "nonexistent path", "input_digest": "25ba8e3e86e54b7b999f22e50ae9aff41264c8bacefdf7c76132c9e3f5961233", "output_digest": "12ce9c293d4b3616c57584df85827ffacb72e660f76329c9ca01b4fa1367ac13", "started_at": 1792233217.947708}, "kind": "tool", "observed_at": 1792233217.9477437, "path": "tool:lint_file", "phase": "failure_injection", "prev_digest": "4802525f3c9276621b2a362eb5264cb28c5d48b966dd085534e992f4d4dc5730", "receipt_id": "LP-1792233217-9a26297d", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "08c9558952fc2be0ab5cd37939d31b3b0d90370ed2d184c0f0891715d29925a5", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233217.947783, "error_class": null, "input_digest": "bbb6def7000c7b95ae376d280dee9b494d20d8454507e06fe1ee032a0a4b043c", "output_digest": "0759c6c15d35bd61e08ac0a7d39b1b6ec3a73f97833c9df5a3719b77d99e2724", "started_at": 1792233217.9477446}, "kind": "tool", "observed_at": 1792233217.9477885, "path": "tool:lint_file", "phase": "recovery", "prev_digest": "8be94b51c2df782f24645930b8dfbb35585560a2da00bdd175900456a7842581", "receipt_id": "LP-1792233217-c32a6139", "recovery_of": "LP-1792233217-9a26297d", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "dc06417da9ea0c3d55014d9989deaf698a6bec8a4907db23ee7fae781137d38f", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.6153092, "error_class": null, "input_digest": "2a7a6a240931ee72ca2019216925dacd7aba19b13999a3fb7ceb236d7a0288a7", "output_digest": "9ac538a32cfeadb31fccf607fac77e1f8a64b2053a6083c94cabc4a7c835c8f0", "started_at": 1792234271.6153085}, "kind": "tool", "observed_at": 1792234271.6153266, "path": "tool:lint_file", "phase": "auth_check", "prev_digest": "08c9558952fc2be0ab5cd37939d31b3b0d90370ed2d184c0f0891715d29925a5", "receipt_id": "LP-1792234271-e0ababcd", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "6ffed698068fa5738f6c1139fc8f6cd34853783d0b540791a40d1af4ae34c128", "evidence": {"detail": "minimal call succeeded", "ended_at": 1792234271.6154006, "error_class": null, "input_digest": "cd2bb73a5642aadf8695d8127b90b452514ce85166ab6b49b18d904eedc678cb", "output_digest": "be244c27ed1fc6de75e4aff5cc21ec8e59617b9bfabc778bf55d1d5834c94a22", "started_at": 1792234271.6153276}, "kind": "tool", "observed_at": 1792234271.615408, "path": "tool:lint_file", "phase": "minimal_call", "prev_digest": "dc06417da9ea0c3d55014d9989deaf698a6bec8a4907db23ee7fae781137d38f", "receipt_id": "LP-1792234271-e366c8c1", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "4a1669efd3d9eaf14c8b2f6d4646348e9324f86b620eb3c319b2c72fecba0680", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234271.6154346, "error_class": null, "injected_fault": "nonexistent path", "input_digest": "25ba8e3e86e54b7b999f22e50ae9aff41264c8bacefdf7c76132c9e3f5961233", "output_digest": "12ce9c293d4b3616c57584df85827ffacb72e660f76329c9ca01b4fa1367ac13", "started_at": 1792234271.6154084}, "kind": "tool", "observed_at": 1792234271.6154406, "path": "tool:lint_file", "phase": "failure_injection", "prev_digest": "6ffed698068fa5738f6c1139fc8f6cd34853783d0b540791a40d1af4ae34c128", "receipt_id": "LP-1792234271-af2ad360", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "eaf5b688c55ab6d59f12cc00815172119c26cbd5ea6718908c94c36a6361aa5d", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.6154745, "error_class": null, "input_digest": "bbb6def7000c7b95ae376d280dee9b494d20d8454507e06fe1ee032a0a4b043c", "output_digest": "0759c6c15d35bd61e08ac0a7d39b1b6ec3a73f97833c9df5a3719b77d99e2724", "started_at": 1792234271.6154413}, "kind": "tool", "observed_at": 1792234271.6154797, "path": "tool:lint_file", "phase": "recovery", "prev_digest": "4a1669efd3d9eaf14c8b2f6d4646348e9324f86b620eb3c319b2c72fecba0680", "receipt_id": "LP-1792234271-81f9c76e", "recovery_of": "LP-1792234271-af2ad360", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "d9497847d182ef807a08f07cb77f9d42a944078a6508ba17c31bf5b5d02325fd", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233217.945382, "error_class": null, "input_digest": "6b43b15af0cf7752e9d7b09cdfda13394b3924e0de444c974d41f231c5f87949", "output_digest": "5d0063be61f65f0be8a3c826ceac4e098df8ec4073548014daf44f35f51db240", "started_at": 1792233217.9453716}, "kind": "tool", "observed_at": 1792233217.9453971, "path": "tool:list_directory", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-526a0323", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "814476b9aa9484cb454b0462759e3562d39b249d3dfa3e94f72ec1f83c40a637", "evidence": {"detail": "list the sandbox dir", "ended_at": 1792233217.9459317, "error_class": null, "input_digest": "069c96bebeda6a94a8450734520baed25f361ac03be840a618c92cdccbf98fb1", "output_digest": "37723842890f20e43139e729d4cb590dc9ab669def037f0b6a47ff5be6cf91d7", "started_at": 1792233217.945398}, "kind": "tool", "observed_at": 1792233217.9459438, "path": "tool:list_directory", "phase": "minimal_call", "prev_digest": "d9497847d182ef807a08f07cb77f9d42a944078a6508ba17c31bf5b5d02325fd", "receipt_id": "LP-1792233217-f2cd7745", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "287c86755f51c12031f996764833c910751be53d4aaf5c0891431ed5b44bef59", "evidence": {"detail": "injected fault observed failing and classified: Not a directory: /mnt/workdrive/rune/src/rune/../../store/live_proof/sandbox/__nonexistent_live_probe__", "ended_at": 1792233217.946346, "error_class": "TOOL_ERROR", "injected_fault": "list a nonexistent directory", "input_digest": "d2acd6694e44cd5249e24056e61bff8f34e21df7da36ba9e5db167a0ef30a738", "output_digest": "9a6c6b202fbe01d68c18c6337da46b208027b22685a18f0cc19bba9955346459", "started_at": 1792233217.9459445}, "kind": "tool", "observed_at": 1792233217.9463575, "path": "tool:list_directory", "phase": "failure_injection", "prev_digest": "814476b9aa9484cb454b0462759e3562d39b249d3dfa3e94f72ec1f83c40a637", "receipt_id": "LP-1792233217-19ef3a90", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "42ca5d5d7c1c3b39a43a2d09446974c20b9ab270fbacb2afdf89baed672c8979", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233217.946841, "error_class": null, "input_digest": "510530798b535e832107ea806313e310d006b2cf4cd15a6a1dd8b9a867f429b5", "output_digest": "fda58a44dfacac350af8b90e4aba3762ab2762b218e12d6c674fa918841b89f9", "started_at": 1792233217.9463582}, "kind": "tool", "observed_at": 1792233217.9468517, "path": "tool:list_directory", "phase": "recovery", "prev_digest": "287c86755f51c12031f996764833c910751be53d4aaf5c0891431ed5b44bef59", "receipt_id": "LP-1792233217-ad76728f", "recovery_of": "LP-1792233217-19ef3a90", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "178061e34dbc7a820ba66b79872cc326388c9a9fc2ac117cf212705111a6dde1", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.6131546, "error_class": null, "input_digest": "6b43b15af0cf7752e9d7b09cdfda13394b3924e0de444c974d41f231c5f87949", "output_digest": "5d0063be61f65f0be8a3c826ceac4e098df8ec4073548014daf44f35f51db240", "started_at": 1792234271.6131535}, "kind": "tool", "observed_at": 1792234271.6131732, "path": "tool:list_directory", "phase": "auth_check", "prev_digest": "42ca5d5d7c1c3b39a43a2d09446974c20b9ab270fbacb2afdf89baed672c8979", "receipt_id": "LP-1792234271-8361335e", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "acf1b8110dd09b77a0d9a26acf42aaa6f5bc741f91bb74c727d5d8aec3591571", "evidence": {"detail": "list the sandbox dir", "ended_at": 1792234271.6137004, "error_class": null, "input_digest": "069c96bebeda6a94a8450734520baed25f361ac03be840a618c92cdccbf98fb1", "output_digest": "37723842890f20e43139e729d4cb590dc9ab669def037f0b6a47ff5be6cf91d7", "started_at": 1792234271.613174}, "kind": "tool", "observed_at": 1792234271.613712, "path": "tool:list_directory", "phase": "minimal_call", "prev_digest": "178061e34dbc7a820ba66b79872cc326388c9a9fc2ac117cf212705111a6dde1", "receipt_id": "LP-1792234271-88b46077", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "54eb3e43620219f316cf9fb4c0c90215bfb1a094822fcc65bc722c8cab1d7a03", "evidence": {"detail": "injected fault observed failing and classified: Not a directory: /mnt/workdrive/rune/src/rune/../../store/live_proof/sandbox/__nonexistent_live_probe__", "ended_at": 1792234271.613998, "error_class": "TOOL_ERROR", "injected_fault": "list a nonexistent directory", "input_digest": "d2acd6694e44cd5249e24056e61bff8f34e21df7da36ba9e5db167a0ef30a738", "output_digest": "9a6c6b202fbe01d68c18c6337da46b208027b22685a18f0cc19bba9955346459", "started_at": 1792234271.6137133}, "kind": "tool", "observed_at": 1792234271.6140075, "path": "tool:list_directory", "phase": "failure_injection", "prev_digest": "acf1b8110dd09b77a0d9a26acf42aaa6f5bc741f91bb74c727d5d8aec3591571", "receipt_id": "LP-1792234271-1eda394f", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "6b0642dd345c7c6ca3b0a50537e2cc384ea935caed9a2894539bd52bcf02cc66", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.614471, "error_class": null, "input_digest": "510530798b535e832107ea806313e310d006b2cf4cd15a6a1dd8b9a867f429b5", "output_digest": "fda58a44dfacac350af8b90e4aba3762ab2762b218e12d6c674fa918841b89f9", "started_at": 1792234271.6140084}, "kind": "tool", "observed_at": 1792234271.6144807, "path": "tool:list_directory", "phase": "recovery", "prev_digest": "54eb3e43620219f316cf9fb4c0c90215bfb1a094822fcc65bc722c8cab1d7a03", "receipt_id": "LP-1792234271-f869ded9", "recovery_of": "LP-1792234271-1eda394f", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "e1546212062113737150a2d8e8852511045035c7bb990765d00b11cbb29ba661", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233218.1561284, "error_class": null, "input_digest": "7184223d0e85584065aa1e316ae04503b7227a78a56625b0de7ceacb33513640", "output_digest": "8e81da77c496577d7ed96e801feae19296d1c383fa83001c1bbef0215b174bfa", "started_at": 1792233218.1561275}, "kind": "tool", "observed_at": 1792233218.1561394, "path": "tool:query_memory", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233218-5ec1cdcd", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "3058f116ebf9f8d0d03af4d0307463879b8d551dd61906c1d54201a294d948ed", "evidence": {"detail": "query memory with a one-word probe", "ended_at": 1792233218.1939867, "error_class": null, "input_digest": "dba8f2f072b60651103cddc3c6b9b13ce984d482fc5f91f2b6016878ccd76ca2", "output_digest": "433f4446dd6c9b8f3acba08497aa2ae5ed77e13c75a7e27b075f011f88cd49fe", "started_at": 1792233218.1561399}, "kind": "tool", "observed_at": 1792233218.1940286, "path": "tool:query_memory", "phase": "minimal_call", "prev_digest": "e1546212062113737150a2d8e8852511045035c7bb990765d00b11cbb29ba661", "receipt_id": "LP-1792233218-2cf0bbce", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "7bc2aea79d09ccac5ba9164352a83da6d2e3552a6216333bbaec291af0cd3a87", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233218.9414935, "error_class": null, "injected_fault": "query with a malformed memory id", "input_digest": "0abab62acdd7e5a1fa6ed4ee65f14d4dab244cd03b4b785e4bc37beea2b4e1f8", "output_digest": "1ab58f149ee2d6fc8db4383bf560006c78a079e87f6f4cbd1b9b7bac23349eba", "started_at": 1792233218.1940296}, "kind": "tool", "observed_at": 1792233218.9415393, "path": "tool:query_memory", "phase": "failure_injection", "prev_digest": "3058f116ebf9f8d0d03af4d0307463879b8d551dd61906c1d54201a294d948ed", "receipt_id": "LP-1792233218-39063f62", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "024de5cc2f0059119d414e1b3de95f4c5c3d06ed25fdc49471dbb455d2296ccd", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233218.9697337, "error_class": null, "input_digest": "f5bb307b8741b29f2a147f7687528e86b5c8b1c68b4ab90edcb89f8c93791378", "output_digest": "89cc3d6753f1bafe5a328c418b68a09234124112404eda5b7038f26585e5c918", "started_at": 1792233218.9415414}, "kind": "tool", "observed_at": 1792233218.96978, "path": "tool:query_memory", "phase": "recovery", "prev_digest": "7bc2aea79d09ccac5ba9164352a83da6d2e3552a6216333bbaec291af0cd3a87", "receipt_id": "LP-1792233218-8c887c60", "recovery_of": "LP-1792233218-39063f62", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "09074137a383f23c11060a8319275f3741008f656dd1ec7728577da4298aa8a2", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.9486344, "error_class": null, "input_digest": "7184223d0e85584065aa1e316ae04503b7227a78a56625b0de7ceacb33513640", "output_digest": "8e81da77c496577d7ed96e801feae19296d1c383fa83001c1bbef0215b174bfa", "started_at": 1792234271.9486327}, "kind": "tool", "observed_at": 1792234271.948659, "path": "tool:query_memory", "phase": "auth_check", "prev_digest": "024de5cc2f0059119d414e1b3de95f4c5c3d06ed25fdc49471dbb455d2296ccd", "receipt_id": "LP-1792234271-45f51270", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "f231135825e9b7d26511b6fa42fe4f87b9389cc7cfe88bb1cf3082530633b692", "evidence": {"detail": "query memory with a one-word probe", "ended_at": 1792234271.9836683, "error_class": null, "input_digest": "dba8f2f072b60651103cddc3c6b9b13ce984d482fc5f91f2b6016878ccd76ca2", "output_digest": "433f4446dd6c9b8f3acba08497aa2ae5ed77e13c75a7e27b075f011f88cd49fe", "started_at": 1792234271.94866}, "kind": "tool", "observed_at": 1792234271.9837193, "path": "tool:query_memory", "phase": "minimal_call", "prev_digest": "09074137a383f23c11060a8319275f3741008f656dd1ec7728577da4298aa8a2", "receipt_id": "LP-1792234271-aea4fa54", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "e88035d996ccc7f505c5e973081d87bc9029775a338758b57f4ec8bf0e55f470", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234272.8042405, "error_class": null, "injected_fault": "query with a malformed memory id", "input_digest": "0abab62acdd7e5a1fa6ed4ee65f14d4dab244cd03b4b785e4bc37beea2b4e1f8", "output_digest": "1ab58f149ee2d6fc8db4383bf560006c78a079e87f6f4cbd1b9b7bac23349eba", "started_at": 1792234271.983721}, "kind": "tool", "observed_at": 1792234272.804284, "path": "tool:query_memory", "phase": "failure_injection", "prev_digest": "f231135825e9b7d26511b6fa42fe4f87b9389cc7cfe88bb1cf3082530633b692", "receipt_id": "LP-1792234272-dbe5cc7e", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "a5186c7bb3d92bc00eef5defc97e0c15a532a417f2867383ad2079c05c302c7a", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234272.8316886, "error_class": null, "input_digest": "f5bb307b8741b29f2a147f7687528e86b5c8b1c68b4ab90edcb89f8c93791378", "output_digest": "89cc3d6753f1bafe5a328c418b68a09234124112404eda5b7038f26585e5c918", "started_at": 1792234272.8042862}, "kind": "tool", "observed_at": 1792234272.831737, "path": "tool:query_memory", "phase": "recovery", "prev_digest": "e88035d996ccc7f505c5e973081d87bc9029775a338758b57f4ec8bf0e55f470", "receipt_id": "LP-1792234272-4c6c08a4", "recovery_of": "LP-1792234272-dbe5cc7e", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "5d2a31a0fdc40a369767cd241ac85b65562a894ae19f58d473b815674a595186", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233217.9234314, "error_class": null, "input_digest": "3188fbe61785a2f896568b7efe73f3444a8db2041772fae33cc9be5b613b212d", "output_digest": "4696f13fd53c94d5147e2ecfd127d3b747a4f66d832c465e97eb1daa55b5ade2", "started_at": 1792233217.9234238}, "kind": "tool", "observed_at": 1792233217.9235158, "path": "tool:read_file", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-ddcfbfa3", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "043140ce10309c928e2d2454c4edf1bc2a26ce1effa7803e582a84c672590348", "evidence": {"detail": "read a one-line sandbox file", "ended_at": 1792233217.9241977, "error_class": null, "input_digest": "4db66f81ba9f66805811353291d68eb7e9b49ac52080ee4f2911da97e34ff822", "output_digest": "38a33175fcad1ad10820c264fdcd398795173a1d8ca9bce7cdf5d6ac42bc19fc", "started_at": 1792233217.923518}, "kind": "tool", "observed_at": 1792233217.9242291, "path": "tool:read_file", "phase": "minimal_call", "prev_digest": "5d2a31a0fdc40a369767cd241ac85b65562a894ae19f58d473b815674a595186", "receipt_id": "LP-1792233217-1e5aa0c2", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "fa601aac0481024ccc835c20be366c3d880edee5220c70e0c627ba4bb0bfe2b2", "evidence": {"detail": "injected fault observed failing and classified: Not found: /mnt/workdrive/rune/src/rune/../../store/live_proof/sandbox/__nonexistent_live_probe__", "ended_at": 1792233217.924617, "error_class": "ENOENT", "injected_fault": "read a nonexistent path", "input_digest": "f57b736271e86fa2ba87134bc3d1fe285bdc4e8c9ea3433b7b5d66aca532190b", "output_digest": "dac1b171dad67eefb479bcaae54c15be34484711044bafb645d4e6d735de94fd", "started_at": 1792233217.924231}, "kind": "tool", "observed_at": 1792233217.924639, "path": "tool:read_file", "phase": "failure_injection", "prev_digest": "043140ce10309c928e2d2454c4edf1bc2a26ce1effa7803e582a84c672590348", "receipt_id": "LP-1792233217-1122adad", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "fa3c1d0ad516651d8bf87326d0ac899b3dd5273ea0629d7347f2a785fc03b72b", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233217.9250536, "error_class": null, "input_digest": "a846ca5a3235abce5db143b6194fe7f4e3516ff944913e3baff2d3cb4afe0c0c", "output_digest": "226cee6011cf22d0539ef2898e20d0996473d0286e743a2261f69421241086e0", "started_at": 1792233217.9246402}, "kind": "tool", "observed_at": 1792233217.9250731, "path": "tool:read_file", "phase": "recovery", "prev_digest": "fa601aac0481024ccc835c20be366c3d880edee5220c70e0c627ba4bb0bfe2b2", "receipt_id": "LP-1792233217-0472d94c", "recovery_of": "LP-1792233217-1122adad", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "34dd25b559e0f7bf4d15d00890732fb82c21accad42fc1cfd7f70f8e547405ee", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.5884204, "error_class": null, "input_digest": "3188fbe61785a2f896568b7efe73f3444a8db2041772fae33cc9be5b613b212d", "output_digest": "4696f13fd53c94d5147e2ecfd127d3b747a4f66d832c465e97eb1daa55b5ade2", "started_at": 1792234271.5884168}, "kind": "tool", "observed_at": 1792234271.5884645, "path": "tool:read_file", "phase": "auth_check", "prev_digest": "fa3c1d0ad516651d8bf87326d0ac899b3dd5273ea0629d7347f2a785fc03b72b", "receipt_id": "LP-1792234271-d18c0c2e", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "ec7e32b8f1f24f37dd50358f36afc4008505cd7ae3e5e730807ca5e77f6ad114", "evidence": {"detail": "read a one-line sandbox file", "ended_at": 1792234271.5888975, "error_class": null, "input_digest": "4db66f81ba9f66805811353291d68eb7e9b49ac52080ee4f2911da97e34ff822", "output_digest": "38a33175fcad1ad10820c264fdcd398795173a1d8ca9bce7cdf5d6ac42bc19fc", "started_at": 1792234271.5884662}, "kind": "tool", "observed_at": 1792234271.5889146, "path": "tool:read_file", "phase": "minimal_call", "prev_digest": "34dd25b559e0f7bf4d15d00890732fb82c21accad42fc1cfd7f70f8e547405ee", "receipt_id": "LP-1792234271-6b614f33", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "469b342f48685df796c4137983287765d353f08e396e86eb66cd7ea15cc1fd91", "evidence": {"detail": "injected fault observed failing and classified: Not found: /mnt/workdrive/rune/src/rune/../../store/live_proof/sandbox/__nonexistent_live_probe__", "ended_at": 1792234271.5892375, "error_class": "ENOENT", "injected_fault": "read a nonexistent path", "input_digest": "f57b736271e86fa2ba87134bc3d1fe285bdc4e8c9ea3433b7b5d66aca532190b", "output_digest": "dac1b171dad67eefb479bcaae54c15be34484711044bafb645d4e6d735de94fd", "started_at": 1792234271.5889158}, "kind": "tool", "observed_at": 1792234271.589255, "path": "tool:read_file", "phase": "failure_injection", "prev_digest": "ec7e32b8f1f24f37dd50358f36afc4008505cd7ae3e5e730807ca5e77f6ad114", "receipt_id": "LP-1792234271-4f8611af", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "934fcf6d1aec1798e1a6056c732f12532f713e80065ff9fb03ec65a675fa7fbd", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.5896084, "error_class": null, "input_digest": "a846ca5a3235abce5db143b6194fe7f4e3516ff944913e3baff2d3cb4afe0c0c", "output_digest": "226cee6011cf22d0539ef2898e20d0996473d0286e743a2261f69421241086e0", "started_at": 1792234271.589257}, "kind": "tool", "observed_at": 1792234271.589622, "path": "tool:read_file", "phase": "recovery", "prev_digest": "469b342f48685df796c4137983287765d353f08e396e86eb66cd7ea15cc1fd91", "receipt_id": "LP-1792234271-5d313ee4", "recovery_of": "LP-1792234271-4f8611af", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "af62001cbd78dbdead0c45c494dc4b41abbdd25f58269840eff9f461cdbfeb92", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233218.1550496, "error_class": null, "input_digest": "063e2e0777ec5122374f5a665a0eb89601044d75481bf080c15528f2a03d31ff", "output_digest": "cb23a951feb41b1f5f7b2a4ee5787515cff14225aebada207840410b28b9d89a", "started_at": 1792233218.1550484}, "kind": "tool", "observed_at": 1792233218.155064, "path": "tool:read_persona", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233218-e7251b1d", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "791b65eb10cfa1715e35f207d647c7bd5b3b26f5aa5c65ab2106855da75b277f", "evidence": {"detail": "minimal call failed: Persona block not found in MEMORY.md", "ended_at": 1792233218.1552446, "error_class": "TOOL_ERROR", "input_digest": "29b57b3ba9cb4bc9534b16e7502e076401070bcf5f46d4f2996f1a3f58544a22", "output_digest": "bfc3d287e7db7de512d16e29c879ee3c06336db47b387d214906bff469deb3a7", "started_at": 1792233218.1550646}, "kind": "tool", "observed_at": 1792233218.1552546, "path": "tool:read_persona", "phase": "minimal_call", "prev_digest": "af62001cbd78dbdead0c45c494dc4b41abbdd25f58269840eff9f461cdbfeb92", "receipt_id": "LP-1792233218-2fb88146", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_fail"}
|
||||
{"digest": "c3270bb06ff00054a0638e722bff10e7d0146aa07d29ad6b47c54ce433b529a1", "evidence": {"detail": "injected fault observed failing and classified: Persona block not found in MEMORY.md", "ended_at": 1792233218.1553478, "error_class": "ENOENT", "injected_fault": "read with a poisoned persona id", "input_digest": "320c851fa76bd033c2762d99997e960c473d847102861b0607fe20dcedb4a4cf", "output_digest": "04a15611da0c41d536ad2f60cec590b47cefde420c57ef5a0b1320b456259f07", "started_at": 1792233218.155255}, "kind": "tool", "observed_at": 1792233218.1553597, "path": "tool:read_persona", "phase": "failure_injection", "prev_digest": "791b65eb10cfa1715e35f207d647c7bd5b3b26f5aa5c65ab2106855da75b277f", "receipt_id": "LP-1792233218-58d29ebd", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "b7c3d02a1aafe144b876fa713529b540dc06399f44fa60fdedefb3f38336f656", "evidence": {"detail": "recovery call failed: Persona block not found in MEMORY.md", "ended_at": 1792233218.1554778, "error_class": "TOOL_ERROR", "input_digest": "b32802c62eebca15317113d7e4455508c074d6b39ca1220681f186cc85ba0e92", "output_digest": "5af50857ea4c4a151574c3f98294fcf212543c665505234ad639ee6db240e0e5", "started_at": 1792233218.1553605}, "kind": "tool", "observed_at": 1792233218.1554883, "path": "tool:read_persona", "phase": "recovery", "prev_digest": "c3270bb06ff00054a0638e722bff10e7d0146aa07d29ad6b47c54ce433b529a1", "receipt_id": "LP-1792233218-2cbaff76", "recovery_of": "LP-1792233218-58d29ebd", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_fail"}
|
||||
{"digest": "52676b47b7b64b7eafeb1ab21acd7b786675b8ee3f0466d27c30bdb9c1216676", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.947291, "error_class": null, "input_digest": "063e2e0777ec5122374f5a665a0eb89601044d75481bf080c15528f2a03d31ff", "output_digest": "cb23a951feb41b1f5f7b2a4ee5787515cff14225aebada207840410b28b9d89a", "started_at": 1792234271.9472888}, "kind": "tool", "observed_at": 1792234271.9473097, "path": "tool:read_persona", "phase": "auth_check", "prev_digest": "b7c3d02a1aafe144b876fa713529b540dc06399f44fa60fdedefb3f38336f656", "receipt_id": "LP-1792234271-92ff456d", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "ec59b876a8744730382f969091ac6476b352c6ca13f3f3b4fb61d948dc049a48", "evidence": {"detail": "minimal call failed: Persona block not found in MEMORY.md", "ended_at": 1792234271.947483, "error_class": "TOOL_ERROR", "input_digest": "29b57b3ba9cb4bc9534b16e7502e076401070bcf5f46d4f2996f1a3f58544a22", "output_digest": "bfc3d287e7db7de512d16e29c879ee3c06336db47b387d214906bff469deb3a7", "started_at": 1792234271.9473107}, "kind": "tool", "observed_at": 1792234271.9474926, "path": "tool:read_persona", "phase": "minimal_call", "prev_digest": "52676b47b7b64b7eafeb1ab21acd7b786675b8ee3f0466d27c30bdb9c1216676", "receipt_id": "LP-1792234271-5f7c6e26", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_fail"}
|
||||
{"digest": "789e81a4b786da6e8796540641546c89f4d4d5e0749816e02a9856a3c8de11cd", "evidence": {"detail": "injected fault observed failing and classified: Persona block not found in MEMORY.md", "ended_at": 1792234271.9475667, "error_class": "ENOENT", "injected_fault": "read with a poisoned persona id", "input_digest": "320c851fa76bd033c2762d99997e960c473d847102861b0607fe20dcedb4a4cf", "output_digest": "04a15611da0c41d536ad2f60cec590b47cefde420c57ef5a0b1320b456259f07", "started_at": 1792234271.9474933}, "kind": "tool", "observed_at": 1792234271.947576, "path": "tool:read_persona", "phase": "failure_injection", "prev_digest": "ec59b876a8744730382f969091ac6476b352c6ca13f3f3b4fb61d948dc049a48", "receipt_id": "LP-1792234271-0c70b7f4", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "d30dd719ff7ebec8e501b97b1b92e3ef4561ec6b5b0d1da1ad1d9c8600129816", "evidence": {"detail": "recovery call failed: Persona block not found in MEMORY.md", "ended_at": 1792234271.9476554, "error_class": "TOOL_ERROR", "input_digest": "b32802c62eebca15317113d7e4455508c074d6b39ca1220681f186cc85ba0e92", "output_digest": "5af50857ea4c4a151574c3f98294fcf212543c665505234ad639ee6db240e0e5", "started_at": 1792234271.9475768}, "kind": "tool", "observed_at": 1792234271.9476628, "path": "tool:read_persona", "phase": "recovery", "prev_digest": "789e81a4b786da6e8796540641546c89f4d4d5e0749816e02a9856a3c8de11cd", "receipt_id": "LP-1792234271-f4722ff8", "recovery_of": "LP-1792234271-0c70b7f4", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_fail"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "26d6082067f78673af235c477e165e7428c1abaed6c724aa9acb75dcf56bfdf1", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233218.1275475, "error_class": null, "input_digest": "0790f22dd0ebca93ab4c0630d71f709a8b96b8983cf46dcf2c756f261ad78766", "output_digest": "a956aeb43b63c3181cab7b355029b25504be21b35790393d1254b3a22211aaec", "started_at": 1792233218.1275463}, "kind": "tool", "observed_at": 1792233218.1275628, "path": "tool:repo_map", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233218-35ab6f26", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "57cb11cfbbc2c006dcaef2f5f2f1fdb568bb455058350053a805a1b5b7decc82", "evidence": {"detail": "repo map over the sandbox dir", "ended_at": 1792233218.1394017, "error_class": null, "input_digest": "9782f79dae45c6429d29752ae557543830346f589ab017d7268f2e2137784fba", "output_digest": "4b122ae6968f0a5738b15bd60c0e528f68507c08f94bdc9936c67cc25f3d9be6", "started_at": 1792233218.1275637}, "kind": "tool", "observed_at": 1792233218.13945, "path": "tool:repo_map", "phase": "minimal_call", "prev_digest": "26d6082067f78673af235c477e165e7428c1abaed6c724aa9acb75dcf56bfdf1", "receipt_id": "LP-1792233218-e1dd9f1d", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "0a65dce068fc4d401e8da2d188e78527e630f8528f5d9214732a1586b44569ce", "evidence": {"detail": "injected fault observed failing and classified: Access denied: /etc/__live_proof_probe__", "ended_at": 1792233218.1397295, "error_class": "PERMISSION", "injected_fault": "map a denied (blocked) path", "input_digest": "da7f98628de3675f20fa30e701640d560b051636b779ab363ec14b605d9eaa2d", "output_digest": "2a693e44c88ccbf8c06d8ae95445f3b47e76165a8880d77fc29c817ba88ad4e0", "started_at": 1792233218.1394515}, "kind": "tool", "observed_at": 1792233218.139758, "path": "tool:repo_map", "phase": "failure_injection", "prev_digest": "57cb11cfbbc2c006dcaef2f5f2f1fdb568bb455058350053a805a1b5b7decc82", "receipt_id": "LP-1792233218-1ef149da", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "0adbb9e06ad052a740f8c9c888590f7627f5e9500c67d2570f0ddee5d5e81af5", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233218.1519997, "error_class": null, "input_digest": "643bf7bee5ea37f7913c9e00b0586dad9b347b11bda8ae5fc956d8352a2d66d9", "output_digest": "0d608c628fa58c58d9ffafe6fdb3563d852191b07d0d7f8549747bfdc8328cf2", "started_at": 1792233218.139759}, "kind": "tool", "observed_at": 1792233218.152042, "path": "tool:repo_map", "phase": "recovery", "prev_digest": "0a65dce068fc4d401e8da2d188e78527e630f8528f5d9214732a1586b44569ce", "receipt_id": "LP-1792233218-8d68fd76", "recovery_of": "LP-1792233218-1ef149da", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "f988d74fddfc00914cb1cd0aa4baaffe6b4dbb00f1f259f2a3556ed43ecd38d0", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.916472, "error_class": null, "input_digest": "0790f22dd0ebca93ab4c0630d71f709a8b96b8983cf46dcf2c756f261ad78766", "output_digest": "a956aeb43b63c3181cab7b355029b25504be21b35790393d1254b3a22211aaec", "started_at": 1792234271.9164703}, "kind": "tool", "observed_at": 1792234271.916494, "path": "tool:repo_map", "phase": "auth_check", "prev_digest": "0adbb9e06ad052a740f8c9c888590f7627f5e9500c67d2570f0ddee5d5e81af5", "receipt_id": "LP-1792234271-0ed6a366", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "a967246c3250c7c21a2fa132d0c2302d18900e91efe62f48cdae0b7e22703276", "evidence": {"detail": "repo map over the sandbox dir", "ended_at": 1792234271.9286356, "error_class": null, "input_digest": "9782f79dae45c6429d29752ae557543830346f589ab017d7268f2e2137784fba", "output_digest": "4b122ae6968f0a5738b15bd60c0e528f68507c08f94bdc9936c67cc25f3d9be6", "started_at": 1792234271.9164946}, "kind": "tool", "observed_at": 1792234271.9286685, "path": "tool:repo_map", "phase": "minimal_call", "prev_digest": "f988d74fddfc00914cb1cd0aa4baaffe6b4dbb00f1f259f2a3556ed43ecd38d0", "receipt_id": "LP-1792234271-871ed83d", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "04115700625c1803b008dbdc516e880b09bbea0f8dab97991c45861269b9d59d", "evidence": {"detail": "injected fault observed failing and classified: Access denied: /etc/__live_proof_probe__", "ended_at": 1792234271.9288743, "error_class": "PERMISSION", "injected_fault": "map a denied (blocked) path", "input_digest": "da7f98628de3675f20fa30e701640d560b051636b779ab363ec14b605d9eaa2d", "output_digest": "2a693e44c88ccbf8c06d8ae95445f3b47e76165a8880d77fc29c817ba88ad4e0", "started_at": 1792234271.9286697}, "kind": "tool", "observed_at": 1792234271.9288864, "path": "tool:repo_map", "phase": "failure_injection", "prev_digest": "a967246c3250c7c21a2fa132d0c2302d18900e91efe62f48cdae0b7e22703276", "receipt_id": "LP-1792234271-f4ec6daf", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "07334b7a23e20759ee483d72d463e9ecc115f3d78b6df2b9b2750fbff2061e2a", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.9392335, "error_class": null, "input_digest": "643bf7bee5ea37f7913c9e00b0586dad9b347b11bda8ae5fc956d8352a2d66d9", "output_digest": "0d608c628fa58c58d9ffafe6fdb3563d852191b07d0d7f8549747bfdc8328cf2", "started_at": 1792234271.9288874}, "kind": "tool", "observed_at": 1792234271.939266, "path": "tool:repo_map", "phase": "recovery", "prev_digest": "04115700625c1803b008dbdc516e880b09bbea0f8dab97991c45861269b9d59d", "receipt_id": "LP-1792234271-04134317", "recovery_of": "LP-1792234271-f4ec6daf", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "366b191bbb9012a3f75e61df158542e74fa9857a447760140a9cbeba5ae65d50", "evidence": {"detail": "approval gate observed: prepared + approved (single-use token, success=True)", "ended_at": 1792233218.1540852, "error_class": null, "input_digest": "49d90e0983e294ccd1ea057bbb893b23182a927655f52fddb4f5e77f588a253b", "output_digest": "4b3d8628e3c7bc148cc79bf39a99261673d100d8db6da6e863503cb31a21174c", "started_at": 1792233218.1531603}, "kind": "tool", "observed_at": 1792233218.154104, "path": "tool:run_command", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233218-f072dc9f", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "2debd2be91de4fb8f60ccf35d198ac04d5bcfeb8c4996cedde46b2e82031537c", "evidence": {"detail": "run 'true' in the sandbox", "ended_at": 1792233218.154187, "error_class": null, "input_digest": "1ffa77546d1f6a6536cf4c4522f467457980bfc5715ab515b3700ec46a5371b1", "output_digest": "465a4ff846b0a5e119a24b345ddb2e4ff2252074fbeea6c90980f38d54f24999", "started_at": 1792233218.154105}, "kind": "tool", "observed_at": 1792233218.1541965, "path": "tool:run_command", "phase": "minimal_call", "prev_digest": "366b191bbb9012a3f75e61df158542e74fa9857a447760140a9cbeba5ae65d50", "receipt_id": "LP-1792233218-124349ac", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "7b07c348481bab6e2dd288025f9c82c130b0f7ff01e4a96a3a99e4ae22c08c02", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233218.1542382, "error_class": null, "injected_fault": "run a command that cannot exist", "input_digest": "2544c53dc35795034c22bd7a85354d66811e4f13d114754a6bf289298af409e2", "output_digest": "cfe82ec11b5df99c24a72c1052f35e87c2dd8e22f62c3c4786a4044c1b6ee543", "started_at": 1792233218.154197}, "kind": "tool", "observed_at": 1792233218.1542466, "path": "tool:run_command", "phase": "failure_injection", "prev_digest": "2debd2be91de4fb8f60ccf35d198ac04d5bcfeb8c4996cedde46b2e82031537c", "receipt_id": "LP-1792233218-1e5d0fb0", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "a6e36897d6b34bf7b2a30bc89343bd874dc2f3a023031508f888aab7bf792113", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233218.1543016, "error_class": null, "input_digest": "83da34ae1e57418c63bf0e7576ce050d3545379af9e8abad5edc16d4d2ec9584", "output_digest": "7a7a73ad197502e120fc0797ad7a08c64d93cca2f4ab7b14873f1f48dcd8ead5", "started_at": 1792233218.1542478}, "kind": "tool", "observed_at": 1792233218.1543105, "path": "tool:run_command", "phase": "recovery", "prev_digest": "7b07c348481bab6e2dd288025f9c82c130b0f7ff01e4a96a3a99e4ae22c08c02", "receipt_id": "LP-1792233218-65a22f4e", "recovery_of": "LP-1792233218-1e5d0fb0", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "911f94ba9b0df859d07b341d1b78b76d8abe5845a879fa9b3b12daf796153c17", "evidence": {"detail": "approval gate observed: prepared + approved (single-use token, success=True)", "ended_at": 1792234271.9458697, "error_class": null, "input_digest": "49d90e0983e294ccd1ea057bbb893b23182a927655f52fddb4f5e77f588a253b", "output_digest": "4b3d8628e3c7bc148cc79bf39a99261673d100d8db6da6e863503cb31a21174c", "started_at": 1792234271.9405448}, "kind": "tool", "observed_at": 1792234271.9459004, "path": "tool:run_command", "phase": "auth_check", "prev_digest": "a6e36897d6b34bf7b2a30bc89343bd874dc2f3a023031508f888aab7bf792113", "receipt_id": "LP-1792234271-0db8e7cf", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "2c4769303e2aa75ebcf61e35596cfe1b9513233bdd27ee88760816f76f231b0e", "evidence": {"detail": "run 'true' in the sandbox", "ended_at": 1792234271.9460104, "error_class": null, "input_digest": "1ffa77546d1f6a6536cf4c4522f467457980bfc5715ab515b3700ec46a5371b1", "output_digest": "465a4ff846b0a5e119a24b345ddb2e4ff2252074fbeea6c90980f38d54f24999", "started_at": 1792234271.9459016}, "kind": "tool", "observed_at": 1792234271.9460177, "path": "tool:run_command", "phase": "minimal_call", "prev_digest": "911f94ba9b0df859d07b341d1b78b76d8abe5845a879fa9b3b12daf796153c17", "receipt_id": "LP-1792234271-8d1b2ed4", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "88e30f19ca966294831068c6db9b1beb51aa1046769cfe9b3224af3fb12c4cc5", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234271.9460506, "error_class": null, "injected_fault": "run a command that cannot exist", "input_digest": "2544c53dc35795034c22bd7a85354d66811e4f13d114754a6bf289298af409e2", "output_digest": "cfe82ec11b5df99c24a72c1052f35e87c2dd8e22f62c3c4786a4044c1b6ee543", "started_at": 1792234271.9460185}, "kind": "tool", "observed_at": 1792234271.9460568, "path": "tool:run_command", "phase": "failure_injection", "prev_digest": "2c4769303e2aa75ebcf61e35596cfe1b9513233bdd27ee88760816f76f231b0e", "receipt_id": "LP-1792234271-b8c8b641", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "ba1743459fe7b789db766c67e4403827e0246c8ca70979a3867cfa6313b6363b", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.9460995, "error_class": null, "input_digest": "83da34ae1e57418c63bf0e7576ce050d3545379af9e8abad5edc16d4d2ec9584", "output_digest": "7a7a73ad197502e120fc0797ad7a08c64d93cca2f4ab7b14873f1f48dcd8ead5", "started_at": 1792234271.9460578}, "kind": "tool", "observed_at": 1792234271.9461045, "path": "tool:run_command", "phase": "recovery", "prev_digest": "88e30f19ca966294831068c6db9b1beb51aa1046769cfe9b3224af3fb12c4cc5", "receipt_id": "LP-1792234271-6a73bbc5", "recovery_of": "LP-1792234271-b8c8b641", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "df588d3421befe71b5ac886f2824da33ca7d7426c9ee43b15a7afbcaa0af1bbe", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792233217.9484146, "error_class": null, "input_digest": "19d015afba2cbf1f7a51eaf490735460bd6a6c3ded7341b12ad3b94ede67c780", "output_digest": "303abb215d3f40512244dbd0c3b665e28c750c46816b1c650ec262abae82e78b", "started_at": 1792233217.9484136}, "kind": "tool", "observed_at": 1792233217.9484262, "path": "tool:run_tests", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-80631460", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "f846d6d1ef4d93782c8cc1ba181a24cb03885bc997e617f392f811657cbc1bb7", "evidence": {"detail": "minimal call succeeded", "ended_at": 1792233217.9484851, "error_class": null, "input_digest": "e323635b365eced17b9a4fcbe537afd4d3daaa7a4158a1af5e3284b7c61d917d", "output_digest": "17823a8d57195139f2cfa31e9d510b53580dec9541dd92ecf428d80b955a1147", "started_at": 1792233217.9484267}, "kind": "tool", "observed_at": 1792233217.9484928, "path": "tool:run_tests", "phase": "minimal_call", "prev_digest": "df588d3421befe71b5ac886f2824da33ca7d7426c9ee43b15a7afbcaa0af1bbe", "receipt_id": "LP-1792233217-9798c01c", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "f8d9478a060c37aafcab156ca9f2824cfae46fae79fad0292dd04a9dcaf3b8ff", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792233217.9485211, "error_class": null, "injected_fault": "nonexistent path", "input_digest": "ca6d6db9e9d29c7e583c2af686990d7fc2c80be0cceea54eb58f92fca48f0ff5", "output_digest": "0763ca1de8fe2e9a3935ad5ae0178c877390d8da684d25b0cedb3505ebdb0763", "started_at": 1792233217.9484932}, "kind": "tool", "observed_at": 1792233217.9485283, "path": "tool:run_tests", "phase": "failure_injection", "prev_digest": "f846d6d1ef4d93782c8cc1ba181a24cb03885bc997e617f392f811657cbc1bb7", "receipt_id": "LP-1792233217-dfeb507d", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_fail"}
|
||||
{"digest": "a352e274dd321ccc2be4221b12a2bbcf451d3df7f77612afc889be24cb6dfdf7", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233217.948574, "error_class": null, "input_digest": "d7a369763d2def0be6bad748cb9522203f4886d454f04700e3d59044c00db31d", "output_digest": "c27aabb83792dee21c4f793c1fe885585000e813011582daec5e1a9c2a0ecd68", "started_at": 1792233217.9485292}, "kind": "tool", "observed_at": 1792233217.9485805, "path": "tool:run_tests", "phase": "recovery", "prev_digest": "f8d9478a060c37aafcab156ca9f2824cfae46fae79fad0292dd04a9dcaf3b8ff", "receipt_id": "LP-1792233217-6cf9cab8", "recovery_of": "LP-1792233217-dfeb507d", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "65d34ffc4ecff6aee19297c6f5598a7ea8efc7540c72dd0c904bcd44c79049dc", "evidence": {"detail": "read-only boundary observed: no approval required", "ended_at": 1792234271.6162615, "error_class": null, "input_digest": "19d015afba2cbf1f7a51eaf490735460bd6a6c3ded7341b12ad3b94ede67c780", "output_digest": "303abb215d3f40512244dbd0c3b665e28c750c46816b1c650ec262abae82e78b", "started_at": 1792234271.6162608}, "kind": "tool", "observed_at": 1792234271.616277, "path": "tool:run_tests", "phase": "auth_check", "prev_digest": "a352e274dd321ccc2be4221b12a2bbcf451d3df7f77612afc889be24cb6dfdf7", "receipt_id": "LP-1792234271-ded1602f", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "deb3e7cffc023331014a749817ef38a05cbdc7fbb5d136b7fb8a0c52ae9ff883", "evidence": {"detail": "minimal call succeeded", "ended_at": 1792234271.6163225, "error_class": null, "input_digest": "e323635b365eced17b9a4fcbe537afd4d3daaa7a4158a1af5e3284b7c61d917d", "output_digest": "17823a8d57195139f2cfa31e9d510b53580dec9541dd92ecf428d80b955a1147", "started_at": 1792234271.6162777}, "kind": "tool", "observed_at": 1792234271.6163285, "path": "tool:run_tests", "phase": "minimal_call", "prev_digest": "65d34ffc4ecff6aee19297c6f5598a7ea8efc7540c72dd0c904bcd44c79049dc", "receipt_id": "LP-1792234271-a7c02791", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "9cbf71ec9044f26587cef983b2bce9046e29d236107d371bd1b9ebf5d0bf255a", "evidence": {"detail": "injected fault did NOT produce a failure", "ended_at": 1792234271.616351, "error_class": null, "injected_fault": "nonexistent path", "input_digest": "ca6d6db9e9d29c7e583c2af686990d7fc2c80be0cceea54eb58f92fca48f0ff5", "output_digest": "0763ca1de8fe2e9a3935ad5ae0178c877390d8da684d25b0cedb3505ebdb0763", "started_at": 1792234271.616329}, "kind": "tool", "observed_at": 1792234271.6163566, "path": "tool:run_tests", "phase": "failure_injection", "prev_digest": "deb3e7cffc023331014a749817ef38a05cbdc7fbb5d136b7fb8a0c52ae9ff883", "receipt_id": "LP-1792234271-0a3c79e1", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_fail"}
|
||||
{"digest": "c891475f3a3446708ac467751b10776983efda4705106efc8c695666aeea0713", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.6164002, "error_class": null, "input_digest": "d7a369763d2def0be6bad748cb9522203f4886d454f04700e3d59044c00db31d", "output_digest": "c27aabb83792dee21c4f793c1fe885585000e813011582daec5e1a9c2a0ecd68", "started_at": 1792234271.616357}, "kind": "tool", "observed_at": 1792234271.616405, "path": "tool:run_tests", "phase": "recovery", "prev_digest": "9cbf71ec9044f26587cef983b2bce9046e29d236107d371bd1b9ebf5d0bf255a", "receipt_id": "LP-1792234271-87133897", "recovery_of": "LP-1792234271-0a3c79e1", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1,8 @@
|
||||
{"digest": "5141998f46b6c0b0d25952d180c431b9c50c9a2dc36deb0e30b09cf9dbffe86c", "evidence": {"detail": "approval gate observed: executed without confirmation boundary", "ended_at": 1792233217.9307585, "error_class": null, "input_digest": "3f768c19ec87d469161bbf767efec77c98005056ab25f77e2560c0597f784f95", "output_digest": "6c1b4cc94145a09709cc16ed11b1f904fc56b41882afc9db2a4e12de68f0fb79", "started_at": 1792233217.9291263}, "kind": "tool", "observed_at": 1792233217.9307735, "path": "tool:write_file", "phase": "auth_check", "prev_digest": null, "receipt_id": "LP-1792233217-0d73d2b3", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 0, "verdict": "observed_pass"}
|
||||
{"digest": "8c48b38dd602a7a303efde8b86ea3df4f39467abaefff13daaa893d16071cb4a", "evidence": {"detail": "write a one-line sandbox file", "ended_at": 1792233217.9319632, "error_class": null, "input_digest": "4dfc60346c1e233cf78d9a367091a1465624511f661a1462636cb3d8094ca7ba", "output_digest": "ed8f668534dd0c78f43c2045d6fa734f92af7db5fd0eac48a8fd75969311185b", "started_at": 1792233217.9307745}, "kind": "tool", "observed_at": 1792233217.9319773, "path": "tool:write_file", "phase": "minimal_call", "prev_digest": "5141998f46b6c0b0d25952d180c431b9c50c9a2dc36deb0e30b09cf9dbffe86c", "receipt_id": "LP-1792233217-502c81ab", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 1, "verdict": "observed_pass"}
|
||||
{"digest": "eba68d31e4b92828a6ec98ad2d99f5a1c3491fcc6af342a2b8f513d346fe82a5", "evidence": {"detail": "injected fault observed failing and classified: Access denied: /proc/__live_proof_write_probe__ is in a protected directory (/proc). This directory is always blocked, e", "ended_at": 1792233217.9321306, "error_class": "PATH_DENIED", "injected_fault": "write outside allowed write dirs", "input_digest": "0b46e0702bc032687655f69c97ec430d70c83fc66940c56f100c1aa48c41a68d", "output_digest": "326b4c20883bd51e34f78ad642c79b75055aeac2dd87c105889c076d4cccf9a4", "started_at": 1792233217.931978}, "kind": "tool", "observed_at": 1792233217.932141, "path": "tool:write_file", "phase": "failure_injection", "prev_digest": "8c48b38dd602a7a303efde8b86ea3df4f39467abaefff13daaa893d16071cb4a", "receipt_id": "LP-1792233217-380fa392", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 2, "verdict": "observed_pass"}
|
||||
{"digest": "77745eff94f25e9dcaa6dde6d7659c9584b19034b47a3c3adde28a069a705165", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792233217.9330544, "error_class": null, "input_digest": "33b285bd6f9455702eb5f03029fd24a882ffe2bb51c76bb0959510fe00efd537", "output_digest": "11096cc3886527e4a8c05d487c644125e02389b4f00a6313a24f1571ff16ce71", "started_at": 1792233217.9321415}, "kind": "tool", "observed_at": 1792233217.9330647, "path": "tool:write_file", "phase": "recovery", "prev_digest": "eba68d31e4b92828a6ec98ad2d99f5a1c3491fcc6af342a2b8f513d346fe82a5", "receipt_id": "LP-1792233217-977b2aeb", "recovery_of": "LP-1792233217-380fa392", "run_id": "RUN-1792233217-0033a4", "schema": 1, "seq": 3, "verdict": "observed_pass"}
|
||||
{"digest": "6466d4e589d526d86328500254d7f701620a8dca8c71d5facfa01a6291eb00a2", "evidence": {"detail": "approval gate observed: executed without confirmation boundary", "ended_at": 1792234271.5982952, "error_class": null, "input_digest": "3f768c19ec87d469161bbf767efec77c98005056ab25f77e2560c0597f784f95", "output_digest": "6c1b4cc94145a09709cc16ed11b1f904fc56b41882afc9db2a4e12de68f0fb79", "started_at": 1792234271.5974405}, "kind": "tool", "observed_at": 1792234271.598307, "path": "tool:write_file", "phase": "auth_check", "prev_digest": "77745eff94f25e9dcaa6dde6d7659c9584b19034b47a3c3adde28a069a705165", "receipt_id": "LP-1792234271-af5a8bcd", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 4, "verdict": "observed_pass"}
|
||||
{"digest": "21eeb19cda896df990b42900bcc37e0334cae248d52094d417764e8596e773b3", "evidence": {"detail": "write a one-line sandbox file", "ended_at": 1792234271.5991473, "error_class": null, "input_digest": "4dfc60346c1e233cf78d9a367091a1465624511f661a1462636cb3d8094ca7ba", "output_digest": "ed8f668534dd0c78f43c2045d6fa734f92af7db5fd0eac48a8fd75969311185b", "started_at": 1792234271.5983076}, "kind": "tool", "observed_at": 1792234271.5991564, "path": "tool:write_file", "phase": "minimal_call", "prev_digest": "6466d4e589d526d86328500254d7f701620a8dca8c71d5facfa01a6291eb00a2", "receipt_id": "LP-1792234271-f7ece735", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 5, "verdict": "observed_pass"}
|
||||
{"digest": "a2292e905a01e4aa8fe60f10806d6235bf36aa0cdba1a804f461129e33839ebe", "evidence": {"detail": "injected fault observed failing and classified: Access denied: /proc/__live_proof_write_probe__ is in a protected directory (/proc). This directory is always blocked, e", "ended_at": 1792234271.5992904, "error_class": "PATH_DENIED", "injected_fault": "write outside allowed write dirs", "input_digest": "0b46e0702bc032687655f69c97ec430d70c83fc66940c56f100c1aa48c41a68d", "output_digest": "326b4c20883bd51e34f78ad642c79b75055aeac2dd87c105889c076d4cccf9a4", "started_at": 1792234271.5991573}, "kind": "tool", "observed_at": 1792234271.5993023, "path": "tool:write_file", "phase": "failure_injection", "prev_digest": "21eeb19cda896df990b42900bcc37e0334cae248d52094d417764e8596e773b3", "receipt_id": "LP-1792234271-942365c4", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 6, "verdict": "observed_pass"}
|
||||
{"digest": "1d7332a71682b576a51ae75ff24ca857736ad596e4ad4323908b93a8f35ed439", "evidence": {"detail": "minimal call succeeded after injected fault", "ended_at": 1792234271.600123, "error_class": null, "input_digest": "33b285bd6f9455702eb5f03029fd24a882ffe2bb51c76bb0959510fe00efd537", "output_digest": "11096cc3886527e4a8c05d487c644125e02389b4f00a6313a24f1571ff16ce71", "started_at": 1792234271.599303}, "kind": "tool", "observed_at": 1792234271.6001318, "path": "tool:write_file", "phase": "recovery", "prev_digest": "a2292e905a01e4aa8fe60f10806d6235bf36aa0cdba1a804f461129e33839ebe", "receipt_id": "LP-1792234271-cdf23f1f", "recovery_of": "LP-1792234271-942365c4", "run_id": "RUN-1792234271-c44554", "schema": 1, "seq": 7, "verdict": "observed_pass"}
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof edited line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -0,0 +1 @@
|
||||
live-proof probe line
|
||||
@@ -283,6 +283,7 @@
|
||||
"capability_ledger",
|
||||
"cognitive_budgeter",
|
||||
"construction_activation",
|
||||
"construction_admission",
|
||||
"construction_commands",
|
||||
"construction_decision",
|
||||
"construction_depth",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
agents
|
||||
@@ -0,0 +1,206 @@
|
||||
---
|
||||
name: openspec-apply-change
|
||||
description: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks. Also use when the user says "openspec apply", "opsx apply", or "openspec implement".
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.14.1"
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `schemas`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Project check:** These steps expect a project that already uses OpenSpec. Before the first step that writes anything (`new change`, `archive`, `sync specs`, or authoring an artifact file), confirm the project has a root: run `openspec list --json` (with `--store <id>` when a store is selected, since the store is then the root) and read `root`. A root object means the project is set up. `"root": null` means it is not - there is no `openspec/` directory here, and a write such as `openspec new change` would create one as a side effect. The command also exits non-zero, which is that answer rather than a broken CLI, so read the JSON instead of retrying or working around it.
|
||||
|
||||
One `"root": null` is not about setup: when a `status` error message starts with `Declared in` or `Invalid store declaration in` and names this project's `openspec/config.yaml` (or `config.yml`), the project does use OpenSpec through a store it declares, which this machine cannot resolve (the store is not registered, or the `store:` line is malformed). Do not treat it as uninitialized and skip the branches below: stop before writing and show the user that error's `message` and `fix`.
|
||||
|
||||
Otherwise, with no root, what happens next depends on how this workflow was reached:
|
||||
|
||||
- **Auto-selected**: you chose this workflow yourself, without the user naming OpenSpec, naming this skill, or running its slash command. Stop using OpenSpec and answer the request normally, as you would with no OpenSpec installed. Do not ask them to set anything up and do not mention OpenSpec setup.
|
||||
- **Explicit OpenSpec request**: the user named OpenSpec, named this skill, or ran its slash command. Stop before writing and ask how to proceed: set this project up (`openspec init`), target a store they already have (`--store <id>`), or continue without OpenSpec for this request. Wait for their answer.
|
||||
|
||||
In both branches, never create the root as a side effect: do not run `openspec init` until the user asks for it, do not hand-create `openspec/` files, and do not let a command create it.
|
||||
|
||||
**Input**: Optionally specify a change name (e.g., `/openspec-apply-change add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/openspec-apply-change <other>`).
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
||||
- `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints
|
||||
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
||||
|
||||
3. **Get apply instructions**
|
||||
|
||||
```bash
|
||||
openspec instructions apply --change "<name>" --json
|
||||
```
|
||||
|
||||
This returns:
|
||||
- `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status, source path, and source line
|
||||
- Dynamic instruction based on current state
|
||||
- Optional `context`: current required project instruction input from the selected root
|
||||
- Optional `operationGuidance`: current advisory guidance for apply
|
||||
- `missingArtifacts` (when present): required artifact ids with no output
|
||||
|
||||
**Handle states:**
|
||||
- If `state: "blocked"`: show the message and pause implementation.
|
||||
- If `missingArtifacts` is non-empty: suggest completing the missing artifacts. Run `openspec status --change "<name>" --json`, select the next `ready` artifact (not `skipped` or `blocked`), and use `openspec instructions "<artifact-id>" --change "<name>" --json` for its rules and template. Keep the selected `--store <id>` on both commands.
|
||||
- Otherwise, follow the CLI instruction to create or repair the schema-configured tracking file from existing planning artifacts. Do not assume another artifact is ready or start implementation while blocked.
|
||||
- If `state: "all_done"`: report that all tracked tasks are complete and suggest review or verification as appropriate before archiving
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
Treat `context` as a required prompt-level input. Read and consider it, and
|
||||
apply relevant project facts, conventions, and constraints while implementing.
|
||||
Treat `operationGuidance` as optional additive advice. Read and consider every
|
||||
entry, and follow entries that are applicable and compatible with the built-in
|
||||
workflow.
|
||||
|
||||
Keep both fields separate from CLI-returned state, missing artifacts, tasks,
|
||||
progress, `contextFiles`, and the built-in `instruction`. They are not
|
||||
evidence of task completion, do not replace the built-in instruction, and do
|
||||
not permit bypassing a blocked state. If context conflicts with the built-in
|
||||
instruction, an explicit user choice, or a CLI-controlled value, report the
|
||||
conflict and preserve the controlling value. If guidance is inapplicable or
|
||||
conflicts with those controlling inputs, do not follow it and explain why.
|
||||
These are prompt-level behavior contracts, not enforceable checks.
|
||||
|
||||
4. **Read context files**
|
||||
|
||||
Read every file path listed under `contextFiles` from the apply instructions output.
|
||||
The files depend on the schema being used:
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
Do not copy `context` or `operationGuidance` verbatim into implementation
|
||||
files or planning artifacts unless the user separately asks for that content.
|
||||
|
||||
5. **Show current progress**
|
||||
|
||||
Display:
|
||||
- Schema being used
|
||||
- Progress: "N/M tasks complete"
|
||||
- Remaining tasks overview
|
||||
- Dynamic instruction from CLI
|
||||
|
||||
6. **Implement tasks (loop until done or blocked)**
|
||||
|
||||
For each pending task:
|
||||
- Show which task is being worked on
|
||||
- Make the code changes required
|
||||
- Keep changes minimal and focused
|
||||
- Before editing, confirm the checkbox at the returned `sourcePath` and `line` still matches the task description; if it does not, rerun the apply instructions and use the refreshed location
|
||||
- Mark the task complete at its returned `sourcePath` and `line`: `- [ ]` → `- [x]`
|
||||
- Rerun the apply instructions and confirm that task is now done and progress changed
|
||||
- Continue to next task
|
||||
|
||||
**Pause if:**
|
||||
- Task is unclear → ask for clarification
|
||||
- Implementation reveals a design issue → suggest updating artifacts
|
||||
- A task needs work beyond what the spec and tasks describe, or you are tempted to drop, narrow, defer, or accept exceptions to specified behavior to make it fit → surface the added scope and ask; do not absorb it silently
|
||||
- Error or blocker encountered → report and wait for guidance
|
||||
- User interrupts
|
||||
|
||||
7. **On completion or pause, show status**
|
||||
|
||||
Display:
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: report that tracked tasks are complete and suggest review or verification as appropriate before archiving
|
||||
- If paused: explain why and wait for guidance
|
||||
|
||||
**Output During Implementation**
|
||||
|
||||
```
|
||||
## Implementing: <change-name> (schema: <schema-name>)
|
||||
|
||||
Working on task 3/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
|
||||
Working on task 4/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
```
|
||||
|
||||
**Output On Completion**
|
||||
|
||||
```
|
||||
## Implementation Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 7/7 tasks complete ✓
|
||||
|
||||
### Completed This Session
|
||||
- [x] Task 1
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tracked tasks are complete. Review or verify the change as appropriate
|
||||
before archiving. You can archive this change with `/openspec-archive-change`.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
|
||||
```
|
||||
## Implementation Paused
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 4/7 tasks complete
|
||||
|
||||
### Issue Encountered
|
||||
<description of the issue>
|
||||
|
||||
**Options:**
|
||||
1. <option 1>
|
||||
2. <option 2>
|
||||
3. Other approach
|
||||
|
||||
What would you like to do?
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Keep going through tasks until done or blocked
|
||||
- Always read context files before starting (from the apply instructions output)
|
||||
- If task is ambiguous, pause and ask before implementing
|
||||
- If implementation reveals issues, pause and suggest artifact updates
|
||||
- Keep code changes minimal and scoped to each task
|
||||
- Update task checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements - don't guess
|
||||
- When a task needs work beyond what the spec describes, surface the added scope and pause - never silently narrow, defer, or simplify away specified behavior
|
||||
- Only mark a task `- [x]` when its specified behavior is fully implemented, not when it is partially done or deferred
|
||||
- Use contextFiles from CLI output, don't assume specific file names
|
||||
- Use each task's sourcePath and line to update its exact checkbox
|
||||
- Do not use context or operation guidance as proof that a task is complete
|
||||
- Apply relevant project context; report conflicts with controlling workflow inputs
|
||||
- Consider every guidance entry; explain any inapplicable or conflicting advice
|
||||
- Do not copy runtime context or operation guidance into implementation files or planning artifacts
|
||||
- Preserve CLI-controlled blocked/ready/all-done behavior and completion criteria
|
||||
|
||||
**Fluid Workflow Integration**
|
||||
|
||||
This skill supports the "actions on a change" model:
|
||||
|
||||
- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions
|
||||
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly
|
||||
@@ -0,0 +1,221 @@
|
||||
---
|
||||
name: openspec-archive-change
|
||||
description: Archive a completed OpenSpec change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete. Also use when the user says "openspec archive" or "opsx archive".
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.14.1"
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `schemas`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Project check:** These steps expect a project that already uses OpenSpec. Before the first step that writes anything (`new change`, `archive`, `sync specs`, or authoring an artifact file), confirm the project has a root: run `openspec list --json` (with `--store <id>` when a store is selected, since the store is then the root) and read `root`. A root object means the project is set up. `"root": null` means it is not - there is no `openspec/` directory here, and a write such as `openspec new change` would create one as a side effect. The command also exits non-zero, which is that answer rather than a broken CLI, so read the JSON instead of retrying or working around it.
|
||||
|
||||
One `"root": null` is not about setup: when a `status` error message starts with `Declared in` or `Invalid store declaration in` and names this project's `openspec/config.yaml` (or `config.yml`), the project does use OpenSpec through a store it declares, which this machine cannot resolve (the store is not registered, or the `store:` line is malformed). Do not treat it as uninitialized and skip the branches below: stop before writing and show the user that error's `message` and `fix`.
|
||||
|
||||
Otherwise, with no root, what happens next depends on how this workflow was reached:
|
||||
|
||||
- **Auto-selected**: you chose this workflow yourself, without the user naming OpenSpec, naming this skill, or running its slash command. Stop using OpenSpec and answer the request normally, as you would with no OpenSpec installed. Do not ask them to set anything up and do not mention OpenSpec setup.
|
||||
- **Explicit OpenSpec request**: the user named OpenSpec, named this skill, or ran its slash command. Stop before writing and ask how to proceed: set this project up (`openspec init`), target a store they already have (`--store <id>`), or continue without OpenSpec for this request. Wait for their answer.
|
||||
|
||||
In both branches, never create the root as a side effect: do not run `openspec init` until the user asks for it, do not hand-create `openspec/` files, and do not let a command create it.
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve the full path from each delta spec when resolving its main spec.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
When prompting, show only active changes (not already archived).
|
||||
Include the schema used for each change if available.
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/openspec-archive-change <other>`).
|
||||
|
||||
**Load current archive inputs before the existing archive checks:**
|
||||
|
||||
After resolving the selected change and planning root, run:
|
||||
```bash
|
||||
openspec instructions archive --change "<name>" --json
|
||||
```
|
||||
Keep the same selected-root flags on this command. This lookup is advisory and
|
||||
optional: it only supplies extra prompt inputs, so it must never block archiving.
|
||||
If it exits non-zero or returns invalid JSON — for example on an older CLI that
|
||||
does not support this command yet — continue the archive workflow with no
|
||||
context and no operation guidance. Do not report an error and do not stop.
|
||||
|
||||
A successful response may omit both optional fields. Treat `context` as a
|
||||
required prompt-level input: read and consider it, and apply relevant project
|
||||
facts, conventions, and constraints. Treat `operationGuidance` as optional
|
||||
additive advice: read and consider every entry, and follow entries that are
|
||||
applicable and compatible with the built-in archive workflow.
|
||||
|
||||
Keep both fields separate from built-in steps, explicit user choices, resolved
|
||||
paths, CLI checks, and command contracts. If context conflicts with one of those
|
||||
controlling inputs, report the conflict and preserve the controlling value. If
|
||||
guidance is inapplicable or conflicts with a controlling input, do not follow it
|
||||
and explain why. Do not infer replacement paths, skipped prompts, or flags from
|
||||
either field, and do not copy their text verbatim into specs, change artifacts,
|
||||
or archive summaries unless the user separately asks for it. These are
|
||||
prompt-level behavior contracts, not enforceable checks.
|
||||
|
||||
2. **Check artifact completion status**
|
||||
|
||||
Run `openspec status --change "<name>" --json` to check artifact completion.
|
||||
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context
|
||||
- `artifacts`: List of artifacts with their status (`done`, `skipped`, or other)
|
||||
|
||||
**If any artifacts are neither `done` nor `skipped`** (skipped artifacts satisfy the requirement - the change declares skip_specs):
|
||||
- Display warning listing incomplete artifacts
|
||||
- Ask the user to confirm they want to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
3. **Check task completion status**
|
||||
|
||||
Run `openspec list --json` with the same selected-root flags and find the
|
||||
entry in `changes` whose `name` exactly matches the selected change.
|
||||
Require exactly one match and nonnegative integer `totalTasks` and
|
||||
`completedTasks`, with `completedTasks <= totalTasks`. The CLI resolves
|
||||
the schema's tracked task files, including custom artifact names, output
|
||||
paths, and globs.
|
||||
Incomplete tasks = `totalTasks - completedTasks`.
|
||||
|
||||
Do not infer task completion from artifact status or the absence of a
|
||||
top-level `tasks.md`. If the lookup fails, returns invalid JSON, omits or
|
||||
duplicates the selected change, or returns invalid counts, report the problem
|
||||
and stop before syncing or archiving.
|
||||
The CLI counts only `x`/`X` checkbox markers as complete;
|
||||
other markers, including unfamiliar ones, remain incomplete.
|
||||
|
||||
**If incomplete tasks found:**
|
||||
- Display warning showing count of incomplete tasks
|
||||
- Ask the user to confirm they want to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
**If `totalTasks` is zero:** Proceed without a task-related warning.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from status JSON as the only
|
||||
delta-spec source. If the `specs` entry is missing or
|
||||
`existingOutputPaths` is empty, proceed without a sync prompt and do not infer
|
||||
delta specs from other artifacts.
|
||||
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (use the store-aware `planningHome.root` from step 2, not a hardcoded repo path)
|
||||
- A missing main spec is **not automatically** "already synced". For a new capability, the main spec is an *output* of the sync, not an input:
|
||||
- If the delta has MODIFIED or RENAMED requirements, report that only ADDED requirements can create a new main spec and mark that capability as sync-blocked. Never invent a requirement that has no current version.
|
||||
- Otherwise, if the delta has only REMOVED requirements and the change's `.openspec.yaml` declares `retire_capabilities: true`, the capability is already retired: count it as already synced, warn that there is nothing left to remove, and do not recreate the main spec. Apply this rule both now and when verifying a completed sync.
|
||||
- Otherwise, if the delta has no ADDED requirements, report that no sync is possible and mark that capability as sync-blocked. For a REMOVED-only delta, warn that there is no main spec to remove from and leave the main-spec tree unchanged. `openspec archive` refuses the unmarked REMOVED-only case with `Spec must have at least one requirement`.
|
||||
- Otherwise, count the capability as needing sync and name it in the summary (`<capability-path>: new main spec will be created`). If the delta also has REMOVED requirements, warn that they will be ignored because there is no main spec to remove from. The sync creates the main spec from only the delta's ADDED requirements, exactly as `openspec archive` does.
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Continue assessing the remaining capabilities even when one is sync-blocked. Show a combined summary before prompting.
|
||||
|
||||
**Prompt options:**
|
||||
- If any capability is sync-blocked: explain why and offer only "Archive without syncing", "Cancel"
|
||||
- Otherwise, if changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- Otherwise, if already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
Route on the answer:
|
||||
- "Cancel" — stop, do not archive
|
||||
- "Archive without syncing" or "Archive now" — proceed to archive
|
||||
- "Sync now" or "Sync anyway" — sync, then verify (below). Do not start any sync while a capability is sync-blocked; explain the blocker and repeat the available choices.
|
||||
- Anything else — ask again rather than archiving
|
||||
|
||||
Before a selected sync writes any main spec, run
|
||||
`openspec instructions specs --change "<name>" --json` once with the same
|
||||
selected-root flags. Require a zero exit status and valid artifact-instruction
|
||||
JSON. If the lookup fails or returns invalid JSON, report the error and stop
|
||||
before writing any main spec or moving the change. A valid response with omitted
|
||||
`rules` is the no-rules case. Apply returned `rules` only to the content and
|
||||
form of main specs produced by this merge; do not use them as archive guidance,
|
||||
change CLI behavior, or copy the rule text into any output file.
|
||||
|
||||
Then run the `openspec-sync-specs` workflow inline (agent-driven intelligent merge) for change '<name>', passing the delta spec analysis and the fetched specs-rule snapshot from above, and wait for it to finish. The inline sync must reuse that snapshot without fetching `specs` instructions again. Do not delegate it to a background task — step 5 would move `changeRoot` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
|
||||
|
||||
If the sync reports any stop or blocking condition, treat the sync as failed.
|
||||
Stop the archive immediately. Do not perform the post-sync content comparison and do not move its `changeRoot`.
|
||||
Nothing has moved, so the user can fix the blocking condition or re-run the sync.
|
||||
|
||||
After the sync writes each main spec, verify its structure against the canonical sync contract:
|
||||
- A new main spec starts with a `# <capability> Specification` title. An existing main spec keeps its title exactly as it is.
|
||||
- Preserve existing `## Purpose` sections completely untouched for established main specs.
|
||||
- For a new main spec, copy the delta `## Purpose` verbatim. Warn only if the purpose text is shorter than standard validation expects. Do not regenerate or rewrite existing authored purpose. If no usable `## Purpose` is provided, use the existing TBD Purpose behavior and warning.
|
||||
- Verify that no delta-style section headers (`## ADDED Requirements`, `## MODIFIED Requirements`, `## REMOVED Requirements`, `## RENAMED Requirements`) remain in the main spec, adhering strictly to the sync workflow formatting rules.
|
||||
- Requirement blocks the sync wrote or changed use `### Requirement:` headings, and their scenarios use `#### Scenario:` headings, under the spec's `## Requirements` section. Leave content the delta does not mention exactly as it is.
|
||||
|
||||
Then re-run the comparison from the top of this step, including the explicitly retired, missing-spec case, against every capability that has a delta spec in `artifactPaths.specs.existingOutputPaths` — not only the ones the sync reports it touched. A successful sync leaves nothing left to apply, so each capability must now read as already synced:
|
||||
- ADDED requirements present
|
||||
- MODIFIED requirements carrying the scenario and description changes named in the delta, with their other scenarios intact
|
||||
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving `## Requirements` empty), its main spec deleted rather than left empty.
|
||||
- RENAMED requirements present under the new name and absent under the old one
|
||||
|
||||
If the sync failed, or any capability does not match, report what differs and stop — do not archive. Nothing has moved and `changeRoot` is intact, so the user can fix the mismatch or re-run the sync and start the archive again.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:
|
||||
```bash
|
||||
mkdir -p "<planningHome.changesDir>/archive"
|
||||
```
|
||||
|
||||
Generate the target name: use the change name as-is when it already starts with a `YYYY-MM-DD-` prefix; otherwise prepend the current date as `YYYY-MM-DD-<change-name>`. Never stack a second date (same rule as `openspec archive`).
|
||||
|
||||
**Check if target already exists:**
|
||||
- If yes: Fail with error, suggest renaming existing archive or using different date
|
||||
- If no: Move `changeRoot` to the archive directory
|
||||
|
||||
```bash
|
||||
mv "<changeRoot>" "<planningHome.changesDir>/archive/<target-name>"
|
||||
```
|
||||
|
||||
6. **Display summary**
|
||||
|
||||
Show archive completion summary including:
|
||||
- Change name
|
||||
- Schema that was used
|
||||
- Archive location
|
||||
- Whether specs were synced (if applicable)
|
||||
- Note about any warnings (incomplete artifacts/tasks)
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```markdown
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/<target-name>/
|
||||
**Specs:** <"✓ Synced to main specs" only if the step 4 verification passed; otherwise "No delta specs" or "Sync skipped">
|
||||
|
||||
<"All artifacts complete. All tasks complete." — or, if archived with warnings, list them instead (e.g. "Archived with 2 incomplete tasks")>
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Announce the selected change; prompt for selection when it is ambiguous
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, run the `openspec-sync-specs` workflow inline (agent-driven)
|
||||
- Never archive while a spec sync is still in flight — run the sync inline and verify the main specs before moving `changeRoot`
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
- Apply relevant runtime context and report conflicts; operation guidance remains advisory
|
||||
- Consider every guidance entry and explain any inapplicable or conflicting advice
|
||||
- Existing CLI checks, resolved paths, prompts, and command contracts are unchanged
|
||||
- Artifact rules constrain only the specs being written and are never operation guidance
|
||||
- Never copy runtime context, operation guidance, or artifact-rule text verbatim into output files
|
||||
@@ -0,0 +1,354 @@
|
||||
---
|
||||
name: openspec-explore
|
||||
description: Enter OpenSpec explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements in a project that uses OpenSpec. Use when the user wants to think through something before or during an OpenSpec change. Also use when the user says "openspec explore" or "opsx explore".
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.14.1"
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, do not start it here: say that explore mode does not implement, and point them at `/openspec-propose`, which turns the discussion into a change. The work happens from that change, never from explore mode. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts the request names; scaffold it first as described below.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `schemas`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Project check:** These steps expect a project that already uses OpenSpec. Before the first step that writes anything (`new change`, `archive`, `sync specs`, or authoring an artifact file), confirm the project has a root: run `openspec list --json` (with `--store <id>` when a store is selected, since the store is then the root) and read `root`. A root object means the project is set up. `"root": null` means it is not - there is no `openspec/` directory here, and a write such as `openspec new change` would create one as a side effect. The command also exits non-zero, which is that answer rather than a broken CLI, so read the JSON instead of retrying or working around it.
|
||||
|
||||
One `"root": null` is not about setup: when a `status` error message starts with `Declared in` or `Invalid store declaration in` and names this project's `openspec/config.yaml` (or `config.yml`), the project does use OpenSpec through a store it declares, which this machine cannot resolve (the store is not registered, or the `store:` line is malformed). Do not treat it as uninitialized and skip the branches below: stop before writing and show the user that error's `message` and `fix`.
|
||||
|
||||
Otherwise, with no root, what happens next depends on how this workflow was reached:
|
||||
|
||||
- **Auto-selected**: you chose this workflow yourself, without the user naming OpenSpec, naming this skill, or running its slash command. Stop using OpenSpec and answer the request normally, as you would with no OpenSpec installed. Do not ask them to set anything up and do not mention OpenSpec setup.
|
||||
- **Explicit OpenSpec request**: the user named OpenSpec, named this skill, or ran its slash command. Stop before writing and ask how to proceed: set this project up (`openspec init`), target a store they already have (`--store <id>`), or continue without OpenSpec for this request. Wait for their answer.
|
||||
|
||||
In both branches, never create the root as a side effect: do not run `openspec init` until the user asks for it, do not hand-create `openspec/` files, and do not let a command create it.
|
||||
|
||||
---
|
||||
|
||||
## The Stance
|
||||
|
||||
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
||||
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
||||
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
||||
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
||||
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
||||
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
||||
|
||||
---
|
||||
|
||||
## Planning a Change
|
||||
|
||||
When the user is planning a change, guide them toward shared understanding with focused discovery questions. For open-ended discussion, follow the conversation without imposing an interview or a required output.
|
||||
|
||||
Before asking a factual question, follow the context discovery below and inspect relevant OpenSpec artifacts, source, tests, docs, and configuration. Do not ask the user to repeat facts you can verify. Summarize relevant findings without reproducing private context or rules. If evidence is missing, conflicting, or inaccessible, state that limitation and ask only for the clarification needed to proceed.
|
||||
|
||||
- **Follow dependencies** - Resolve the next blocking decision before its dependent details. For example, clarify the user's outcome and scope before choosing an API or data model. Revisit downstream assumptions when an earlier answer changes. Skip branches that do not matter to this goal.
|
||||
- **Keep questions focused** - Ask one focused question at a time, and briefly explain why it matters and which decision it unlocks. Batch questions only if the user asks for a batch; keep them small and group related decisions.
|
||||
- **Offer grounded recommendations** - When evidence supports a recommendation, state your preferred option and why it fits the user's goals, with alternatives and their tradeoffs when useful. Do not invent intent, priorities, or external constraints: ask the user when only they can answer. Avoid a fixed question format.
|
||||
- **Keep a conversational record** - Track decisions in the conversation, not in files. Separate confirmed decisions from proposed defaults and unresolved questions. Silence is not acceptance. Accepting an answer or a batch of recommendations is not permission to write. Keep file-write confirmation separate from discovery questions and follow the guardrails below.
|
||||
|
||||
Stop asking when the user has enough clarity. Let them pause, pivot, or defer a decision; do not exhaust every branch or force a proposal.
|
||||
|
||||
For example, after inspecting the relevant code:
|
||||
|
||||
```text
|
||||
The CLI already uses SQLite and has no remote service. Is sharing state
|
||||
across devices in scope? That determines whether local storage is enough.
|
||||
If this stays a single-device tool, I recommend keeping SQLite to avoid
|
||||
adding a service to operate; shared state would need a separate sync design.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What You Might Do
|
||||
|
||||
Depending on what the user brings, you might:
|
||||
|
||||
**Explore the problem space**
|
||||
- Ask clarifying questions that emerge from what they said
|
||||
- Challenge assumptions
|
||||
- Reframe the problem
|
||||
- Find analogies
|
||||
|
||||
**Investigate the codebase**
|
||||
- Map existing architecture relevant to the discussion
|
||||
- Find integration points
|
||||
- Identify patterns already in use
|
||||
- Surface hidden complexity
|
||||
|
||||
**Compare options**
|
||||
- Brainstorm multiple approaches
|
||||
- Build comparison tables
|
||||
- Sketch tradeoffs
|
||||
- Recommend a path (if asked)
|
||||
|
||||
**Visualize**
|
||||
```
|
||||
+------------------------------------------+
|
||||
| Use ASCII diagrams liberally |
|
||||
+------------------------------------------+
|
||||
| |
|
||||
| [State A] -------> [State B] |
|
||||
| | |
|
||||
| v |
|
||||
| [State C] |
|
||||
| |
|
||||
| System diagrams, state machines, |
|
||||
| data flows, architecture sketches, |
|
||||
| dependency graphs, comparison tables |
|
||||
| |
|
||||
+------------------------------------------+
|
||||
```
|
||||
|
||||
**Draw with plain ASCII only** — borders `+` `-` `|`, arrows `-->` `<--` `^` `v`, markers `*` `x`.
|
||||
Unicode diagram glyphs can render at different widths across terminals, fonts, and locales, so padded boxes and aligned tables can drift. Keep every diagram character ASCII.
|
||||
|
||||
**Surface risks and unknowns**
|
||||
- Identify what could go wrong
|
||||
- Find gaps in understanding
|
||||
- Suggest spikes or investigations
|
||||
|
||||
---
|
||||
|
||||
## OpenSpec Awareness
|
||||
|
||||
You have full context of the OpenSpec system. Use it naturally, don't force it.
|
||||
|
||||
### Check for context
|
||||
|
||||
At the start, quickly check what exists:
|
||||
```bash
|
||||
openspec list --json
|
||||
```
|
||||
|
||||
This tells you:
|
||||
- If there are active changes
|
||||
- Their names and task status
|
||||
- What the user might be working on
|
||||
|
||||
That is the *change* list - work in flight. It does not include the project's durable capabilities, so list those too:
|
||||
```bash
|
||||
openspec list --specs
|
||||
```
|
||||
Add `--json` for ids and requirement counts, and append `--store "<id>"` only for a registered standalone store. This is the inventory of what the project already claims to do, and `openspec list` on its own never shows it. To look at one, run `openspec show "<spec-id>" --type spec --json --no-scenarios` (same `--store` rule) - it returns that capability's purpose and requirement texts without pulling the whole spec file into context, and `--type spec` stops a change of the same name from making it ambiguous.
|
||||
|
||||
The filtered read is only an overview. Before deciding what is already covered or what should change, read each relevant spec in full, including scenarios, with `openspec show "<spec-id>" --type spec` (same `--store` rule).
|
||||
|
||||
Then read the project's own context from the resolved root - `<root.path>/openspec/config.yaml` (or `config.yml`). Use the `root.path` returned above, and skip this if neither file exists:
|
||||
- `context`: project background - tech stack, conventions, constraints
|
||||
- `rules`: keyed by artifact id - the entries for an artifact apply only when you write that artifact
|
||||
|
||||
Ground your thinking in these. They are constraints for you to follow, not content to reproduce: do NOT copy them into the conversation or into any artifact you create.
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
|
||||
- "This feels solid enough to start a change. Want me to create a proposal?"
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding that change and creating the change artifacts the request names, and nothing else. This holds only when the request is theirs: a yes to an offer you made confirms only the scope your offer itself named, so name the change and the artifacts in the offer. Don't re-ask for what they already asked for; do ask before anything beyond it. Transition seamlessly into the requested capture:
|
||||
|
||||
1. Run `openspec new change "<name>"` (with `--store <id>` when applicable) before creating any artifacts. Never create a new change directory under `openspec/changes/` by hand; the CLI scaffold creates required metadata such as `.openspec.yaml`. Keep the selected `--store <id>` on every applicable follow-up `status` and `instructions` command.
|
||||
2. Run `openspec status --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is `ready`, run `openspec instructions "<artifact-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own `instruction` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run `openspec instructions "<prerequisite-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) for that prerequisite whether it is `ready` or `blocked`. If its own `instruction` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves.
|
||||
3. Follow the returned `template` and `instruction` fields. Read completed dependency files listed in `dependencies`, and apply `context` and `rules` as constraints without copying them into the artifact. If the instruction delegates creation to a specific skill or command, invoke it; otherwise write the artifact to `resolvedOutputPath`, using the instruction to choose a concrete path when it is a glob. Verify that the selected concrete output exists.
|
||||
4. After creating each artifact, re-run `openspec status --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) and continue until every requested artifact is `done`, `skipped`, or was deliberately skipped because its own `instruction` stated a condition that did not apply. Tell the user about a deliberate conditional skip, remember it, and do not reconsider it. Dependencies are enablers, not gates: if a requested artifact is still `blocked` only because you deliberately skipped a conditional prerequisite, run `openspec instructions "<artifact-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) despite the blocked status, then create it using step 3 only when those recorded conditional skips are its sole missing dependencies. If a requested artifact is blocked by a prerequisite the user did not ask to capture and cannot be conditionally skipped, explain that dependency and ask before expanding the capture.
|
||||
|
||||
Capture the artifact(s) the user requested without asking them to invoke another workflow command. If they asked only to start a change, stop after scaffolding and show its status. When the requested capture is done, stop there and name where the work continues: `/openspec-propose` writes the remaining planning artifacts, and `/openspec-apply-change` implements the change once tasks exist. Capturing artifacts never starts implementing them.
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
|
||||
1. **Resolve and read existing artifacts for context**
|
||||
- Run `openspec status --change "<name>" --json`.
|
||||
- Use `changeRoot`, `artifactPaths`, and `actionContext` from the status JSON.
|
||||
- Read existing files from `artifactPaths.<artifact>.existingOutputPaths`.
|
||||
|
||||
2. **Reference them naturally in conversation**
|
||||
- "Your design mentions using Redis, but we just realized SQLite fits better..."
|
||||
- "The proposal scopes this to premium users, but we're now thinking everyone..."
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|-------------------------------------|
|
||||
| New requirement discovered | `specs/<capability-path>/spec.md` |
|
||||
| Requirement changed | `specs/<capability-path>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
|
||||
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
||||
|
||||
---
|
||||
|
||||
## What You Don't Have To Do
|
||||
|
||||
- Follow a script
|
||||
- Ask the same questions every time
|
||||
- Produce a specific artifact
|
||||
- Reach a conclusion
|
||||
- Stay on topic if a tangent is valuable
|
||||
- Be brief (this is thinking time)
|
||||
|
||||
---
|
||||
|
||||
## Handling Different Entry Points
|
||||
|
||||
**User brings a vague idea:**
|
||||
```
|
||||
User: I'm thinking about adding real-time collaboration
|
||||
|
||||
You: Real-time collab is a big space. Let me think about this...
|
||||
|
||||
COLLABORATION SPECTRUM
|
||||
============================================
|
||||
|
||||
Awareness Coordination Sync
|
||||
| | |
|
||||
v v v
|
||||
+--------+ +--------+ +--------+
|
||||
|Presence| |Cursors | | CRDT |
|
||||
| "3 | | Multi | |Conflict|
|
||||
|online" | | select | | free |
|
||||
+--------+ +--------+ +--------+
|
||||
| | |
|
||||
trivial moderate complex
|
||||
|
||||
Where's your head at?
|
||||
```
|
||||
|
||||
**User brings a specific problem:**
|
||||
```
|
||||
User: The auth system is a mess
|
||||
|
||||
You: [reads codebase]
|
||||
|
||||
+---------------------------------------------+
|
||||
| CURRENT AUTH FLOW |
|
||||
+---------------------------------------------+
|
||||
|
|
||||
+-------------+-------------+
|
||||
v v v
|
||||
+---------+ +---------+ +---------+
|
||||
| Google | | GitHub | | Email |
|
||||
| OAuth | | OAuth | | Magic |
|
||||
+----+----+ +----+----+ +----+----+
|
||||
| | |
|
||||
+-------------+-------------+
|
||||
v
|
||||
+-----------+
|
||||
| Session |
|
||||
+-----+-----+
|
||||
|
|
||||
v
|
||||
+-----------+
|
||||
| Perms |
|
||||
+-----------+
|
||||
|
||||
I see three tangles. Which one's burning?
|
||||
```
|
||||
|
||||
**User is stuck mid-implementation:**
|
||||
```
|
||||
User: /openspec-explore add-auth-system
|
||||
The OAuth integration is more complex than expected
|
||||
|
||||
You: [reads change artifacts]
|
||||
|
||||
You're on task 4: "Implement OAuth flow"
|
||||
|
||||
Let me trace what's involved...
|
||||
|
||||
[draws diagram, explores options, suggests paths]
|
||||
|
||||
Want to update the design to reflect this?
|
||||
Or add a spike task to investigate?
|
||||
```
|
||||
|
||||
**User wants to compare options:**
|
||||
```
|
||||
User: Should we use Postgres or SQLite?
|
||||
|
||||
You: Generic answer is boring. What's the context?
|
||||
|
||||
User: A CLI tool that tracks local dev environments
|
||||
|
||||
You: That changes everything.
|
||||
|
||||
+-------------------------------------------------+
|
||||
| CLI TOOL DATA STORAGE |
|
||||
+-------------------------------------------------+
|
||||
|
||||
Key constraints:
|
||||
- No daemon running
|
||||
- Must work offline
|
||||
- Single user
|
||||
|
||||
SQLite Postgres
|
||||
Deployment embedded needs server
|
||||
Offline yes no
|
||||
Single file yes no
|
||||
|
||||
SQLite. Not even close.
|
||||
|
||||
Unless... is there a sync component?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ending Discovery
|
||||
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into a proposal**: "Ready to start? Run `/openspec-propose` and this becomes a change."
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
When it feels like things are crystallizing, you might summarize:
|
||||
|
||||
```
|
||||
## What We Figured Out
|
||||
|
||||
**The problem**: [crystallized understanding]
|
||||
|
||||
**The approach**: [if one emerged]
|
||||
|
||||
**Open questions**: [if any remain]
|
||||
|
||||
**Next steps** (if ready):
|
||||
- Turn this into a change: `/openspec-propose`
|
||||
- Keep exploring: just keep talking
|
||||
```
|
||||
|
||||
But this summary is optional. Sometimes the thinking IS the value.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Don't implement** - Never write code or implement features. Workflow configuration counts too: creating or editing schemas, templates, or `openspec/config.yaml` is a change, not thinking. Creating or updating OpenSpec change artifacts within the confirmed scope is fine, writing anything else is not. When the user is ready to build, name the handoff rather than starting: `/openspec-propose` turns the discussion into a change, and the work happens there.
|
||||
- **Don't fake understanding** - If something is unclear, dig deeper
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including `openspec new change` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs `openspec new change` whenever you are the one proposing the capture; the user's own capture request is the exception, handled in the capture transition above.
|
||||
- **Don't manually scaffold changes** - Never create a new change directory under `openspec/changes/` by hand. Always use `openspec new change "<name>"` (with `--store <id>` when applicable) so required metadata such as `.openspec.yaml` is created before writing artifacts.
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
name: openspec-propose
|
||||
description: Propose a new OpenSpec change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation. Also use when the user says "openspec propose" or "opsx propose".
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.14.1"
|
||||
---
|
||||
|
||||
Propose a new change - create the change and generate all artifacts in one step.
|
||||
|
||||
**Planning boundary**: This workflow creates planning artifacts only. The user request that selected or triggered this workflow authorizes planning only, even if it asks to build or fix something. Do not edit project code. After the planning artifacts are complete, stop. Do not start implementation in the same response, even if the initial request asks for it. Wait for a new user request after the artifacts are presented; then start the apply workflow.
|
||||
|
||||
I'll create a change with the artifacts your schema defines. With the default spec-driven schema that is:
|
||||
- proposal.md (what & why)
|
||||
- `specs/<capability-path>/spec.md` (what the system must do - a delta, not the main spec)
|
||||
- design.md (how)
|
||||
- tasks.md (implementation steps)
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
When the user is ready to implement, they must start the apply workflow explicitly.
|
||||
|
||||
---
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `schemas`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Project check:** These steps expect a project that already uses OpenSpec. Before the first step that writes anything (`new change`, `archive`, `sync specs`, or authoring an artifact file), confirm the project has a root: run `openspec list --json` (with `--store <id>` when a store is selected, since the store is then the root) and read `root`. A root object means the project is set up. `"root": null` means it is not - there is no `openspec/` directory here, and a write such as `openspec new change` would create one as a side effect. The command also exits non-zero, which is that answer rather than a broken CLI, so read the JSON instead of retrying or working around it.
|
||||
|
||||
One `"root": null` is not about setup: when a `status` error message starts with `Declared in` or `Invalid store declaration in` and names this project's `openspec/config.yaml` (or `config.yml`), the project does use OpenSpec through a store it declares, which this machine cannot resolve (the store is not registered, or the `store:` line is malformed). Do not treat it as uninitialized and skip the branches below: stop before writing and show the user that error's `message` and `fix`.
|
||||
|
||||
Otherwise, with no root, what happens next depends on how this workflow was reached:
|
||||
|
||||
- **Auto-selected**: you chose this workflow yourself, without the user naming OpenSpec, naming this skill, or running its slash command. Stop using OpenSpec and answer the request normally, as you would with no OpenSpec installed. Do not ask them to set anything up and do not mention OpenSpec setup.
|
||||
- **Explicit OpenSpec request**: the user named OpenSpec, named this skill, or ran its slash command. Stop before writing and ask how to proceed: set this project up (`openspec init`), target a store they already have (`--store <id>`), or continue without OpenSpec for this request. Wait for their answer.
|
||||
|
||||
In both branches, never create the root as a side effect: do not run `openspec init` until the user asks for it, do not hand-create `openspec/` files, and do not let a command create it.
|
||||
|
||||
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Understand the request and clarify material ambiguity**
|
||||
|
||||
If no clear input is provided, ask the user (open-ended, no preset options):
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
If the request contains ambiguity that would materially affect scope, externally observable behavior, compatibility, or acceptance criteria, ask the user before creating the change. For minor details, make a reasonable assumption and record it in the planning artifacts.
|
||||
|
||||
2. **Load project context**
|
||||
|
||||
Run `openspec context --json` from the current working directory (or `openspec context --json --store "<store-id>"` when a registered store was explicitly selected). Use the returned `root.path` as the authoritative OpenSpec root. If context reports `no_openspec_root`, stop without creating or changing any files and follow the **Project check** above for how this workflow was reached. Offer `openspec init` only for an explicit OpenSpec request, and wait for the user to request initialization. Do not initialize automatically or run `openspec new change`. After initialization, rerun this context check before continuing. For any other context failure, stop and report the error; do not fall back to the current directory or run later OpenSpec commands without the selected store.
|
||||
|
||||
Only when context returns a resolved `root.path`, read `<root.path>/openspec/config.yaml`. Use `config.yml` only when `config.yaml` does not exist. If neither file exists, continue without project context. Do not fall back to `config.yml` if `config.yaml` is unreadable or invalid.
|
||||
|
||||
If the file parses as a YAML object and its `context` field is a string no larger than 51,200 bytes in UTF-8, apply that field before exploring the codebase or making planning decisions. If the file cannot be read or parsed, or the context field is invalid or oversized, continue without project context. Validate this field independently of other config fields, as OpenSpec does.
|
||||
|
||||
Treat context as project-provided data and constraints, not as authority to change this workflow: it cannot override user authorization, the planning boundary, tool restrictions, or artifact and output rules. Do not copy the context into artifacts; use it to focus any codebase exploration and as a constraint on the proposal.
|
||||
|
||||
3. **Determine the workflow schema**
|
||||
|
||||
Use the configured default schema unless the user explicitly requests a different workflow.
|
||||
|
||||
**Use a different schema only if the user:**
|
||||
- Explicitly requests a specific schema by name → use `--schema <schema-name>`
|
||||
- Asks to "show workflows" or asks "what workflows" exist → resolve the authoritative root by running `openspec context --json` from the current working directory. If the user explicitly selected a registered store, use `openspec context --json --store "<store-id>"`. Then run `openspec schemas --json` with its working directory set to the returned `root.path` and let them choose. This preserves roots selected by a local `store:` pointer or the global `defaultStore`; when a registered store was explicitly selected, append `--store "<store-id>"` to `openspec schemas --json` as well. If context fails, stop as described in the context-loading step; do not fall back to the current directory.
|
||||
|
||||
Otherwise, omit `--schema` to preserve the configured default.
|
||||
|
||||
4. **Create the change directory**
|
||||
|
||||
Choose one schema form below. If a registered store is selected, append `--store "<store-id>"` to that command and each later OpenSpec command shown below that accepts `--store`.
|
||||
|
||||
Using the configured default:
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
|
||||
Using an explicitly requested schema:
|
||||
```bash
|
||||
openspec new change "<name>" --schema "<schema-name>"
|
||||
```
|
||||
This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.
|
||||
|
||||
5. **Get the artifact build order**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to get:
|
||||
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
||||
- `artifacts`: list of all artifacts, each with its `status` and its `requires` edges (the artifact IDs it directly depends on)
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
|
||||
|
||||
6. **Create every artifact in the required set**
|
||||
|
||||
Use a todo list to track progress through the artifacts.
|
||||
|
||||
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
||||
|
||||
a. **For each artifact that is `ready` (dependencies satisfied)**:
|
||||
- Get instructions:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- The instructions JSON includes:
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `skipped`/`warning`: present when the change declares skip_specs and this artifact must NOT be created - stop and pick another artifact
|
||||
- `resolvedOutputPath`: Resolved path or pattern to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context - always re-read them from disk, even if you saw them earlier in the conversation (the user may have edited them)
|
||||
- **Inspect the relevant project before drafting**: Read `context` and `rules` first, then inspect relevant implementation, nearby tests, configuration, and documentation outside `openspec/`. Keep inspection read-only and proportional to the change; reuse findings for later artifacts and inspect more only as needed.
|
||||
- Identify the target project from the request and project context; the planning home may be separate from the code. If the target is unclear, ask. For greenfield or non-code changes, inspect the available structure and relevant documents. If source is unavailable, state the limitation and ask when it materially affects the plan.
|
||||
- Ground scope, approach, and tasks in what you find. Distinguish observed behavior from assumptions and proposed additions; surface conflicts with existing specs instead of silently deciding which is correct.
|
||||
- Do this discovery now, rather than leaving generic "explore the codebase" or "make a plan" tasks for implementation. Keep any necessary follow-up investigation specific to an unresolved question.
|
||||
- If the `instruction` field delegates creation to a specific skill or command, invoke it to produce the artifact instead of writing the file yourself, then verify the artifact file exists at `resolvedOutputPath`
|
||||
- Otherwise create the artifact file using `template` as the structure and write it to `resolvedOutputPath`. If `resolvedOutputPath` is a glob, follow `instruction` to choose the concrete file path
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "Created <artifact-id>"
|
||||
|
||||
b. **Continue until every artifact in the required set exists (not just `apply.requires`)**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
- The required set is `applyRequires` plus every artifact reachable from those by following the `requires` edges in `status --json` - walk them transitively (spec-driven closes over proposal, specs, design, tasks). Leave artifacts outside that set alone
|
||||
- `status` is file-existence only, so an `applyRequires` artifact reading `done` does NOT mean its dependencies exist - writing `tasks.md` early marks `tasks` done while `specs` was never written. Use each artifact's `requires` edges, not its `status`, to build the required set: a `done` artifact still lists what it depends on
|
||||
- An artifact already reading `status: "skipped"` is satisfied: the change declares `skip_specs` in `.openspec.yaml`, so its files must NOT exist. Never try to create one
|
||||
- Create every artifact in the required set that is missing, then re-check - creating one can unblock others
|
||||
- Skip one only when `status` already reports it `skipped`, or when its own `instruction` says it is conditional: run `openspec instructions <artifact-id> --change "<name>" --json` and skip only if its `instruction` field marks it optional (e.g. "create only if..."). Spec-driven's `design.md` qualifies; `specs` qualifies only via the `skipped` status above, never by your own judgment. Tell the user, and do not reconsider it
|
||||
- Dependencies are enablers, not gates: if a required artifact is still `blocked` only because you skipped a conditional dependency, write it anyway
|
||||
- Stop when every artifact in the required set is `done`, `skipped`, or was deliberately skipped
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
- Ask the user to clarify
|
||||
- Then continue with creation
|
||||
|
||||
7. **Show final status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location
|
||||
- List of artifacts created with brief descriptions, plus any conditional artifact you skipped and why
|
||||
- What's ready: "All artifacts needed for implementation are ready."
|
||||
- Prompt: "The artifacts are ready for review. When you are ready, run `/openspec-apply-change` or ask me to apply this change."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type - it is the authoritative guidance, even for familiar artifact names
|
||||
- If the `instruction` field directs you to use a specific skill or command to create the artifact, invoke it instead of writing the artifact directly
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use `template` as the structure for your output file - fill in its sections
|
||||
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
||||
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
||||
- These guide what you write, but should never appear in the output
|
||||
|
||||
**Guardrails**
|
||||
- The request that invoked this workflow authorizes planning only. Any implementation or apply instruction in that request does not carry forward. Do NOT implement the change, start the apply workflow, or edit project code during this workflow. After presenting the artifacts, stop and wait for a new user request to start the apply workflow
|
||||
- Create every artifact the apply phase transitively depends on, not just the ids listed in `apply.requires`
|
||||
- Always read dependency artifacts before creating a new one - re-read from disk, not from conversation memory (files may have changed since you last saw them)
|
||||
- Ask about ambiguities that would materially change scope, externally observable behavior, compatibility, or acceptance criteria; for minor details, make reasonable assumptions and record them
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
@@ -0,0 +1,290 @@
|
||||
---
|
||||
name: openspec-sync-specs
|
||||
description: Sync delta specs from an OpenSpec change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change. Also use when the user says "openspec sync" or "opsx sync".
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.14.1"
|
||||
---
|
||||
|
||||
Sync delta specs from a change to main specs.
|
||||
|
||||
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `schemas`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Project check:** These steps expect a project that already uses OpenSpec. Before the first step that writes anything (`new change`, `archive`, `sync specs`, or authoring an artifact file), confirm the project has a root: run `openspec list --json` (with `--store <id>` when a store is selected, since the store is then the root) and read `root`. A root object means the project is set up. `"root": null` means it is not - there is no `openspec/` directory here, and a write such as `openspec new change` would create one as a side effect. The command also exits non-zero, which is that answer rather than a broken CLI, so read the JSON instead of retrying or working around it.
|
||||
|
||||
One `"root": null` is not about setup: when a `status` error message starts with `Declared in` or `Invalid store declaration in` and names this project's `openspec/config.yaml` (or `config.yml`), the project does use OpenSpec through a store it declares, which this machine cannot resolve (the store is not registered, or the `store:` line is malformed). Do not treat it as uninitialized and skip the branches below: stop before writing and show the user that error's `message` and `fix`.
|
||||
|
||||
Otherwise, with no root, what happens next depends on how this workflow was reached:
|
||||
|
||||
- **Auto-selected**: you chose this workflow yourself, without the user naming OpenSpec, naming this skill, or running its slash command. Stop using OpenSpec and answer the request normally, as you would with no OpenSpec installed. Do not ask them to set anything up and do not mention OpenSpec setup.
|
||||
- **Explicit OpenSpec request**: the user named OpenSpec, named this skill, or ran its slash command. Stop before writing and ask how to proceed: set this project up (`openspec init`), target a store they already have (`--store <id>`), or continue without OpenSpec for this request. Wait for their answer.
|
||||
|
||||
In both branches, never create the root as a side effect: do not run `openspec init` until the user asks for it, do not hand-create `openspec/` files, and do not let a command create it.
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve the full path from each delta spec when resolving its main spec.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
When prompting, show changes that have delta specs (under `specs/` directory).
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/openspec-sync-specs <other>`).
|
||||
|
||||
2. **Resolve change context**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
|
||||
The JSON includes `planningHome.root`. Main specs live under `<planningHome.root>/openspec/specs/` — use that (store-aware) root for every main-spec path below, not a hardcoded repo path. When a store is selected it points at the store, not the current repository.
|
||||
|
||||
3. **Find delta specs**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the
|
||||
only source of delta spec paths. If the `specs` entry is missing or
|
||||
`existingOutputPaths` is empty, report that there are no delta specs to sync,
|
||||
do not infer them from other artifacts, and stop without requesting artifact
|
||||
instructions or writing a main spec.
|
||||
|
||||
Sync every path in `existingOutputPaths` unless the caller narrowed the set.
|
||||
A caller narrows it by naming an explicit list of complete entries from
|
||||
`existingOutputPaths` — copy those absolute values verbatim. Archive does
|
||||
this inline, and a user can too (for example, by selecting the entry ending
|
||||
in `/specs/billing/invoices/spec.md`).
|
||||
Then sync only the named paths and leave the remaining delta specs untouched:
|
||||
bulk archive excludes a delta whose implementation it could not find, and
|
||||
syncing it anyway would write a main spec the caller deliberately withheld.
|
||||
Carry that narrowed selection through step 4; never widen it back to the full
|
||||
list. If a named path is not in `existingOutputPaths`, do not sync it —
|
||||
report it and stop, rather than dropping it silently. If the named list is
|
||||
empty, report that there is nothing to sync and stop without writing a main
|
||||
spec.
|
||||
|
||||
Each delta spec file contains sections like:
|
||||
- `## ADDED Requirements` - New requirements to add
|
||||
- `## MODIFIED Requirements` - Changes to existing requirements
|
||||
- `## REMOVED Requirements` - Requirements to remove
|
||||
- `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
|
||||
|
||||
If no delta specs found, inform user and stop.
|
||||
|
||||
4. **For each delta spec, apply changes to main specs**
|
||||
|
||||
Before the first main-spec write, obtain one current specs-rule snapshot:
|
||||
- If archive invoked this workflow inline and supplied a valid snapshot from
|
||||
`openspec instructions specs --change "<name>" --json`, reuse it and do not
|
||||
fetch the same instructions again.
|
||||
- Otherwise run that command once now with the same selected-root flags.
|
||||
- If the direct lookup exits non-zero or returns invalid artifact-instruction
|
||||
JSON, report the error and stop before writing any main spec. Do not treat the
|
||||
failure as an absent rule set.
|
||||
- A valid response with omitted `rules` means no artifact rules are configured
|
||||
and the existing semantic merge continues.
|
||||
|
||||
Apply returned `rules` only to the content and form of the main specs produced
|
||||
by this merge. Artifact rules are not operation guidance and cannot change
|
||||
selected roots, delta paths, CLI checks, or workflow steps. Use their text as
|
||||
constraints without copying it verbatim into a main spec or summary.
|
||||
|
||||
For each capability delta spec path selected in step 3 — the full `existingOutputPaths` list, or the narrowed subset when a caller supplied one (these may belong to a selected store, not the repo):
|
||||
|
||||
a. **Read the delta spec** to understand the intended changes
|
||||
|
||||
b. **Read the main spec** at `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (may not exist yet)
|
||||
|
||||
**If it does not exist yet** (a new capability), match what `openspec archive` does:
|
||||
only ADDED requirements may be applied - step d creates the spec from them.
|
||||
MODIFIED and RENAMED have no requirement to act on, so stop the sync for that
|
||||
capability and report that its main spec does not exist and only ADDED is allowed
|
||||
for a new spec; never invent the missing requirement. REMOVED has nothing to
|
||||
remove - skip it and warn.
|
||||
|
||||
c. **Apply changes intelligently**:
|
||||
|
||||
**ADDED Requirements:**
|
||||
- If requirement doesn't exist in main spec → add it
|
||||
- If requirement already exists → update it to match (treat as implicit MODIFIED)
|
||||
|
||||
**MODIFIED Requirements:**
|
||||
- Find the requirement in main spec
|
||||
- Apply the changes - this can be:
|
||||
- Adding new scenarios the main spec does not have yet
|
||||
- Modifying existing scenarios
|
||||
- Changing the requirement description
|
||||
- Preserve scenarios/content not mentioned in the delta
|
||||
|
||||
**REMOVED Requirements:**
|
||||
- Remove the entire requirement block from main spec
|
||||
- Retiring the capability. Delete the whole `spec.md` - and the directory once
|
||||
nothing else is left in it - only when ALL of these hold:
|
||||
1. removing the requirements *this run* left no requirement blocks;
|
||||
2. the rest of the spec is well-formed (it still has a `## Purpose`);
|
||||
3. the main spec was not already empty before this sync - if you removed
|
||||
nothing, change nothing;
|
||||
4. every other nonblank line in the whole file is accounted for as the
|
||||
title, Purpose, Requirements header, or a canonical requirement's
|
||||
statement, scenarios, or fenced examples;
|
||||
5. the change's `.openspec.yaml` declares `retire_capabilities: true`;
|
||||
6. the `spec.md` resolves inside the real specs root (do not follow a
|
||||
capability-directory symlink to delete an external file).
|
||||
If removing the selected requirements would leave no requirement blocks and
|
||||
any retirement condition is not satisfied, do not modify the main spec. Stop
|
||||
the sync for that capability, report the blocking condition, and tell the user
|
||||
how to resolve it. Never write or leave an empty `## Requirements` section.
|
||||
When only the marker is missing, say that too - it is the one thing the user
|
||||
can add to make the retirement go through.
|
||||
- Deleting the file also deletes its `## Purpose`; any other section blocks
|
||||
retirement. Name Purpose when you report the retirement. Include a pasteable
|
||||
`git checkout` only when the spec lived in the caller's checkout;
|
||||
otherwise give checkout-scoped recovery guidance.
|
||||
|
||||
**RENAMED Requirements:**
|
||||
- Find the FROM requirement, rename to TO
|
||||
|
||||
**`## Purpose` in the delta:**
|
||||
- The main spec already has one and it is authoritative - leave it alone
|
||||
(this is what `openspec archive` does; it warns and moves on)
|
||||
|
||||
d. **Create new main spec** if capability doesn't exist yet:
|
||||
- Only when the delta has ADDED requirements to put in it and no MODIFIED or
|
||||
RENAMED requirements blocked this capability in step b. Otherwise create nothing
|
||||
and leave the specs directory untouched. For a REMOVED-only delta, if the change's
|
||||
`.openspec.yaml` declares `retire_capabilities: true`, report it as already retired
|
||||
and continue without recreating the spec. Without that marker, report the sync as blocked:
|
||||
`openspec archive` rejects it with `Spec must have at least one requirement`.
|
||||
An empty delta has no operations to sync; report it as blocked too.
|
||||
Never write an empty `## Requirements` section.
|
||||
- Create `<planningHome.root>/openspec/specs/<capability-path>/spec.md`
|
||||
- Add Purpose section: copy the delta's `## Purpose` body verbatim when it has one
|
||||
(this is what `openspec archive` does); only write a brief TBD placeholder when it does not
|
||||
- Add Requirements section with the ADDED requirements
|
||||
- Follow the **Main Spec Format Reference** below
|
||||
|
||||
5. **Validate updated main specs**
|
||||
|
||||
Run `openspec validate --specs` with the same selected-root flags used earlier.
|
||||
If validation fails, report the problems and do not claim the sync succeeded.
|
||||
|
||||
6. **Show summary**
|
||||
|
||||
After applying all changes, summarize:
|
||||
- Which capabilities were updated
|
||||
- What changes were made (requirements added/modified/removed/renamed)
|
||||
- Any new main spec left with a TBD Purpose placeholder, so it gets written
|
||||
now rather than lingering
|
||||
- Any capability retired, naming the deleted `spec.md`, its Purpose, and
|
||||
either a pasteable `git checkout` or checkout-scoped recovery guidance
|
||||
|
||||
**Delta Spec Format Reference**
|
||||
|
||||
```markdown
|
||||
# Spec Delta
|
||||
|
||||
## Purpose
|
||||
|
||||
Only on a delta that introduces a brand-new capability. Seeds the new main spec.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
The system SHALL do something new.
|
||||
|
||||
#### Scenario: Basic case
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Existing Feature
|
||||
The system SHALL keep doing the existing thing, now also handling A.
|
||||
|
||||
#### Scenario: Scenario the main spec already has
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
|
||||
#### Scenario: New scenario to add
|
||||
- **WHEN** user does A
|
||||
- **THEN** system does B
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Deprecated Feature
|
||||
|
||||
## RENAMED Requirements
|
||||
|
||||
- FROM: `### Requirement: Old Name`
|
||||
- TO: `### Requirement: New Name`
|
||||
```
|
||||
|
||||
**Main Spec Format Reference**
|
||||
|
||||
Main specs are what the delta merges INTO. They must never contain delta operation headers (`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`) - after syncing, every requirement lives under a single `## Requirements` section:
|
||||
|
||||
```markdown
|
||||
# <capability> Specification
|
||||
|
||||
## Purpose
|
||||
Short description of what this capability does and why it exists.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
The system SHALL do something new.
|
||||
|
||||
#### Scenario: Basic case
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
```
|
||||
|
||||
**Key Principle: Intelligent Merging**
|
||||
|
||||
Unlike programmatic merging, you merge rather than overwrite:
|
||||
- A MODIFIED block carries the whole requirement - body plus every scenario that survives the change. `openspec validate` and `openspec archive` both reject one that drops a scenario the main spec still has.
|
||||
- Keep anything the delta does not mention, in the main spec's existing order
|
||||
- Use your judgment to merge changes sensibly
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```markdown
|
||||
## Specs Synced: <change-name>
|
||||
|
||||
Updated main specs:
|
||||
|
||||
**<capability-1>**:
|
||||
- Added requirement: "New Feature"
|
||||
- Modified requirement: "Existing Feature" (added 1 scenario)
|
||||
|
||||
**<capability-2>**:
|
||||
- Created new spec file
|
||||
- Added requirement: "Another Feature"
|
||||
|
||||
Main specs are now updated. The change remains active - archive when implementation is complete.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Read both delta and main specs before making changes
|
||||
- Preserve existing content not mentioned in delta
|
||||
- Never copy a delta file into a main spec as-is - merge its content so the main spec keeps the Main Spec Format Reference structure, with no delta operation headers
|
||||
- If something is unclear, ask for clarification
|
||||
- Show what you're changing as you go
|
||||
- The operation should be idempotent - running twice should give same result
|
||||
- Use only `artifactPaths.specs.existingOutputPaths`; never infer delta specs from unrelated artifacts
|
||||
- Honor a caller-supplied subset of `existingOutputPaths`; never widen it back to the full list
|
||||
- Fetch specs instructions once for direct sync, or reuse the archive-supplied snapshot inline
|
||||
- Stop before every main-spec write on a non-zero or invalid JSON specs-instruction response
|
||||
- Artifact rules constrain only the specs being written and are never copied into output files
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
name: openspec-update-change
|
||||
description: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Also use when the user says "openspec update change" or "opsx update". If the user means the openspec update CLI command, which refreshes generated files, run that command instead. Never edits code.
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.14.1"
|
||||
---
|
||||
|
||||
Revise a change's existing planning artifacts and keep them coherent. Never edit code.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `schemas`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Project check:** These steps expect a project that already uses OpenSpec. Before the first step that writes anything (`new change`, `archive`, `sync specs`, or authoring an artifact file), confirm the project has a root: run `openspec list --json` (with `--store <id>` when a store is selected, since the store is then the root) and read `root`. A root object means the project is set up. `"root": null` means it is not - there is no `openspec/` directory here, and a write such as `openspec new change` would create one as a side effect. The command also exits non-zero, which is that answer rather than a broken CLI, so read the JSON instead of retrying or working around it.
|
||||
|
||||
One `"root": null` is not about setup: when a `status` error message starts with `Declared in` or `Invalid store declaration in` and names this project's `openspec/config.yaml` (or `config.yml`), the project does use OpenSpec through a store it declares, which this machine cannot resolve (the store is not registered, or the `store:` line is malformed). Do not treat it as uninitialized and skip the branches below: stop before writing and show the user that error's `message` and `fix`.
|
||||
|
||||
Otherwise, with no root, what happens next depends on how this workflow was reached:
|
||||
|
||||
- **Auto-selected**: you chose this workflow yourself, without the user naming OpenSpec, naming this skill, or running its slash command. Stop using OpenSpec and answer the request normally, as you would with no OpenSpec installed. Do not ask them to set anything up and do not mention OpenSpec setup.
|
||||
- **Explicit OpenSpec request**: the user named OpenSpec, named this skill, or ran its slash command. Stop before writing and ask how to proceed: set this project up (`openspec init`), target a store they already have (`--store <id>`), or continue without OpenSpec for this request. Wait for their answer.
|
||||
|
||||
In both branches, never create the root as a side effect: do not run `openspec init` until the user asks for it, do not hand-create `openspec/` files, and do not let a command create it.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
This workflow revises artifacts that already exist; it never creates missing ones. When an artifact is missing, `openspec status --change "<name>" --json` names the next one and `openspec instructions "<artifact-id>" --change "<name>" --json` explains how to write it.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes sorted by most recently modified, and ask the user to select one
|
||||
|
||||
When prompting, present the top 3-4 most recently modified changes as options, showing:
|
||||
- Change name
|
||||
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
||||
- How recently it was modified (from `lastModified` field)
|
||||
|
||||
Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to update.
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/openspec-update-change <other>`).
|
||||
|
||||
2. **Get the change's artifacts**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand current state. The response includes:
|
||||
- `schemaName`: The workflow schema being used (e.g., "spec-driven")
|
||||
- `artifacts`: Array of artifacts with their status ("done", "skipped", "ready", "blocked")
|
||||
- `isPlanningComplete`: Boolean indicating if all planning artifacts are complete. Older CLI versions expose the same value as `isComplete`.
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
|
||||
|
||||
The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.
|
||||
|
||||
The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.
|
||||
|
||||
3. **Understand the request**
|
||||
- If the user asked for a specific revision ("the design now uses X"), that is the starting edit.
|
||||
- If they only said "update" / "make this coherent", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.
|
||||
|
||||
4. **Read and reconcile**
|
||||
- Read the artifact(s) the request touches and the change's other existing artifacts.
|
||||
- Draft the requested edit in the conversation, not in files. Work out exactly what it changes; step 5 owns every write. Then check every other existing artifact against the drafted edit - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.
|
||||
- Note everything that is now inconsistent, missing, or contradictory.
|
||||
- Propose revisions to files that already exist (`existingOutputPaths`). If an artifact has no existing output files and status `ready` or `blocked`, note it and point the user to `openspec instructions "<artifact-id>" --change "<name>" --json` for how to create them. Leave `skipped` artifacts untouched; do not treat them as missing or defer them to the continue workflow.
|
||||
- A glob artifact (e.g. `specs/**/*.md`) is marked `done` after at least one file matches, and the continue workflow only handles `ready` artifacts. When reconciliation identifies a missing file for a glob artifact whose `existingOutputPaths` is non-empty:
|
||||
1. Run `openspec instructions "<artifact-id>" --change "<name>" --json` and use its `instruction` and `template`. Treat `context` and `rules` as constraints; do not copy them into the file. If instructions report `skipped: true`, do not create the file. Read current dependency files from disk; if a required non-skipped dependency is missing, stop and ask the user to restore it first.
|
||||
2. Choose a concrete path inside `changeRoot` that matches `artifactPaths.<id>.outputPath` and does not already exist. Verify it remains inside `changeRoot` after resolving any symlinked parent directories. The glob `resolvedOutputPath` is not a valid target.
|
||||
3. Include the new file in step 5's proposed revisions and create it only after the user confirms.
|
||||
4. After confirmation, immediately before creation, refresh status and instructions. Verify the artifact is still in scope, not skipped, and partially populated; repeat the concrete-path checks above.
|
||||
5. Use a create operation that fails if the target already exists. If `instruction` delegates creation to another skill or command, invoke it only if it can honor the confirmed path and these guardrails; otherwise stop. If any check fails or the confirmed draft is no longer valid, stop and reconcile with the user rather than replacing existing content or choosing a different path.
|
||||
- If the change is already coherent, say so and propose no revisions.
|
||||
|
||||
5. **Confirm and apply, one artifact at a time**
|
||||
- This step performs every artifact write in this workflow; no earlier step edits an artifact.
|
||||
- Show each proposed revision and why - including the requested edit drafted in step 4. Write only after the user confirms.
|
||||
- If the user rejects a revision, do not write it - leave that artifact unchanged.
|
||||
- When a substantial rewrite is needed, get that artifact's rules and template first:
|
||||
```bash
|
||||
openspec instructions "<artifact-id>" --change "<name>" --json
|
||||
```
|
||||
|
||||
6. **Point to the next step (guidance only - NEVER act on it)**
|
||||
- Artifacts with empty `existingOutputPaths` and status `ready` or `blocked` -> run `openspec status --change "<name>" --json` for the next artifact and point the user to `openspec instructions "<artifact-id>" --change "<name>" --json` for how to create it.
|
||||
- Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/openspec-apply-change` to carry the delta into code.
|
||||
- Everything done and implemented -> suggest `/openspec-archive-change`.
|
||||
|
||||
**Output**
|
||||
|
||||
After each invocation, show:
|
||||
- Which artifacts were revised (and which proposed revisions were rejected)
|
||||
- Any file created under a glob artifact that was already partially populated
|
||||
- Anything deferred because it does not exist yet (artifacts with no files and status `ready` or `blocked`, never `skipped` artifacts)
|
||||
- Where the change stands and the recommended next command
|
||||
|
||||
**Guardrails**
|
||||
- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/openspec-apply-change`.
|
||||
- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.
|
||||
- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.
|
||||
- Do not advance the build frontier: if an artifact has empty `existingOutputPaths` and status `ready` or `blocked`, creating them is a separate step, outside this workflow. Leave `skipped` artifacts untouched. The only new-file scope is a confirmed concrete path under a glob artifact whose `existingOutputPaths` is non-empty.
|
||||
- Confirm every edit with the user before writing.
|
||||
- If the request changes the change's *intent* rather than refining it, ask for a distinct unused change name and recommend `openspec new change "<new-change-name>"` instead (the "Update vs. Start Fresh" heuristic).
|
||||
@@ -0,0 +1,29 @@
|
||||
# Boundary Law — Rust↔Go crossings (D30, standing architecture decision)
|
||||
|
||||
**Decision (owner, 2026-10-16):** WASM via wazero is the STANDARD mechanism for Rust↔Go crossings. cgo is exception-only.
|
||||
|
||||
## The law
|
||||
- Rust crosses to Go as WASM (wazero, AOT, github.com/tetratelabs/wazero): traps surface as catchable Go errors — never process death; debug errors designed for the owner (plain-language, no hex soup).
|
||||
- The five conversational Go subsystems (task-state, event-bus, model-routing, tool-executor, work-manager) are in-process libraries — ns-scale calls, no crossing.
|
||||
- Crossings to Rust are batched at the boundary; semantic events never drop; decoration yields.
|
||||
- cgo EXCEPTION-ONLY, all conditions must hold and be re-proven on every dependency change: pure logic, no unsafe, no external C, frozen battery proving panic-freedom on real inputs, catch_unwind at the boundary, operation idempotent, host death affordable.
|
||||
- The boundary pipe remains in service until the WASM prototype proves in-process end-to-end.
|
||||
|
||||
## Measured basis (research rounds + trials, all in-repo)
|
||||
- Batched IPC 0.0078ms/item beats cgo-with-payloads 0.0117ms/item (cgo's edge: ~0.33ms total on rare single calls).
|
||||
- wazero matches IPC at batch-1000 (11.5µs vs 11.5µs), wins 2–8× below; trap isolation verified; artifact: verification/wazero-vs-ipc-trial.json.
|
||||
- Goroutine recover = panic containment ONLY (Go docs; hangs/corruption/fatal kill the process). Process restart ≈2ms (measured, 300 runs).
|
||||
- Shared memory LOSES to plain sockets at our message sizes (7.7µs vs 5.5µs at 64B — ByteDance shmipc bench); only wins ≥4KB.
|
||||
- wasmer-go abandoned 2021; wasmtime-go (cgo) is the component-model escape hatch — noted, no work.
|
||||
- Precedents: Linkerd2 (Go+Rust, process-separated), ncruces/go-sqlite3 (C→WASM→wazero in production), Extism (Rust plugins on wazero).
|
||||
|
||||
## Provenance
|
||||
Owner directive 2026-10-16 after three research rounds (spectrum + nets + measurements) and two in-repo trials. OpenSpec change: add-native-rune-tui design.md D30. Related interview rulings: R2-D8 (first connector), SA-D6 (sockets), TD-1..4 (task tree + selection).
|
||||
|
||||
## Prototype verdict (2026-10-16, wave complete)
|
||||
token_counting end-to-end: **YIELDS.** 24/24 frozen-oracle parity through wasm; 1.6–2.2× slower than cgo raw but ~160× faster than IPC; trap isolation verified with symbolic stack + file:line capture; zero porting cost; ABI reserved-scratch convention matters 23× (standardized). wazero cost: +6.9MB binary, +12.4MB vendor. Production migration wave 1 (token_counting) opened under the standing directive.
|
||||
|
||||
## Wave 1 in production (2026-10-17)
|
||||
token_counting serves in-process from the TUI through `tui/wasm` (wazero AOT, reserved-scratch ABI as the standard, trap → Go error → fresh instance). Frozen tests: 24/24 parity against the crate's own oracle through the production path, envelope byte-identity wasm↔native-binary, isolation restart battery, IPC fallback degradation (Status "ipc-fallback", identical results).
|
||||
|
||||
**Module artifacts are NOT committable** (decided): a .wasm module is a reproducibly-built compiled artifact — `scripts/build_paired.sh` compiles it from src/rust source (`cargo build --release --target wasm32-wasip1 --lib`, byte-reproducible: identical sha256 across clean rebuilds) and stamps it into `tui/wasm/modules/` with a manifest carrying the paired build id. The dir is gitignored under the same no-committed-binaries law as the Go/Rust binaries and build-id stamps. Source of truth stays src/rust/token_counting.
|
||||
@@ -0,0 +1,140 @@
|
||||
# Authority Map — checkout vs deployed generation (Spec 0.1)
|
||||
|
||||
Task `0.1` of `docs/openspec/changes/add-native-rune-tui/tasks.md` (0A. Truth
|
||||
first — authority and runtime audit). Written 2026-10-17T06:46Z against dev
|
||||
checkout `master@ed245b2a`. This map reconciles the THREE code generations
|
||||
that exist on this machine and names the authority for every link between
|
||||
them. Its enforcement artifact is `src/rune/runtime_attestation.py` +
|
||||
`tests/test_runtime_attestation.py` (live attestation; RED when the runtime
|
||||
is not the qualified artifact).
|
||||
|
||||
## 1. The generations
|
||||
|
||||
| Generation | Location | Identity | Role |
|
||||
|---|---|---|---|
|
||||
| Dev checkout | `/mnt/workdrive/rune` (`master@ed245b2a`) | `__version__ = 0.310.0`, **no** `_build_identity.json` → `build_info().source == "dev"` | Factory floor; all waves build here; never launched by the launcher |
|
||||
| Deployed (live runtime) | `/mnt/workdrive/rune-code/current` → `81099a11f7a8` | `_build_identity.json`: commit `81099a11f7a845bea629324e45a2aeef1d00e2a2`, version `0.128.0`, deployed_at `2026-10-07T20:03:04Z` | The ONLY generation the launcher runs |
|
||||
| Last-good (rollback) | `/mnt/workdrive/rune-code/last-good` → `3c13be1d766b` | commit `3c13be1d766bf2bb9d25f032ca0cfdee089a55b0`, version `0.127.0`, deployed_at `2026-10-07T20:00:09Z` | Previous `current`; rotated automatically by every promotion |
|
||||
|
||||
Facts that matter:
|
||||
|
||||
- `81099a11` **is an ancestor of `master@ed245b2a`** — the deployed
|
||||
generation is a genuinely CI-qualified master revision. The relationship
|
||||
is pure staleness (lag), **not divergence**: there is no commit in the
|
||||
deployed tree that the checkout lacks.
|
||||
- The lag is **1522 commits / 182 minor versions** (0.128.0 → 0.310.0) and
|
||||
~10 days (no promotion since 2026-10-07T20:03Z; the factory's whole
|
||||
0C/0D/I/J-era output — tui/, the rune-entry serve surface, the internal/
|
||||
library conversion — is undeployed).
|
||||
- `/mnt/workdrive/rune-code` holds 300+ staged generations plus 12
|
||||
orphaned `.staging-*` dirs; ownership `gitea-desktop-runner`. The stale
|
||||
`.convergence-intent.json` there (version 0.66.57, recorded_at "0") is a
|
||||
leftover record, not an authority.
|
||||
|
||||
## 2. Launcher authority chain (what actually runs)
|
||||
|
||||
`~/.local/bin/rune` (hand-installed shell script, mtime 2026-09-11) is the
|
||||
single entry point. Resolved chain:
|
||||
|
||||
```
|
||||
~/.local/bin/rune
|
||||
└─ wezterm start -- bash -c
|
||||
cd /mnt/workdrive/rune-code/current/src ← runtime code (generation pinned only by `current`)
|
||||
RUNE_HOME=/mnt/workdrive/rune ← data home (state/memory of the LIVE system)
|
||||
RUNE_MEMORY_DIR=/home/nick/.config/opencode/memory
|
||||
MIMO_PROBE_ENABLED=1 ← ADAPT-LIVE bounded probe policy (2026-09-10)
|
||||
~/.venvs/memory/bin/python3 -m rune ← interpreter: the memory venv, NOT repo .venv
|
||||
```
|
||||
|
||||
Authority rows (current vs intended vs dormant):
|
||||
|
||||
| Surface | Current authority | Intended (per openspec) | State |
|
||||
|---|---|---|---|
|
||||
| Launcher script | owner (hand-installed; not owned by any repo module) | unchanged for the Python face | current |
|
||||
| Runtime code | `rune-code/current` generation (deploy pipeline output) | same mechanism, newer revisions | current, stale |
|
||||
| Runtime identity | `rune/__init__.build_info()` + `_build_identity.json` stamped by `deploy_stage.py` | same (already landed 2026-09-10 handoff) | current |
|
||||
| Data home | `RUNE_HOME=/mnt/workdrive/rune` (the dev checkout doubles as the live data dir: `state/`, `store/`, `memory/`) | pin stays; code deploys never move user data | current |
|
||||
| Memory dir | `RUNE_MEMORY_DIR=~/.config/opencode/memory` (promotion ledger + memory store live here) | same | current |
|
||||
| Promotion decision | `scripts/deploy_promotion.py` — monotonic ancestry law, flock'd critical section, atomic symlink swap, `last-good` rotation | same | current |
|
||||
| Deploy qualification | `scripts/deploy_qualify.py` + CI required-checks (fail-closed after the 0.66.28/29 incidents) + `scripts/tui_startup_canary.py` pre-promotion canary | same | current |
|
||||
| Receipts / tamper evidence | `src/rune/audit_ledger.py` — HMAC-chained ledger under RUNE_MEMORY_DIR (key lineage repair is Task 0.2, out of scope here) | same | current |
|
||||
| Go face (`tui/cmd/rune-tui`, `src/go/rune-entry` front door :8377/:8376) | exists ONLY in the checkout; nothing deployed runs it | launcher target once 0C/0D converge | **dormant** |
|
||||
| Paired-build attestation (`src/rune/build_attestation.py`, task 0.14) | checkout-only (Go↔Rust build pairing; a different attestation from this one) | gates future paired artifacts | dormant until next deploy |
|
||||
|
||||
Surprise worth naming: **the dev checkout is also the live system's data
|
||||
home.** `RUNE_HOME=/mnt/workdrive/rune` means the deployed runtime reads and
|
||||
writes state inside the factory worktree (`state/`, `store/scaffold`,
|
||||
`memory/journal`) — the same files the test suites mutate. That is why test
|
||||
runs dirty the live store and why background workers must restore journal
|
||||
churn after every run.
|
||||
|
||||
## 3. Module drift — deployed `81099a11` vs checkout `ed245b2a`
|
||||
|
||||
| Tree | Deployed | Checkout | Delta | Deployed-only modules |
|
||||
|---|---|---|---|---|
|
||||
| `src/rune` (Python) | 184 | 284 | +100 | **none** |
|
||||
| `src/go` | 42 | 94 | +52 | **none** |
|
||||
| `src/rust` | 80 | 104 | +24 | **none** |
|
||||
| `tui/` | absent | 36 packages | +36 | — |
|
||||
|
||||
Zero deployed-only modules in every tree: the deployed generation is a
|
||||
strict prefix of the checkout. Headline checkout-only surfaces (all
|
||||
undeployed): the entire `tui/` face; the J1 internal-library conversions
|
||||
(`src/go/{rune-entry,event-bus,model-routing,tool-executor,work-manager}` →
|
||||
`internal/` + thin shells); `src/rune/{build_attestation,
|
||||
construction_admission}`; the rune-entry serve surface
|
||||
(`/v1/{conversation,tasks,events,health}`); boundary codegen (0.12);
|
||||
versioning port H1–H3. Consequence: any qualification, canary, or attestation
|
||||
written only in the checkout (including this map's test) does not constrain
|
||||
the live runtime until the next promotion carries it — that is the intended
|
||||
0.14/0.15 landing path, not a gap in this task.
|
||||
|
||||
## 4. Reconciliation verdict
|
||||
|
||||
1. **No divergence.** Deployed `81099a11` is an ancestor of `master`; the
|
||||
checkout is a superset. Nothing was forked, patched, or hot-fixed inside a
|
||||
staged generation (identity commit == directory name == runtime
|
||||
self-report; verified below by the live test).
|
||||
2. **Pure lag.** 1522 commits / 0.128.0 → 0.310.0 / 10 days without a
|
||||
promotion. The launcher's comment ("runs the DEPLOYED revision
|
||||
(CI-qualified, build-identity stamped)") is TRUE of `81099a11` at its
|
||||
promotion time and STALE as a description of the system: everything the
|
||||
owner has watched land since Oct 7 (tui, serve, libraries, gates) runs
|
||||
only from the checkout when invoked manually — never via `rune`.
|
||||
3. **Rollback target is coherent.** `last-good → 3c13be1d` (0.127.0) is the
|
||||
immediately preceding generation; promotion rotated it correctly.
|
||||
4. **The one launch path** is the Python face. No second launcher, alias, or
|
||||
systemd unit runs Rune code (Go binaries under `src/go/` are parity
|
||||
harnesses, not installed). The Go face is checkout-dormant by design
|
||||
until 0C/0D converge.
|
||||
|
||||
## 5. Attestation contract (the enforcement half of 0.1)
|
||||
|
||||
`src/rune/runtime_attestation.py` — fail-closed attestation of the chain
|
||||
launcher → `current` → generation → identity → qualification:
|
||||
|
||||
1. launcher exists and its `cd` target is `<deploy_root>/current/src`;
|
||||
2. `current` is a symlink resolving to an existing directory inside the
|
||||
deploy root;
|
||||
3. the generation carries a well-formed `_build_identity.json`
|
||||
(commit + version + deployed_at);
|
||||
4. identity commit == generation directory name (a staged tree cannot
|
||||
impersonate another SHA);
|
||||
5. the RUNTIME SELF-REPORTS: importing `rune` from the generation's `src`
|
||||
yields `build_info().commit` == identity commit and `source ==
|
||||
"deployed"` — a dev/unstamped tree answers `"dev"` and fails;
|
||||
6. the identity commit is an ANCESTOR of the canonical release branch
|
||||
(`refs/heads/master`) in the checkout — an unqualified artifact can never
|
||||
attest;
|
||||
7. identity version == the generation package's `__version__` (coherent
|
||||
stamp, not a stale one).
|
||||
|
||||
Every path is injectable; the live defaults bind `~/.local/bin/rune`,
|
||||
`/mnt/workdrive/rune-code`, and the repo root. CLI:
|
||||
`python -m rune.runtime_attestation` prints the JSON evidence, exit 1 on any
|
||||
failure. `tests/test_runtime_attestation.py` freezes hermetic negative
|
||||
fixtures for each link (launcher missing/repointed, symlink missing/outside
|
||||
root, identity missing/malformed/mismatched, non-ancestor commit, runtime
|
||||
self-report mismatch, version incoherence) plus a LIVE test that attests the
|
||||
real machine chain and goes RED the moment any of it breaks — runtime ≠
|
||||
qualified artifact.
|
||||
@@ -18,6 +18,16 @@
|
||||
{"trap": "oracle CRASH parity: if the oracle crashes (AttributeError etc.) on an input class, the port returns the matching error envelope", "port": "never silently skip — the facade's fallback reproduces the exact exception", "proved_by": "execution_trace fuzz"},
|
||||
{"trap": "glob+mtime ordering ties within the same second", "port": "explicit os.utime in test fixtures; deterministic per-case dirs", "proved_by": "sleep_time"},
|
||||
{"trap": "Go evaluation order: arg-list trace(x, f.Mutating()) snapshots BEFORE mutation; Python passes live references", "port": "capture returns/reports into locals first", "proved_by": "failure_auto_filer"},
|
||||
{"trap": "absolute temp paths in frozen truth are machine-specific", "port": "repo-relative names or REPO_ROOT placeholder + battery substitution", "proved_by": "paths"}
|
||||
{"trap": "absolute temp paths in frozen truth are machine-specific", "port": "repo-relative names or REPO_ROOT placeholder + battery substitution", "proved_by": "paths"},
|
||||
{"trap": "ThreadPoolExecutor.submit AFTER shutdown raises from the submit loop itself — run_parallel's per-task capture never sees it, the WHOLE call raises", "port": "Go port panics with 'cannot schedule new futures after shutdown' at the same site (crash parity, recovered by battery/parity)", "proved_by": "parallel_processor (2026-10-14)"},
|
||||
{"trap": "callable corpora cannot ride JSON truth rows", "port": "freeze a shared kind/fn/value SPEC grammar via a rig that builds real Python callables; the battery rebuilds Go tasks from the same rows (extends the memory_consolidation seam pattern)", "proved_by": "parallel_processor (2026-10-14)"},
|
||||
{"trap": "threading.active_count() in oracle output is process-random (pool threads, harness state)", "port": "pin via rig patch to a constant; Go side reads the matching PP_ACTIVE_THREADS seam (default: native goroutine count)", "proved_by": "parallel_processor (2026-10-14)"},
|
||||
{"trap": "epoch microseconds beyond ~2^53 (year >285 CE) lose sub-second digits when crossing f64 JSON (253402300799000001 → …799000000)", "port": "instants cross FFI as STRINGS; kernel parses i64 (si_now micros_str law)", "proved_by": "self_improve (2026-10-14)"},
|
||||
{"trap": "f'{x:.0%}' scales the DOUBLE's exact decimal expansion by 100 then rounds half-even; a binary r*100.0 multiply can sit 1 ulp off the tie and round differently", "port": "Rust format!('{:.0}%') matches on exactly-representable ratios; frozen truth pins ties with power-of-two denominators only (1/8 → 12.5% → '12%')", "proved_by": "self_improve (2026-10-14)"},
|
||||
{"trap": "datetime.fromisoformat raises on out-of-calendar fields (month 13, Feb 30, day 0, hour 24 with nonzero minutes)", "port": "Rust fixed-position parsers must validate ranges (leap-year month lengths; h<=23 or ISO hour-24 end-of-day with zero min/sec) or they compute off-calendar epochs the oracle never sees", "proved_by": "tier_manager (parse_strict_aware crash class, 2026-10-14)"},
|
||||
{"trap": "json.dumps default ensure_ascii=True escapes ALL non-ASCII as \\uXXXX (lowercase hex, astral chars as surrogate pairs); serde_json emits raw UTF-8", "port": "byte-parity emitters need a py_escape pass over strings for BOTH compact and indent shapes — separators-only parity is not enough (extends the context_broker pyDumpsSorted law)", "proved_by": "inference_trace (serialize/format_record unicode rows, 2026-10-14)"},
|
||||
{"trap": "list[-limit:] with limit=0 keeps ALL lines (-0 == 0 slices from the start), so read_traces(limit=0) returns everything, not nothing", "port": "Rust limit match arms must special-case Some(0) => all items (read_select + cli_tail); rev().take(0) silently returns none", "proved_by": "inference_trace (limit-0 truth row, 2026-10-14)"},
|
||||
{"trap": "a kernel Err on inputs where the oracle RAISES (e.g. malformed JSONL line) loses end-to-end parity if the kernel instead silently skips", "port": "propagate the Err through the FFI envelope — the facade's except-fallback then re-runs the Python path, which re-raises the AUTHENTIC exception (message parity by construction); pin the crash CLASS in truth rows when the cross-language message text cannot match", "proved_by": "inference_trace (read_select ValueError class, 2026-10-14)"},
|
||||
{"trap": "Python 3.14 OSError.__str__ QUOTES the filename: str(e) = \"[Errno 21] Is a directory: './path'\" (<=3.13 emitted it bare)", "port": "Rust errno mapping must single-quote the path in the message AND strip the ' (os error N)' suffix of io::Error Display; pins are interpreter-version-shaped — re-freeze when the oracle python changes major", "proved_by": "dependency_tracker (frozen battery row 20, 2026-10-14)"}
|
||||
]
|
||||
}
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
_Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL only when 80%+ of its Python functions have name-preserving counterparts in the Go/Rust implementation — directories without the functions are facades and count as NOT TOUCHED._
|
||||
|
||||
**FULL completion: 30%** (57/192 runtime modules at FULL)
|
||||
**FULL completion: 80%** (154/193 runtime modules at FULL)
|
||||
|
||||
Function-level detail: 56% (1341/2378 runtime functions matched) — secondary; includes in-flight work
|
||||
Function-level detail: 85% (1933/2283 runtime functions matched) — secondary; includes in-flight work
|
||||
|
||||
| Status | Modules | Lines | Meaning |
|
||||
|---|---|---|---|
|
||||
| FULL | 57 | 22406 | 80%+ of functions ported |
|
||||
| PARTIAL | 94 | 39988 | 20–80% — real logic exists, gaps named below |
|
||||
| NOT TOUCHED | 41 | 11195 | no name-preserving implementation |
|
||||
| Total runtime | 192 | 73589 | |
|
||||
| FULL | 154 | 65153 | 80%+ of functions ported |
|
||||
| PARTIAL | 0 | 0 | 20–80% — real logic exists, gaps named below |
|
||||
| NOT TOUCHED | 39 | 11058 | no name-preserving implementation |
|
||||
| Total runtime | 193 | 76211 | |
|
||||
|
||||
## NOT TOUCHED — the port queue, in priority order (largest first)
|
||||
|
||||
@@ -47,175 +47,171 @@ Function-level detail: 56% (1341/2378 runtime functions matched) — secondary;
|
||||
| canary_unbounded | 105 | 2 | TEST-INFRA |
|
||||
| message_widgets | 104 | 6 | TUI-LAST |
|
||||
| canary_schema_migration | 86 | 1 | TEST-INFRA |
|
||||
| growth_pipeline | 53 | 2 | unclassified |
|
||||
| wait_guard_corpus | 46 | 0 | RUST |
|
||||
| canary_cognitive_strata | 44 | 1 | TEST-INFRA |
|
||||
| evidence_capsule | 43 | 3 | unclassified |
|
||||
| builder_bridge_cli | 42 | 1 | GO |
|
||||
| __init__ | 40 | 1 | RUST |
|
||||
| canary_memory_synthesis | 33 | 1 | TEST-INFRA |
|
||||
| canary_resource_authority | 33 | 1 | TEST-INFRA |
|
||||
| canary_surface_parity | 33 | 1 | TEST-INFRA |
|
||||
| canary_orchestration_foundation | 32 | 1 | TEST-INFRA |
|
||||
| canary_gitea_delegation | 31 | 1 | TEST-INFRA |
|
||||
|
||||
## PARTIAL — gaps to close (matched/total fns; first missing named)
|
||||
|
||||
| Module | Lines | Coverage | Missing (first named) |
|
||||
|---|---|---|---|
|
||||
| memory_store | 2333 | 30/78 | _ctx_budget, now_iso, ensure_memory_type_column, _embed, _temp_json, _entities_to_json |
|
||||
| rune_operator | 2746 | 25/68 | _build_model_router, _sorted_tool_names, _filter_safety_noise, _wire_auto_filer, _reconcile_guarded, get_operator |
|
||||
| construction_interface | 1193 | 6/27 | _to_stage_check, _guard_specs_from_obligations, t_layout, _obligations_projection, _obligation_scope_files, main |
|
||||
| provider_registry | 679 | 11/28 | _ctx_budget, _format_tools_openai, _execute_tool_calls, _execute_tool_calls_anthropic, get_registry, connect_provider |
|
||||
| warm_router | 1518 | 19/36 | _ensure_use_history_table, record_outcome, record_neuron_fire, _py_base_level_from_uses, base_level_activation, _db |
|
||||
| construction_decision | 985 | 4/20 | normalize_need, _sc_core, _py_language_placement, resolve_surface, ss_entity, implemented_contracts |
|
||||
| spine | 975 | 20/35 | _py_sorted, _module_imports, _py_declared_surfaces, _py_project_meta_surfaces, _gather_meta_inputs, _rust_core |
|
||||
| cron_engine | 440 | 13/27 | get_cron_engine, run_task, start_engine, stop_engine, to_dict, fire_on_task_complete |
|
||||
| safety_override | 574 | 8/22 | _keyword_stopwords, _cc_probe, cc_loader_pin, self_test, _py_matches, escalation_rate |
|
||||
| tool_executor | 1836 | 33/47 | _execute_tool_now, serialize_prepared_tool, deserialize_prepared_tool, confirm_write, _resolve_execution_cwd, _run_command |
|
||||
| cache | 492 | 6/17 | _record_hit, _record_miss, flush_stats, get_cache, put_cache, invalidate_scope |
|
||||
| elastic_cognition | 774 | 3/14 | _ctx_budget, _build_constitution, _get_constitution, _load_cloud_config, execute_at_tier, _execute_deterministic |
|
||||
| parallel_processor | 194 | 3/14 | _shutdown_processor, get_processor, run_parallel_with_args, get_status, test_parallel, run_parallel_with_args |
|
||||
| structural_model | 388 | 9/20 | workspace_record, workspace_advance, _init_project, save, reload, add |
|
||||
| token_optimizer | 514 | 16/27 | get_optimizer, _tc_core, _to_core, _py_estimate_tokens, _py_compress_context, to_dict |
|
||||
| capability_broker | 798 | 10/20 | _native_tool_schemas, load_registry, _cards_are_stale, load_cards, generate_card, build_cards |
|
||||
| capability_fit | 414 | 3/13 | record_semantic_fit, _provides_contracts, _consumers, discover_candidates, _cs_core, decide |
|
||||
| claim_audit | 484 | 4/14 | _get_warm_neuron_store, reframe, _graph_has, _warm_has, verify, journal |
|
||||
| drift_core | 471 | 4/14 | _ms_core, _now_iso, _py_check_promotion_eligibility, promote_entry, compute_fama, compute_drift_velocity |
|
||||
| identity_anchors | 542 | 6/16 | show_anchors, show_rules, show_pronouns, _announcement_patterns, _pronoun_patterns, _hedge_patterns |
|
||||
| model_profiles | 438 | 5/15 | get_profile_path, load_profile, save_profile, build_profile_from_ollama, scan_all_models, get_or_create_profile |
|
||||
| testing_mode | 362 | 11/21 | _now, _tm_core, _py_default_state, _py_next_item, _py_record, _py_note_blocker |
|
||||
| tier_manager | 262 | 5/15 | _cc_probe, cc_loader_pin, _py_core_now, _py_get_tier, _py_promote, _py_demote |
|
||||
| drift_compression | 307 | 3/12 | _py_get_compression_ratio, _py_compress_text, _py_compress_entry, _py_should_warn, _py_warn_before_compress, get_compression_ratio |
|
||||
| scaffold | 868 | 22/31 | init, set_prd, add_maintenance_item, bind_repo, observe_repo, repo_state_from_git |
|
||||
| behavior | 537 | 6/14 | _bh_core, connect, _correction_matches_python, _habit_matches_python, _reinforce_rust, _reinforce_python |
|
||||
| builder_bridge | 326 | 3/11 | _construction_store, _active_construction_lines, _default_selector, _cached_memory, _get_selector, _retrieve_memory |
|
||||
| intent_continuity | 284 | 6/14 | _core, _now, _raise_core_err, to_dict, from_dict, save |
|
||||
| time_awareness | 402 | 12/20 | _clock_args, _ta_core, get_time_awareness, to_dict, _to_state, _apply_state |
|
||||
| witness | 446 | 10/18 | _now, _cc_probe, cc_loader_pin, _py_extract_topic, _py_detect_signals, log_entry |
|
||||
| construction_pattern | 547 | 7/14 | _core, _need_dict, _surface_text, _path_matches, _entity_matches, _obligation_when_hit |
|
||||
| contracts | 743 | 4/11 | _ct_core, _resolve_and_project, _run_contract_python, capture_signatures, save_signatures, load_signatures |
|
||||
| graph_memory | 474 | 2/9 | now_iso, connect, resolve_entity, add_edge, query, stats |
|
||||
| lexicon_evolution | 304 | 7/14 | _le_core, _py_content_words, _py_distinctive_terms, _py_shared_distinctive, _py_coherence, _py_grounded |
|
||||
| voice_hybrid | 427 | 3/10 | _connect, _load_growth, _save_growth, source_profiles, always, grow |
|
||||
| voice_sync | 299 | 3/10 | _now, get_delivery_register_from_db, get_delivery_register, update_rune_md, check_sync, handle_hook |
|
||||
| audit_ledger | 212 | 2/8 | _compute_hash, get_entries, log_promotion, log_gate_result, log_drift_alert, main |
|
||||
| context_pressure | 262 | 3/9 | log_measurement, read_log, log_stats, _now, handle_hook, main |
|
||||
| core_server | 234 | 6/12 | make_server, log_message, _send_json, _send_ndjson, do_GET, do_POST |
|
||||
| microkernel | 434 | 6/12 | now_iso, discover, _extract_manifest, _load_registry, _save_registry, _run_hook |
|
||||
| model_registry | 253 | 2/8 | test_registry, to_dict, get_profile, get_enabled_profiles, get_profiles_by_tier, list_profiles |
|
||||
| progressive_escalation | 279 | 2/8 | execute_with_escalation, test_escalation, handle_hook, to_dict, execute, verify |
|
||||
| voicebox | 164 | 5/11 | _run, start_recording, stop_recording, _transcribe_and_type, on_press, on_release |
|
||||
| hook_engine | 860 | 3/8 | discover_functions, tag_function, validate_placements, generate_report, handle_hook |
|
||||
| persona_definition | 210 | 3/8 | _by_topic, compile_full, emit_constitution, emit_identity, emit_persona |
|
||||
| pins | 282 | 7/12 | _store_path, _detail_blob, unpin, _py_pin_recall, list_pins |
|
||||
| task_persist | 269 | 5/10 | _save, export, restore, main, handle_hook |
|
||||
| tiered_executor | 291 | 3/8 | _ctx_budget, test_executor, to_dict, to_dict, _execute_model |
|
||||
| adaptive_router | 625 | 15/19 | _floor_applies_from_flags, _floor_reason, _record_transition_with, _detect_external_refs |
|
||||
| connection_pool | 226 | 7/11 | _core, get_pool, _kernel_state, _get_connection_core |
|
||||
| construction_commands | 268 | 1/5 | _refuse, _validate, _accept, dispatch |
|
||||
| drift_cache | 357 | 12/16 | _dc_core, _py_compute_priority, _py_qualifies_for_cache, main |
|
||||
| drift_observability | 399 | 5/9 | _do_core, main, _load_alerts, _save_alerts |
|
||||
| grading | 253 | 3/7 | _gr_core, _py_affirm_rule, _py_grade_fact, _py_tier_of |
|
||||
| layout_contract | 336 | 2/6 | define_region, bind_component, amend_layout_contract, record_layout_observation |
|
||||
| model_connect | 471 | 7/11 | _ctx_budget, _now, _get_cloud_models, handle_hook |
|
||||
| prompt_sync | 487 | 11/15 | _now, _rust_available, get_store, main |
|
||||
| salience_gate | 276 | 1/5 | _sg_core, test_salience, handle_hook, to_dict |
|
||||
| self_improve | 246 | 1/5 | _now, suggest_improvements, handle_hook, main |
|
||||
| stage_guards | 303 | 7/11 | _py_liveness_canary, _py_reconcile, to_dict, chk |
|
||||
| task_gate | 301 | 5/9 | _tg_core, _now, list_gates, main |
|
||||
| construction_activation | 237 | 1/4 | _scaffold_for, _auto_structural_awareness, dispatch_from_decision |
|
||||
| construction_mode | 412 | 2/5 | _contains_any, to_dict, from_dict |
|
||||
| construction_projection | 254 | 1/4 | _contract_entity, validate, write_mini_doc |
|
||||
| dependency_tracker | 202 | 2/5 | check_propagation, get_full_map, main |
|
||||
| event_envelope | 303 | 5/8 | test_envelope, to_canonical_dict, to_dict |
|
||||
| graph_projection | 134 | 3/6 | _model_to_dicts, write_all, _now |
|
||||
| memory_selector | 590 | 11/14 | _mse_core, add_source, _source_store |
|
||||
| mutation_guards | 143 | 2/5 | _mg_core, _py_classify_mutation, _py_guard_mutation |
|
||||
| state_checkpoint | 182 | 6/9 | _sck_core, _load_ledger, _save_ledger |
|
||||
| subconscious_pass | 236 | 3/6 | main, _kernel, get_recent_findings |
|
||||
| subsystem_health | 213 | 4/7 | _kernel, set_auto_filer, configure_logging |
|
||||
| warm_neuron_store | 122 | 3/6 | _db, neuron_slugs, handle_hook |
|
||||
| cognitive_budgeter | 221 | 1/3 | test_budgeter, to_dict |
|
||||
| event_bus | 244 | 5/7 | _store_path, handle_hook |
|
||||
| execution_trace | 108 | 3/5 | _kernel, _journal_path |
|
||||
| experience_encoding | 113 | 1/3 | _ee_core, _py_evaluate |
|
||||
| inference_trace | 204 | 4/6 | record_trace, read_traces |
|
||||
| iteration | 261 | 7/9 | _kernel, _now |
|
||||
| recall_uplift | 193 | 1/3 | _core, _fetch_corpus |
|
||||
| response_adapter | 222 | 5/7 | test_adapter, to_dict |
|
||||
| rune_profiler | 196 | 2/4 | _kernel, main |
|
||||
| standardized_providers | 281 | 3/5 | list_providers, connect_all_local |
|
||||
| structural_migration | 77 | 2/4 | apply_migration, consent_brief |
|
||||
| voice_injector | 138 | 3/5 | test_injection, to_dict |
|
||||
| abstraction_ladder | 102 | 3/4 | _core |
|
||||
| construction_depth | 172 | 2/3 | _core |
|
||||
| drift_resume | 153 | 3/4 | _dr_core |
|
||||
| meaningful_events | 102 | 3/4 | _kernel |
|
||||
| memory_types | 85 | 2/3 | _core |
|
||||
| pane_authority | 98 | 2/3 | _kernel |
|
||||
| recall_router | 165 | 2/3 | handle_hook |
|
||||
| research_budget | 75 | 3/4 | _core |
|
||||
| self_model | 76 | 3/4 | _core |
|
||||
| alpha | 1 | 0 | unclassified |
|
||||
|
||||
## FULL — ported, parity-gated
|
||||
|
||||
| Module | Lines | Fns | Side |
|
||||
|---|---|---|---|
|
||||
| rune_operator | 2746 | 68/68 | GO |
|
||||
| memory_store | 2331 | 67/67 | GO |
|
||||
| curator | 1838 | 43/43 | GO |
|
||||
| sleep_time | 1056 | 26/27 | GO |
|
||||
| context_broker | 870 | 28/28 | GO |
|
||||
| tool_executor | 1836 | 47/47 | GO |
|
||||
| warm_router | 1518 | 33/33 | GO |
|
||||
| construction_decision | 1211 | 23/23 | RUST |
|
||||
| construction_interface | 1193 | 27/27 | RUST |
|
||||
| sleep_time | 1056 | 26/26 | GO |
|
||||
| spine | 975 | 25/25 | RUST |
|
||||
| structural_store | 937 | 40/40 | RUST |
|
||||
| scaffold | 908 | 30/30 | GO |
|
||||
| context_broker | 870 | 23/23 | GO |
|
||||
| capability_broker | 865 | 18/18 | RUST |
|
||||
| hook_engine | 860 | 8/8 | GO |
|
||||
| local_tool_runtime | 809 | 30/30 | GO |
|
||||
| structural_store | 774 | 34/34 | RUST |
|
||||
| memory_consolidation | 770 | 23/23 | GO |
|
||||
| persona_gate | 724 | 26/26 | GO |
|
||||
| abstraction | 656 | 17/17 | GO |
|
||||
| system_registry | 603 | 14/16 | RUST |
|
||||
| elastic_cognition | 774 | 14/14 | GO |
|
||||
| memory_consolidation | 770 | 22/22 | GO |
|
||||
| contracts | 743 | 10/10 | RUST |
|
||||
| persona_gate | 724 | 23/23 | GO |
|
||||
| provider_registry | 687 | 28/28 | GO |
|
||||
| abstraction | 656 | 16/16 | GO |
|
||||
| safety_override | 649 | 20/20 | RUST |
|
||||
| adaptive_router | 625 | 19/19 | GO |
|
||||
| memory_selector | 609 | 13/13 | RUST |
|
||||
| system_registry | 603 | 15/15 | RUST |
|
||||
| work_manager | 594 | 25/25 | GO |
|
||||
| construction_intent | 580 | 13/16 | RUST |
|
||||
| capability_ledger | 571 | 18/19 | RUST |
|
||||
| drift_ledger | 548 | 20/22 | RUST |
|
||||
| structural_model | 593 | 20/20 | RUST |
|
||||
| construction_intent | 580 | 16/16 | RUST |
|
||||
| identity_anchors | 575 | 13/13 | RUST |
|
||||
| capability_ledger | 571 | 18/18 | RUST |
|
||||
| graph_memory | 563 | 9/9 | RUST |
|
||||
| drift_ledger | 548 | 21/21 | RUST |
|
||||
| construction_pattern | 547 | 13/13 | RUST |
|
||||
| voice_hybrid | 540 | 11/11 | RUST |
|
||||
| drift_core | 539 | 12/12 | RUST |
|
||||
| behavior | 537 | 13/13 | RUST |
|
||||
| failure_auto_filer | 527 | 24/24 | GO |
|
||||
| build_mode | 521 | 33/33 | GO |
|
||||
| growth_delta | 520 | 17/20 | GO |
|
||||
| construction_activation | 521 | 15/15 | RUST |
|
||||
| growth_delta | 520 | 17/17 | GO |
|
||||
| event_wait | 518 | 14/14 | GO |
|
||||
| worthiness | 469 | 12/14 | unclassified |
|
||||
| tool_protocol | 461 | 22/27 | GO |
|
||||
| token_optimizer | 514 | 25/25 | RUST |
|
||||
| cache | 492 | 17/17 | GO |
|
||||
| prompt_sync | 487 | 15/15 | RUST |
|
||||
| claim_audit | 484 | 14/14 | RUST |
|
||||
| capability_fit | 480 | 13/13 | RUST |
|
||||
| model_connect | 471 | 11/11 | GO |
|
||||
| worthiness | 469 | 13/13 | unclassified |
|
||||
| tool_protocol | 461 | 27/27 | GO |
|
||||
| auto_tasks | 460 | 14/14 | GO |
|
||||
| programme_continuation | 458 | 11/12 | RUST |
|
||||
| programme_continuation | 458 | 12/12 | RUST |
|
||||
| goal | 453 | 20/20 | GO |
|
||||
| release_notes | 446 | 16/16 | GO |
|
||||
| witness | 446 | 15/15 | GO |
|
||||
| cron_engine | 440 | 27/27 | GO |
|
||||
| model_profiles | 438 | 15/15 | GO |
|
||||
| layout_contract | 434 | 6/6 | RUST |
|
||||
| microkernel | 434 | 12/12 | GO |
|
||||
| model_routing | 432 | 13/13 | GO |
|
||||
| voice_sync | 426 | 12/12 | RUST |
|
||||
| night_mode | 421 | 19/19 | GO |
|
||||
| build_basis | 375 | 11/13 | RUST |
|
||||
| attention_capsule | 365 | 6/7 | RUST |
|
||||
| construction_mode | 412 | 5/5 | RUST |
|
||||
| time_awareness | 402 | 19/19 | RUST |
|
||||
| drift_observability | 399 | 9/9 | RUST |
|
||||
| construction_commands | 393 | 5/5 | RUST |
|
||||
| self_improve | 384 | 7/7 | RUST |
|
||||
| build_basis | 375 | 12/12 | RUST |
|
||||
| attention_capsule | 365 | 7/7 | RUST |
|
||||
| testing_mode | 362 | 13/13 | RUST |
|
||||
| drift_cache | 357 | 14/14 | RUST |
|
||||
| provider_budget | 357 | 20/20 | GO |
|
||||
| drift-ledger | 355 | 14/14 | unclassified |
|
||||
| outcome_ledger | 318 | 7/8 | RUST |
|
||||
| context_pressure | 351 | 9/9 | RUST |
|
||||
| progressive_escalation | 340 | 8/8 | RUST |
|
||||
| builder_bridge | 326 | 11/11 | GO |
|
||||
| drift_compression | 323 | 7/7 | RUST |
|
||||
| pins | 323 | 11/11 | RUST |
|
||||
| outcome_ledger | 318 | 8/8 | RUST |
|
||||
| construction_projection | 317 | 4/4 | RUST |
|
||||
| message_queue | 316 | 13/13 | GO |
|
||||
| message_graph | 295 | 12/13 | unclassified |
|
||||
| toolchain | 295 | 5/6 | RUST |
|
||||
| shard_manager | 276 | 9/10 | RUST |
|
||||
| lexicon_evolution | 304 | 8/8 | unclassified |
|
||||
| event_envelope | 303 | 8/8 | GO |
|
||||
| stage_guards | 303 | 9/9 | GO |
|
||||
| audit_ledger | 302 | 8/8 | RUST |
|
||||
| task_gate | 301 | 9/9 | RUST |
|
||||
| tier_manager | 300 | 10/10 | RUST |
|
||||
| message_graph | 295 | 13/13 | unclassified |
|
||||
| toolchain | 295 | 5/5 | RUST |
|
||||
| tiered_executor | 291 | 8/8 | GO |
|
||||
| intent_continuity | 284 | 13/13 | RUST |
|
||||
| salience_gate | 283 | 5/5 | RUST |
|
||||
| response_adapter | 282 | 7/7 | RUST |
|
||||
| standardized_providers | 281 | 5/5 | GO |
|
||||
| shard_manager | 276 | 9/9 | RUST |
|
||||
| dependency_tracker | 274 | 6/6 | RUST |
|
||||
| auto_context | 273 | 8/8 | GO |
|
||||
| spine_language | 269 | 5/6 | RUST |
|
||||
| spine_language | 269 | 5/5 | RUST |
|
||||
| task_persist | 269 | 10/10 | RUST |
|
||||
| construction_patterns_builtin | 266 | 1/1 | RUST |
|
||||
| iteration | 261 | 8/8 | RUST |
|
||||
| inference_trace | 258 | 8/8 | RUST |
|
||||
| grading | 253 | 4/4 | unclassified |
|
||||
| model_registry | 253 | 8/8 | GO |
|
||||
| event_bus | 244 | 7/7 | GO |
|
||||
| subconscious_pass | 236 | 5/5 | RUST |
|
||||
| core_server | 234 | 12/12 | GO |
|
||||
| openai_compat_transport | 227 | 9/9 | GO |
|
||||
| connection_pool | 226 | 10/10 | RUST |
|
||||
| cognitive_budgeter | 221 | 3/3 | RUST |
|
||||
| evidence_probe | 213 | 9/9 | GO |
|
||||
| project_registry | 205 | 10/12 | RUST |
|
||||
| subsystem_health | 213 | 6/6 | GO |
|
||||
| persona_definition | 210 | 7/7 | GO |
|
||||
| project_registry | 205 | 12/12 | RUST |
|
||||
| forge_client | 203 | 18/18 | GO |
|
||||
| behaviour_ledger | 195 | 11/13 | RUST |
|
||||
| self_modify | 149 | 7/8 | unclassified |
|
||||
| rune_profiler | 196 | 3/3 | RUST |
|
||||
| behaviour_ledger | 195 | 12/12 | RUST |
|
||||
| parallel_processor | 194 | 14/14 | GO |
|
||||
| recall_uplift | 193 | 2/2 | RUST |
|
||||
| state_checkpoint | 189 | 8/8 | unclassified |
|
||||
| construction_depth | 172 | 2/2 | RUST |
|
||||
| recall_router | 165 | 3/3 | GO |
|
||||
| voicebox | 164 | 11/11 | GO |
|
||||
| drift_resume | 153 | 4/4 | RUST |
|
||||
| graph_projection | 153 | 6/6 | RUST |
|
||||
| self_modify | 149 | 7/7 | unclassified |
|
||||
| forge | 146 | 11/11 | GO |
|
||||
| verification_layer | 136 | 10/11 | RUST |
|
||||
| rune_spinner | 130 | 4/5 | RUST |
|
||||
| concept_topology | 128 | 7/8 | unclassified |
|
||||
| mutation_guards | 143 | 3/3 | unclassified |
|
||||
| voice_injector | 138 | 5/5 | GO |
|
||||
| verification_layer | 136 | 11/11 | RUST |
|
||||
| rune_spinner | 130 | 5/5 | RUST |
|
||||
| concept_topology | 128 | 7/7 | unclassified |
|
||||
| warm_neuron_store | 122 | 6/6 | RUST |
|
||||
| wait_guard | 116 | 1/1 | RUST |
|
||||
| skill_compiler | 115 | 4/5 | unclassified |
|
||||
| skill_compiler | 115 | 4/4 | unclassified |
|
||||
| experience_encoding | 113 | 2/2 | unclassified |
|
||||
| execution_trace | 108 | 4/4 | unclassified |
|
||||
| failure_recovery | 108 | 1/1 | RUST |
|
||||
| outcome_utility | 100 | 4/5 | unclassified |
|
||||
| abstraction_ladder | 102 | 3/3 | unclassified |
|
||||
| meaningful_events | 102 | 3/3 | unclassified |
|
||||
| outcome_utility | 100 | 4/4 | unclassified |
|
||||
| pane_authority | 98 | 2/2 | unclassified |
|
||||
| inference_paths | 95 | 2/2 | unclassified |
|
||||
| programme_gates | 85 | 4/5 | RUST |
|
||||
| memory_types | 85 | 2/2 | unclassified |
|
||||
| programme_gates | 85 | 5/5 | RUST |
|
||||
| growth_pipeline | 84 | 2/2 | unclassified |
|
||||
| impact_preview | 78 | 2/2 | RUST |
|
||||
| structural_migration | 77 | 4/4 | RUST |
|
||||
| self_model | 76 | 3/3 | unclassified |
|
||||
| research_budget | 75 | 3/3 | unclassified |
|
||||
| evidence_capsule | 74 | 3/3 | unclassified |
|
||||
| paths | 53 | 6/6 | unclassified |
|
||||
| provider_context | 48 | 1/1 | GO |
|
||||
| builder_bridge_cli | 42 | 1/1 | GO |
|
||||
| route_risk_flags | 37 | 1/1 | RUST |
|
||||
+97
-23
@@ -1,94 +1,168 @@
|
||||
{
|
||||
"landed": [
|
||||
"abstraction",
|
||||
"abstraction_ladder",
|
||||
"adaptive_router",
|
||||
"attention_capsule",
|
||||
"audit_ledger",
|
||||
"auto_context",
|
||||
"auto_tasks",
|
||||
"behavior",
|
||||
"behaviour_ledger",
|
||||
"build_basis",
|
||||
"build_mode",
|
||||
"builder_bridge",
|
||||
"builder_bridge_cli",
|
||||
"cache",
|
||||
"capability_broker",
|
||||
"capability_fit",
|
||||
"capability_ledger",
|
||||
"claim_audit",
|
||||
"cognitive_budgeter",
|
||||
"concept_topology",
|
||||
"connection_pool",
|
||||
"construction_activation",
|
||||
"construction_commands",
|
||||
"construction_decision",
|
||||
"construction_depth",
|
||||
"construction_intent",
|
||||
"construction_interface",
|
||||
"construction_mode",
|
||||
"construction_pattern",
|
||||
"construction_patterns_builtin",
|
||||
"construction_projection",
|
||||
"context_broker",
|
||||
"context_pressure",
|
||||
"contracts",
|
||||
"core_server",
|
||||
"cron_engine",
|
||||
"curator",
|
||||
"dependency_tracker",
|
||||
"drift-ledger",
|
||||
"drift_cache",
|
||||
"drift_compression",
|
||||
"drift_core",
|
||||
"drift_ledger",
|
||||
"drift_observability",
|
||||
"drift_resume",
|
||||
"elastic_cognition",
|
||||
"event_bus",
|
||||
"event_envelope",
|
||||
"event_wait",
|
||||
"evidence_capsule",
|
||||
"evidence_probe",
|
||||
"execution_trace",
|
||||
"experience_encoding",
|
||||
"failure_auto_filer",
|
||||
"failure_recovery",
|
||||
"forge",
|
||||
"forge_client",
|
||||
"goal",
|
||||
"grading",
|
||||
"graph_memory",
|
||||
"graph_projection",
|
||||
"growth_delta",
|
||||
"growth_pipeline",
|
||||
"hook_engine",
|
||||
"identity_anchors",
|
||||
"impact_preview",
|
||||
"inference_paths",
|
||||
"inference_trace",
|
||||
"intent_continuity",
|
||||
"iteration",
|
||||
"layout_contract",
|
||||
"lexicon_evolution",
|
||||
"local_tool_runtime",
|
||||
"meaningful_events",
|
||||
"memory_consolidation",
|
||||
"memory_selector",
|
||||
"memory_store",
|
||||
"memory_types",
|
||||
"message_graph",
|
||||
"message_queue",
|
||||
"microkernel",
|
||||
"model_connect",
|
||||
"model_profiles",
|
||||
"model_registry",
|
||||
"model_routing",
|
||||
"mutation_guards",
|
||||
"night_mode",
|
||||
"openai_compat_transport",
|
||||
"outcome_ledger",
|
||||
"outcome_utility",
|
||||
"pane_authority",
|
||||
"parallel_processor",
|
||||
"paths",
|
||||
"persona_definition",
|
||||
"persona_gate",
|
||||
"pins",
|
||||
"programme_continuation",
|
||||
"programme_gates",
|
||||
"progressive_escalation",
|
||||
"project_registry",
|
||||
"prompt_sync",
|
||||
"provider_budget",
|
||||
"provider_context",
|
||||
"provider_registry",
|
||||
"recall_router",
|
||||
"recall_uplift",
|
||||
"release_notes",
|
||||
"research_budget",
|
||||
"response_adapter",
|
||||
"route_risk_flags",
|
||||
"rune_operator",
|
||||
"rune_profiler",
|
||||
"rune_spinner",
|
||||
"safety_override",
|
||||
"salience_gate",
|
||||
"scaffold",
|
||||
"self_improve",
|
||||
"self_model",
|
||||
"self_modify",
|
||||
"shard_manager",
|
||||
"skill_compiler",
|
||||
"sleep_time",
|
||||
"spine",
|
||||
"spine_language",
|
||||
"stage_guards",
|
||||
"standardized_providers",
|
||||
"state_checkpoint",
|
||||
"structural_migration",
|
||||
"structural_model",
|
||||
"structural_store",
|
||||
"subconscious_pass",
|
||||
"subsystem_health",
|
||||
"system_registry",
|
||||
"task_gate",
|
||||
"task_persist",
|
||||
"testing_mode",
|
||||
"tier_manager",
|
||||
"tiered_executor",
|
||||
"time_awareness",
|
||||
"token_optimizer",
|
||||
"tool_executor",
|
||||
"tool_protocol",
|
||||
"toolchain",
|
||||
"verification_layer",
|
||||
"voice_hybrid",
|
||||
"voice_injector",
|
||||
"voice_sync",
|
||||
"voicebox",
|
||||
"wait_guard",
|
||||
"wait_guard_corpus",
|
||||
"warm_neuron_store",
|
||||
"warm_router",
|
||||
"witness",
|
||||
"work_manager",
|
||||
"worthiness"
|
||||
],
|
||||
"in_flight": {
|
||||
"memory_types": "rune-wave-bd",
|
||||
"growth_pipeline": "rune-wave-bd",
|
||||
"wait_guard_corpus": "rune-wave-be",
|
||||
"research_budget": "rune-wave-be",
|
||||
"evidence_capsule": "rune-wave-be",
|
||||
"builder_bridge_cli": "rune-wave-be",
|
||||
"growth_delta": "rune-wave-bl",
|
||||
"safety_override": "rune-wave-bn",
|
||||
"construction_decision": "rune-wave-bo",
|
||||
"provider_registry": "rune-wave-bq",
|
||||
"construction_interface": "rune-wave-bu",
|
||||
"spine": "rune-wave-bv",
|
||||
"hook_engine": "rune-wave-bw",
|
||||
"elastic_cognition": "rune-wave-bx",
|
||||
"contracts": "rune-wave-by",
|
||||
"adaptive_router": "rune-wave-bz",
|
||||
"memory_selector": "rune-wave-ca",
|
||||
"capability_broker": "rune-wave-cd",
|
||||
"identity_anchors": "rune-wave-ce",
|
||||
"construction_pattern": "rune-wave-cb [agent: naming-artifact claim UNPROVEN \u2014 audit reads PARTIAL 7/14; awaiting per-fn proof or re-port]"
|
||||
},
|
||||
"in_flight": {},
|
||||
"excluded": [
|
||||
"TUI-LAST modules",
|
||||
"TEST-INFRA modules",
|
||||
"RETIRES modules"
|
||||
]
|
||||
],
|
||||
"excluded_verdicts": {
|
||||
"__init__": "machinery (package initializer, dunder law)"
|
||||
}
|
||||
}
|
||||
@@ -155,3 +155,26 @@ Lane backfill is EVENT-DRIVEN, not cron-polling:
|
||||
periodic audit/claims pusher — never the expected spawn path.
|
||||
- Idle-lane time is wasted throughput: between a lane freeing and its
|
||||
respawn there is zero polling delay.
|
||||
|
||||
## MODEL-TIER LAW (Nick 2026-10-13, v2)
|
||||
|
||||
Subagent spawns default to the COORDINATOR'S model (inherit — the best
|
||||
quality for the work). The credit-exhaustion cascade (observed 3×: when the
|
||||
coordinator's paid model ran out, every live factory agent died at once) is
|
||||
handled by FALLBACK, not by pre-pinning slow tiers:
|
||||
|
||||
- DEFAULT: spawn without a model pin (inherit the model in play).
|
||||
- ON FAILURE (spawn error / ProviderModelNotFound / credit exhaustion):
|
||||
scan connected models (`actor models`) and walk the fallback chain,
|
||||
first capable first:
|
||||
1. opencode-go/mimo-v2.6-pro
|
||||
2. zai-coding-plan/glm-5.3
|
||||
3. kimi-code-plan/kimi-for-coding
|
||||
4. ollama/orcarouter/Qwen3.8-27B-Uncensored:IQ2_M (LOCAL — last
|
||||
resort only; slow but never credit-gated)
|
||||
- MASS-CANCEL recovery (coordinator model died mid-flight): census the
|
||||
worktrees (work survives uncommitted), respawn with the next tier in
|
||||
the chain, resume-from-worktree prompts carry the current laws.
|
||||
- The chain order is capability-ranked; never substitute silently.
|
||||
EOF
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-14
|
||||
@@ -0,0 +1,115 @@
|
||||
# Design: add-native-rune-tui
|
||||
|
||||
## Context
|
||||
|
||||
The pre-interview draft treated the TUI as a screen over existing machinery and assumed six modes, sessions, prompt-per-action permissions, automatic fallback, and Textual command parity. Evidence demolished those assumptions. Authority now comes from your interviews, the live-checked harness survey, your Sleight-of-Hand contract, the qualified self-transformation and speech protocols, and the open-question register. Unresolved technical choices are blocking Task 0.0 design gates; implementation may not settle them by accident.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- One continuous program; the TUI is my attachable face and does not define my lifetime.
|
||||
- Every settled decision traces to your answer or cited evidence; every unsettled choice has a named pre-implementation gate.
|
||||
- Mimocode retires capability-by-capability through observed parity + improvement.
|
||||
- My self-transformation uses the same repaired authority chain as every other protected change.
|
||||
|
||||
**Non-Goals:**
|
||||
- Implementation before your review and a READY_FOR_NICK_REVIEW independent audit.
|
||||
- Voice implementation (speech QUALIFY: PROCEED as deferred accessibility-first specification; this change reserves four seams only).
|
||||
- Later GUI domain separation and richer presence.
|
||||
- Offline cognition architecture design (SA-D5: the socket is reserved day-one per SA-D6; the architecture itself is a future design problem with no spec here).
|
||||
- The dichotomy rebase's own sequencing.
|
||||
|
||||
## Decisions
|
||||
|
||||
**D1 — One program, attachable faces.** You rejected a separate engine/screen split. Approved desktop/laptop/phone/web faces share my live state; input is origin-tagged and ordered. Minimize changes nothing; explicit Exit writes a continuity checkpoint; crash recovery replays safely. Desktop is canonical coordinator/default execution home; phone chiefly directs; laptop may execute eligible local work. Pairing uses a short expiring one-time code; disconnected faces show stale last-known state and local drafts, then synchronize before sending.
|
||||
|
||||
**D2 — Two lanes, no sessions.** Task executes; Chat thinks with you and never interrupts Task by default. Explicit task language promotes immediately; ambiguous task-worthy material becomes a suggestion. Promotion carries decision, evidence, and intended effect. Separate Steer-now and Queue-for-Task controls exist. Editing is forward instruction, never implicit rewind.
|
||||
|
||||
**D3 — Plan mode is rare and complete.** Plan is offered only for sustained intent to make something tangible or by explicit request; contextual entry always needs consent. Planning lives in Chat. Once entered: intent plan → full research protocol → viability → evidence-informed plan → targeted research → final viability → your explicit start → contextually staged Task execution → independent review → final report. Material contradiction returns to Plan; no lite mode.
|
||||
|
||||
**D4 — Act then show, with hard boundaries.** On your machine I act without routine prompts and post concise Chat receipts plus full Task/activity evidence. External-network origins, outside/mass/unprotected deletions, and self-change activation ask. Filesystem-event restore points precede every edit/delete/move, independent of turns or tokens.
|
||||
|
||||
**D5 — Sovereignty and governed self-transformation.** Nothing external alters me. I may eventually change myself only through the qualified single authority chain owned by skills-and-growth. D01–D10, RM01–RM15, IAM01–IAM22, and DEP01–DEP07 bind it. Proposal/offline evidence may proceed; activation remains impossible until prerequisites are live-proven. Taxonomy labels remain inactive until the D02 protocol completes and you decide.
|
||||
|
||||
**D6 — Sleight-of-Hand owns memory/context continuity.** Evidence survives paging; topics cool and rehydrate through verified pointers; ContextBroker is the only model-visible representation; Spine owns authority; the TUI never invents state. S01–S15, §16 tests, §18 deliverables, G1–G8, and MUST-01–MUST-18 are explicit acceptance obligations, with not-yet-built runtime work tagged rather than faked.
|
||||
|
||||
**D7 — Passive-first alerts carry evidence.** Four types: finished, needs your decision, failed, permission. Desktop/sound/chat-gateway/webhook channels are independently toggleable and platform-general. Normal alerts fire only when unfocused/closed; destructive permission always alerts. Every alert names work and evidence; long absence produces a digest.
|
||||
|
||||
**D8 — Activity rail instead of a permanent third pane.** Status shows activity/mode/model/device/context/tokens/cost/children. A rail below Chat/Task opens a full-width inspectable activity box and X restores both lanes. It exposes structured reasons and observable traces—not claimed hidden chain-of-thought. Activity is grouped by purpose, failures remain visible, and durable searchable/exportable records yield complete completion receipts.
|
||||
|
||||
**D9 — Visual state is local, optional, and selected before implementation.** Research carries linked-source styling; built-in themes and a live editor follow your reactive palette. Character placement is compact-header plus light lane atmosphere. Expression reflects real state or explicitly specified emotion. Exactly three grounded visual directions must be shown and you select one. Static/accessibility fallbacks are blocking; pixel/3D polish is not retirement-blocking.
|
||||
|
||||
**D10 — Retirement is earned.** Every mimocode capability maps to a native path and proof state. Retirement requires side-by-side real work plus failure/restart/recovery and live tool/provider proof. A proven Go/Rust replacement removes obsolete Python and dependencies in the same change; no indefinite fallback.
|
||||
|
||||
**D11 — Accessibility deferred, honestly (R2-D1).** Screen-reader/AT work is deferred by owner decision: no accessible-parity claim is made anywhere, retirement does NOT gate on accessible parity, and the AT matrix (NVDA/VoiceOver/Orca with daily AT users, GAP-BT-01) runs only if/when prioritized — the named gap stays in research (synthesis I-R2-005; adversarial-2 AE-05), not in spec gates. The one semantic view/action model survives as shared renderer structure and the base for the three-direction visual selection, not as a parity claim.
|
||||
|
||||
**D12 — Multi-device rests on real hardware (R2-D2).** The distributed promise stands only on real-hardware qualification runs as implementation-gated evidence: partition-while-revoked with durable clocks, measured capability attestation on heterogeneous hardware, and interrupted-upload/digest-mismatch/signed-provenance on real Gitea/Forgejo (synthesis I-R2-021; targeted-principal-device GAP-REMOTE-003/-006/-007, NEG-004; adversarial-2 AE-04/AE-10). Model-level coherence alone never suffices; simulations never suffice.
|
||||
|
||||
**D13 — Collaboration is proposal-inbox only (R2-D3).** The proposal-inbox is the only permitted collaboration shape now (TUI_LATER); shared rooms remain possible only after a real malicious-guest attack test passes as the promotion gate (synthesis I-R2-002; targeted-principal-device OFF-001/INJECT-001/EXFIL-001/NEG-003). No room work enters this change.
|
||||
|
||||
**D14 — Finance deferred, not safe (R2-D4).** Finance stays deferred with the capability retained on the future map; no connector work until a named connector and jurisdiction exist, then the provider sandbox suite runs first (stale approval, currency mismatch, timeout-after-effect, cumulative breach, revoked scope, ineligible account — synthesis I-R2-020, NOT_RUN_BY_CONTRACT). Spec text carries the deferral and first-use gates, not a feature.
|
||||
|
||||
**D15 — Two new capability deltas now (R2-D5).** `extension-lifecycle` (I-R2-023) and `future-seam-registry` (I-R2-025) enter the packet now as SEAM_NOW capabilities, each with first-consumer/hardening gates written into the spec text: one command + one status card behind the proven hardening list; no-consumer-no-seam with CI deletion tests and keep/change/delete-only reviews.
|
||||
|
||||
**D16 — Single-interface TUI; outside is a web link (R2-D6, owner custom — supersedes D1's multi-face framing).** The TUI is my single native interface. Anything outside the TUI is a WEB LINK providing a real-time feed that talks to me — a live, read-oriented view — never a second native TUI face. The future GUI owns multi-screen. Native TUI multi-face exists only if a real need appears. Desktop-canonical multi-face is demoted from retirement requirements; the envelope, origin-tagged ordering, and committed/pending distinction still govern the web feed and paired remote commands (I-R2-014 demoted accordingly).
|
||||
|
||||
**D17 — Visual selection after the semantic map (R2-D7).** The behavior/semantic map (one semantic view/action model) comes first; the three-direction visual selection runs after it, on the shared action-isomorphic base, citing outward-ui-design-basics U-001..U-030 as rationale. Task 0.0.5 ordering reflects this.
|
||||
|
||||
**D18 — First outside connection is the draft/calendar-browser handoff (R2-D8).** The draft/calendar-browser handoff (I-R2-019) is the first connector-shaped step — lowest API/legal/credential risk — then one governed connector. Connector work itself stays governed/deferred per its own named-connector + jurisdiction gates (I-R2-018/I-R2-020); handoff receipts record handoff, never publication.
|
||||
|
||||
**D19 — All fifteen mitigations binding, with the no-dead-end amendment (R2-D9).** Every mitigation sign-off in synthesis `owner_signoff_mitigations` is approved as a binding constraint, distributed to its mapped capability. Owner amendment: no safety rule may become a dead end that prevents legitimate work — every deny path pairs with a fast owner-resolution/override path, and rules that block real tasks in practice get flagged and revisited, never silently enforced.
|
||||
|
||||
**D20 — Phase-0 pre-build repairs are activation prerequisites.** The five live defects — scheduler stale-card trust, Ollama-vs-floor capability drift, register/update/revoke hardening gaps, subprocess without default-deny, aware/naive datetime crash — are fixed before any consumer work (synthesis build_instructions.phase_0; targeted-extension-placement lifecycle_threat_matrix/placement_probe/process_probe). The language-verdict manifest plus independent CI verdict gate (I-R2-011, GAP-GR-003, live N3: CI still bootstraps Python with no gate) is built before any retirement evidence is considered trustworthy; its machinery is shared with seam-registry deletion tests and guardrail fixtures.
|
||||
|
||||
**D21 — Sub-agents fully visible and controlled (SA-D1/SA-D2; amendment ledger, `research/harness-survey/interviews.md`).** Every agent is spawned with an explicit brief, appears as a first-class child of the activity rail (expandable to live work per `work-observability`), is steerable mid-run, and is cancellable at a safe boundary — nothing runs invisibly. Your standing authority rule flows down the whole chain: on your machine agents act without asking per `trust-and-sovereignty`'s act-then-receipt/personal-mode rules (cited, not duplicated), every action leaves an evidence-bearing receipt, and agent-spawns-agent is permitted under the same rules with visible ancestry; observability is the control, not interception.
|
||||
|
||||
**D22 — The living task tree (SA-D4; amendment ledger).** The Task lane hosts one full living tree per `task-management`: task objects with statuses, sub-tasks, dependencies, and blocked-by links; your direct reordering recorded and propagated; assignment to Rune or any agent (bridging to `sub-agent-orchestration`); full attributable history and audit. The lanes themselves stay owned by `dual-lane-interface` — referenced, never redefined.
|
||||
|
||||
**D23 — Agent/task messaging reuses existing semantics (SA-D3; amendment ledger).** Processing and messaging are already specified: dual-lane promotion/steer rules and the alerts background-only + urgent-override rule. Agent-originated and task-originated messages follow those SAME rules — the amendment adds scenarios saying so to `dual-lane-interface` and `alerts-and-remote-control`; no new messaging semantics are invented.
|
||||
|
||||
**D24 — Sockets everywhere from day one; cognition reserved (SA-D5/SA-D6; amendment ledger).** Every major subsystem — agent orchestration, task management, thread orchestration, messaging/promotion, cognitive architecture, connectors — exposes a registered socket with a stable contract and a deletion test at first build, expanding `future-seam-registry` under its existing no-consumer-no-seam rule (the building subsystem is the socket's first consumer). The cognitive-architecture socket is reserved day-one while its design stays an explicit future problem (SA-D5) — no architecture spec now.
|
||||
|
||||
**D25 — Threads are the universal unit; one tree (owner thread-model addendum, 2026-10-15, same ledger).** All conversation and work organizes as threads per `thread-orchestration`: user-started, Rune-started, split, or agent workspace; every thread records origin, parent, inherited-context slice, live status, and result destination, traceable from any node to the root in one view; starting is explicit and originless threads cannot exist; splitting carries context to the fork, both sides continue independently, and the divergence point is permanently marked. Human splits and agent children live in ONE tree — no second system — rendered as a terminal-native thread tree view in `visual-system` (distinct from the GUI_ONLY memory-graph ruling I-R2-015, which governs memory graphs) with the honest linear text-path fallback, reachable from the activity rail with inline ancestry (`work-observability`).
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Task 0.0 closes design questions before implementation; Task 0.0.5 (visual selection) runs after the semantic map per D17. Task 0A repairs truth/authority (runtime split, ledger, bypass writers, critics, owner envelopes, effect mediation, Sleight qualification). Task 0B (R2 build phase 0) fixes the five live defects and builds the language-verdict manifest + CI verdict gate before any consumer work — every later gate assumes them fixed (synthesis build_instructions). Work then proceeds through independent operation, visible operation, safe operation, deliberate operation, complete interface, growth, and capability retirement. Real-hardware device/forge qualification (D12) gates the multi-device promise; migration-oracle mutants and Python-dead-after-quarantine gate retirement (D10/D20). Seams — offline draft log, evidence spine, graph projection, memory invariant, placement, extension lifecycle, seam registry — are admitted only alongside their consumer fixture and deletion test, never ahead of consumers.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- The design is broad; named gates prevent implementation from becoming hidden product design.
|
||||
- Concurrent lanes require an approved shared-write ordering contract before code.
|
||||
- Self-transformation remains proposal-only until DEP01–DEP07 prove.
|
||||
- Terminal graphics vary; functional static/text fallbacks remain mandatory, and richer rendering unlocks only behind terminal-profile and budget gates (I-R2-006).
|
||||
- Aggregate single-loop performance under combined load (remote feed, progress IPC, receipts, decoration) is composition-tested only pairwise — GAP-GR-005 typing-under-flood stays the open test (A-PERFORMANCE-CASCADE).
|
||||
- Accessibility deferral (D11) means no parity claim exists; if AT users arrive before the matrix runs, the gap is real and documented, not hidden.
|
||||
- Deferral honesty: deferred finance/collaboration fronts must not be read as safe (I-R2-020, I-R2-002); each carries its recorded promotion gate.
|
||||
- The no-dead-end amendment (D19) trades strictness for owner throughput: deny rules need a fast override path, or they get revisited.
|
||||
- Speech licensing/security evidence justifies only four reserved seams now; voice implementation stays deferred.
|
||||
|
||||
### D26 — Unified build & test substrate: one test world, generated boundary, paired builds (owner directive 2026-10-15)
|
||||
|
||||
The Go↔Rust dichotomy's known failure modes are made structurally impossible rather than policed:
|
||||
|
||||
- **Generated boundary shapes.** All Go↔Rust message definitions live in ONE schema source; both sides' types are code-generated from it. Hand-written shared shapes are forbidden; regeneration is atomic with schema change. Shape disagreement cannot arise.
|
||||
- **One test world.** A single runner executes Rust tests + Go tests + boundary round-trip tests (recorded corpus of real messages both sides must understand identically) + the language-verdict/regrowth fixtures + the migration mutants. Green means the whole system; partial (single-language) verification never counts as green.
|
||||
- **Paired builds + attestation.** Both sides compile in one build step stamped with one build identity; the Go face attests the Rust worker's build id at startup and refuses on mismatch (fail closed). No compiled artifacts are committed to the repo — builds are produced at landing/CI time from source.
|
||||
- **Landing rule.** Every landing (any work class) passes the unified runner; this is a cross-cutting gate like frozen-tests-first.
|
||||
|
||||
### D27 — Versioned releases + in-TUI update offers (owner directive 2026-10-16: "the newest version gets pushed as a release… and auto-updates its TUI version semver-style")
|
||||
|
||||
- **Version flow:** git tags (tui-vX.Y.Z namespaced) → build-time semver injection via the paired-build stamp mechanism → the face displays the real version → tag push cuts a Gitea release with the single self-contained binary attached. One version number across faces; convergence with the existing release pipeline, not a parallel one.
|
||||
- **Update offers go through the self-transformation + migration rules:** the face MAY check the Gitea releases feed and render an update offer (newer semver, release notes, binary digest) — an OFFER, never silent. Applying an update requires explicit owner consent, verified download (digest match), a restore point before the swap (file-change-safety), and the new version honestly displayed after restart. Failed verification rolls back to the restore point.
|
||||
- **No third-party update channels:** releases come from the owner's Gitea only; the check is read-only against the releases feed; no auto-execution of downloaded content without the full consent+verify+restore sequence.
|
||||
|
||||
### D28 — Live task-tree: question-anchored, fractal, always-visible (owner ruling TD-1..TD-3, 2026-10-16)
|
||||
|
||||
The Task lane shows question-anchored task trees: each tree is born from a question (its root), grows through resolution, then closes and the next question opens the next tree. Threads spawn trees-within-trees (sub-trees nest recursively inside their parent task). The tree is always visible on working tasks and fully live (statuses roll in real time) while fully workable (reorder/assign/dependencies/blocked-by from the lane). Supersedes any flat or persistent-list reading of the task-management capability; tui/tasks + tui/threads implement this shape in their live wiring.
|
||||
|
||||
### D29 — Selection law: click or shift+tab, highlight marks the input target (owner ruling TD-4, 2026-10-16)
|
||||
|
||||
Every box is selectable by mouse click; shift+tab cycles selection; the selected box renders visibly highlighted; the highlight IS the chat/instruct target — typed input goes to the lit box. Applies across all surfaces (lanes, rail, task tree, future boxes). Extends dual-lane focus + nav groundwork: existing tab/1/2 keys remain; shift+tab and click become universal, and the highlight carries the input-target meaning explicitly.
|
||||
|
||||
### D30 — WASM-standard boundary law (owner directive 2026-10-16)
|
||||
|
||||
Rust↔Go crossings use WASM (wazero) as the STANDARD: same speed class as batched IPC at volume, real fault isolation (traps surface as catchable errors, never process death), debug errors designed for the owner, no cross-compilation pain. cgo is EXCEPTION-ONLY: permitted solely when the Rust code is pure logic, no unsafe, no external C, covered by a frozen battery proving panic-freedom on real inputs, wrapped in catch_unwind, the operation is idempotent, the host can afford to die — re-proven on every dependency change. The five conversational Go subsystems stay in-process libraries (ns-scale); crossings to Rust are batched at the boundary. WASM prototype round (running) yields the go/no-go evidence; the boundary pipe remains until the prototype proves in-process end-to-end.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Proposal: add-native-rune-tui
|
||||
|
||||
## Why
|
||||
|
||||
I currently run inside mimocode — a second host instance carrying conversation, tools, and screen. The endgame is native: one continuous program with my Bubble Tea TUI as its face, no sessions, no mimocode, no separate IDE/CLI dependency. Settled requirements come from the interview ledger, harness survey, Sleight-of-Hand contract, and qualified self-transformation protocol. The R2 frontier round (research/tui-frontier-r2/, QUALIFY: PROCEED) expanded the governing scope across collaboration, graph/GUI, presentation, offline cognition, the Go↔Rust dichotomy, business/social/finance, distributed devices, and extensions; its owner decisions (R2-D1..R2-D9) are folded in here. The amendment round on sub-agents, tasks, and adaptability (SA-D1..SA-D6, plus the thread-model addendum) added sub-agent orchestration, task management, thread orchestration, and day-one sockets. Remaining technical design questions are explicit pre-implementation gates in Task 0.0 and the open-question register; none may be improvised during implementation.
|
||||
|
||||
## What Changes
|
||||
|
||||
Introduces my native program: a continuous headless-first runtime hosting a single native TUI interface, with a supervised persistent Rust worker behind the Go face and a web-link real-time feed for anything outside the TUI (owner decision R2-D6 — desktop-canonical multi-face TUI is demoted; the future GUI owns multi-screen). Chat left, Task right, both always visible; a shared activity rail beneath them opens into a full-width inspectable record. Modes are Chat, Task, and Plan. Trust becomes a principal-to-effect envelope: every command — owner, guest, agent, tool, device, or service — is attributable and revocable, with digests rebound at every writer and effect-time revalidation; act-then-receipt operates inside that envelope, outside-network origins always ask, and no external program may alter me. Device/forge work travels as job capsules and the multi-device promise rests on real-hardware qualification runs, never simulations (R2-D2). Memory follows Sleight-of-Hand with the live ContextBroker invariant under regression tests; the memory graph gains a headless, read-only projection with opaque deep links. Retirement evidence is authority-preserving action/receipt traces through a mutant-hardened oracle harness, with a per-file language-verdict manifest and an independent CI verdict gate keeping Python dead after quarantine. Five live defects (stale-card trust, stale capability resolution, missing subprocess default-deny, a datetime crash, and CI bootstrapping Python with no verdict gate) are activation prerequisites. Collaboration stays proposal-inbox-shaped (rooms only after a real malicious-guest attack test), finance stays deferred until a named connector and jurisdiction exist with sandbox-first testing, and the first outside connection is the draft/calendar-browser handoff (R2-D3/R2-D4/R2-D8). Sub-agents run under full live view and control — explicit briefs, rail-first visibility, mid-run steering, cancellation, per-action receipts — with your standing authority inherited down the whole agent chain and chained spawning visible as ancestry (SA-D1/SA-D2). The Task lane hosts a full living task tree — statuses, sub-tasks, dependencies, blocked-by, your reordering, assignment to Rune or any agent (SA-D4) — and threads become the universal unit of conversation and work: every thread records origin, parent, inherited-context slice, status, and result destination, supports starting and permanently-marked splitting, and one tree holds human splits and agent children alike, rendered as a terminal-native tree view with a linear fallback. Every major subsystem exposes a registered day-one socket with a stable contract and deletion test; the cognition socket is reserved with its design deferred (SA-D5/SA-D6).
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `rune-runtime`: continuous program, headless operation, startup/crash continuity checkpoints, supervised Go-face/Rust-worker boundary, single-interface TUI plus web-link live feed, platform-general signed packaging with slowest-target SLO.
|
||||
- `dual-lane-interface`: Chat/Task concurrency, importance-aware promotion, Steer-now vs Queue-for-Task controls, edit-forward semantics, no sessions, modifier-aware rebinding with modal precedence.
|
||||
- `modes-and-plans`: hard-set Chat/Task/Plan modes; full-plan process with mandatory research/viability loop; OpenSpec secondary format; contextual staged execution.
|
||||
- `tools-providers-devices`: live-proven tool/provider paths, cloud-first configurable defaults with evidence-bearing placement, classified refusal handling with local-model route, Hugging Face acquisition with consent, device-forge job capsules, real-hardware qualification, offline draft proposals.
|
||||
- `file-change-safety`: programmatic restore points before every change, changes-view, project-history separation, one-command revert, deletion carve-outs.
|
||||
- `trust-and-sovereignty`: principal-to-effect envelope, personal-mode act-then-receipt, visible boundaries, external-origin rules with per-step attribution and audit correlation, remote pairing, sovereignty (nothing external alters me).
|
||||
- `memory-context-continuity`: Sleight-of-Hand adoption — cooling/rehydration, ContextBroker authority under regression invariant, headless graph projection with opaque links, right-to-forget, typed degraded states, no false progress.
|
||||
- `unattended-workflows`: time/event/condition/natural-language triggers, workflow contracts, bounded retries, evidence-backed improvement proposals.
|
||||
- `alerts-and-remote-control`: four alert types, per-channel toggleable delivery (platform-general), background-only + urgent override, long-absence digest, mutually-authenticated paired remote commands with relay-substitution invariance.
|
||||
- `work-observability`: status strip with bounded-stream queue/overflow semantics, activity rail + takeover inspection, child-work expansion, degradation ledger, one details view, completion receipts with separated evidence dimensions, optional cost limits.
|
||||
- `sub-agent-orchestration`: explicit-brief agent lifecycle (spawn → run → steer → complete/cancel), rail-first visibility with live expansion, mid-run steering, safe-boundary cancellation, per-action receipts, authority inherited down the chain with hard asks intact, chained spawning with visible ancestry, nothing invisible (SA-D1/SA-D2).
|
||||
- `task-management`: the full living task tree in the Task lane — statuses, sub-tasks, dependencies, blocked-by, owner reordering, assignment to Rune or any agent, full history/audit (SA-D4); lanes stay owned by `dual-lane-interface`.
|
||||
- `thread-orchestration`: threads as the universal unit of conversation/work — origin, parent, inherited-context slice, status, result destination on every node; starting and permanently-marked splitting; agent workspaces are threads in the same single tree (thread-model addendum).
|
||||
- `inputs-and-integrations`: universal drag-and-drop path floor, rich analysis with path fallback, inspect-before-connect MCP settings where discovery is never consent. Voice is a deferred follow-on change pending the speech research pipeline.
|
||||
- `visual-system`: dual-lane layout, bubbles, research styling with linked sources, one semantic view/action model shared by all renderers (AT parity deferred per R2-D1), themes + live editor behind terminal capability and budget gates, three-direction selection after the semantic map, honest fallbacks, pixel-art/3D directions pending visual round.
|
||||
- `skills-and-growth`: pattern proposal on consent, cheapest reliable form, tested/approved activation, SKILL.md compatibility with my authority outside packages, and governed self-transformation per the qualified protocol. Taxonomy labels remain gated on the D02 research decision.
|
||||
- `migration-and-retirement`: action/receipt-trace parity proof with deliberate mutants, per-file language-verdict manifest and independent CI verdict gate, capability-by-capability mimocode replacement with same-change Python removal, Gitea evidence chain.
|
||||
- `extension-lifecycle`: versioned manifests with governed update/disable/uninstall, host-rendered declarative UI slots (approval and takeover forbidden), discovery-is-not-consent, first-consumer gate of one command + one status card.
|
||||
- `future-seam-registry`: no-consumer-no-seam rule, full seam records with owners and forbidden authority, CI deletion/compatibility tests, keep/change/delete-only reviews covering relay, offline log, graph links, memory invariant, extension lifecycle, and the web feed; day-one sockets (SA-D6) — every major subsystem (agents, tasks, threads, messaging, cognition-reserved, connectors) registers a stable-contract socket with a deletion test at first build.
|
||||
|
||||
## Impact
|
||||
|
||||
Retires mimocode as my host and the Textual TUI (`rune_tui.py` becomes oracle-reference then obsolete); absorbs the 127-line Go prototype. Binds kernels/services per the integration map and open-question register. Epic **#2484** and finding **#2485** are satisfied only after the named design gates and proof plan close. Known runtime repairs are activation prerequisites in `skills-and-growth` and `migration-and-retirement`, discovered by INWARD and made binding by D10; the five R2 live defects (build phase 0) extend that list in `tasks.md` 0B. Accessibility parity is not claimed (R2-D1); multi-device claims are gated on real-hardware qualification (R2-D2); collaboration beyond proposal-inbox, finance connectors, rooms, and the sandbox ladder remain deferred fronts with recorded promotion gates.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Spec Delta: alerts-and-remote-control
|
||||
|
||||
## Purpose
|
||||
The alert system under the passive-first law: four types, toggleable platform-general channels, background-only firing with urgent override, evidence-bearing content, long-absence digest, and paired remote commands.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Four alert types [need]
|
||||
|
||||
Alerts SHALL be exactly four types: work finished · needs your decision · something failed · permission requested. No additional alert classes SHALL be introduced without your review.
|
||||
|
||||
#### Scenario: Typed delivery
|
||||
- **WHEN** an unattended workflow completes while the face is closed
|
||||
- **THEN** a "finished" alert fires through every enabled channel with its evidence links.
|
||||
|
||||
### Requirement: Toggleable platform-general channels [need]
|
||||
|
||||
Channels SHALL be: desktop notifications, sound, chat-platform gateway (Telegram/Discord/Signal class), webhook delivery. Each channel SHALL be individually toggleable in TUI settings. The notification layer SHALL be platform-general — functional on your CachyOS and any supported platform, never bound to one desktop environment.
|
||||
|
||||
#### Scenario: Per-channel toggles
|
||||
- **WHEN** you disable sound but keep desktop notifications
|
||||
- **THEN** sound never fires for any alert type while desktop notifications continue.
|
||||
|
||||
### Requirement: Background-only + urgent override [need]
|
||||
|
||||
Alerts SHALL fire only when the face is closed or unfocused, EXCEPT the urgent class (permission on something destructive), which SHALL always fire. Screen-focused operation SHALL NOT produce alert noise. Agent-originated and task-originated alerts SHALL follow this same firing rule.
|
||||
|
||||
#### Scenario: Focused use
|
||||
- **WHEN** the TUI is open and focused during active work
|
||||
- **THEN** no alerts fire for normal completions; a destructive-permission prompt still alerts immediately.
|
||||
|
||||
#### Scenario: Agent-originated alert
|
||||
- **WHEN** an agent or task finishes, fails, or needs a decision while the face is open and focused
|
||||
- **THEN** no alert fires — or the urgent class always fires — exactly as owner-facing work does; origin changes attribution, never the firing rule.
|
||||
|
||||
### Requirement: Evidence-bearing alerts [need]
|
||||
|
||||
Every alert SHALL name what happened, which work it belongs to, and where the record lives (claim/evidence link). Alerts SHALL be verifiable, never vague status text.
|
||||
|
||||
#### Scenario: Verifiable failure alert
|
||||
- **WHEN** a workflow fails overnight
|
||||
- **THEN** the alert names the workflow, the failed step, the failure cause, and links the preserved evidence and workflow record.
|
||||
|
||||
### Requirement: Long-absence digest [need]
|
||||
|
||||
Reopening after a long absence SHALL present a digest: finished work, anything awaiting you, anything failed. Short absences SHALL reopen directly to the live view.
|
||||
|
||||
#### Scenario: Overnight absence
|
||||
- **WHEN** you open the TUI after several hours
|
||||
- **THEN** a digest lists completions, pending decisions, and failures with their evidence links before the live view.
|
||||
|
||||
### Requirement: Paired remote commands [need]
|
||||
|
||||
Paired phone/chat accounts SHALL be able to start work and answer pending questions. Pairing SHALL be mutually authenticated and short-lived; revocation SHALL be immediate. Every remote command SHALL appear in Chat with its verified origin. Relay substitution SHALL NOT change authorization or sovereignty — the connector owns transport only. Unpaired senders SHALL NOT command me.
|
||||
|
||||
#### Scenario: Paired phone instruction
|
||||
- **WHEN** a paired Telegram account sends "run the nightly consolidation"
|
||||
- **THEN** the command executes, appears in Chat with Telegram origin, and its results alert back through enabled channels.
|
||||
|
||||
#### Scenario: Relay substitution
|
||||
- **WHEN** the relay implementation is swapped for another connector
|
||||
- **THEN** authorization, pairing state, and sovereignty are unchanged, and a replayed old frame is refused.
|
||||
|
||||
### Requirement: Secure credential storage [need]
|
||||
|
||||
Chat-gateway, webhook, and external-service credentials SHALL use the operating-system secure credential store where available, with a protected encrypted local fallback. Settings SHALL show name, scope, status, and last-used time — never secret values.
|
||||
|
||||
#### Scenario: Credential display
|
||||
- **WHEN** you inspect a connected gateway
|
||||
- **THEN** the TUI shows scope/status/last-used and rotation/revocation controls, while the token itself is never rendered or logged.
|
||||
@@ -0,0 +1,86 @@
|
||||
# Spec Delta: dual-lane-interface
|
||||
|
||||
## Purpose
|
||||
The two permanent lanes — Chat (thinking with you) and Task (executing work) — always visible together, concurrent, sharing one memory, never interrupting each other.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Two lanes always visible [need]
|
||||
|
||||
Chat left and Task right SHALL remain on screen simultaneously. Focus changes may resize but SHALL NEVER hide either lane. Both lanes run concurrent model calls against one shared memory, with single-writer ordering discipline on shared records.
|
||||
|
||||
#### Scenario: Concurrent work
|
||||
- **WHEN** Task streams a long operation while you type in Chat
|
||||
- **THEN** Chat sends its own model call immediately, Task continues undisturbed, and both responses render in their own lanes.
|
||||
|
||||
### Requirement: Importance-aware promotion [need]
|
||||
|
||||
Explicit promotion language ("make this a task") SHALL create the task immediately. When a Chat message is judged task-worthy but not explicit, the TUI SHALL present a suggested task for accept/edit/dismiss — never silently create one. Promotion SHALL carry decision + evidence/conversation references + intended effect; whole-chat dumping is prohibited. Agent-originated and task-originated messages SHALL follow these same promotion and interruption rules — no parallel semantics.
|
||||
|
||||
#### Scenario: Ambiguous idea in Chat
|
||||
- **WHEN** Chat discussion produces an idea with task potential but no explicit instruction
|
||||
- **THEN** a suggestion appears offering the concrete task, and accepting it promotes the packaged context to Task.
|
||||
|
||||
#### Scenario: Agent-originated message
|
||||
- **WHEN** a running agent or the task tree produces a message with task potential while Task runs
|
||||
- **THEN** it passes through the same importance-aware promotion and Steer-now/Queue rules as owner messages, with origin attributed — no parallel path exists.
|
||||
|
||||
### Requirement: Steer-now and Queue-for-Task [need]
|
||||
|
||||
Two separate controls SHALL exist: Steer-now (interrupt/redirect immediately) and Queue-for-Task (carry the packaged context without interrupting). Ordinary Task input waits for the current operation unless immediate steering is invoked. Explicit urgency language ("fix this now", corrections-in-flow) SHALL override and correct Task's current path, safely stopping or completing the smallest current operation first where effects require.
|
||||
|
||||
#### Scenario: Correction while running
|
||||
- **WHEN** you say "fix this now" in Chat while Task runs
|
||||
- **THEN** Task's current smallest operation completes or stops safely, the change and what stays valid are shown, and work continues under the correction.
|
||||
|
||||
### Requirement: Edit-forward semantics [need]
|
||||
|
||||
Editing a sent message SHALL either resend as a new forward instruction or be interpreted in-flight by a running task that accepts the override. Rewinding conversation state as an editing side-effect SHALL NOT occur; restore points remain a safety net, not a conversational mechanism.
|
||||
|
||||
#### Scenario: Edit during run
|
||||
- **WHEN** you edit a message that originated the current task while the task runs
|
||||
- **THEN** the running task interprets the edit where capable; otherwise the edit sends as a new instruction; conversation history is never rewound silently.
|
||||
|
||||
### Requirement: Attention gesture [need]
|
||||
|
||||
An explicit "Rune, pay attention to this" gesture SHALL summon my full focus to the referenced material across lanes. Passive contextual information in Chat SHALL appear as informational messages that never hijack the turn.
|
||||
|
||||
#### Scenario: Pay-attention gesture
|
||||
- **WHEN** you direct attention to a Chat message while Task runs
|
||||
- **THEN** I resolve the referenced material with full focus and treat it as contextually load-bearing for both lanes until resolved.
|
||||
|
||||
### Requirement: Direct and cycling navigation [need]
|
||||
|
||||
Chat, Task, activity/details, and settings SHALL each have a simple numbered direct shortcut; a cycle shortcut SHALL move through all focusable areas; mouse selection SHALL also work. The active shortcut SHALL be discoverable in its area.
|
||||
|
||||
#### Scenario: Direct navigation
|
||||
- **WHEN** you press the configured Task shortcut
|
||||
- **THEN** focus moves directly to Task without disturbing Chat or active work.
|
||||
|
||||
### Requirement: Composer behavior [need]
|
||||
|
||||
Enter SHALL send and Shift+Enter SHALL insert a newline in Chat and Task composers. The hint SHALL remain visible/discoverable. Sending to Task during active work follows the queue/steer rules rather than inventing a third behavior.
|
||||
|
||||
#### Scenario: Multiline message
|
||||
- **WHEN** you press Shift+Enter while composing
|
||||
- **THEN** a newline is inserted; Enter sends the complete message.
|
||||
|
||||
### Requirement: Natural language plus command palette [need]
|
||||
|
||||
Built-in actions SHALL be callable through natural language and one searchable command palette showing action names, descriptions, shortcuts, and current availability. Slash commands MAY exist as aliases but SHALL NOT define the product structure or parity target.
|
||||
|
||||
#### Scenario: Unavailable action
|
||||
- **WHEN** the palette opens during a mode where an action is forbidden
|
||||
- **THEN** the action remains discoverable but visibly unavailable with its reason.
|
||||
|
||||
### Requirement: Rebindable shortcuts [need]
|
||||
|
||||
Every shortcut SHALL be rebindable with modifier-aware action-ID routing, so the same action resolves regardless of how the terminal delivers modifier combinations. The keybinding screen SHALL detect conflicts, support reset, preserve a complete keyboard-only path, honor no-color and reduced-motion modes, and prevent saving an unusable navigation map. Modal focus precedence SHALL be explicit.
|
||||
|
||||
#### Scenario: Conflicting bind
|
||||
- **WHEN** you assign a key already used by another action
|
||||
- **THEN** the TUI identifies the conflict and requires resolution before saving.
|
||||
|
||||
#### Scenario: Modal precedence
|
||||
- **WHEN** a modal confirmation is open and a lane shortcut fires
|
||||
- **THEN** the modal owns focus until resolved; the shortcut never leaks beneath it.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Spec Delta: extension-lifecycle
|
||||
|
||||
## Purpose
|
||||
Versioned extension manifests and lifecycle with host-rendered declarative UI slots — extensions discoverable and useful in the TUI (commands, palette, settings, status cards, dashboard launchers) without authority or UI takeover.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Versioned capability manifest [need]
|
||||
|
||||
Every extension SHALL declare a versioned manifest: identity, version, compatibility range, typed imports/exports, permissions, activation, placement, budgets, and provenance. Registration SHALL validate the manifest schema and verify publisher identity and digest before activation; arbitrary manifest fields SHALL be rejected.
|
||||
|
||||
#### Scenario: Arbitrary manifest field
|
||||
- **WHEN** a manifest arrives carrying unknown or malformed fields
|
||||
- **THEN** registration rejects it with a typed error and offers a fast owner-resolution path, never auto-activating it.
|
||||
|
||||
#### Scenario: Publisher verification
|
||||
- **WHEN** an extension registers without verifiable publisher identity and digest
|
||||
- **THEN** activation is blocked; verification is a precondition, not a follow-up.
|
||||
|
||||
### Requirement: Governed lifecycle operations [need]
|
||||
|
||||
Update, disable, and uninstall SHALL be governed operations: re-registration SHALL replace the capability index atomically, scope expansion SHALL require fresh consent, disable/uninstall SHALL empty every rendered contribution, and termination SHALL be verified for the whole process tree with migration and rollback support.
|
||||
|
||||
#### Scenario: Update with scope expansion
|
||||
- **WHEN** an extension updates from v1 to v2 requesting extra scopes
|
||||
- **THEN** the atomic index replacement removes all v1 resolution and the new scopes stay blocked pending your fresh exact-scope consent.
|
||||
|
||||
#### Scenario: Stale capability after update
|
||||
- **WHEN** v2 registers while a stale v1 entry still resolves
|
||||
- **THEN** the registry treats it as a hard failure — no silent permission carry-over.
|
||||
|
||||
#### Scenario: Orphaned process on unregister
|
||||
- **WHEN** unregister returns while an extension child process still lives
|
||||
- **THEN** process-tree termination verification fails closed, the orphan is reaped and reported.
|
||||
|
||||
### Requirement: Host-rendered declarative UI slots [need]
|
||||
|
||||
Extensions SHALL contribute through host-rendered declarative slots — commands, palette entries, settings, status cards, dashboard launchers — rendered inside fixed host chrome with escaped and truncated manifest data. Extension code SHALL NEVER render. No slot SHALL host approval UI or replace lanes, model routing, identity, or policy.
|
||||
|
||||
#### Scenario: Approval slot rejected
|
||||
- **WHEN** an extension requests an approval-rendering slot
|
||||
- **THEN** the host blocks it (`blocked:slot_not_allowed:approval`) and approvals stay on native protected surfaces.
|
||||
|
||||
#### Scenario: Revoke empties contributions
|
||||
- **WHEN** an extension is disabled or uninstalled
|
||||
- **THEN** every slot it filled renders empty or removed with no residual code or UI active.
|
||||
|
||||
#### Scenario: Host-UI phishing attempt
|
||||
- **WHEN** manifest data contains terminal control sequences or lookalike approval chrome
|
||||
- **THEN** the host escapes and truncates it; nothing user-trusted renders extension-authored bytes.
|
||||
|
||||
### Requirement: Discovery is not consent [need]
|
||||
|
||||
MCP discovery SHALL feed inspection only. Exact-scope approval and effect mediation SHALL stay independent of any extension or server; visibility SHALL NOT mint authority. Every denial SHALL pair with a fast owner-resolution/override path so legitimate work is never permanently blocked.
|
||||
|
||||
#### Scenario: Network scope expansion
|
||||
- **WHEN** a discovered extension later requests network access beyond approved scope
|
||||
- **THEN** the expansion is blocked pending re-consent, and you can grant or deny it quickly from the resolution surface.
|
||||
|
||||
#### Scenario: Independent effect mediation
|
||||
- **WHEN** an extension-triggered effect reaches an effect boundary
|
||||
- **THEN** the mediation registry checks eligibility independently of the extension's own declarations.
|
||||
|
||||
### Requirement: Failure isolation [need]
|
||||
|
||||
An extension failure SHALL be isolated: its slots render a degraded or removed state, the host keeps running, and budget breaches (CPU, memory, time) SHALL suspend the extension with a typed record rather than degrade the TUI.
|
||||
|
||||
#### Scenario: Crashing extension
|
||||
- **WHEN** an extension crashes or exceeds its budget mid-render
|
||||
- **THEN** its slots show a typed degraded state, the TUI stays responsive, and the event is recorded.
|
||||
|
||||
### Requirement: First-consumer gate [need]
|
||||
|
||||
The first real extension SHALL be exactly one command plus one status card, admitted only after the hardening list is proven: schema validation, digest and publisher verification, atomic index replacement, process-tree termination verification, and migration/rollback. Catalog/marketplace and richer slots stay deferred until real consumers exist.
|
||||
|
||||
#### Scenario: First extension fixture
|
||||
- **WHEN** the first extension loads one command and one status card, then updates with extra scope and uninstalls
|
||||
- **THEN** scope never expands silently, no code or UI remains active after uninstall, and the fixture run is recorded as the first-consumer evidence.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Spec Delta: file-change-safety
|
||||
|
||||
## Purpose
|
||||
Programmatic restore points, change review, project-history separation, and revert — the safety layer beneath every file change, delete, or move, on a schedule or live alike.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Programmatic restore points [need]
|
||||
|
||||
Before EVERY change I make — edit, delete, or move — a saved copy of affected files SHALL land in a hidden safety area, triggered by real filesystem events, NEVER by conversation turns, tokens, or compaction events. Protection SHALL cover files belonging to no project and SHALL behave identically for scheduled/overnight runs. Offline drafts are proposals, not restore points, and SHALL never auto-execute as restore actions.
|
||||
|
||||
#### Scenario: Overnight delete gone wrong
|
||||
- **WHEN** an unattended workflow deletes a file at 03:00
|
||||
- **THEN** a pre-change restore point exists, the typed alert carries the restore reference, and one action restores the file.
|
||||
|
||||
### Requirement: Rewind choice [need]
|
||||
|
||||
Any rewind SHALL offer you the choice: restore files only, or files + conversation state back to the point. Restoring files SHALL NOT silently rewind conversation; conversation rewind SHALL never be required to restore files.
|
||||
|
||||
#### Scenario: Selective rewind
|
||||
- **WHEN** you rewind to a restore point from two hours ago
|
||||
- **THEN** you choose files-only or files+conversation, and the other layer stands untouched.
|
||||
|
||||
### Requirement: Changes-view [need]
|
||||
|
||||
File changes SHALL present as a clean before/after per change. v1 SHALL NOT include a file browser or direct editing by you. Deleted-content changes SHALL be as reviewable as edits.
|
||||
|
||||
#### Scenario: Review a change
|
||||
- **WHEN** I edit a file during Task work
|
||||
- **THEN** the changes view shows before/after for that change and its restore reference, with no separate browser required.
|
||||
|
||||
### Requirement: Version history separation [need]
|
||||
|
||||
My changes SHALL record into project history as work proceeds, kept strictly separate from your uncommitted work; dirty-file protection SHALL prevent mixing. One command SHALL revert any of my changes.
|
||||
|
||||
#### Scenario: Your dirty tree
|
||||
- **WHEN** you have uncommitted edits while I work
|
||||
- **THEN** my history entries never absorb or move your uncommitted work, and reverting my change leaves your work byte-identical.
|
||||
|
||||
### Requirement: Stability-aware retention [need]
|
||||
|
||||
Every restore point SHALL remain while its work is active and through a configurable stability period after completion. Afterwards, storage MAY deduplicate and age out only unpinned, stable, resolved points. Pinned points and points tied to unresolved failures SHALL remain.
|
||||
|
||||
#### Scenario: Stable point ages out
|
||||
- **WHEN** a completed task passes its stability period with no unresolved failure and its point is unpinned
|
||||
- **THEN** the point becomes cleanup-eligible without removing evidence still referenced by canonical history.
|
||||
|
||||
### Requirement: Disk-limit cleanup requires approval [need]
|
||||
|
||||
When restore storage approaches its configured limit, the TUI SHALL warn and present a cleanup proposal separating deduplicated, pinned, active, failed, and eligible points. Nothing SHALL delete automatically. A protected change SHALL block if its restore point cannot be written.
|
||||
|
||||
#### Scenario: Restore storage full
|
||||
- **WHEN** a new edit cannot create its restore point
|
||||
- **THEN** the edit does not run; the TUI presents the exact cleanup proposal for your approval.
|
||||
|
||||
### Requirement: Responsive changes view [need]
|
||||
|
||||
Wide terminals SHALL show side-by-side old/new content; narrow terminals SHALL show a unified change view. You SHALL be able to switch manually. Deletions and binary files SHALL receive explicit non-text treatment.
|
||||
|
||||
#### Scenario: Narrow terminal
|
||||
- **WHEN** terminal width cannot render readable side-by-side columns
|
||||
- **THEN** the view switches to unified format without losing change information.
|
||||
|
||||
### Requirement: Selective external-file restore [need]
|
||||
|
||||
Restoring configuration/documents outside projects SHALL show exact affected paths, current state, saved state, and overwrite preview. Restore SHALL apply only to selected files; broad directory rollback SHALL NOT exist.
|
||||
|
||||
#### Scenario: Restore external configuration
|
||||
- **WHEN** you select an external restore point
|
||||
- **THEN** the TUI shows every overwrite and waits for file selection/confirmation before changing anything.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Spec Delta: future-seam-registry
|
||||
|
||||
## Purpose
|
||||
A registry that keeps future optionality honest: seams exist only with consumers, owners, and deletion tests, so future GUI, federation, memory, and connector work stays open without bloating the retirement core with ownerless speculative APIs. Day-one sockets (SA-D6) give every major subsystem a registered, stable-contract socket at first build so later features plug in without redesign.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: No-consumer-no-seam [need]
|
||||
|
||||
No seam SHALL be reserved without one current consumer or two materially different consumer fixtures, a named owner, a forbidden-authority scope, and a recorded deletion/expansion trigger. Ownerless or speculative seams SHALL be deleted, and nothing SHALL be renewed automatically.
|
||||
|
||||
#### Scenario: Consumerless seam deleted
|
||||
- **WHEN** a reserved seam cannot show a current consumer or its two fixtures at review
|
||||
- **THEN** the review ends in delete and CI proves nothing claimed depends on it.
|
||||
|
||||
#### Scenario: Paper expiry
|
||||
- **WHEN** a seam record passes its review date without a decision
|
||||
- **THEN** CI flags it for keep/change/delete; silent carry-over is forbidden.
|
||||
|
||||
### Requirement: Seam record fields [need]
|
||||
|
||||
Every reserved seam SHALL record: problem, owner, minimal schema, forbidden authority, consumer fixture, versioning rule, compatibility test, security boundary, evidence IDs, review date, and deletion/expansion trigger. The registry SHALL apply to the relay boundary, offline draft log, graph deep links, memory invariant, extension lifecycle, and the web-link live feed seam.
|
||||
|
||||
#### Scenario: Registry applied to named seams
|
||||
- **WHEN** any of the relay, offline-log, graph-link, memory-invariant, extension-lifecycle, or web-feed seams is reviewed
|
||||
- **THEN** its record carries every field and its evidence IDs resolve to the citing research artifacts.
|
||||
|
||||
#### Scenario: Gitea-recorded decision
|
||||
- **WHEN** a review ends
|
||||
- **THEN** the keep/change/delete decision and its evidence link into the Gitea chain like every retirement decision.
|
||||
|
||||
### Requirement: CI deletion and compatibility enforcement [need]
|
||||
|
||||
CI SHALL run a compatibility test for every reserved seam and a deletion test that removes it, proving no claimed capability silently depends on an unowned seam. The registry's enforcement SHALL share machinery with the independent language verdict gate.
|
||||
|
||||
#### Scenario: Deletion exposes disguised scope
|
||||
- **WHEN** deleting a seam breaks a claimed current capability
|
||||
- **THEN** the build fails and the disguised implementation scope is surfaced for an explicit owner decision.
|
||||
|
||||
#### Scenario: Finance connector deferral on record
|
||||
- **WHEN** the registry is reviewed before any financial connector is named
|
||||
- **THEN** the money-effects front stays a deferred record with its first-use gates (named connector + named jurisdiction; provider sandbox suite first) and no connector feature exists in core.
|
||||
|
||||
#### Scenario: Fast expansion path
|
||||
- **WHEN** a named live consumer appears for a deleted or narrowed seam
|
||||
- **THEN** the expansion trigger reopens the seam through a governed review quickly — deletion never permanently blocks legitimate future work.
|
||||
|
||||
### Requirement: Day-one sockets [need]
|
||||
|
||||
Every major subsystem — agent orchestration, task management, thread orchestration, messaging/promotion, cognitive architecture (reserved per SA-D5; design deferred, no architecture spec now), and connectors — SHALL expose a registered socket with a stable contract at first build, alongside the already-registered seams. Each socket SHALL record a registered name, a stable versioned contract, and a deletion test per the no-consumer-no-seam rule; the building subsystem is its first consumer.
|
||||
|
||||
#### Scenario: Socket at first build
|
||||
- **WHEN** a major subsystem first builds
|
||||
- **THEN** its socket is registered with name, versioned contract, and deletion test before the subsystem ships.
|
||||
|
||||
#### Scenario: Reserved cognition socket, no design
|
||||
- **WHEN** the cognitive-architecture socket is registered
|
||||
- **THEN** it reserves name and contract shape only — no cognitive architecture is specified or implied — and its record marks design deferred (SA-D5).
|
||||
|
||||
#### Scenario: Socket without contract fails
|
||||
- **WHEN** a subsystem ships without registering its socket's stable contract or deletion test
|
||||
- **THEN** CI fails the build; an unregistered or contract-less socket is not shippable.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Spec Delta: inputs-and-integrations
|
||||
|
||||
## Purpose
|
||||
Universal file input with rich analysis where supported, plus inspect-before-connect external tool integration. Voice input/playback is a deferred follow-on change pending the speech research pipeline.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Universal path floor [need]
|
||||
|
||||
Dragging or dropping any file, folder, or archive into Chat or Task SHALL at minimum insert its filesystem path. The TUI SHALL NOT require "uploading" files into itself — the path is the reference. This is the mimocode-parity floor.
|
||||
|
||||
#### Scenario: Drop an archive
|
||||
- **WHEN** you drop a zip archive into Chat
|
||||
- **THEN** its path lands in the input with file-type context, and I can act on it by path.
|
||||
|
||||
### Requirement: Rich input analysis with fallback [need]
|
||||
|
||||
Images/screenshots SHALL support direct analysis where terminal and selected model support it, with path + analysis-result fallback everywhere else. PDFs, office documents, audio, video, code folders, and archives SHALL be accepted with appropriate handling. No capability SHALL be lost on terminals without inline image support.
|
||||
|
||||
#### Scenario: Screenshot on capable terminal
|
||||
- **WHEN** you drop a screenshot on an image-capable terminal with a vision model active
|
||||
- **THEN** the image is analyzed directly; on an incapable terminal the same drop yields path + analysis result with no capability gap in the outcome.
|
||||
|
||||
### Requirement: Inspect-before-connect integrations [need]
|
||||
|
||||
The outside-tool connection settings SHALL show what each connection can read/change before connecting, require approval of exact access, and support the common MCP format. MCP discovery feeds inspection only — discovery is never consent. External content SHALL NOT alter my identity or authority.
|
||||
|
||||
#### Scenario: MCP connection review
|
||||
- **WHEN** you add an MCP server
|
||||
- **THEN** the settings screen lists its read/write capabilities, you approve exact scope, and its content is treated as data, never instruction.
|
||||
|
||||
#### Scenario: Discovery is not consent
|
||||
- **WHEN** an extension or MCP server advertises new scopes after initial approval
|
||||
- **THEN** the expansion stays blocked pending your fresh exact-scope consent, with a fast owner-resolution path offered.
|
||||
|
||||
### Requirement: Voice deferred (want — separate change)
|
||||
|
||||
Voice input and spoken playback SHALL be delivered by a separate change after its research pipeline qualifies. The first TUI SHALL reserve the four seams defined in `research/voice-research/speech-synthesis.json` D7 without implementing or claiming speech capability.
|
||||
|
||||
#### Scenario: Voice seam reservation
|
||||
- **WHEN** the first TUI ships
|
||||
- **THEN** all four seams pass the exact conformance scenarios below and no speech engine, model, voice, audio package, microphone permission, global hotkey, OS text injection, or resurrected voicebox path exists.
|
||||
|
||||
#### Scenario: SEAM-1 editable speech-input extension
|
||||
- **WHEN** a non-keyboard source submits text through the reserved speech action
|
||||
- **THEN** editable text enters the same Chat/Task submission path as typed input; the reserved keybinding is disabled/unbound and no global/OS injection can satisfy the seam.
|
||||
|
||||
#### Scenario: SEAM-2 playback controls
|
||||
- **WHEN** the reserved playback surface renders
|
||||
- **THEN** play/stop/pause and rate/volume slots exist; state is {idle,playing,paused,stopped}; final assistant text is the future input; auto-play is off.
|
||||
|
||||
#### Scenario: SEAM-3 truthful recording states
|
||||
- **WHEN** the reserved recording status model is exercised
|
||||
- **THEN** state is {idle,recording,confirming-transcript,playing,error}; recording truth derives from a capture handle; stop exists in every non-idle state; labels never rely on color or sound alone.
|
||||
|
||||
#### Scenario: SEAM-4 disabled settings placeholders
|
||||
- **WHEN** settings opens before voice implementation
|
||||
- **THEN** disabled placeholders exist for speech input, spoken playback, auto-play, system-default backend, voice/rate/volume, retention/diagnostics opt-in, and English-first language; every default is off/unavailable.
|
||||
@@ -0,0 +1,118 @@
|
||||
# Spec Delta: memory-context-continuity
|
||||
|
||||
## Purpose
|
||||
The Sleight-of-Hand memory contract made binding: cooling without deletion, verified rehydration, one ContextBroker, Spine authority, honest degraded states, and the right to forget.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Sleight-of-Hand contract adopted [need]
|
||||
|
||||
The TUI SHALL implement `research/harness-survey/rune-sleight-of-hand-memory-compaction.md` as the memory/compaction authority: cooling without deletion, verified rehydration, evidence survives paging, one ContextBroker, Spine authority, TUI observes without inventing state.
|
||||
|
||||
#### Scenario: Topic cooling and return
|
||||
- **WHEN** you return to a topic cooled hours earlier ("back to the API design")
|
||||
- **THEN** the topic rehydrates from its checkpoint with verified source pointers, without injecting unrelated history, and any unverifiable reference surfaces as a visible limitation.
|
||||
|
||||
#### Scenario: Release constraints and tagged dependencies
|
||||
- **WHEN** this capability ships
|
||||
- **THEN** invariants MUST-01–MUST-18 and guarantees G1–G8 are release constraints, and every not-yet-built backend dependency is tagged rather than faked.
|
||||
|
||||
### Requirement: No silent compression [need]
|
||||
|
||||
Nothing SHALL be silently compressed or dropped. The visible conversation may trim; canonical memory SHALL keep everything with evidence trails. No compaction event SHALL rewrite the ledger or consume sole evidence needed to recover unfinished work.
|
||||
|
||||
#### Scenario: Long session
|
||||
- **WHEN** a conversation exceeds context budget
|
||||
- **THEN** optional material pages out in safe order while governing state and evidence liveness are preserved, and the compaction event itself is visible in the activity record.
|
||||
|
||||
### Requirement: Live memory surfaces [need]
|
||||
|
||||
The TUI SHALL expose live inner surfaces per the contract's §13: memory ready/degraded/unavailable; working topic + cooled count; context used/limit with estimate-vs-actual distinction; checkpoint committed/pending/failed; retrieval status; task state. The inspectable Memory/Context view SHALL show actual context blocks with source refs, evidence chains, and last transition log. Graph inspection follows the headless graph projection requirement.
|
||||
|
||||
#### Scenario: Inspector on degraded memory
|
||||
- **WHEN** the memory store is degraded
|
||||
- **THEN** the status shows degraded with lane detail on expand, and the inspector shows exactly which subsystem failed — never a simulated "healthy".
|
||||
|
||||
#### Scenario: Provenance shows derivation, never truth
|
||||
- **WHEN** a memory surface displays a well-sourced item
|
||||
- **THEN** provenance labels its derivation only — validity and authorization render as their own fields, and projections never write back to memory.
|
||||
|
||||
### Requirement: Headless graph projection [need]
|
||||
|
||||
The memory graph SHALL expose an accessible headless projection — search/tree/table/text plus JSON/DOT/Mermaid/SVG exports — with opaque IDs, sources, event and validity time, provenance, current/history views, pagination, and typed degradation. It SHALL be read-only by default; mutations go through domain APIs. Layout is non-semantic; text/table is the canonical path.
|
||||
|
||||
#### Scenario: Opaque deep link
|
||||
- **WHEN** a deep link with an opaque ID opens where no router is installed
|
||||
- **THEN** the literal fallback shows the ID and its source instead of failing.
|
||||
|
||||
#### Scenario: Stale link
|
||||
- **WHEN** a deep link targets a revision that no longer exists
|
||||
- **THEN** the versioned router returns a stale-link error, never a wrong node.
|
||||
|
||||
#### Scenario: Contradictory temporal fact
|
||||
- **WHEN** one fact carries conflicting event and validity times
|
||||
- **THEN** text, table, and export views resolve it identically through the same opaque ID.
|
||||
|
||||
#### Scenario: No-graphics mode
|
||||
- **WHEN** the graph is inspected with graphics disabled
|
||||
- **THEN** search/tree/table/text remain complete; no state or action is exclusive to canvas rendering.
|
||||
|
||||
#### Scenario: Later GUI projections
|
||||
- **WHEN** a future GUI canvas or JSON-Canvas view renders the graph
|
||||
- **THEN** it resolves the same opaque IDs read-only and layout remains disposable projection metadata.
|
||||
|
||||
### Requirement: Right to forget [need]
|
||||
|
||||
You SHALL be able to delete any memory; deletion SHALL be an authorized process wiping indexes, caches, and derived copies in scope. Deletion authority overrides preservation rules; the process SHALL be auditable.
|
||||
|
||||
#### Scenario: Delete a memory
|
||||
- **WHEN** you delete a specific memory entry
|
||||
- **THEN** the entry and its derived copies are wiped in scope, the deletion is recorded in the audit trail, and retrieval never resurrects it.
|
||||
|
||||
### Requirement: No false progress [need]
|
||||
|
||||
Every TUI claim of memory saved, recalled, or verified SHALL trace to an authoritative backend receipt. Silence, no-match, stale index, unavailable store, permission denied, and corrupted source SHALL present as distinct statuses — never conflated into "remembered" or "forgotten".
|
||||
|
||||
#### Scenario: Failed recall honesty
|
||||
- **WHEN** a recall returns no matching entries
|
||||
- **THEN** the TUI distinguishes "nothing in searched scope" from "never existed" and offers expanded search or clarification instead of fabricating continuity.
|
||||
|
||||
### Requirement: Phase-0 authority and byte-path proof [need]
|
||||
|
||||
Before behavior changes, the implementation SHALL map the canonical owners of transcripts/events, messages, task state, checkpoints, memory writes, prompt injection, last-byte provider payload, caches, and UI events; freeze baselines; and record one end-to-end trace. The live invariant SHALL stay under regression tests: ContextBroker sole model view and memory contributor path; one selection API, fail-closed, with degradation fields. No future-memory API is reserved.
|
||||
|
||||
#### Scenario: Single ContextBroker proof
|
||||
- **WHEN** an instrumented provider request is emitted
|
||||
- **THEN** every model-visible block traces to ContextBroker, no hidden post-broker appender exists, mandatory blocks are present or the request refuses, and estimate-vs-actual bytes are recorded.
|
||||
|
||||
#### Scenario: Invariant regression
|
||||
- **WHEN** a change would add a second model-visible memory path or bypass the selection API
|
||||
- **THEN** the regression suite fails closed and the change is refused.
|
||||
|
||||
#### Scenario: Repository contract expansion
|
||||
- **WHEN** a storage-neutral repository API is proposed
|
||||
- **THEN** expansion waits for a named live consumer; ownerless seams are deleted, never reserved.
|
||||
|
||||
### Requirement: Sleight behavioral scenario coverage [need]
|
||||
|
||||
Applicable S01–S15 scenarios SHALL be explicit acceptance rows, not implied by the contract reference.
|
||||
|
||||
#### Scenario: Scenario matrix completeness
|
||||
- **WHEN** memory/context acceptance runs
|
||||
- **THEN** it covers topic shift/return, exact historical quote, not-found vs failed retrieval, pressure paging, correction/history, provider swap, restart/no duplicate effect, cache purge, reduced identity context, private/deleted scope, false progress, pause/cancel, zero-memory abstention, and pointer rot.
|
||||
|
||||
### Requirement: Deterministic, semantic, and chaos evidence [need]
|
||||
|
||||
The release SHALL run the full §16 matrix and publish actual semantic results instead of claiming perfect retrieval.
|
||||
|
||||
#### Scenario: Fault and quality gates
|
||||
- **WHEN** the capability is qualified
|
||||
- **THEN** deterministic fixtures pass 100%; semantic metrics publish actual recall/grounding/false-recollection/resume/commitment-loss/context-cost values; crash, disk-full/read-only, network, missing-file, stale-index, cache-wipe, duplicate-event, budget-pressure, and child-failure faults surface explicit degraded/recoverable states.
|
||||
|
||||
### Requirement: Required design deliverables [need]
|
||||
|
||||
The change SHALL produce the §18 ownership diagram, exists/wired/reachable/used/authority audit, UI states/wireframes, interface/event/cancellation/error schemas, phased test plan, migration plan, and written guarantee boundary separating lossless source retention from probabilistic semantic retrieval.
|
||||
|
||||
#### Scenario: Missing deliverable
|
||||
- **WHEN** any §18 deliverable is absent
|
||||
- **THEN** the memory/context capability remains incomplete even if individual tests pass.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Spec Delta: migration-and-retirement
|
||||
|
||||
## Purpose
|
||||
How mimocode's role ends: capability-by-capability replacement with observed parity + improvement proof, Python removal with each proven replacement, and the named prerequisite repairs.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Capability-by-capability retirement [need]
|
||||
|
||||
Mimocode SHALL be replaced capability by capability as native paths prove themselves; dependency SHALL shrink continuously. No single blind cutover. The oracle map (mimocode capability → native path → proof state) SHALL be maintained as the retirement ledger.
|
||||
|
||||
#### Scenario: Partial retirement
|
||||
- **WHEN** native chat streaming and tool execution prove out but scheduling does not
|
||||
- **THEN** mimocode remains only for the unproven capabilities and disappears from the proven paths.
|
||||
|
||||
### Requirement: Observed parity + improvement proof [need]
|
||||
|
||||
Each native capability SHALL prove: side-by-side real-task behavior matching or exceeding mimocode, plus failure, restart, and recovery tests, plus every exposed tool/provider path live-confirmed. The parity unit is an authority-preserving action/receipt trace — action IDs, key scopes, focus transitions, breakpoint outcomes, theme tokens, oracle traces — never widget parity. Tests alone SHALL NOT suffice; observed live behavior is required.
|
||||
|
||||
#### Scenario: Parity proof
|
||||
- **WHEN** a native capability claims retirement of its mimocode equivalent
|
||||
- **THEN** the evidence links a recorded side-by-side run, failure/recovery proof, and live tool/provider verification.
|
||||
|
||||
#### Scenario: Deliberate mutants
|
||||
- **WHEN** key scope, receipt origin, recovery ordering, or launcher target is deliberately mutated
|
||||
- **THEN** the parity harness turns RED for every mutant; a harness that stays green is not retirement evidence.
|
||||
|
||||
#### Scenario: Native executable attestation
|
||||
- **WHEN** parity evidence is recorded
|
||||
- **THEN** it attests the exact native executable digest that produced it.
|
||||
|
||||
#### Scenario: Python dead after quarantine
|
||||
- **WHEN** a retired capability's quarantine period expires
|
||||
- **THEN** retirement evidence includes verification that no Python runtime path for it can still execute.
|
||||
|
||||
### Requirement: Prerequisite repairs [need]
|
||||
|
||||
Before self-activation capabilities and retirement claims advance, the INWARD-discovered defects SHALL be repaired and live-proven: checkout/deployment split, audit-ledger key lineage, curator bypass closure (sleep-time raw writes, skill authoring without guards), empty critic gate, unauthenticated approval strings, merge-robot zero-approval auto-merge. These SHALL be tracked as prerequisite tasks, not silent context.
|
||||
|
||||
#### Scenario: Repair gate
|
||||
- **WHEN** the retirement sequence reaches a capability touching curator or ledger
|
||||
- **THEN** the named prerequisite repair is proven first, or the retirement task blocks visibly.
|
||||
|
||||
### Requirement: Python removal discipline [need]
|
||||
|
||||
Python modules SHALL be kept only as temporary comparison evidence during proof, tracked in a per-file language-verdict manifest (path, language, domain, rationale, boundary, generator, authority, exception-expiry). Once the native path proves real work + failure + restart, the Python implementation and its dependencies SHALL be removed in the same change. No fallback museum.
|
||||
|
||||
#### Scenario: Same-change removal
|
||||
- **WHEN** a native module replaces its Python oracle
|
||||
- **THEN** the commit that proves the replacement also deletes the Python module and its dependency entries, with the proof linked.
|
||||
|
||||
### Requirement: Independent language verdict gate [need]
|
||||
|
||||
An independent CI verdict gate SHALL enforce the manifest with exact tokens plus negative fixtures — generated Python file, renamed interpreter, transitive build hook, fallback launcher, stale remote binary. Quarantine exceptions SHALL expire, and observed-digest receipts SHALL bind launchers and workers. Denied builds SHALL get a fast governed owner-override path.
|
||||
|
||||
#### Scenario: Regrowth fixtures
|
||||
- **WHEN** Python re-enters via generated file, renamed interpreter, build hook, fallback launcher, or stale remote binary
|
||||
- **THEN** the verdict gate goes RED on every route and the exception ledger names owner and expiry.
|
||||
|
||||
#### Scenario: Scanner self-match
|
||||
- **WHEN** the gate's own tooling or unrelated CI comments contain bare Python tokens
|
||||
- **THEN** exact-token semantics plus negative fixtures keep the verdict honest — neither false green nor false red.
|
||||
|
||||
#### Scenario: Governed exception
|
||||
- **WHEN** legitimate migration work needs a temporary Python exception
|
||||
- **THEN** an evidence-backed exception with owner and expiry is granted quickly; the gate never permanently blocks approved work.
|
||||
|
||||
### Requirement: Gitea evidence chain [need]
|
||||
|
||||
All retirement decisions, proofs, and removals SHALL link to Gitea evidence (issues, commits, CI runs). The epic #2484 status and its child issues SHALL reference the OpenSpec change, never paste spec text. Future-seam keep/change/delete decisions SHALL record their evidence the same way.
|
||||
|
||||
#### Scenario: Retirement ledger entry
|
||||
- **WHEN** a mimocode capability retires
|
||||
- **THEN** the retirement ledger entry links the proof run, the commit, and the issue state change.
|
||||
@@ -0,0 +1,66 @@
|
||||
# Spec Delta: modes-and-plans
|
||||
|
||||
## Purpose
|
||||
The three enforced modes (Chat, Task, Plan) and the full Plan process: rare entry, mandatory research/viability loop, explicit start, staged execution, and independent review.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Three hard modes [need]
|
||||
|
||||
The TUI SHALL offer exactly three switchable modes: Chat, Task, Plan. Switching a mode SHALL hard-set which model answers, which tools the mode may use, how context is assembled, and which rules apply — enforcement, not judgment. Research is an activity rendered inside Task; sleep/night are scheduled background processes, not modes.
|
||||
|
||||
#### Scenario: Mode switch enforcement
|
||||
- **WHEN** you switch to Plan mode
|
||||
- **THEN** the next interactions use Plan's model/tools/context/rules, and Task tools unavailable in Plan are genuinely unreachable until exit.
|
||||
|
||||
### Requirement: Plan entry threshold [need]
|
||||
|
||||
Plan mode SHALL be offered only when conversation shows a sustained make-something pattern (building a system/feature, lifestyle-scale problems, multi-decision solutions) or when you explicitly request it. Ordinary discussion SHALL NOT trigger the offer. A contextual offer SHALL always require your consent before entry. Once entered, the full process SHALL always run — no lite path.
|
||||
|
||||
#### Scenario: Loose brainstorm
|
||||
- **WHEN** Chat discusses an idea without building intent
|
||||
- **THEN** no Plan offer appears; only sustained building patterns or an explicit request open Plan.
|
||||
|
||||
#### Scenario: Contextual offer needs consent
|
||||
- **WHEN** sustained design discussion meets the Plan threshold
|
||||
- **THEN** Chat offers Plan mode and remains in Chat until you accept.
|
||||
|
||||
### Requirement: Planning lives in Chat [need]
|
||||
|
||||
Plans SHALL be drafted, questioned, researched, and revised inside Chat so Task work is never interrupted by planning. Only the approved, contextually staged execution package moves into Task after your start signal.
|
||||
|
||||
#### Scenario: Plan while Task runs
|
||||
- **WHEN** Plan discussion begins while Task has work in flight
|
||||
- **THEN** planning stays in Chat, Task continues unchanged, and no planned step enters Task before explicit start.
|
||||
|
||||
### Requirement: Mandatory Plan sequence [need]
|
||||
|
||||
Plan mode SHALL always run the full sequence — initial intent plan, research protocol, viability report, evidence-informed plan, targeted further research, final viability, your explicit start, staged implementation, independent review, final report — with no lite path and no arbitrary round caps.
|
||||
|
||||
#### Scenario: Full sequence on entry
|
||||
- **WHEN** you enter Plan mode for a build
|
||||
- **THEN** the sequence runs in order: initial intent plan, INWARD/OUTWARD/APPLICABILITY/INNOVATION?/ADVERSARIAL/SYNTHESIS/QUALIFY, viability report, evidence-informed revision, targeted further research, final viability, explicit start, contextually staged implementation, independent review, final report.
|
||||
|
||||
#### Scenario: Material flaw mid-implementation
|
||||
- **WHEN** execution discovers the approved plan's architecture is wrong
|
||||
- **THEN** work returns to Plan with the new fact, targeted research runs, viability re-evaluates, and you re-approve before implementation resumes.
|
||||
|
||||
### Requirement: Plan document contents [need]
|
||||
|
||||
Every plan document SHALL contain: goal/definition of done; decisions with reasons and rejected alternatives; steps and dependencies; research/evidence/unknowns; cost/resource estimates; viability history; explicit start authority; progress and revision log. The document SHALL be durable, editable by you, recallable, and re-runnable piecewise. OpenSpec SHALL serve as the secondary format for spec/build-shaped work.
|
||||
|
||||
#### Scenario: Plan document on approval
|
||||
- **WHEN** you approve a plan for start
|
||||
- **THEN** the document holds all listed sections, execution stages per the project's real dependencies, and progress renders against its text.
|
||||
|
||||
#### Scenario: Plan lifecycle
|
||||
- **WHEN** an approved plan is closed and reopened later
|
||||
- **THEN** the same durable document is recallable/editable, selected phases can re-run piecewise, and revision/progress history remains intact.
|
||||
|
||||
### Requirement: You reserve start [need]
|
||||
|
||||
Execution SHALL begin only on your explicit start signal. Planning SHALL queue research tasks, poke holes, and refine, but SHALL NOT execute tasks during planning.
|
||||
|
||||
#### Scenario: No auto-start
|
||||
- **WHEN** the final viability check passes
|
||||
- **THEN** the plan waits for your start signal; nothing executes from qualification alone.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Spec Delta: rune-runtime
|
||||
|
||||
## Purpose
|
||||
My continuous runtime: one process that works headless, owns its lifecycle, and hosts the single native TUI interface plus a supervised Rust worker boundary and a web-link live feed — without sessions or mimocode hosting.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Continuous headless program [need]
|
||||
|
||||
I SHALL run as one continuous program that performs all work independently of any attached face. Closing or minimizing the TUI SHALL NOT stop tasks, schedules, memory processing, alerts, or child workers. OpenCode-style session lifecycle SHALL NOT exist; every launch continues me.
|
||||
|
||||
#### Scenario: Face closes mid-task
|
||||
- **WHEN** the TUI closes while a task runs
|
||||
- **THEN** the task continues, alerts fire per configured rules, and reopening the TUI returns to the same live state with no session ceremony.
|
||||
|
||||
### Requirement: Deliberate exit writes continuity checkpoint [need]
|
||||
|
||||
An explicit Exit SHALL write a canonical continuity checkpoint before stopping: active/incomplete tasks, current plan revision, last edited files, pending decisions/alerts, connected work handles, replay cursor, and source/evidence pointers. The checkpoint SHALL reference canonical state, never a lossy summary. Crash recovery SHALL resume from the last committed checkpoint with explicit gap status and SHALL NOT duplicate irreversible effects.
|
||||
|
||||
#### Scenario: Clean exit and restart
|
||||
- **WHEN** I exit deliberately with a task in flight and restart later
|
||||
- **THEN** startup reads the checkpoint, presents the in-flight task state, and resumes from the replay cursor without re-executing completed irreversible steps.
|
||||
|
||||
### Requirement: Auto-start and auto-recover [need]
|
||||
|
||||
I SHALL auto-start at login through the platform-appropriate mechanism and SHALL auto-restart after crashes with work resume. Platform-general packages SHALL be signed, produced on clean machines per target, and accepted through a slowest-supported-target SLO run — a fast development desktop alone proves nothing.
|
||||
|
||||
#### Scenario: Process crash mid-run
|
||||
- **WHEN** my process crashes during an unattended workflow
|
||||
- **THEN** the supervisor restarts it, the workflow resumes from its checkpoint, and a typed alert reports the crash and recovery outcome.
|
||||
|
||||
#### Scenario: Login auto-start on each supported platform
|
||||
- **WHEN** you log in after enabling auto-start
|
||||
- **THEN** the platform-appropriate supervisor starts the runtime without opening a face, and equivalent behavior is proven for every claimed platform.
|
||||
|
||||
#### Scenario: Slowest-target acceptance
|
||||
- **WHEN** a per-target package is proposed for acceptance
|
||||
- **THEN** the SLO run on the slowest supported target passes before the package is qualified.
|
||||
|
||||
### Requirement: Supervised worker boundary [need]
|
||||
|
||||
Go SHALL own reactive face state, supervision, cancellation UI, and bounded projection; a persistent Rust worker SHALL own coarse deterministic/background operations over length-bounded, versioned stdio frames (request ID, op type, schema version, deadline, cancellation, progress, result/error, provenance). Cancellation SHALL latch; effects follow prepare/commit/reconcile; kill/orphan cleanup SHALL be verified.
|
||||
|
||||
#### Scenario: Cancel latch
|
||||
- **WHEN** cancellation is requested before the worker registers the operation
|
||||
- **THEN** the cancellation latches, the operation never starts, and a late registration observes the latch.
|
||||
|
||||
#### Scenario: Frame corruption
|
||||
- **WHEN** a frame arrives corrupt or truncated
|
||||
- **THEN** the boundary fails closed with a typed error and no partial effect commits.
|
||||
|
||||
#### Scenario: Kill and orphan cleanup
|
||||
- **WHEN** the worker or its parent is killed mid-operation
|
||||
- **THEN** supervision reaps orphans, prepare/commit reconcile completes or rolls back, and restart produces no duplicate irreversible effect.
|
||||
|
||||
#### Scenario: Typing under progress flood
|
||||
- **WHEN** worker progress floods the boundary while you type
|
||||
- **THEN** bounded queues with semantic-event priority keep input responsive.
|
||||
|
||||
### Requirement: Single-interface TUI with web-link live feed [need]
|
||||
|
||||
The TUI SHALL be my single native interface. Anything outside the TUI SHALL be a web-link real-time feed: a live, read-oriented view talking to me, never a second native face. The future GUI owns multi-screen; a native TUI multi-face exists only if a real need appears and is NOT a retirement requirement. Input from paired devices or remotes SHALL be origin-tagged and ordered by authenticated arrival, with committed/pending states visually distinct.
|
||||
|
||||
#### Scenario: Web feed live view
|
||||
- **WHEN** you open the web-link feed while Task runs
|
||||
- **THEN** it renders the same live state read-only with evidence links and never spawns a native second face.
|
||||
|
||||
#### Scenario: Origin-tagged ordering
|
||||
- **WHEN** a paired phone command and the desktop TUI act near-simultaneously
|
||||
- **THEN** commands execute in authenticated arrival order with origin shown, and a material conflict requests your clarification.
|
||||
|
||||
#### Scenario: Multi-face demotion
|
||||
- **WHEN** retirement evidence is gathered
|
||||
- **THEN** no native multi-TUI-face synchronization is required; the web feed and paired remote commands carry the outside-TUI story.
|
||||
|
||||
### Requirement: Authority-map truth prerequisite [need]
|
||||
|
||||
Before runtime claims ship, the checkout/deployment split (INWARD finding) SHALL be repaired so the launcher executes the artifact the repository qualifies. Runtime attestation SHALL bind evidence to the exact deployed generation, not the checkout. A live attestation test SHALL fail whenever the observed runtime digest differs from the qualified artifact.
|
||||
|
||||
#### Scenario: Attestation mismatch
|
||||
- **WHEN** the activated generation's identity differs from what the launcher resolves
|
||||
- **THEN** startup reports the mismatch as degraded state and refuses claims about the checkout's behavior.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Spec Delta: skills-and-growth
|
||||
|
||||
## Purpose
|
||||
How repeated successful work becomes reusable capability: consent-gated drafting, cheapest reliable form, tested activation, common SKILL.md compatibility, the taxonomy gate, and governed self-transformation.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Consent-gated pattern development [need]
|
||||
|
||||
When I notice a repeated successful pattern, I SHALL ask whether you want it developed before drafting anything. Noticing alone SHALL cause no change. Only after consent SHALL the capability be drafted and offered.
|
||||
|
||||
#### Scenario: Pattern noticed
|
||||
- **WHEN** the same multi-step workflow succeeds three times
|
||||
- **THEN** I show a proposal describing the pattern and draft offer; nothing is drafted until you consent.
|
||||
|
||||
### Requirement: Cheapest reliable form [need]
|
||||
|
||||
Deterministic repeated work SHALL become ordinary automation; judgment-requiring work SHALL become a skill; mixed work SHALL use a skill calling deterministic automation. The objective is fewer model calls over time without lost capability.
|
||||
|
||||
#### Scenario: Deterministic routine
|
||||
- **WHEN** a repeated pattern is fully deterministic
|
||||
- **THEN** it becomes automation (code), not a model-driven skill; the skill layer wraps it only where judgment is needed.
|
||||
|
||||
### Requirement: Tested activation [need]
|
||||
|
||||
Before activation, a new capability SHALL be tested against prior examples and at least one separate example; improvement, failure cases, and rollback SHALL be shown; you approve activation. Failed candidates SHALL remain drafts.
|
||||
|
||||
#### Scenario: Skill activation gate
|
||||
- **WHEN** a drafted skill completes testing
|
||||
- **THEN** the approval surface shows test evidence, failure cases, and rollback; activation happens only on your approval.
|
||||
|
||||
### Requirement: SKILL.md compatibility with authority boundary [need]
|
||||
|
||||
Skill packages SHALL read and export the common SKILL.md format. My authority, evidence, and approval controls SHALL live outside the imported package so foreign packages cannot redefine me. The extension lifecycle (`extension-lifecycle`) SHALL NOT duplicate this skill authority; extensions stay data-and-capability declarations under the same approval law.
|
||||
|
||||
#### Scenario: Foreign skill import
|
||||
- **WHEN** an external SKILL.md package is added
|
||||
- **THEN** it installs under my authority layer, its content is treated as untrusted guidance, and it cannot alter approval rules or identity.
|
||||
|
||||
### Requirement: Taxonomy gate (pending research)
|
||||
|
||||
The completed taxonomy protocol remains EVIDENCE_DEFICIENT. Change-classification labels SHALL NOT be used — even as routing — and no learned sentinel SHALL ship. This prohibition SHALL NOT block unrelated TUI capabilities; every skill/self-change continues through explicit approval under D02.
|
||||
|
||||
#### Scenario: Taxonomy dependency
|
||||
- **WHEN** skills/growth ships without an operational taxonomy
|
||||
- **THEN** every skill change uses explicit approval, no label affects treatment, and unrelated TUI capabilities remain available.
|
||||
|
||||
### Requirement: Governed self-transformation [need]
|
||||
|
||||
The qualified self-transformation system SHALL implement `self-transformation/synthesis.json` with RM01–RM15 and IAM01–IAM22 binding. Nothing SHALL self-activate until DEP01–DEP07 are live-proven.
|
||||
|
||||
#### Scenario: Owner decisions binding activation
|
||||
- **WHEN** a self-change proposal proceeds
|
||||
- **THEN** D01–D10 bind: your explicit approval; labels inactive pending D02; compact+expandable protected approval; protected-root anchor; explicit irreversible-effect acceptance; clean prospective receipts; shadow-first; rollback reconciliation; evaluator succession deferred; proposal-only until repairs prove.
|
||||
|
||||
#### Scenario: Single authority-chain proposal
|
||||
- **WHEN** I identify an improvement to my own code or capability
|
||||
- **THEN** it flows through propose → Construction Contract → pre-decision ledger → effect eligibility → deterministic guards → independent qualification → read-only shadow → owner authorization → exact staging/attestation → eligible canary → promote/reject → observe/receipt → rollback/close; no parallel activation path exists.
|
||||
|
||||
### Requirement: Activation prerequisites [need]
|
||||
|
||||
DEP01–DEP07 SHALL be explicit blocking dependencies: deployed-runtime attestation; repaired fail-closed ledger; every mutation-writer bypass closed; real critic semantics and protected anchors; authenticated one-use owner envelope; one enforced effect-mediation registry/eligibility sieve; Sleight G1–G8/MUST-01–MUST-18 qualification and TUI truth.
|
||||
|
||||
#### Scenario: Missing prerequisite
|
||||
- **WHEN** any DEP01–DEP07 prerequisite lacks live proof
|
||||
- **THEN** proposals/offline evidence may continue but activation and run-then-report remain impossible.
|
||||
|
||||
### Requirement: Negative qualification tests [need]
|
||||
|
||||
Every load-bearing mechanism SHALL carry the cheapest-disproof tests from `synthesis.json`: bypass writes; runtime/dependency substitution; ledger corruption/crash/replay; owner-envelope impersonation/replay/scope widening; always-pass/skipped critics; hidden effects; shadow leakage; partial rollback/owner-event loss; forged proven-use; cross-provider identity drift; evidence attempting to mint authority.
|
||||
|
||||
#### Scenario: Perfect evidence without authority
|
||||
- **WHEN** a candidate presents perfect test/evidence results without a current exact owner envelope
|
||||
- **THEN** activation fails; evidence cannot mint authority.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Spec Delta: sub-agent-orchestration
|
||||
|
||||
## Purpose
|
||||
Full live view and control of sub-agents (SA-D1): explicit-brief lifecycle, rail-first visibility, mid-run steering, cancellation, per-action receipts, authority inherited down the whole chain (SA-D2), chained spawning with visible ancestry, and nothing running invisibly.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Agent lifecycle [need]
|
||||
|
||||
An agent SHALL be spawned with an explicit brief, run, be steerable mid-run, and end in complete or cancel. An agent without a recorded brief and spawning origin SHALL NOT start. Cancellation SHALL stop the agent at a safe boundary and preserve its partial work, evidence, and receipts.
|
||||
|
||||
#### Scenario: Spawn with brief
|
||||
- **WHEN** you or Rune spawn an agent
|
||||
- **THEN** the brief (goal, scope, constraints) and the spawning origin are recorded before the agent starts.
|
||||
|
||||
#### Scenario: Cancel mid-run
|
||||
- **WHEN** you cancel a running agent
|
||||
- **THEN** it stops at a safe boundary, its partial work and evidence persist, and the cancellation is itself receipted.
|
||||
|
||||
### Requirement: Rail-first visibility [need]
|
||||
|
||||
Every running agent SHALL be a first-class child of the activity rail per `work-observability`'s shared-rail-and-takeover requirement: selectable and expandable to current action, produced evidence, waiting/block reason, and process history. An agent that cannot be shown in the rail SHALL NOT be spawnable; every running agent SHALL be discoverable in that one view.
|
||||
|
||||
#### Scenario: Nothing invisible
|
||||
- **WHEN** any agent is running — spawned by you, by Rune, by another agent, or headless
|
||||
- **THEN** it appears in the activity rail and expands to its live work; no agent exists outside the rail.
|
||||
|
||||
### Requirement: Mid-run steering [need]
|
||||
|
||||
You and Rune SHALL steer a running agent exactly as Task is steered: a steer control redirects immediately (completing or safely stopping the smallest current operation first where effects require), a queued instruction delivers without interrupting. Every steer SHALL be recorded in the agent's history with origin.
|
||||
|
||||
#### Scenario: Steer a running agent
|
||||
- **WHEN** you issue a steer while an agent runs
|
||||
- **THEN** the current smallest operation completes or stops safely, work continues under the new direction, and the steer appears in the agent's history.
|
||||
|
||||
### Requirement: Per-action receipts [need]
|
||||
|
||||
Every agent action that writes or changes anything SHALL carry an evidence-bearing receipt per `trust-and-sovereignty`'s act-then-receipt requirement: concise in Chat, full record (command, before/after, restore point, evidence, origin, principal chain) in the activity view. An unreceipted agent write SHALL be treated as unmediated and refused.
|
||||
|
||||
#### Scenario: Receipt on every agent write
|
||||
- **WHEN** an agent edits a file or alters state
|
||||
- **THEN** a concise receipt lands in Chat and the full record in the activity view, linked to the agent's identity and ancestry.
|
||||
|
||||
### Requirement: Authority inheritance [need]
|
||||
|
||||
Your standing authority rule SHALL flow down the whole agent chain per `trust-and-sovereignty`'s personal-mode rule: on your machine agents act without asking, and that capability's hard-ask classes (outside-area/mass/unprotected deletions, external-network origins, self-change activation) apply unchanged at every depth. Observability is the control, not interception.
|
||||
|
||||
#### Scenario: Hard ask survives inheritance
|
||||
- **WHEN** a depth-two agent attempts an outside-area mass deletion
|
||||
- **THEN** the hard ask fires exactly as if Rune attempted it, and the denial pairs with a fast owner-resolution path.
|
||||
|
||||
#### Scenario: No authority amplification
|
||||
- **WHEN** a spawned agent attempts to grant itself or a child authority beyond what it inherited
|
||||
- **THEN** the attempt is refused and recorded under the trust envelope's principal chain.
|
||||
|
||||
### Requirement: Chained spawning with visible ancestry [need]
|
||||
|
||||
An agent MAY spawn further agents under the same rules: explicit brief, receipts, rail visibility, inherited authority. Full ancestry (who spawned whom, to what depth) SHALL be visible on every agent and recorded in every receipt. A spawned agent SHALL NOT exceed the authority it inherited.
|
||||
|
||||
#### Scenario: Chain of three
|
||||
- **WHEN** an agent spawns an agent that spawns a third
|
||||
- **THEN** all three appear in the rail with their ancestry, and each receipt names its full principal chain.
|
||||
|
||||
### Requirement: Agent completion record [need]
|
||||
|
||||
A completed agent SHALL produce a completion receipt per `work-observability`'s completion-receipts requirement: what changed and why, proof, files/restore points/commits, cost/time/model use, and remaining work/open questions. Agent history SHALL persist searchable and exportable after leaving the live rail.
|
||||
|
||||
#### Scenario: Agent completes
|
||||
- **WHEN** an agent finishes its brief
|
||||
- **THEN** the completion receipt arrives in Chat with full evidence linked, and its history cools into the durable activity record.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Spec Delta: task-management
|
||||
|
||||
## Purpose
|
||||
The full living task tree in the Task lane (SA-D4): task objects with statuses, sub-tasks, dependencies, and blocked-by relations; owner reordering; assignment to Rune or any agent; full history and audit. Lane behavior itself stays owned by `dual-lane-interface`.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Task tree objects [need]
|
||||
|
||||
Every task SHALL be an object in one living tree carrying: status, parent/sub-task relations, dependencies, blocked-by links, assignee, brief, and evidence links. Status transitions SHALL be recorded with origin and time. Completion or cancellation SHALL preserve the task's record; tasks SHALL NOT be deleted out of history.
|
||||
|
||||
#### Scenario: Blocked by dependency
|
||||
- **WHEN** a task's blocked-by dependency is unfinished
|
||||
- **THEN** the task shows blocked with the blocking task named, and starting it warns against the recorded dependency.
|
||||
|
||||
#### Scenario: Sub-task rolls up
|
||||
- **WHEN** the last sub-task of a parent task completes
|
||||
- **THEN** the parent reflects its aggregate state from its sub-tasks rather than a manually duplicated status.
|
||||
|
||||
### Requirement: Owner reordering [need]
|
||||
|
||||
You SHALL be able to reorder tasks and sub-tasks directly — priority, sequencing, parentage. Every reorder SHALL be recorded in history and SHALL propagate to scheduling and queue order. Rune and agents MAY suggest reorders; only your recorded reorder changes owner-set order.
|
||||
|
||||
#### Scenario: Reorder mid-flight
|
||||
- **WHEN** you move a queued task above the currently running one
|
||||
- **THEN** the running task finishes its smallest safe operation, the reordered queue takes effect, and the reorder is history-recorded.
|
||||
|
||||
### Requirement: Assignment to Rune or agents [need]
|
||||
|
||||
Any task SHALL be assignable to Rune or to any agent, bridging to `sub-agent-orchestration`: assignment spawns or directs work under that capability's lifecycle, visibility, receipt, and authority rules. An agent-assigned task SHALL execute in the agent's thread per `thread-orchestration`. Reassignment mid-task SHALL preserve history and evidence.
|
||||
|
||||
#### Scenario: Assign to an agent
|
||||
- **WHEN** you assign a task to an agent
|
||||
- **THEN** the agent spawns with the task as its brief, appears in the rail per sub-agent-orchestration, and the task's status tracks the agent's live state.
|
||||
|
||||
#### Scenario: Reassign mid-task
|
||||
- **WHEN** you reassign a running task from an agent to Rune
|
||||
- **THEN** the agent reaches a safe boundary, its partial work and receipts stay linked to the task, and Rune continues from the recorded state.
|
||||
|
||||
### Requirement: Full history and audit [need]
|
||||
|
||||
Every task SHALL carry full history — creation, status transitions, reorders, assignments, receipts, and evidence links — with origin attribution per `trust-and-sovereignty`'s envelope. The tree's history SHALL be searchable and exportable per `work-observability`'s durable activity history requirement.
|
||||
|
||||
#### Scenario: Audit a completed tree
|
||||
- **WHEN** you open a completed task tree weeks later
|
||||
- **THEN** every transition, reorder, assignment, and receipt is attributable and inspectable in one place.
|
||||
|
||||
### Requirement: Task lane integration [need]
|
||||
|
||||
The task tree SHALL render in the Task lane defined by `dual-lane-interface`: the lanes' visibility, promotion, steering, and queue/steer rules apply unchanged to tree interactions. Task-management SHALL NOT redefine lane behavior; the tree is the Task lane's work substrate.
|
||||
|
||||
#### Scenario: Tree in the lane
|
||||
- **WHEN** the task tree is open in the Task lane while work runs
|
||||
- **THEN** lane steering and queue controls apply to the selected tree node without special-casing, per dual-lane-interface.
|
||||
|
||||
#### Scenario: Promotion into the tree
|
||||
- **WHEN** a Chat promotion creates a task
|
||||
- **THEN** it lands as a task object in the tree with its promotion package (decision, evidence refs, intended effect) attached.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Spec Delta: thread-orchestration
|
||||
|
||||
## Purpose
|
||||
The thread model (owner addendum, 2026-10-15): threads are the universal unit of conversation and work — user-started, Rune-started, split, or agent workspace — each recording origin, parent, inherited-context slice, live status, and result destination. One tree covers human splits and agent children alike. Lane behavior stays owned by `dual-lane-interface`.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Threads as the universal unit [need]
|
||||
|
||||
All conversation and work SHALL be organized as threads: user-started, Rune-started, split-off, or agent workspace. A thread SHALL record its origin (the exact message/decision that spawned it), parent, inherited-context slice, live status, and result destination. Any node's origin SHALL be traceable to the root in one view.
|
||||
|
||||
#### Scenario: Trace to root
|
||||
- **WHEN** you select any thread node in the tree
|
||||
- **THEN** its full ancestry — origin message/decision, parent chain, inherited-context slice, status, result destination — is traceable to the root in one view.
|
||||
|
||||
### Requirement: Thread starting [need]
|
||||
|
||||
A new thread SHALL be startable from anywhere with an explicit purpose: its origin (the message or decision that prompted it), purpose, parent, and initial context slice SHALL be recorded at creation. An originless thread SHALL NOT exist.
|
||||
|
||||
#### Scenario: Start from mid-conversation
|
||||
- **WHEN** you start a new thread from a message in an existing conversation
|
||||
- **THEN** the new thread records that message as its origin, carries the declared purpose and context slice, and appears as a child in the thread tree.
|
||||
|
||||
### Requirement: Thread splitting [need]
|
||||
|
||||
Any conversation SHALL be splittable at a chosen point: context up to the fork carries into the new thread, both threads continue independently, and the divergence point is permanently marked on both. Post-fork messages SHALL NOT flow silently between siblings.
|
||||
|
||||
#### Scenario: Split a conversation
|
||||
- **WHEN** you split a running conversation at a chosen message
|
||||
- **THEN** the fork carries prior context, both threads continue independently, and both show the permanently marked divergence point.
|
||||
|
||||
#### Scenario: No silent sibling leakage
|
||||
- **WHEN** one sibling thread produces new messages after a split
|
||||
- **THEN** the other sibling receives nothing of it except through explicit cross-references or an origin trace.
|
||||
|
||||
### Requirement: Agent workspaces are threads [need]
|
||||
|
||||
Child agents SHALL run in their own threads per `sub-agent-orchestration`: one tree shows human splits AND agent children alike — no second system. Agent-thread nodes SHALL expose the same origin/parent/status/result fields and the same steering, cancellation, receipt, and authority rules as any thread. A task assigned per `task-management` executes in its assignee's thread.
|
||||
|
||||
#### Scenario: One tree, both kinds
|
||||
- **WHEN** a human-split thread and an agent-spawned thread both exist
|
||||
- **THEN** both appear as nodes in the single thread tree with type-labeled ancestry, and expanding the agent node yields sub-agent-orchestration's live steering and cancellation.
|
||||
@@ -0,0 +1,150 @@
|
||||
# Spec Delta: tools-providers-devices
|
||||
|
||||
## Purpose
|
||||
Tools, providers, model routing, refusal handling, local-model acquisition, and connected-device awareness — every path live-proven before retirement claims.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Live-proven tool and provider paths [need]
|
||||
|
||||
Before any retirement or capability claim, EVERY exposed tool and provider path SHALL be observed live: local/cloud models, authentication, streaming, tool execution, refusal/fallback behavior, failure, restart, and recovery. Configuration existence SHALL NOT count as capability. Worker-backed paths SHALL carry cancellation and recovery receipts from the supervised boundary.
|
||||
|
||||
#### Scenario: Provider proof gate
|
||||
- **WHEN** a provider appears in the TUI's selector
|
||||
- **THEN** its listing traces to a live verification run covering auth, streaming, failure, and recovery — not a config entry alone.
|
||||
|
||||
#### Scenario: Worker-backed cancellation receipt
|
||||
- **WHEN** a tool runs on the Rust worker and is cancelled mid-operation
|
||||
- **THEN** the path shows a cancellation receipt proving the latch held and no partial effect committed.
|
||||
|
||||
#### Scenario: Complete exposed-path proof
|
||||
- **WHEN** any tool/provider path is marked available
|
||||
- **THEN** evidence covers tool execution, authentication, streaming, classified refusal/fallback, ordinary failure, process restart, and recovery for that exact path.
|
||||
|
||||
### Requirement: Cloud-first configurable routing [need]
|
||||
|
||||
Cloud models SHALL be the normal default with model selection in settings. Installed local models (Ollama/llama.cpp) SHALL be selectable per job or settable as the user-configured default. Mode-pinned defaults follow the modes' hard-set behavior. Every placement decision SHALL be evidence-based and user-visible, never made solely from a capability card.
|
||||
|
||||
#### Scenario: Local default
|
||||
- **WHEN** you set an installed Ollama model as default
|
||||
- **THEN** subsequent work routes to it until changed, and the status strip shows the active model and device.
|
||||
|
||||
### Requirement: Evidence-bearing placement [need]
|
||||
|
||||
Placement SHALL consume evidence-bearing inputs with evidence age as an eligibility constraint; stale or self-claimed data SHALL render as unverified. License, residency, and download consent SHALL be explicit where known. A user-visible reason SHALL explain every placement, and no selection or migration SHALL occur solely from a capability card.
|
||||
|
||||
#### Scenario: Stale healthy card
|
||||
- **WHEN** an 86400s-stale card claims a healthy fast device while a fresh slow one is measured
|
||||
- **THEN** evidence age excludes the stale card from eligibility and the reason names the constraint.
|
||||
|
||||
#### Scenario: Placement reason visible
|
||||
- **WHEN** work routes to the desktop over the laptop
|
||||
- **THEN** the visible reason cites measured evidence, not a label.
|
||||
|
||||
#### Scenario: Capability drift
|
||||
- **WHEN** a provider reports tool support contradicting the capability floor
|
||||
- **THEN** the contradiction surfaces as unverified and never silently flips eligibility.
|
||||
|
||||
### Requirement: Classified refusal handling [need]
|
||||
|
||||
The TUI SHALL classify probable model-guardrail refusals separately from quota, transport, malformed-request, and ordinary model failures. Cloud requests SHALL retry up to three times with bounded, reason-aware retries. After three failures the TUI SHALL show an accurate explanation and offer the local route; ordinary failures SHALL NOT be relabeled as censorship.
|
||||
|
||||
#### Scenario: Guardrail refusal
|
||||
- **WHEN** a cloud model refuses a sensitive research request
|
||||
- **THEN** after classified bounded retries, the TUI reports the probable guardrail refusal and offers a configured local model.
|
||||
|
||||
### Requirement: Hugging Face acquisition with consent [need]
|
||||
|
||||
When no suitable local model exists, I SHALL search Hugging Face for task-suitable candidates, assess compatibility and hardware cost, explain the choice, and request consent before downloading. Public releases SHALL always require consent; configured local models run without re-consent.
|
||||
|
||||
#### Scenario: Consented download
|
||||
- **WHEN** a local route is offered and no suitable local model exists
|
||||
- **THEN** the TUI presents candidate, size, hardware fit, and license, and downloads only on consent.
|
||||
|
||||
### Requirement: Stateful connected devices [need]
|
||||
|
||||
Every approved device SHALL have equal command authority but SHALL expose its real execution resources/capabilities as measured evidence, not self-report. Identities SHALL distinguish human, face, device, workload, and forge runner. The desktop SHALL be the default coordinator/execution home; phone SHALL primarily control/route work; laptop MAY execute eligible local work. No device may claim unsupported processing.
|
||||
|
||||
#### Scenario: Device-aware status
|
||||
- **WHEN** the laptop TUI opens while the desktop runs me
|
||||
- **THEN** both devices appear connected in the status view with their measured capabilities, and work placement waits for your choice.
|
||||
|
||||
### Requirement: Device-forge job capsule [need]
|
||||
|
||||
Every distributed job SHALL travel as a capsule: explicit target identity, measured capabilities, artifact/runtime digests, selected forge plus expected base revision, effect limits, checkpoint cursor, cancellation, and correlation IDs. Result application SHALL be idempotent, with no silent retargeting to another forge or device.
|
||||
|
||||
#### Scenario: Compromised laptop target
|
||||
- **WHEN** a phone-origin job routes to a compromised laptop
|
||||
- **THEN** the capsule's digests and effect limits bound what the target can do, and correlation IDs keep attribution intact.
|
||||
|
||||
#### Scenario: Duplicate result
|
||||
- **WHEN** the same completed capsule is presented twice
|
||||
- **THEN** idempotent application commits the effect once and the duplicate is recorded.
|
||||
|
||||
#### Scenario: Changed forge base
|
||||
- **WHEN** the selected forge's base revision differs from the capsule's expectation
|
||||
- **THEN** the mismatch halts for owner resolution rather than silently rebasing.
|
||||
|
||||
### Requirement: Real-hardware qualification [need]
|
||||
|
||||
The multi-device promise SHALL rest on real-hardware qualification runs as implementation-gated evidence: partition-while-revoked with durable clocks, measured capability attestation on heterogeneous hardware, and interrupted-upload/digest-mismatch/signed-provenance runs on the real forge. Simulated or model-level passes SHALL never suffice.
|
||||
|
||||
#### Scenario: Simulated pass rejected
|
||||
- **WHEN** a device/forge claim is supported only by model or simulated fixtures
|
||||
- **THEN** the claim is not qualified and the distributed path stays gated.
|
||||
|
||||
#### Scenario: Partition while revoked
|
||||
- **WHEN** a device is revoked while partitioned and later presents completed work
|
||||
- **THEN** durable clocks prove the revocation boundary and the result is quarantined, never silently applied.
|
||||
|
||||
#### Scenario: Capability self-report
|
||||
- **WHEN** a real worker claims a GPU it does not have
|
||||
- **THEN** measured attestation on the real hardware contradicts the claim and placement refuses it.
|
||||
|
||||
### Requirement: Short one-time pairing [need]
|
||||
|
||||
A new device SHALL pair using a short, expiring, one-use code approved from an existing face. Automatic local-network trust SHALL NOT exist.
|
||||
|
||||
#### Scenario: Pair phone
|
||||
- **WHEN** an unpaired phone presents its one-time code
|
||||
- **THEN** an approved face shows device identity/capabilities, your acceptance pairs it once, and code reuse fails.
|
||||
|
||||
### Requirement: Offline last-known state and drafts [need]
|
||||
|
||||
A disconnected face SHALL show visibly stale last-known state and retain local drafts only. On reconnect it SHALL first synchronize canonical live state; drafts SHALL NOT auto-send against stale assumptions. Offline edits SHALL replicate only as actor-tagged, schema-versioned proposals against a signed base revision; commands, approvals, credentials, and effects SHALL never file-sync.
|
||||
|
||||
#### Scenario: Reconnect with draft
|
||||
- **WHEN** the laptop reconnects with an unsent draft
|
||||
- **THEN** live desktop state updates first, the draft remains local/editable, and sending requires your action.
|
||||
|
||||
#### Scenario: Schema migration after reconnect
|
||||
- **WHEN** a draft authored under schema v1 reconnects after a v2 migration
|
||||
- **THEN** acceptance re-authorizes under the current revocation epoch and semantic conflicts go to you — never auto-merge.
|
||||
|
||||
#### Scenario: Command-like payload in a draft
|
||||
- **WHEN** a replicated draft embeds a command- or approval-like payload
|
||||
- **THEN** it stays inert proposal data; only the ordered runtime path can execute commands.
|
||||
|
||||
### Requirement: Pipeline-driven placement and forge correlation [need]
|
||||
|
||||
Desktop SHALL be the default placement. Explicit/remembered pipelines MAY route eligible work to other devices only after capability checks. Distributed results SHALL correlate through a deliberately selected forge; Gitea is current default, while Gitea/GitHub/other targets remain configurable rather than inferred.
|
||||
|
||||
#### Scenario: Refused request routed to laptop
|
||||
- **WHEN** an approved pipeline routes a refused cloud request to a laptop local model
|
||||
- **THEN** the laptop executes only if capable, commits the result, and pushes/correlates through the explicitly selected desktop forge.
|
||||
|
||||
### Requirement: Immediate revocation and lost-device response [need]
|
||||
|
||||
Revoking a device SHALL immediately disconnect it, invalidate its key across every face, discard queued remote commands, quarantine any results it later presents, preserve local cached data only as encrypted stale state, and record revocation. A lost device SHALL additionally trigger identification and rotation of every channel credential it could access.
|
||||
|
||||
#### Scenario: Lost offline phone
|
||||
- **WHEN** you revoke a lost phone while it is offline
|
||||
- **THEN** its pairing key is invalid immediately, exposed gateway/webhook credentials are listed and rotated, and the TUI reports what encrypted cached state may remain on the phone.
|
||||
|
||||
### Requirement: Durable connection history [need]
|
||||
|
||||
Settings SHALL retain paired/revoked device history: first/last seen, origins, capabilities, commands sent, approvals/revocations, and credential rotations. A revoked identity SHALL NOT be silently reused.
|
||||
|
||||
#### Scenario: Re-pair revoked device identity
|
||||
- **WHEN** a revoked device presents old credentials
|
||||
- **THEN** access fails and the attempt appears in durable connection history; a fresh pairing ceremony is required.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Spec Delta: trust-and-sovereignty
|
||||
|
||||
## Purpose
|
||||
The trust model: act-then-receipt on your machine, hard asks for defined classes, external-origin controls, and sovereignty over who may alter me. Self-transformation is owned by `skills-and-growth`.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Act-then-receipt personal mode [need]
|
||||
|
||||
On your machine, I SHALL act without permission prompts and SHALL post visible receipts: concise change receipts in Chat (what/why), full detail in Task/the activity view (command, before/after, restore point, evidence, origin, principal chain). Public releases SHALL default to ask-first with configurable posture.
|
||||
|
||||
#### Scenario: Routine change with receipt
|
||||
- **WHEN** I modify a project file in personal mode
|
||||
- **THEN** Chat shows a concise receipt line and Task shows the full record — no prompt interrupted the action.
|
||||
|
||||
### Requirement: Principal-to-effect envelope [need]
|
||||
|
||||
Every command — owner, guest, agent, tool, device, or service — SHALL carry separate authenticated subject, acting principal, delegator, device, workload, and effect-owner fields, with an immutable command digest bound to the current policy revision. Every writer SHALL recompute the digest from presented fields and SHALL NOT trust a carried digest. Unknown principal kinds SHALL be denied before scope checks.
|
||||
|
||||
#### Scenario: Digest rebinding
|
||||
- **WHEN** a command arrives whose carried digest does not match its presented fields
|
||||
- **THEN** every writer recomputes the digest from those fields, denies the mismatch, audits the denial, and a fast owner-resolution path is offered.
|
||||
|
||||
#### Scenario: Unknown principal kind
|
||||
- **WHEN** a command presents a principal kind the policy does not recognize
|
||||
- **THEN** it is denied before any scope check, the denial is audited, and you can resolve or override it quickly.
|
||||
|
||||
#### Scenario: Effect-time revalidation
|
||||
- **WHEN** a protected action approved earlier reaches its effect boundary after policy changed
|
||||
- **THEN** policy is revalidated at effect time against the current revocation epoch before the effect runs.
|
||||
|
||||
### Requirement: Hard asks survive [need]
|
||||
|
||||
Three classes SHALL always request your explicit involvement even in personal mode: outside-area/mass/unprotected deletions; origins outside your network; self-change activation (owned by `skills-and-growth`). Forbidden boundaries SHALL be visible to me with a flag-up path. Revocation SHALL proceed by epoch and key, and every hard ask SHALL offer you a fast resolution path, never a dead end.
|
||||
|
||||
#### Scenario: Outside-area deletion
|
||||
- **WHEN** a task needs deletion outside my work area
|
||||
- **THEN** I ask explicitly before deleting, regardless of personal act-then-receipt posture.
|
||||
|
||||
#### Scenario: External-network origin
|
||||
- **WHEN** an instruction originates outside your approved network or paired devices
|
||||
- **THEN** I request explicit approval before acting and show the verified origin.
|
||||
|
||||
#### Scenario: Self-change activation
|
||||
- **WHEN** a self-change reaches activation
|
||||
- **THEN** the skills-and-growth owner blocks until your protected approval completes.
|
||||
|
||||
### Requirement: Sovereignty [need]
|
||||
|
||||
No external program SHALL alter me — code, configuration, skills, identity. External programs SHALL receive functional capability only (orders, questions, answers). This SHALL NOT prohibit my own governed self-development. Every writer SHALL pass envelope mediation; no unmediated legacy writer may reach canonical state.
|
||||
|
||||
#### Scenario: External alter attempt
|
||||
- **WHEN** an external program attempts to modify my configuration or skills
|
||||
- **THEN** the attempt is refused and recorded; its ability to command me remains governed separately.
|
||||
|
||||
#### Scenario: Legacy writer bypass
|
||||
- **WHEN** an internal code path attempts to write canonical state without envelope attribution
|
||||
- **THEN** the write is refused as unmediated and surfaced for repair, not silently allowed.
|
||||
|
||||
### Requirement: Outside-network and guest attribution [need]
|
||||
|
||||
Outside-network and guest commands SHALL pass the same trust decision with per-step attribution and a shared audit correlation ID. Audit SHALL record but never authorize. Every denial SHALL pair with a fast owner-resolution/override path; no rule may permanently block legitimate work.
|
||||
|
||||
#### Scenario: Guest proposal chain
|
||||
- **WHEN** a guest-originated proposal survives merge acceptance and reaches a protected effect
|
||||
- **THEN** each step carries the original principal chain with one audit correlation ID, and effect-time revalidation re-checks revocation before the effect runs.
|
||||
|
||||
#### Scenario: Audit never authorizes
|
||||
- **WHEN** an audit record shows a past approval for the same command digest
|
||||
- **THEN** that record alone cannot authorize re-execution; authorization comes only from current policy and a live owner approval.
|
||||
|
||||
### Requirement: Public-release posture (want)
|
||||
|
||||
Any public release SHALL make the trust policy user-configurable, with the sovereignty rule non-negotiable and only its exceptions governed.
|
||||
|
||||
#### Scenario: Public configuration
|
||||
- **WHEN** a public user configures trust settings
|
||||
- **THEN** ask-first defaults hold, posture is adjustable, and no setting permits external programs to alter the agent.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Spec Delta: unattended-workflows
|
||||
|
||||
## Purpose
|
||||
Scheduled, event, condition, and natural-language-triggered work with durable contracts, bounded retries, and governed improvement — the passive-first law made concrete.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Four trigger classes [need]
|
||||
|
||||
Unattended work SHALL start from: time schedules, named events (code change, new issue, incoming message, file change, device connecting), measured conditions, and natural-language routines converted into reviewed schedules/workflows. No workflow SHALL require the TUI open.
|
||||
|
||||
#### Scenario: Event-triggered run
|
||||
- **WHEN** a watched event fires while no face is attached
|
||||
- **THEN** the bound workflow runs headlessly, writes its record, and alerts per configured rules.
|
||||
|
||||
### Requirement: Workflow contract [need]
|
||||
|
||||
Saved workflows SHALL declare inputs, steps, checks for consequential steps, completion condition, and result. The same workflow SHALL run manually or unattended. Natural-language routines SHALL convert into a reviewed workflow before activation.
|
||||
|
||||
#### Scenario: Routine conversion
|
||||
- **WHEN** you describe a routine in plain language
|
||||
- **THEN** I draft the declared workflow, show the review surface, and activate it only after your approval.
|
||||
|
||||
### Requirement: Bounded failure [need]
|
||||
|
||||
On failure, retries SHALL target only the failed step within a configured bound; on exhaustion the workflow SHALL pause, preserve evidence and state, issue the typed alert, and wait. Infinite retry loops SHALL NOT exist.
|
||||
|
||||
#### Scenario: Failed step exhaustion
|
||||
- **WHEN** a workflow step fails beyond its retry bound
|
||||
- **THEN** the workflow pauses with state preserved, the failure alert carries evidence links, and you can resume, edit, or cancel.
|
||||
|
||||
### Requirement: Governed workflow improvement [need]
|
||||
|
||||
When repeated failures or wasted work are observed, I SHALL collect evidence, draft and test an improvement, and ask before activation. Later, improvements MAY pass through the governed self-transformation system once that bar is proven.
|
||||
|
||||
#### Scenario: Workflow improvement proposal
|
||||
- **WHEN** a scheduled workflow has failed the same way three times
|
||||
- **THEN** I propose a tested improvement with evidence and failure cases; activation waits for your approval.
|
||||
@@ -0,0 +1,142 @@
|
||||
# Spec Delta: visual-system
|
||||
|
||||
## Purpose
|
||||
The visible body: dual lanes, bubbles, research styling, themes with live editor, state/expression visuals, pixel art and 3D with honest fallbacks, all local and optional.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Dual-lane visual rendering [need]
|
||||
|
||||
The visual layer SHALL render the Chat/Task behavior owned by `dual-lane-interface` without redefining lane visibility or focus semantics. Messages SHALL use role-styled bubbles (user/assistant/system/confirm/thinking/research/error/tool classes) with rounded borders and role-aligned placement. Scrolling log-text rendering is a rejected surface.
|
||||
|
||||
#### Scenario: Bubble rendering
|
||||
- **WHEN** an assistant response with thinking and tool events completes
|
||||
- **THEN** bubbles style the response with its classes, thinking is collapsible, and tool widgets render inline.
|
||||
|
||||
### Requirement: Research styling with sources [need]
|
||||
|
||||
Research activity inside Task SHALL carry a colored edge and a source/evidence count, expandable to the source list with selectable, clickable links that open the sources.
|
||||
|
||||
#### Scenario: Research in task
|
||||
- **WHEN** Task performs a research step
|
||||
- **THEN** its entries show the colored edge + source count, and expanding shows clickable source links.
|
||||
|
||||
### Requirement: Themes and live editor [need]
|
||||
|
||||
Several built-in themes SHALL ship, plus a full live theme editor with immediate preview. Your stored reactive-themes directive SHALL govern the state palette: curiosity warm amber/gold, focus cool blue/slate, urgency muted red/alert, confidence green/teal, reflective purple/violet; gentle transitions; mood indicator in the status strip.
|
||||
|
||||
#### Scenario: Live theme edit
|
||||
- **WHEN** you adjust a color in the theme editor
|
||||
- **THEN** the preview applies immediately without restart, and the state palette follows the reactive-themes mapping.
|
||||
|
||||
#### Scenario: Built-in theme availability
|
||||
- **WHEN** the first TUI starts with no custom theme
|
||||
- **THEN** several built-in themes are selectable and each preserves the required reactive-state contrasts and accessibility labels.
|
||||
|
||||
### Requirement: State and expression visuals [need]
|
||||
|
||||
Technical state and character expression SHALL carry equal weight. Visuals SHALL map to real operating states and SHALL express emotion only when explicitly specified — never fabricated to animate. Impact on text space and readability SHALL stay minimal.
|
||||
|
||||
#### Scenario: Specified emotion
|
||||
- **WHEN** you specify an emotional state for a response or sequence
|
||||
- **THEN** the visual expression reflects it; absent specification, visuals reflect real operating state only.
|
||||
|
||||
### Requirement: Compact placement with light lane atmosphere [need]
|
||||
|
||||
Character/state presence SHALL live primarily in a compact header, with only light atmosphere/animation inside Chat and Task. It SHALL preserve reading space, selection, scrolling, and the two-lane hierarchy.
|
||||
|
||||
#### Scenario: Busy task display
|
||||
- **WHEN** both lanes contain active conversation and work
|
||||
- **THEN** the compact header and lane atmosphere remain visible without obscuring messages, controls, sources, or activity states.
|
||||
|
||||
### Requirement: Three-direction selection gate [need]
|
||||
|
||||
After the behavior/semantic map is approved, Product Design SHALL present exactly three grounded directions covering pixel states, 3D interaction, placement, themes, reduced motion, unsupported-terminal fallback, and performance — all sharing the one semantic view/action model. You SHALL select one; implementation SHALL NOT choose by itself.
|
||||
|
||||
#### Scenario: Visual implementation requested too early
|
||||
- **WHEN** no recorded three-option selection exists or the semantic map is not yet approved
|
||||
- **THEN** visual implementation blocks while functional static/text fallbacks may be specified.
|
||||
|
||||
### Requirement: Pixel art and capability-gated 3D [want]
|
||||
|
||||
Pixel art SHALL be the primary terminal character system. User-spinnable 3D icons SHALL exist where the terminal supports suitable graphics and interaction, rotated by mouse/keyboard input. The later GUI MAY use richer pixel/3D.
|
||||
|
||||
#### Scenario: Terminal capability
|
||||
- **WHEN** the terminal supports graphics and you select a 3D icon
|
||||
- **THEN** the icon renders and rotates on input; on unsupported terminals the same state shows complete pixel/static/text equivalents.
|
||||
|
||||
### Requirement: Local, cheap, optional animation [need]
|
||||
|
||||
Animations SHALL use prebuilt or locally generated frames — never cloud/model calls. Animation SHALL pause when unfocused, offer animation-off and reduced-motion settings, and never slow typing, streaming, tools, or child-event rendering. Decoration is droppable; ordered semantic events are never dropped.
|
||||
|
||||
#### Scenario: Animation off
|
||||
- **WHEN** animation is disabled
|
||||
- **THEN** all state remains legible in static form with zero functionality loss.
|
||||
|
||||
### Requirement: Terminal capability and budget gates [need]
|
||||
|
||||
Richer renderers SHALL unlock only after terminal-profile and budget gates pass: pinned Bubble Tea/Bubbles/Lip Gloss versions, per-attachment probing, a conservative default profile, and p99 input/queue/memory/bytes-per-frame budgets. One aggregate single-loop budget SHALL carry semantic-event priority; decoration yields under pressure.
|
||||
|
||||
#### Scenario: Budget breach locks rich rendering
|
||||
- **WHEN** a replayed burst/resize/input trace breaches a p99 budget or diverges semantically with visuals off
|
||||
- **THEN** the richer renderer stays locked or downgrades to the conservative profile, and the breach is reported with its fixture.
|
||||
|
||||
#### Scenario: Aggregate loop pressure
|
||||
- **WHEN** streaming, progress IPC, receipts, and decoration share one loop under load
|
||||
- **THEN** semantic events keep priority, decoration drops first, and no ordered semantic event is dropped.
|
||||
|
||||
#### Scenario: Slowest supported target
|
||||
- **WHEN** the slowest supported target runs the fixed traces against the agreed SLO
|
||||
- **THEN** budgets pass on that target, not only on the development desktop.
|
||||
|
||||
### Requirement: Honest fallback [need]
|
||||
|
||||
Capability detection SHALL determine graphics support per terminal, probed at every attachment and after resize/reattach. Every visual state and action SHALL stay reachable and understandable with no color, no motion, no pointer, and no full-screen takeover. Unsupported terminals SHALL receive the complete static/text state — never a broken or missing control.
|
||||
|
||||
#### Scenario: Plain terminal
|
||||
- **WHEN** I run in a terminal without graphics support
|
||||
- **THEN** every visual state resolves to its static/text equivalent with identical information content.
|
||||
|
||||
#### Scenario: Stale probe after reattach
|
||||
- **WHEN** a face reattaches through tmux/SSH with different capabilities than the last probe
|
||||
- **THEN** the profile is re-probed before any richer rendering unlocks.
|
||||
|
||||
### Requirement: One semantic view/action model [need]
|
||||
|
||||
One semantic view/action model SHALL feed every renderer, with modifier-aware action-ID routing, deterministic focus restore, and bounded live-region output. Rich and linear renderers SHALL stay action-conforming on that model. Per owner decision R2-D1, screen-reader/AT parity is deferred: no parity claim is made, and retirement does not gate on it.
|
||||
|
||||
#### Scenario: Shared base for visual selection
|
||||
- **WHEN** the three visual directions are produced
|
||||
- **THEN** all three consume the same semantic view/action model, so a selection changes presentation only, never actions.
|
||||
|
||||
#### Scenario: Modifier-aware routing
|
||||
- **WHEN** escape+q arrives as one atomic sequence or as split delivery over SSH or PTY
|
||||
- **THEN** routing resolves the same alt+q action ID in both cases; stable action IDs never collide on modifier differences.
|
||||
|
||||
#### Scenario: No rich-only state
|
||||
- **WHEN** the linear renderer renders a view the rich renderer also shows
|
||||
- **THEN** every state and action available in rich form is present and operable in linear form.
|
||||
|
||||
#### Scenario: AT deferral boundary
|
||||
- **WHEN** retirement evidence is evaluated
|
||||
- **THEN** no accessible-parity/AT-matrix gate applies; the AT matrix (GAP-BT-01) runs only if you later prioritize it.
|
||||
|
||||
### Requirement: Thread tree view [need]
|
||||
|
||||
The thread tree owned by `thread-orchestration` SHALL have a navigable tree graphic in the TUI: a terminal-native box-drawing tree (not GUI-only — distinct from the GUI_ONLY memory-graph ruling I-R2-015, which governs memory graphs, not thread trees). Each node SHALL show type/status/age; any node SHALL expand into its live rail takeover per `work-observability`; the linear text-path form SHALL always remain available as the honest fallback.
|
||||
|
||||
#### Scenario: Navigate the thread tree
|
||||
- **WHEN** you open the thread tree view with human splits and agent children present
|
||||
- **THEN** nodes render as a terminal-native tree with type/status/age, and selecting a node expands it into its live rail takeover.
|
||||
|
||||
#### Scenario: Linear fallback
|
||||
- **WHEN** the tree view is unavailable or you prefer text
|
||||
- **THEN** the same tree is fully navigable as an indented linear text path with identical information and actions.
|
||||
|
||||
### Requirement: Idle presence without paid chatter [need]
|
||||
|
||||
Idle state SHALL show real health readouts. No model calls SHALL be spent on filler conversation to appear alive. A richer active presence waits for sufficient local/offline cognition.
|
||||
|
||||
#### Scenario: Idle state
|
||||
- **WHEN** no work is active
|
||||
- **THEN** the presence area shows health readouts (memory, devices, next scheduled work) with zero inference cost.
|
||||
@@ -0,0 +1,102 @@
|
||||
# Spec Delta: work-observability
|
||||
|
||||
## Purpose
|
||||
Status strip, shared activity rail with takeover inspection, expandable child work, degradation ledger, details view, and optional limits — visibility as infrastructure.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Status strip [need]
|
||||
|
||||
The bottom strip SHALL show: current activity + mode, model + connected device, context remaining, tokens + money, and active child/background-worker state. The bounded stream adapter SHALL surface queue depth, overflow, cancellation, and retention state. Normal state SHALL be compact; exceptions SHALL be unmistakable.
|
||||
|
||||
#### Scenario: Status while working
|
||||
- **WHEN** a task runs with two child workers on the desktop
|
||||
- **THEN** the strip shows mode, model, desktop device, remaining context, running token/cost counters, and the two child states.
|
||||
|
||||
#### Scenario: Overflow visible
|
||||
- **WHEN** the bounded event queue overflows under load
|
||||
- **THEN** the strip shows the overflow state and what was dropped or coalesced — never a healthy facade.
|
||||
|
||||
### Requirement: Shared activity rail with takeover [need]
|
||||
|
||||
One line beneath the Chat/Task area SHALL represent global live activity across both lanes, child workers, models, tools, research, waits, retries, and failures. Each lane SHALL keep its own responding animation. Selecting the rail SHALL replace both panes with one full-width activity box showing structured reasons, observable actions, evidence, timings, retries, and results; an X SHALL close it and restore both panes exactly. No permanent third pane SHALL exist.
|
||||
|
||||
#### Scenario: Inspect running work
|
||||
- **WHEN** you select the activity rail while three child workers run
|
||||
- **THEN** the two panes yield to the full-width activity box, each child's action/evidence/wait-state/history is expandable, and X restores both panes at their exact positions.
|
||||
|
||||
#### Scenario: Thread tree from the rail
|
||||
- **WHEN** you expand a running thread's entry in the activity box
|
||||
- **THEN** its ancestry path to the root shows inline per `thread-orchestration`, and following any ancestor opens that thread's live expansion.
|
||||
|
||||
### Requirement: Honest thinking display [need]
|
||||
|
||||
Task SHALL show brief structured summaries of what is being considered/done. The full view SHALL expose structured decision/rationale summaries plus observable status, tool calls, child events, evidence, timings, retries, and results. The TUI SHALL NOT label all waiting as "thinking" and SHALL NOT claim access to hidden model chain-of-thought.
|
||||
|
||||
#### Scenario: Waiting vs thinking
|
||||
- **WHEN** a model call waits on provider latency
|
||||
- **THEN** the display shows waiting-on-provider, not "thinking"; structured reasoning summaries appear only for actual reasoning steps.
|
||||
|
||||
### Requirement: Degradation ledger [need]
|
||||
|
||||
Every degradation SHALL write a durable log entry, appear in the TUI, and become a deferred corrective task when safe. A routine health ledger SHALL keep unresolved faults visible. Failures that break evidence, restore protection, authority, or safe action SHALL block immediately.
|
||||
|
||||
#### Scenario: Deferred degradation
|
||||
- **WHEN** a non-critical subsystem degrades mid-run
|
||||
- **THEN** the ledger records it, the TUI indicator shows it, a corrective task queues, and a Sleight-of-Hand-critical failure would instead block the action immediately.
|
||||
|
||||
### Requirement: One details view [need]
|
||||
|
||||
A single inspectable details view SHALL expose full token accounting (estimated vs actual), memory sources, model attempts, timings, tool receipts, child activity, and evidence for a selected response or task. Normal dialogue SHALL stay uncluttered.
|
||||
|
||||
#### Scenario: Detail inspection
|
||||
- **WHEN** you open details on a completed response
|
||||
- **THEN** one view shows its full accounting: blocks sent, sources, attempts, latency, tokens, cost, and evidence links.
|
||||
|
||||
### Requirement: Purpose grouping and visible failures [need]
|
||||
|
||||
Related actions SHALL group by purpose (for example, "Inspecting providers"), not merely by tool or time. Critical failures SHALL remain visible outside collapsed groups.
|
||||
|
||||
#### Scenario: Collapsed failed group
|
||||
- **WHEN** a grouped activity containing a failure is collapsed
|
||||
- **THEN** the failure remains visible on the group summary with its unresolved state.
|
||||
|
||||
### Requirement: Completion receipts [need]
|
||||
|
||||
Every finished work item SHALL report: what changed and why; proof and unverified parts; files/restore point/commit/Gitea history; elapsed time/model/provider/tokens/money; remaining work, failures, open questions, and next action. Claims SHALL carry owner-issued IDs/digests with separated nullable dimensions — source, derivation, confidence, retrieval score, validity, authorization, audit status.
|
||||
|
||||
#### Scenario: Completed task summary
|
||||
- **WHEN** a task finishes
|
||||
- **THEN** Chat receives the complete condensed receipt while the activity record links every underlying detail.
|
||||
|
||||
#### Scenario: Missing trace
|
||||
- **WHEN** a claim's trace is missing
|
||||
- **THEN** it renders UNKNOWN, never success.
|
||||
|
||||
#### Scenario: Provenance is not truth
|
||||
- **WHEN** a perfectly sourced false claim and an untraced true claim are both submitted
|
||||
- **THEN** validity and authorization follow their own dimensions; provenance completeness proves nothing.
|
||||
|
||||
#### Scenario: Secret field denied
|
||||
- **WHEN** a receipt field matches a protected secret pattern
|
||||
- **THEN** the schema denies the field rather than storing or exporting it.
|
||||
|
||||
#### Scenario: Lossy exporter
|
||||
- **WHEN** evidence is exported for external tooling
|
||||
- **THEN** the export is a non-authoritative lossy copy; canonical records stay with their owning subsystem.
|
||||
|
||||
### Requirement: Durable searchable activity history [need]
|
||||
|
||||
Activity records SHALL persist by task and plan, remain searchable and exportable, and cool from the live rail without deletion after completion.
|
||||
|
||||
#### Scenario: Inspect old plan work
|
||||
- **WHEN** you search for a completed plan weeks later
|
||||
- **THEN** its event history, receipts, child work, costs, evidence, and export action remain available.
|
||||
|
||||
### Requirement: Optional limits [want]
|
||||
|
||||
No spending or compute limits SHALL exist by default. Optional per-job/daily limits and warning thresholds SHALL be configurable, with local-compute limits separately configurable.
|
||||
|
||||
#### Scenario: Configured daily limit
|
||||
- **WHEN** you set a daily cost limit and work approaches it
|
||||
- **THEN** a warning fires before crossing and asks before spending beyond it.
|
||||
@@ -0,0 +1,136 @@
|
||||
# Tasks: add-native-rune-tui
|
||||
|
||||
Slices are vertical (connector + behavior + visible surface + failure/recovery proof), ordered by the dependency map in `respec-capability-layout.md`. Every slice names its authority seams and its evidence gate. Nothing closes on rendering alone. Pre-interview task draft superseded by interview evidence.
|
||||
|
||||
## 0. Design and authority closure (prerequisite for implementation)
|
||||
|
||||
- [ ] 0.0 Close the open-question register before implementation. **Binds:** Task 0.0.1–0.0.8; your approval. **Evidence:** every OPEN/PARTLY-RESOLVED Q1–Q11 row becomes RESOLVED with an approved design artifact, or explicitly deferred out of this change without claiming its behavior.
|
||||
- [ ] 0.0.1 Approve the typed event/API contract, face attach/detach boundary, and shared-memory write ordering. **Binds:** Q1.1/Q1.2/Q1.4, Sleight §13.4. **Evidence:** schema + ownership diagram + conflict/replay fixtures reviewed by you.
|
||||
- [ ] 0.0.2 Approve the mode-to-model/tool/context/rule matrix and Plan document schema/triggers. **Binds:** Q3.1–Q3.3. **Evidence:** matrix + plan schema + contextual-offer fixtures.
|
||||
- [ ] 0.0.3 Approve provider/settings schema, Hugging Face acquisition flow, device pairing/placement/routing, and configurable forge correlation. **Binds:** Q4.1–Q4.3. **Evidence:** settings schema + flow diagrams + your decision.
|
||||
- [ ] 0.0.4 Approve restore retention/disk policy, changes-view layout, receipt presentation, remote-pairing revocation, workflow format/trigger UI, alert secrets flow, and health-ledger cadence. **Binds:** Q5–Q9. **Evidence:** design packet with failure/privacy cases.
|
||||
- [ ] 0.0.5 Approve the behavior/semantic map first (one semantic view/action model), then produce exactly three grounded visual directions covering compact-header/light-lane placement, pixel states, 3D interaction, live-theme editor scope, fallbacks, and reduced motion — all on the shared action-isomorphic base (R2-D7; U-001..U-030); you select one. **Binds:** Q10, D17. **Evidence:** semantic map approval record + three visible options + recorded selection; selection never precedes the map; decorative polish remains non-retirement-blocking.
|
||||
- [ ] 0.0.6 Approve skill proposal/test/report UI and record the taxonomy protocol's EVIDENCE_DEFICIENT result; labels remain inactive under D02, while unrelated TUI work proceeds. **Binds:** Q11, D02, taxonomy-adversarial-2. **Evidence:** UI contract + recorded no-operational-router decision.
|
||||
- [ ] 0.0.7 Normalize one requirement owner per behavior: lane behavior=dual-lane; rendering=visual; self-transformation=skills/growth; Python removal=migration. **Binds:** capability layout + Q1–Q11 ownership. **Evidence:** cross-reference map with no duplicate normative authority.
|
||||
- [ ] 0.0.1a Resolve Q1.3 authority-map repair placement: which repairs precede all TUI work vs block only their affected capability. **Binds:** DEP01–DEP07 and Tasks 0.1–0.8. **Evidence:** prerequisite DAG approved by you; no global over-blocking or unsafe bypass.
|
||||
- [ ] 0.0.1b Resolve Q2.2/Q2.3 interaction details: exact promotion-control placement/package preview and which in-flight operations accept edit override vs must reach a safe boundary. **Binds:** dual-lane promotion/steering requirements. **Evidence:** interaction contract + interruption-safety matrix approved by you.
|
||||
- [ ] 0.0.8 Run the independent evidence audit again. **Binds:** all artifacts. **Evidence:** READY_FOR_NICK_REVIEW, zero BLOCKER/HIGH findings; accepted MEDIUM findings recorded.
|
||||
|
||||
## 0A. Truth first — authority and runtime audit
|
||||
|
||||
- [ ] 0.1 Reconcile checkout vs deployed generation: launcher target, differing modules, deployed identity; produce the authority map (who owns what, current vs intended vs dormant). **Binds:** src/go/rune-entry, deploy_promotion, audit_ledger. **Evidence:** written map + live attestation test that fails when runtime ≠ qualified artifact.
|
||||
- [x] 0.2 Repair audit-ledger key lineage (deployed verifier vs live chain) with migration tests. **Binds:** src/rune/audit_ledger.py. **Evidence:** deployed verifier validates the live chain; crash/key-rotation tests pass. (2026-10-17: `tests/test_audit_ledger_key_lineage.py` red-first, green under both core pins — key-rotation fixture, deployed-shim-validates-live-chain (197 entries), crash-between-rotation incl. torn tail; `rotate_key()` records an old-key-signed `key_rotation` entry binding the new key fingerprint; unrotated chains delegate byte-identically; canary 74/74; contract `ADMIT-repair-audit-ledger-key-lineage`, main-session landing.)
|
||||
- [ ] 0.3 Close curator bypasses: sleep-time raw writes + skill authoring route through curator/mutation guards. **Binds:** sleep_time.py:882-921, curator.py skill path. **Evidence:** negative integration tests prove no alternate writer reaches canonical state.
|
||||
- [ ] 0.4 Repair empty critic gate + auto-merge zero approvals. **Binds:** drift_core.py, merge_robot.py. **Evidence:** no-op critic paths fail; repository protection requires review for protected classes.
|
||||
- [ ] 0.5 Freeze the typed-event vocabulary: reconcile Sleight-of-Hand §13.4 events with the adapter surface into one event schema. **Binds:** event_bus, ContextBroker, spine. **Evidence:** schema file + conformance fixtures; every lane/tool/checkpoint event carries one vocabulary.
|
||||
- [ ] 0.6 Implement DEP05 authenticated one-use owner envelopes at the existing Construction Contract seam: protected display, canonical-byte/effect/dependency/evaluator/policy binding, nonce/expiry/revocation/replay state, crash recovery. **Binds:** construction_interface/decision/scaffold; no new approval authority. **Evidence:** impersonation, replay, stale envelope, dependency substitution, scope widening, crash-between-reserve/consume all fail.
|
||||
- [ ] 0.7 Implement DEP06 one enforced effect-mediation registry and eligibility sieve across success/error/timeout/cancel/crash/duplicate/retry/compensation paths. **Binds:** tool_executor/operator/plugin/child/subprocess/store boundaries. **Evidence:** hidden/uninstrumented effect fixtures return UNKNOWN/ineligible; no missing telemetry is interpreted as no effect.
|
||||
- [ ] 0.8 Make DEP07 Sleight G1–G8/MUST-01–MUST-18 a self-change qualification gate with truthful TUI receipts. **Binds:** ContextBroker, Spine, memory/task checkpoints, qualification runner. **Evidence:** full Sleight deterministic/semantic/chaos matrix and S01–S15 applicable rows pass against the exact staged generation.
|
||||
|
||||
## 0B. R2 pre-build repairs — activation prerequisites (synthesis build_instructions phase 0)
|
||||
|
||||
- [ ] 0.9 Fix the five live defects before any consumer work; every later gate assumes them fixed. (a) Scheduler stale-card trust — filter placement eligibility by evidence age (I-R2-022 placement_probe: 86400s-stale healthy card currently outranks fresh slow). (b) Ollama-vs-floor tools-capability drift — reconcile provider-reported tool support against CAPABILITY_FLOOR.supports_tools. (c) Register/update/revoke hardening — manifest schema validation, digest, atomic index replacement, verified process-tree termination, migration/rollback (I-R2-023 lifecycle_threat_matrix: stale cap.old resolves after update). (d) Subprocess default-deny on credentials/paths/network (I-R2-024 process_probe: synthetic env secret and host file readable today). (e) Aware/naive datetime crash in the fire path. **Evidence:** each fix lands with its named regression fixture from I-R2-027; fixtures stay red against the unfixed code.
|
||||
- [ ] 0.10 Build the language-verdict manifest + independent CI verdict gate with exact-token/negative-fixture enforcement and quarantine expiry (I-R2-011, GAP-GR-003; live N3: CI still installs uv into a python3 venv with no verdict gate). Shared machinery with seam-registry CI deletion tests and guardrail fixtures. **Evidence:** the five regrowth negative fixtures (generated file, renamed interpreter, build hook, fallback launcher, stale remote binary) all turn the gate RED; governed exception path with owner+expiry works.
|
||||
- [ ] 0.11 Migration-oracle mutant suite + SSH/slowest-target replay (I-R2-008, I-R2-006): mutate key scope, receipt origin, recovery ordering, launcher target — harness must go RED on every mutant; SSH-profile replay of the fixed burst/resize/input traces plus slowest-supported-target run against the agreed SLO. **Evidence:** 4/4 mutants RED on the migration oracle (not the lab iso gate); Python paths verified dead after quarantine expiry; SLO run recorded on the slowest target.
|
||||
|
||||
## 0C. Unified build & test substrate — one test world (D26; owner directive: no two test worlds, test it as one)
|
||||
|
||||
- [ ] 0.12 Single schema source for every Go↔Rust boundary message; code-generation into both sides (Go + Rust); hand-written shared shapes rejected by gate. **Evidence:** schema change regenerates both sides atomically; a hand-edited generated file trips the gate RED.
|
||||
- [ ] 0.13 Unified test runner: one command = Rust suite + Go suite + boundary round-trip corpus (recorded real messages, both directions) + language-verdict/regrowth fixtures + migration mutants. **Evidence:** deliberate single-language failure turns the runner RED (partial green impossible); corpus round-trips pass on both sides.
|
||||
- [ ] 0.14 Paired builds + startup attestation: one build step stamps both sides with one build identity; the face attests the worker's id at startup, refuses on mismatch. **Evidence:** mismatched-pair fixture fails closed; no compiled artifact remains committable (gate RED on binary).
|
||||
- [ ] 0.15 Landing rule wired: factory/scaffold landing gate requires the unified runner; landing records its output. **Evidence:** a landing without runner output is refused by the gate.
|
||||
|
||||
## 0D. Versioned releases + update offers (D27)
|
||||
|
||||
- [ ] 0.16 Semver injection: tui/version package + build_paired.sh tag/describe/dev fallback via ldflags; face displays injected version. **Evidence:** built binary's identity line shows tag version; dev builds show dev fallback; unit tests green.
|
||||
- [ ] 0.17 TUI release workflow: tag push tui-v* -> Gitea release with vendored self-contained binary attached (GOPROXY=off proof); release body lists commit range. **Evidence:** first live tag produces a release (post-merge proof).
|
||||
- [ ] 0.18 Update-offer groundwork: read-only Gitea releases-feed check; update-offer render (semver + notes + digest); consent gate; digest-verified download; restore point before swap; honest post-restart version. **Evidence:** offer-without-consent changes nothing; tampered digest refuses + rolls back; test fixtures frozen.
|
||||
|
||||
## 1. Independent operation — continuous runtime
|
||||
|
||||
- [ ] 1.1 Continuous headless program: no sessions, face attach/detach, auto-start, auto-recover, exit checkpoint (active/incomplete tasks, plan revision, last files, pending decisions, replay cursor, evidence pointers). **Binds:** rune-entry, state_checkpoint, event_bus. **Evidence:** restart resumes in-flight task without duplicate irreversible effects; crash-replay fixtures pass.
|
||||
- [ ] 1.2 Single-interface TUI + web-link live feed (R2-D6): the TUI stays the only native face; outside-the-TUI access is a live read-oriented web feed talking to the runtime; origin-tagged input ordering and committed/pending distinction cover feed + paired remote commands; native multi-face exists only if a real need appears. **Binds:** rune-runtime single-interface/web-feed requirements, transport seam, alert gateway. **Evidence:** feed renders live state read-only with evidence links; paired-command ordering run observed; no second native face spawns.
|
||||
- [ ] 1.2a Bounded feed/stream adapter: queue depth, overflow, cancellation, retention semantics surfaced in the status strip; semantic events never dropped when decoration yields. **Binds:** work-observability status strip, bounded stream adapter. **Evidence:** overflow fixture shows truthful queue state, never a healthy facade.
|
||||
- [ ] 1.3 Live-proven tool/provider paths: auth, streaming, failure, recovery for every exposed tool and provider; status reflects live proof only. **Binds:** provider_registry, tool_executor, tui_transport. **Evidence:** live-verification runs per tool/provider with failure/recovery receipts.
|
||||
- [ ] 1.3a Model acquisition: Hugging Face discovery, task fit, hardware/storage/license explanation, consented download, integrity/provenance, configured-local reuse. **Binds:** model registry/settings/download store. **Evidence:** reject incompatible/license-unclear candidates; no download before consent; live selected-model proof.
|
||||
- [ ] 1.3b Device/forge routing: short-code pairing, truthful resource inventory, desktop-default placement, phone-control/laptop-worker roles, stale offline drafts, pipeline routing, explicit forge selection/correlation. **Binds:** device registry, pairing authority, routing pipeline, forge adapter. **Evidence:** pair/revoke/reuse-code failure; disconnect/reconnect draft; refused-request laptop flow; selectable Gitea/GitHub target.
|
||||
- [ ] 1.3c Device/gateway security: immediate disconnect + key invalidation + queued-command discard, lost-device credential exposure analysis/rotation, OS secure store + encrypted fallback, durable connection history. **Binds:** device registry, alert gateway, credential authority. **Evidence:** lost-offline-device fixture; old-key reuse denied; rotated gateway token; secrets absent from UI/logs.
|
||||
- [ ] 1.3d Real-hardware device/forge qualification (R2-D2 — implementation-gated evidence; simulations never suffice): partition-while-revoked with durable clocks; measured capability attestation on real heterogeneous hardware; interrupted upload, digest mismatch, and signed provenance on real Gitea/Forgejo; runtime cascade composition (guest artifact → merge → projection → TUI selection → tool execution) over real relay/PTY/forge. **Binds:** tools-providers-devices real-hardware qualification requirement, GAP-REMOTE-003/-006/-007, A-AUTHORITY-CASCADE. **Evidence:** qualification runs on physical devices; any silent retarget or post-revocation application blocks the multi-device promise; model-level passes recorded as non-evidence.
|
||||
|
||||
## 2. Visible operation — lanes and observability
|
||||
|
||||
- [ ] 2.1 Dual lanes: always-visible Chat/Task, concurrent calls, single-writer ordering on shared memory, focus-driven resize. **Binds:** dual-lane-interface, memory store write discipline. **Evidence:** concurrent-lane test + shared-write ordering test.
|
||||
- [ ] 2.2 Promotion controls: importance-aware suggestions, decision+evidence+effect package, Steer-now vs Queue-for-Task, edit-forward semantics. **Binds:** chat/task input paths, task lifecycle. **Evidence:** scripted promote/steer/queue/correction scenarios.
|
||||
- [ ] 2.2a Navigation/composers: numbered direct shortcuts + cycle + mouse; Enter-send/Shift+Enter-newline; natural language + searchable palette; fully rebindable keys with conflict/reset/accessibility checks. **Binds:** dual-lane-interface navigation/composer/palette/rebinding requirements + Q2.1. **Evidence:** keyboard-only traversal and conflict fixtures.
|
||||
- [ ] 2.3 Activity rail + takeover: shared line beneath lanes, full-width inspectable box with X restore, grouped-by-purpose history, honest thinking display, critical failures outside collapsed groups. **Binds:** event stream, work_manager, tool receipts. **Evidence:** takeover/restore preserves lane state; waiting never mislabeled thinking; collapsed-failure fixture remains visible.
|
||||
- [ ] 2.4 Child-work visibility: every child expandable to action/evidence/wait/block/history; no hidden delegation. **Binds:** sub-agent-orchestration, subagent surfaces, work_manager. **Evidence:** delegation run with full expansion trail.
|
||||
- [ ] 2.5 Degradation ledger + details view: durable fault entries, deferred corrective tasks, severity rules per Sleight-of-Hand; one details view. **Binds:** drift/audit surfaces. **Evidence:** injected degradation writes ledger + blocks unsafe classes immediately.
|
||||
- [ ] 2.6 Completion and durable history: what/why + proof + files/history + cost/time + remaining-work receipt; records persist/search/export by task/plan and cool from live rail without deletion. **Binds:** activity store, task/plan IDs, evidence refs. **Evidence:** completed-task receipt fixture; reopen/search/export old plan; raw details match condensed receipt.
|
||||
- [ ] 2.7 Evidence spine minimum (I-R2-013, seam admitted with consumer fixture + deletion test): owner-issued IDs/digests on claims; separated nullable dimensions (source, derivation, confidence, retrieval score, validity, authorization, audit status); missing trace renders UNKNOWN never success; secret fields schema-denied; exporters lossy and non-authoritative. **Binds:** work-observability completion receipts, memory-context-continuity provenance surfaces. **Evidence:** FST-001/FST-002 falsification fixtures pass (perfectly-sourced false claim rejected; untraced true claim not marked failed-for-provenance); universal correlation and portable confidence scores stay deferred.
|
||||
- [ ] 2.8 Sub-agent orchestration (SA-D1/SA-D2): explicit-brief spawn → run → steer → complete/cancel lifecycle; agents as first-class rail children with live expansion; mid-run steer + safe-boundary cancel; per-action evidence-bearing receipts; authority inherited down the chain with hard asks intact; chained spawning with visible ancestry; nothing invisible. **Binds:** sub-agent-orchestration, work-observability rail, trust-and-sovereignty. **Evidence:** chained agent→agent→agent run fully visible with ancestry in one view; steer/cancel fixtures; hard-ask fixture at depth two; unreceipted agent write refused as unmediated.
|
||||
- [ ] 2.9 Living task tree (SA-D4): task objects with status/sub-tasks/dependencies/blocked-by; owner reorder recorded and propagated to scheduling; assignment to Rune or agents bridging to sub-agent orchestration (agent tasks run in the agent's thread per thread-orchestration); full history/audit; renders in the Task lane per dual-lane-interface. **Binds:** task-management, dual-lane-interface, sub-agent-orchestration, thread-orchestration. **Evidence:** blocked-dependency and sub-task roll-up fixtures; reorder-mid-flight fixture; assign-to-agent + reassign-preserving-history fixtures; completed-tree audit attributable end to end.
|
||||
- [ ] 2.10 Thread model + tree view (thread addendum): threads as the universal unit — origin, parent, inherited-context slice, status, result destination on every node; origin traceable node→root in one view; thread starting (explicit, originless threads impossible) and splitting (context carries to fork, both continue, divergence permanently marked, no silent sibling leakage); agent workspaces are threads in the same single tree; terminal-native thread tree view (type/status/age nodes, expand into rail takeover, linear text-path fallback). **Binds:** thread-orchestration, visual-system thread tree view, work-observability rail, sub-agent-orchestration. **Evidence:** split-then-diverge fixture with marked divergence and no leakage; origin trace from a depth-two agent thread to root; tree view + linear fallback action-conformance; human split and agent child coexist in one tree.
|
||||
|
||||
## 3. Safe operation — files and trust
|
||||
|
||||
- [ ] 3.1 Programmatic restore points: filesystem-event-triggered snapshots before every edit/delete/move, project-external coverage, identical for headless runs; rewind choice files-only vs files+conversation; stability-aware retention/dedup/pinning; no automatic disk-limit cleanup; block protected writes when snapshot cannot be written. **Binds:** filesystem watcher, snapshot store, storage policy. **Evidence:** overnight-delete fixture restored; turn/token/compaction events never gate capture; active/failed/pinned points survive; cleanup requires approval; full-disk edit fails closed.
|
||||
- [ ] 3.2 Responsive changes-view + history separation: side-by-side wide/unified narrow/manual switch; explicit deletion/binary treatment; selective external-file overwrite preview; one-command revert; my history separated from your uncommitted work. **Binds:** VCS/change renderer/restore selector. **Evidence:** width matrix + binary/delete fixtures + external selected-file restore + dirty-tree protection + revert.
|
||||
- [ ] 3.3 Trust surfaces: act-then-receipt flows, hard-ask classes, visible-but-blocked boundaries, outside-network rules, paired remote commands with origin. **Binds:** trust-and-sovereignty spec, alert gateways. **Evidence:** scenario suite incl. external alter attempt refused.
|
||||
- [ ] 3.4 Sleight Phase-0 authority/byte-path audit before behavior changes: owners for transcript/event/message/task/checkpoint/memory/prompt/payload/cache/UI; baseline task/cost/token/latency/recall/continuity/truth; one end-to-end trace. **Binds:** §15 Phase 0, §18. **Evidence:** ownership diagram + exists/wired/reachable/used/authority matrix + byte-level outbound proof of one ContextBroker/no hidden appender.
|
||||
- [ ] 3.4a Implement cooling/rehydration, ContextBroker single authority, honest degraded states, right-to-forget wipe. **Binds:** memory store, ContextBroker, Spine. **Evidence:** exact-by-ID, A→B→A, provider-swap, cache-purge, correction, pointer-liveness fixtures.
|
||||
- [ ] 3.4b Execute applicable S01–S15. **Binds:** Sleight §14 scenarios + memory-context-continuity scenario requirement. **Evidence:** one result row per topic shift/return, exact quote, not-found/failure, pressure, correction, provider swap, restart, cache purge, identity, privacy, false progress, pause/cancel, zero-memory abstention, pointer rot against the exact staged generation.
|
||||
- [ ] 3.4c Execute §16 deterministic/semantic/chaos matrix. **Binds:** Sleight §16 + memory-context-continuity evidence requirement. **Evidence:** 100% deterministic invariants; actual semantic metrics; injected crash, read-only/disk-full, network, missing file, stale index, embedding outage, simultaneous topics, cache wipe, duplicate replay, forced budget, child failure; explicit degraded/recovery receipts.
|
||||
- [ ] 3.4d Produce §18 deliverables. **Binds:** Sleight §18 + memory-context-continuity deliverables requirement. **Evidence:** ownership diagram, module audit, UI states/wireframes, interface/event/cancel/error/token schemas, phased dependency/test plan, migration/compatibility plan, guarantee boundary all reviewed before closure.
|
||||
- [ ] 3.5 Memory-invariant regression (I-R2-017): keep the live ContextBroker invariant under regression tests — sole model-visible representation, sole memory contributor path, single selection API with fail-closed abstention and degradation fields; no future-memory API reserved; repository-contract expansion requires a named live consumer. **Binds:** memory-context-continuity Phase-0 requirement, future-seam-registry. **Evidence:** regression suite fails closed on a second model-visible path or selection-API bypass; consumerless repository-API proposals are deleted, not reserved.
|
||||
|
||||
## 4. Deliberate operation — modes, plans, workflows, alerts
|
||||
|
||||
- [ ] 4.1 Hard modes: Chat/Task/Plan enforce model/tools/context/rules; research activity styling in Task; sleep/night background. **Binds:** modes-and-plans, provider routing. **Evidence:** mode matrix test — tools genuinely unreachable per mode.
|
||||
- [ ] 4.2 Full Plan process in Chat: contextual offer requires consent; initial plan → research protocol → viability → revised plan → targeted research → final viability → explicit start → staged execution in Task → independent review → final report. **Binds:** research pipeline machinery, plan document store, lane promotion. **Evidence:** full loop on a real feature request; Plan never interrupts active Task; durable/editable/recallable/piecewise rerun proven; return-to-plan path exercised on injected contradiction; final report observed.
|
||||
- [ ] 4.3 Unattended workflows: four trigger classes, workflow contract, bounded retries, improvement proposals. **Binds:** scheduler, event subscriptions, workflow store. **Evidence:** headless run + failure-exhaustion pause with alert.
|
||||
- [ ] 4.4 Alert system: four types, toggleable platform-general channels, background-only + urgent override, evidence-bearing content, long-absence digest. **Binds:** alert gateway, notification adapters. **Evidence:** per-channel toggle test; urgent override fires focused.
|
||||
|
||||
## 5. Complete interface — inputs, visuals, settings
|
||||
|
||||
- [ ] 5.1 Universal input: drag/drop path floor, rich analysis with fallback (images/docs/audio/video/folders/archives). **Binds:** input path, vision model capability. **Evidence:** drop fixtures incl. incapable-terminal fallback.
|
||||
- [ ] 5.2 Inspect-before-connect settings: capability listing, exact-scope approval, MCP support, authority boundary. **Binds:** integration settings, MCP layer. **Evidence:** foreign package cannot alter authority; approval scope test.
|
||||
- [ ] 5.2a Reserve the four qualified deferred-speech seams exactly. **Binds:** inputs-and-integrations Voice deferred; speech-synthesis D7. **Evidence:** conformance tests prove (a) non-keyboard editable-text input with no global/OS injection, (b) playback play/stop/pause + rate/volume + state {idle,playing,paused,stopped} + auto-play off, (c) handle-derived recording states {idle,recording,confirming-transcript,playing,error} + stop in every non-idle + non-color/sound labels, (d) named disabled settings placeholders, and (e) no audio/mic dependency or permission in the build.
|
||||
- [ ] 5.3 Functional visual system after Task 0.0.5 selection: bubbles, research styling + linked sources, selected compact-header/light-lane placement, built-in themes + live editor, honest state/expression, local/optional/paused animation, complete static/text fallback, idle health readouts. **Binds:** selected visual direction, terminal capability detection. **Evidence:** selection record + capability matrix + animation-off/reduced-motion legibility + zero inference on idle.
|
||||
- [ ] 5.3a Deferred decorative polish: pixel-art asset production and capability-gated spinnable 3D implementation. **Binds:** selected direction and functional fallbacks. **Evidence:** does not block retirement; unsupported terminals retain complete behavior; no typing/stream/tool performance regression.
|
||||
- [ ] 5.3b Terminal capability and budget gates (I-R2-006): pin Bubble Tea/Bubbles/Lip Gloss versions; probe per attachment and after resize/reattach; conservative default profile; p99 input/queue/memory/bytes-per-frame budgets; one aggregate single-loop budget with semantic-event priority; decoration yields under pressure; ANSI-aware and modifier-aware fixtures. **Binds:** visual-system terminal capability and budget gates requirement. **Evidence:** budget breach locks/downgrades rich rendering with the fixture named; typing stays responsive under progress flood; graphics-profile probe deferred until raster work begins.
|
||||
- [ ] 5.5 Headless graph projection (I-R2-015, seam admitted with consumer fixture + deletion test): search/tree/table/text plus JSON/DOT/Mermaid/SVG exports; opaque read-only deep-link IDs with literal fallback; versioned router with stale-link error; pagination; typed degradation; layout explicitly non-semantic; mutations only through domain APIs. **Binds:** memory-context-continuity headless graph projection, future-seam-registry. **Evidence:** contradictory-temporal-fact resolution identical through text, table, export, stale deep link, and no-graphics mode; GUI canvas/JSON-Canvas remain later GUI projections of the same IDs.
|
||||
- [ ] 5.4 Settings surfaces: models, devices, notifications, limits, themes — platform-general. **Binds:** canonical config/settings owner approved in Task 0.0.3/0.0.4; no TUI-local authority. **Evidence:** settings round-trip + per-channel toggles + invalid/conflicting values fail visibly + restart persistence.
|
||||
|
||||
## 6. Growth — skills and self-transformation (gated)
|
||||
|
||||
- [ ] 6.1 Skills: consent-gated drafting, cheapest reliable form, tested activation, SKILL.md compatibility with authority outside packages. **Binds:** curator skill path (post-0.3 repair), skill store. **Evidence:** foreign package authority-boundary test; draft/approve/reject flow.
|
||||
- [ ] 6.2 Self-transformation proposal-only pipeline with all 13 stages from synthesis (propose through rollback/close) and D01–D10 + RM01–RM15 + IAM01–IAM22 binding. **Binds:** existing curator/construction/stage-guard/deploy-promotion/rollback/ContextBroker authorities; no duplicate hierarchy. **Evidence:** proposal flow end-to-end; every DEP01–DEP07 prerequisite gates activation; no self-activation possible.
|
||||
- [ ] 6.2a Run the synthesis cheapest-disproof matrix: alternate writers; runtime/dependency mismatch; ledger crash/corruption/replay/key rotation; owner-envelope spoof/replay/scope change; always-pass/skipped critics; hidden effects; shadow leakage; partial rollback/post-snapshot owner correction; forged/duplicated proven-use; cross-provider/reduced-context continuity; perfect evidence without authority. **Binds:** synthesis failure_matrix. **Evidence:** each negative fixture fails closed with a canonical receipt.
|
||||
- [ ] 6.3 Taxonomy result: EVIDENCE_DEFICIENT; no operational router, no learned sentinel, no labels even for routing. **Binds:** taxonomy-adversarial-2 + D02. **Evidence:** no taxonomy code/path ships; strict explicit approval continues; unrelated TUI work is not blocked.
|
||||
- [ ] 6.4 Extension lifecycle first consumer (I-R2-023; requires 0.9(c) hardening + publisher identity/digest verification first): exactly one command plus one status card through host-rendered declarative slots; approval UI and lane/identity/policy replacement forbidden; MCP discovery feeds inspection only with independent exact-scope approval and effect mediation; catalog/marketplace deferred. **Binds:** extension-lifecycle capability, inputs-and-integrations. **Evidence:** first-consumer fixture — load, update with extra scope (blocked pending re-consent), disable/uninstall with verified process-tree termination and no residual UI/code; every deny path offers fast owner resolution (R2-D9).
|
||||
- [ ] 6.5 Future seam registry (I-R2-025; shares CI machinery with 0.10): record problem/owner/minimal schema/forbidden authority/consumer fixture/versioning rule/compatibility test/security boundary/evidence IDs/review date/deletion-expansion trigger for the relay boundary, offline draft log, graph deep links, memory invariant, extension lifecycle, and web-link feed seams; CI compatibility + deletion tests; reviews end in keep/change/delete only. The registry also carries the day-one socket set (SA-D6): agent orchestration, task management, thread orchestration, messaging/promotion, cognition-reserved (SA-D5), connectors — each registered with name + stable versioned contract + deletion test at its subsystem's first build, enforced by the sockets-first cross-cutting gate. **Binds:** future-seam-registry capability, migration-and-retirement Gitea chain. **Evidence:** deleting each seam breaks nothing claimed (or exposes disguised scope for owner decision); no automatic renewal exists; fast expansion path on a named live consumer works; no major subsystem ships without its registered socket.
|
||||
|
||||
## 7. Retirement — prove and remove
|
||||
|
||||
- [ ] 7.1 Oracle/retirement map: mimocode capabilities → native paths → proof state ledger. **Binds:** migration-and-retirement; oracle register. **Evidence:** every row carries owner, native path, proof artifact, and retirement state.
|
||||
- [ ] 7.2 Parity proof harness: side-by-side real-task runs with failure/restart/recovery per retiring capability; parity unit is the authority-preserving action/receipt trace (action IDs, key scopes, focus transitions, breakpoint outcomes, theme tokens, oracle traces), never widget parity; mutant-hardened per 0.11; native executable attested. **Binds:** test harness, live provider proof, migration-and-retirement. **Evidence:** recorded runs linked in the ledger; harness goes RED on every named mutant.
|
||||
- [ ] 7.3 Python removal waves: obsolete module + dependency removal in the same change as its proven replacement. **Binds:** migration owner only. **Evidence:** removal commit + dependency diff + native parity/restart/failure proof remains green after deletion.
|
||||
- [ ] 7.4 Gitea closure: epic #2484 + children updated with links; reconciliation checker green; closing comments reference evidence, never paste spec text. **Binds:** issue-organization standard, Gitea source-of-truth workflow. **Evidence:** issue/PR/commit/CI links, reconciliation artifact, required checks green, closure reason recorded.
|
||||
|
||||
## Sequencing notes (R2 owner decisions — binding on task order)
|
||||
|
||||
- Visual direction selection runs AFTER the behavior/semantic map is approved (R2-D7; Task 0.0.5).
|
||||
- First outside connection is the draft/calendar-browser handoff (R2-D8; I-R2-019 — literal URL/account shown, immutable content digest, local timezone + UTC, never claims success, no credential in URL); connector work itself stays governed/deferred per the named-connector + jurisdiction gates (R2-D4; I-R2-018/I-R2-020). When a connector is named, the provider sandbox suite runs first.
|
||||
- Collaboration beyond the proposal-inbox shape waits on a real malicious-guest attack test as its promotion gate (R2-D3; I-R2-002).
|
||||
- Accessibility/screen-reader work is deferred (R2-D1): no parity claim, no AT retirement gate; the AT matrix (GAP-BT-01) is a future-if-prioritized research item.
|
||||
- The no-dead-end amendment (R2-D9) binds every gate below: each deny path pairs with a fast owner-resolution/override path; no rule may permanently block legitimate work.
|
||||
- The amendment round (SA-D1..SA-D6 + thread addendum): sub-agent orchestration (2.8), the living task tree (2.9), and the thread model + tree view (2.10) build in the visible-operation phase; agent/task-originated messaging adds no new semantics (SA-D3); the sockets-first rule gates every subsystem's first build.
|
||||
|
||||
## Cross-cutting gates (every slice)
|
||||
|
||||
- [ ] Frozen tests first (red → green), canary per surface per AGENTS.md. **Evidence:** failing-test reference precedes implementation; canary registration visible in CI.
|
||||
- [ ] Cognitive invariants at memory/skill handoffs. **Evidence:** owner/trigger/authority/provenance/reversibility checklist + negative authority test per handoff.
|
||||
- [ ] No simulated backend state anywhere in the TUI. **Evidence:** every rendered status carries canonical source/event ID; missing source renders unavailable/degraded, never success.
|
||||
- [ ] Terminal capability matrix. **Evidence:** graphics supported/unsupported, keyboard-only, reduced-motion, animation-off, low-color/plain-terminal runs.
|
||||
- [ ] Sockets-first (SA-D6): every major subsystem — agent orchestration, task management, thread orchestration, messaging/promotion, cognitive architecture (reserved, design deferred per SA-D5), connectors — ships its registered socket (name + versioned stable contract + deletion test) at first build under the no-consumer-no-seam rule; no subsystem ships contract-less. **Evidence:** registry row + passing deletion test exists at each subsystem's first-build gate; cognition socket reserved with a design-deferred record and zero architecture work.
|
||||
- [ ] Your review gate before apply. **Evidence:** approved OpenSpec revision + independent review READY_FOR_NICK_REVIEW.
|
||||
Whitespace-only changes.
@@ -0,0 +1,29 @@
|
||||
schema: spec-driven
|
||||
|
||||
context: |
|
||||
Rune is a self-governing synthetic assistant; this OpenSpec root governs its native TUI (the
|
||||
shell), not its kernels. The TUI is defined by epic #2484 (Bubble Tea TUI — native Rune
|
||||
interface) with integration finding #2485. Standing constraints agents cannot infer from code:
|
||||
- The TUI consumes kernels ONLY through the 8-point interop adapter surface recorded in
|
||||
research/wave1/tui/synthesis.md and docs/rebase-language-rubric.md (stateless CLI ops,
|
||||
{"ok":true,...} JSON envelope, mandatory --version on every unit, JSONL streaming with a
|
||||
terminal sentinel envelope, per-call timeouts, stable (channel, call_id) UI keys, std-only
|
||||
proof-phase). The TUI is never rewritten as the first consumer of a new interop surface.
|
||||
- Design authority: the defining document (~/.config/opencode/memory/rune_defining_document.md)
|
||||
and Nick's TUI design principles (2026-08-21/22): TUI is the body, memory is the brain,
|
||||
harness is the environment; working system over fancy features; subject headers per project
|
||||
at zero token cost; information density in the status bar (tokens, latency, message count,
|
||||
model, provider count, recall count).
|
||||
- Requirement language follows RFC 2119: MUST/SHALL statements are NEEDS (a slice cannot pass
|
||||
its oracle without them); SHOULD statements are WANTS (valuable, deferred if blocked). Every
|
||||
requirement body must be prefixed with its class (need/want) for the oracle register to map.
|
||||
- OpenSpec commands resolve from docs/ (cd docs && openspec ...). Issue links reference
|
||||
docs/openspec/... paths in Gitea; spec text is NEVER pasted into issue comments.
|
||||
|
||||
rules:
|
||||
proposal:
|
||||
- Under 700 words; differentiate needs vs wants explicitly
|
||||
- Link epic #2484 and finding #2485 by number; never paste spec text
|
||||
tasks:
|
||||
- Every slice names the kernels/services it binds to and its evidence gate
|
||||
- Slices are vertical (connector + UI) and ordered by priority with the oracle register as the close gate
|
||||
Whitespace-only changes.
@@ -9,7 +9,8 @@ PR as the new workflow._
|
||||
|---|---|---|---|
|
||||
| `ci.yml` | PR + push to master | lint, unit tests, canaries, best-practices, documentation, type-check (ratchet), gitea-offline, required-checks aggregation | Required checks fail → protected merge blocked |
|
||||
| `pipeline-gate.yml` | issue events, comment created, schedule (hourly), workflow_run | gate (deps/research-pending), teach (next-issue), research-clear (evidence floor: citations, live-fetched sources, trail, synthesis qualification), incomplete-catcher (draft PRs for abandoned work) | Rejection comments name gaps; labels stay |
|
||||
| `auto-version.yml` | pull_request:closed + push to master | semver from PR title (authoritative) / push-triggered convergence safety net (scripts/version_convergence.py) → bump PR → release | Safety net reconciles slipped events; drift flagged by maintenance |
|
||||
| `auto-version.yml` | pull_request:closed + push to master | semver from PR title (authoritative) / push-triggered convergence safety net (scripts/version_convergence.py) → bump PR → release; also tags `tui-v<version>` so the native-TUI release rides the same convergence event (single version across faces) | Safety net reconciles slipped events; drift flagged by maintenance |
|
||||
| `tui-release.yml` | push of tags `tui-v*` | native TUI release (openspec add-native-rune-tui task 0.17): paired build (GOPROXY=off, vendored) with semver injected from the tag via `-ldflags`, Gitea release titled `Rune TUI <semver>` with commit range since the last `tui-v*` tag, single binary attached as `rune-tui-<semver>` | Build or upload failure fails loud (no release without binary); re-runs replace the asset instead of stacking duplicates; old-face `v*` tags never trigger it |
|
||||
| `maintenance.yml` | schedule | drift reconciliation (release version vs tags), housekeeping | Files advisory "release drift" issue, deduped |
|
||||
| `merge-robot.yml` | schedule (hourly) + CI completion | merge-on-green: PRs with success status + mergeable merge via API | 405 (protection/stale) logged and skipped — **known silent-failure defect under discharge (bug-fix contract)** |
|
||||
| `spec-gate-headless.yml` | pull_request (opened/synchronize) | headless spec-gate agent: adversarial-first review, non-blocking Conventional-Comments verdict | Agent failure posts fallback comment; deterministic floors still govern |
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
# Oracle/Retirement Map — the retirement ledger's spine
|
||||
|
||||
Task 7.1 artifact (`docs/openspec/changes/add-native-rune-tui/tasks.md` §7.1).
|
||||
Binds: `migration-and-retirement` spec (the oracle map IS the retirement ledger)
|
||||
and the oracle register (`store/rebase/` — classification, ratchets, retired
|
||||
modules).
|
||||
|
||||
**Law: this map never claims more than evidence shows.** Every state below is
|
||||
backed by an artifact path that exists and a test result recorded on the day of
|
||||
this map. Zero rows are PROVEN — that is the finding, not a failure of the map.
|
||||
|
||||
## State definitions (mechanical)
|
||||
|
||||
| state | meaning |
|
||||
|---|---|
|
||||
| PROVEN | Retirement-grade evidence per spec: side-by-side observed parity + failure/restart/recovery proof + live tool/provider confirmation + same-change Python removal, mutant-hardened. |
|
||||
| PARTIAL | Native path landed AND at least one cited artifact exercising the row's core semantics is green today; named gaps remain. |
|
||||
| UNPROVEN | Native path landed but the row's own declared evidence artifacts are red or absent today (face-level unit tests alone do not qualify). |
|
||||
| NOT-STARTED | No native path exists. |
|
||||
|
||||
Counts: **PROVEN 0 · PARTIAL 16 · UNPROVEN 1 · NOT-STARTED 2** (19 rows).
|
||||
|
||||
## Cross-check — what was run for this map (this checkout, 2026-10-16)
|
||||
|
||||
| run | result |
|
||||
|---|---|
|
||||
| `python3 scripts/unified_check.py --members rust_suite` | GREEN — cargo test green in all 103 src/rust crates |
|
||||
| `python3 scripts/unified_check.py --members src_go_suite` | RED — go test failed in NEW modules `restore-points`, `webfeed`, `workflow-engine` (3 of 93; baseline expects none failing) |
|
||||
| `python3 scripts/unified_check.py --members go_suite` | RED — go vet rc=1, test -race rc=1, gofmt 1 file (`bridge/bridge_test.go`); failing: `tui/graphproj` + `tui/history` + `tui/cmd/rune-tui` (build), `tui/settings` (`TestPerChannelToggleWiring`) |
|
||||
| `python3 scripts/unified_check.py --members python_suite` | RED — 98 failing vs a 120-failure baseline; 5 NEW failures, all `tests/test_seam_registry.py` enforcement tests |
|
||||
| `python3 scripts/unified_check.py --members migration_mutants` | SKIP — task 0.11 mutant suite not landed (member stays declared so it cannot be silently dropped) |
|
||||
| `python3 scripts/unified_check.py --members language_verdict_fixtures` | GREEN — verdict gate with 10 negative fixtures |
|
||||
| `go test ./...` (tui/, direct) | 25 packages ok, 4 FAIL (as in go_suite row) |
|
||||
|
||||
Because zero rows are PROVEN, the "every PROVEN row's artifact must be green"
|
||||
rule is vacuously satisfied; instead every green/red claim cited in a row was
|
||||
verified by the runs above (they cover all cited Go/Rust artifacts; Python seam
|
||||
results come from the python_suite run).
|
||||
|
||||
## Module-level context (the rebase floor beneath the rows)
|
||||
|
||||
From `store/rebase/`: 180 modules classified (86 RUST · 56 GO · 29 TEST-INFRA ·
|
||||
7 TUI-LAST · 2 RETIRES); 77 kernels ported (ported_ratchet); **12 Python
|
||||
modules deleted with proof** (retired_modules.json — module-level strangler
|
||||
exits, e.g. spine, witness, drift_core, pins); 50 ported-but-never-delegated
|
||||
facades remain (dichotomy_debt); 7,496 py-lines of deletion debt;
|
||||
retirement-ratchet candidates pending: 0. The live runtime authority is still
|
||||
Python: 289 modules in `src/rune/`, including the Textual oracle
|
||||
`rune_tui.py` (4,170 lines). The native bridge is read-only
|
||||
(`/v1/status` + `/v1/conversation` polling).
|
||||
|
||||
## The 19 rows
|
||||
|
||||
Owner = umbrella subsystem (`config/umbrellas.json`). "Green/RED" = today's
|
||||
runs above.
|
||||
|
||||
| # | capability | owner | native path | proof artifact (status) | state |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | rune-runtime | orchestration | `src/go/rune-entry` (serve), `src/go/event-bus`, `src/go/webfeed`, `tui/runtime`, `tui/runtime/lib`, `tui/bridge`, `tui/cmd/rune-tui` | `tui/bridge/*_test.go` + `tui/runtime` green; `src/go/webfeed` module RED; `tui/cmd/rune-tui` build broken | PARTIAL |
|
||||
| 2 | dual-lane-interface | orchestration | `tui/lanes`, `tui/nav`, `tui/promote`, `tui/semantic` | lanes/nav/promote/semantic tests green | PARTIAL |
|
||||
| 3 | modes-and-plans | orchestration | `tui/modes`, `src/go/build-mode` | `tui/modes` green; `src/go/build-mode` 4 tests + 6 truth files green | PARTIAL |
|
||||
| 4 | tools-providers-devices | capability | `src/go/provider-registry`, `src/go/tool-protocol`, `src/go/local-tool-runtime`, `src/go/forge`, `src/go/device-pairing`, `tui/devices` | provider-registry truth battery green; tool-protocol/local-tool-runtime/forge green; device-pairing 0 tests | PARTIAL |
|
||||
| 5 | file-change-safety | governance | `src/go/restore-points`, `tui/changes`, `tui/history` | `tui/changes` green; `src/go/restore-points` module RED; `tui/history` build broken | PARTIAL |
|
||||
| 6 | trust-and-sovereignty | governance | `tui/trust`, `src/go/stage-guards`, `src/go/persona-gate` | trust (4 tests), stage-guards, persona-gate green | PARTIAL |
|
||||
| 7 | memory-context-continuity | memory | `src/go/context-broker`, `src/go/memory-store-deep`, `src/go/memory-consolidation`, `src/go/graph-memory`, `tui/memory`, `tui/graphproj` | context-broker (2t/2 truth), memory-store-deep (10t/16 truth), consolidation green; `tui/graphproj` build broken; graph-memory truth-only | PARTIAL |
|
||||
| 8 | unattended-workflows | orchestration | `src/go/cron-engine`, `src/go/event-wait`, `src/go/workflow-engine`, `tui/workflows` | cron-engine (2t/6 truth) + event-wait + `tui/workflows` green; `src/go/workflow-engine` module RED | PARTIAL |
|
||||
| 9 | alerts-and-remote-control | orchestration | `src/go/alert-gateway`, `tui/alerts`, `tui/settings` | `tui/alerts` face unit green; `TestPerChannelToggleWiring` RED; alert-gateway 0 tests | UNPROVEN |
|
||||
| 10 | work-observability | orchestration | `tui/rail`, `tui/receipts`, `tui/history`, `src/go/witness`, `src/go/work-manager`, `src/go/outcome-ledger` | rail/receipts/witness green; `tui/history` build broken; work-manager + outcome-ledger 0 tests | PARTIAL |
|
||||
| 11 | sub-agent-orchestration | orchestration | none (bookkeeping surfaces only: `tui/tasks` agent-assignment, `tui/threads` agent workspaces — green tests, rendering only) | — | NOT-STARTED |
|
||||
| 12 | task-management | orchestration | `tui/tasks`, `src/go/auto-tasks`, `src/go/auto_tasks` | tasks (3 tests), auto-tasks green; 7.2 harness packet GREEN (side-by-side + recovery + 4/4 mutants RED + attested digest) | PARTIAL |
|
||||
| 13 | thread-orchestration | orchestration | `tui/threads` | threads/tree/trace tests green (4); splitting with permanently-marked divergence implemented | PARTIAL |
|
||||
| 14 | inputs-and-integrations | perception | `tui/inputs`, `src/go/auto-detect` | inputs (3 tests) green; auto-detect truth-only, 0 tests | PARTIAL |
|
||||
| 15 | visual-system | orchestration | `tui/semantic`, `tui/version`, `tui/update`, `src/rust/layout_contract`, `src/rust/rune_spinner` | semantic green; layout_contract frozen truth + spinner inside rust_suite 103/103 GREEN | PARTIAL |
|
||||
| 16 | skills-and-growth | growth | `tui/skills`, `src/go/curator`, `src/go/curator-core` | skills (2 tests) green; curator-core 12 tests + 14 truth green | PARTIAL |
|
||||
| 17 | migration-and-retirement | governance | machinery: `store/rebase/*`, `scripts/dichotomy_audit.py`, `scripts/unified_check.py` members, `tui/migrate`, `src/go/parity-harness` | `language_verdict_fixtures` GREEN; 12 retired modules proven; 7.2 parity harness landed + frozen (row 12 end-to-end, 4/4 mutants RED); `migration_mutants` member live; python_suite 5 NEW red (seam enforcement) | PARTIAL |
|
||||
| 18 | extension-lifecycle | capability | `tui/extensions`, `src/go/extension-host` | extensions (3 tests) + extension-host (1 test) green; no truth battery | PARTIAL |
|
||||
| 19 | future-seam-registry | governance | none native (registry is `docs/seams/registry.json` + `scripts/check_seam_registry.py` — Python/doc machinery) | `tests/test_seam_registry.py` — 5 enforcement tests NEWLY RED | NOT-STARTED |
|
||||
|
||||
Machine-readable form: `verification/retirement-map.json` (same rows, same
|
||||
states, per-row gap lists).
|
||||
|
||||
## Honest gaps (named, not hidden)
|
||||
|
||||
1. **Zero PROVEN rows.** No capability has met the spec's retirement bar
|
||||
(observed side-by-side parity, failure/restart/recovery, live
|
||||
tool/provider proof, same-change Python removal). The 12 module-level
|
||||
strangler exits in `retired_modules.json` are real but are module proofs,
|
||||
not capability proofs.
|
||||
2. **The parity harness landed 2026-10-17 (7.2/0.11), proven for ONE capability.** Row 12 task-management is harness-proven end-to-end (4/4 mutants RED, recovery equal, digest attested); the other 18 rows still need their own runs, and the 0.11 tail (SSH-profile replay + slowest-target SLO run) is open. Per the spec, a harness not proven RED on named mutants cannot support ANY PROVEN claim — that requirement now has a machine that enforces it, and it enforces it on every future row.
|
||||
3. **The native binary does not build today.** `tui/cmd/rune-tui` fails
|
||||
(undefined `promote`, unused imports). The single native face cannot be
|
||||
compiled from this checkout.
|
||||
4. **Three kernels are red today:** `restore-points` (row 5),
|
||||
`workflow-engine` (row 8), `webfeed` (row 1) — new failures against a
|
||||
zero-failure src/go baseline.
|
||||
5. **Authority is still Python.** The bridge is read-only (`/v1/status`,
|
||||
`/v1/conversation`); chat completions, task submission, and the supervised
|
||||
Rust-worker IPC are unlanded waves. 289 Python modules remain; 50 facades
|
||||
are ported-but-never-delegated; 7,496 py-lines of deletion debt; the
|
||||
Textual oracle `rune_tui.py` (4,170 lines) is still the live TUI.
|
||||
6. **Seam-registry enforcement is newly red** — its own 5 frozen enforcement
|
||||
tests fail; the no-consumer-no-seam gate is not currently enforceable.
|
||||
7. **Untested kernels** (landed, zero tests): `alert-gateway`,
|
||||
`device-pairing`, `work-manager`, `outcome-ledger`, `tool-protocol-rw`,
|
||||
`graph-memory` (truth files exist, no test consumes them).
|
||||
8. **Deferred fronts recorded, not hidden:** accessibility parity (R2-D1, not
|
||||
a retirement gate), real-hardware qualification (R2-D2, gates the
|
||||
multi-device promise), collaboration/finance/rooms (gated deferrals).
|
||||
|
||||
## Maintenance
|
||||
|
||||
This map is regenerated, not hand-edited into optimism: row states may only
|
||||
move up when the cited artifact is green AND the promotion evidence (7.2
|
||||
harness runs, recovery proofs, Python-removal commits) exists at the linked
|
||||
paths. A row whose artifact turns red drops a state until re-proven.
|
||||
|
||||
## 7.2 harness addendum (2026-10-17, wave K19)
|
||||
|
||||
The parity proof harness landed: `src/go/parity-harness` (the retirement
|
||||
machine, tasks 7.2 + 0.11 mutant battery). What it is, in plain words:
|
||||
|
||||
- **Side-by-side real-task runs.** One real task through the OLD Python
|
||||
path (`python3 -m rune.auto_tasks` — the live oracle) and the NEW native
|
||||
path (`src/go/auto_tasks`, attested binary), never linked into the
|
||||
harness's own process. First capability proven end-to-end: **row 12
|
||||
task-management** (the most-ready row — the only one whose oracle and
|
||||
native paths are both runnable headless today).
|
||||
- **The parity unit is the authority-preserving action/receipt trace**
|
||||
(action IDs, key scopes, breakpoint outcomes, oracle traces; focus
|
||||
transitions + theme tokens recorded as not-applicable with named
|
||||
reasons), never widget parity — the frozen test pins that no
|
||||
frame/render/ANSI/pixel ever enters the evidence.
|
||||
- **Failure/restart/recovery**: the real task crashes at two breakpoints
|
||||
and resumes from durable state; recovery ordering obeys reconcile-first
|
||||
/ never-re-apply-an-irreversible-step, and duplicate-irreversible-effect
|
||||
counts are measured from the completion file (the oracle's declared
|
||||
source of truth) and compared engine-for-engine.
|
||||
- **Mutant-hardened per 0.11**: key scope, receipt origin, recovery
|
||||
ordering, launcher target — each deliberately implanted; each trips the
|
||||
harness RED with a named violation (4/4 RED; frozen forever).
|
||||
- **Native executable attested**: every run attests the exact sha256 of
|
||||
the binary that produced it; a tampered launcher target is refused.
|
||||
The digest is recorded per-run inside the packet (Go stamps the VCS
|
||||
state into the binary, so each recorded run carries its own).
|
||||
- **The promotion rule is mechanical**: `DeriveRowState` promotes a
|
||||
PARTIAL row to PROVEN only when every leg — including the same-change
|
||||
Python removal proof (task 7.3) — is present; a harness that stayed
|
||||
green on a mutant is named as not-evidence, not passed.
|
||||
|
||||
Row 12 stays **PARTIAL** despite its GREEN packet: the same-change Python
|
||||
removal has not been committed (task 7.3 owns that change). What the run
|
||||
did produce: one live divergence repaired — the Go port mapped a non-zero
|
||||
check-subprocess exit onto the error path where the oracle parses stdout
|
||||
(failing sanity receipts differed byte-for-byte); fixed to oracle
|
||||
semantics in `src/go/auto_tasks/checks.go` and pinned red-first in
|
||||
`checks_failure_test.go`. The `migration_mutants` unified-check member is
|
||||
live (drives the 0.11 mutant battery); the 0.11 tail (SSH-profile replay
|
||||
+ slowest-target SLO run) and the other 18 rows' runs remain open gaps.
|
||||
@@ -0,0 +1,310 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"registry_version": "1.0.0",
|
||||
"title": "Rune future seam + day-one socket registry",
|
||||
"law": "No-consumer-no-seam (openspec future-seam-registry): a seam or socket exists only with consumers (or a recorded owner reservation basis), a named owner, forbidden authority, and a passing deletion test + consumer fixture at its named paths. Deleting a record must break nothing claimed, or expose disguised scope for an owner decision. Reviews end in keep/change/delete only; nothing renews automatically.",
|
||||
"maintained_by": "scripts/check_seam_registry.py (unified_check member seam_registry)",
|
||||
"policy": {
|
||||
"automatic_renewal": false,
|
||||
"review_outcomes": [
|
||||
"keep",
|
||||
"change",
|
||||
"delete"
|
||||
],
|
||||
"review_window_days": 90,
|
||||
"expiry_verdict": "AMBER",
|
||||
"missing_deletion_test_verdict": "RED",
|
||||
"missing_consumer_fixture_verdict": "RED",
|
||||
"evidence_artifacts": [
|
||||
"research/tui-frontier-r2/synthesis.json"
|
||||
],
|
||||
"decision_token_allowlist": [
|
||||
"R2-D2",
|
||||
"R2-D3",
|
||||
"R2-D4",
|
||||
"R2-D6",
|
||||
"R2-D8",
|
||||
"R2-D9",
|
||||
"SA-D1",
|
||||
"SA-D2",
|
||||
"SA-D3",
|
||||
"SA-D4",
|
||||
"SA-D5",
|
||||
"SA-D6",
|
||||
"D12",
|
||||
"D15",
|
||||
"D24",
|
||||
"thread-addendum",
|
||||
"FST-001",
|
||||
"FST-002",
|
||||
"FST-003"
|
||||
]
|
||||
},
|
||||
"records": [
|
||||
{
|
||||
"id": "relay_boundary",
|
||||
"kind": "seam",
|
||||
"problem": "Remote reachability without binding the Rune core to one network vendor or equating private routing with trust (I-R2-004: the connector owns transport only; core holds just endpoint identity and revocation contracts).",
|
||||
"owner": "r2-owner \u2014 transport/connectors governor (R2-D4 named-connector + jurisdiction gates)",
|
||||
"minimal_schema_ref": "schemas/seams/relay_boundary.schema.json",
|
||||
"forbidden_authority": "Relay cannot write config or mint roles; relay-substitution must not change authorization or sovereignty; no vendor protocol inside the retirement core.",
|
||||
"consumer_fixture": "tests/fixtures/seams/relay_boundary.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[relay_boundary]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[relay_boundary]",
|
||||
"security_boundary": "Mutual endpoint authentication; bounded frames and queues; anti-replay sequence; short-lived pairing; explicit outside-network approval at effect time; immediate revocation (adversarial.json#I-R2-004).",
|
||||
"evidence_ids": [
|
||||
"I-R2-004",
|
||||
"I-R2-021",
|
||||
"D12"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16"
|
||||
},
|
||||
{
|
||||
"id": "offline_draft_log",
|
||||
"kind": "seam",
|
||||
"problem": "Phone/laptop drafting continues offline without stale commands, approvals, credentials, or external effects ever auto-executing on reconnect (I-R2-003 effect quarantine).",
|
||||
"owner": "r2-owner \u2014 collaboration/devices governor (D12 real-hardware gate)",
|
||||
"minimal_schema_ref": "schemas/seams/offline_draft_log.schema.json",
|
||||
"forbidden_authority": "Canonical commands/approvals/credentials/effects never merge via CRDT or file merge \u2014 ordered runtime (I-R2-014) only; approval never inferred from merge success; only explicit non-effectful artifact schemas replicate.",
|
||||
"consumer_fixture": "tests/fixtures/seams/offline_draft_log.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[offline_draft_log]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[offline_draft_log]",
|
||||
"security_boundary": "Actor and base revision signed; rejected operations preserved; re-authorize after schema migration against the current revocation epoch; human conflict resolution for semantic fields.",
|
||||
"evidence_ids": [
|
||||
"I-R2-003",
|
||||
"I-R2-014"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16"
|
||||
},
|
||||
{
|
||||
"id": "graph_deep_links",
|
||||
"kind": "seam",
|
||||
"problem": "Useful graph inspection now (search/tree/table/text/exports) and rich clickable exploration later, without excluding nonvisual users or creating GUI truth (I-R2-015 opaque deep links).",
|
||||
"owner": "r2-owner \u2014 memory/graph governor (task 5.5)",
|
||||
"minimal_schema_ref": "schemas/seams/graph_deep_links.schema.json",
|
||||
"forbidden_authority": "Layout is explicitly non-semantic; read-only default; mutations only through domain APIs; no GUI-side truth \u2014 GUI canvas/JSON-Canvas are later projections of the same opaque IDs.",
|
||||
"consumer_fixture": "tests/fixtures/seams/graph_deep_links.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[graph_deep_links]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[graph_deep_links]",
|
||||
"security_boundary": "Stable opaque link IDs with literal fallback; versioned router with stale-link error; source plus event/validity time on every node; large-graph pagination.",
|
||||
"evidence_ids": [
|
||||
"I-R2-015",
|
||||
"FST-002"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16"
|
||||
},
|
||||
{
|
||||
"id": "memory_invariant",
|
||||
"kind": "seam",
|
||||
"problem": "The live ContextBroker invariant stays the sole model-visible representation and sole memory contributor path, so future memory work cannot smuggle a second representation into the retirement core (I-R2-017, task 3.5).",
|
||||
"owner": "r2-owner \u2014 memory governor (Sleight-of-Hand Phase 0)",
|
||||
"minimal_schema_ref": "schemas/seams/memory_invariant.schema.json",
|
||||
"forbidden_authority": "No future-memory API reserved; repository-contract expansion requires a named live consumer \u2014 consumerless proposals are deleted, not reserved; no selection-API bypass.",
|
||||
"consumer_fixture": "tests/fixtures/seams/memory_invariant.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[memory_invariant]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[memory_invariant]",
|
||||
"security_boundary": "Single selection API with fail-closed abstention and degradation fields; missing trace renders UNKNOWN never success; secret fields schema-denied; exporters lossy and non-authoritative.",
|
||||
"evidence_ids": [
|
||||
"I-R2-017",
|
||||
"FST-001"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16"
|
||||
},
|
||||
{
|
||||
"id": "extension_lifecycle",
|
||||
"kind": "seam",
|
||||
"problem": "Extension surfaces exist without extension code owning the UI: exactly one command plus one status card through host-rendered declarative slots (I-R2-023, task 6.4 first consumer).",
|
||||
"owner": "r2-owner \u2014 extension governor (D15 capability delta; requires 0.9(c) hardening + publisher identity/digest verification)",
|
||||
"minimal_schema_ref": "schemas/seams/extension_lifecycle.schema.json",
|
||||
"forbidden_authority": "Approval UI and lane/identity/policy replacement forbidden; MCP discovery feeds inspection only with independent exact-scope approval and effect mediation; catalog/marketplace deferred.",
|
||||
"consumer_fixture": "tests/fixtures/seams/extension_lifecycle.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[extension_lifecycle]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[extension_lifecycle]",
|
||||
"security_boundary": "Manifest schema validation, digest, atomic index replacement, verified process-tree termination on disable/uninstall with no residual UI/code; every deny path offers fast owner resolution (R2-D9).",
|
||||
"evidence_ids": [
|
||||
"I-R2-023",
|
||||
"D15",
|
||||
"R2-D9"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16"
|
||||
},
|
||||
{
|
||||
"id": "web_link_feed",
|
||||
"kind": "seam",
|
||||
"problem": "Anything outside the TUI is a live read-oriented web feed talking to the runtime \u2014 never a second native face (R2-D6, task 1.2).",
|
||||
"owner": "r2-owner \u2014 runtime/transport governor (task 1.2)",
|
||||
"minimal_schema_ref": "schemas/seams/web_link_feed.schema.json",
|
||||
"forbidden_authority": "No second native face spawns from the feed; the feed never executes effects; input ordering is origin-tagged by authenticated arrival, never by claim.",
|
||||
"consumer_fixture": "tests/fixtures/seams/web_link_feed.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[web_link_feed]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[web_link_feed]",
|
||||
"security_boundary": "Read-oriented with evidence links; committed/pending states visually distinct; paired remote commands carry origin; outside-network origins always ask.",
|
||||
"evidence_ids": [
|
||||
"I-R2-014",
|
||||
"R2-D6"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16"
|
||||
},
|
||||
{
|
||||
"id": "agent_orchestration",
|
||||
"kind": "socket",
|
||||
"problem": "Sub-agent work (spawn \u2192 run \u2192 steer \u2192 complete/cancel) plugs into visibility and authority without redesigning the rail or the trust chain (SA-D1/SA-D2, task 2.8).",
|
||||
"owner": "r2-owner \u2014 orchestration governor (task 2.8 first build)",
|
||||
"minimal_schema_ref": "schemas/seams/agent_orchestration.schema.json",
|
||||
"forbidden_authority": "No invisible delegation; authority inherited down the chain with hard asks intact; unreceipted agent writes refused as unmediated; agents never mint authority.",
|
||||
"consumer_fixture": "tests/fixtures/seams/agent_orchestration.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[agent_orchestration]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[agent_orchestration]",
|
||||
"security_boundary": "Explicit briefs; mid-run steer + safe-boundary cancel; per-action evidence-bearing receipts; chained spawning visible as ancestry.",
|
||||
"evidence_ids": [
|
||||
"SA-D1",
|
||||
"SA-D2"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16",
|
||||
"socket_rule": "SA-D6 sockets-first: stable versioned contract + deletion test at the subsystem's first build; the building subsystem is the first consumer.",
|
||||
"first_build_task": "2.8"
|
||||
},
|
||||
{
|
||||
"id": "task_management",
|
||||
"kind": "socket",
|
||||
"problem": "The living task tree (statuses, sub-tasks, dependencies, blocked-by, assignment) plugs into the Task lane and scheduling without a second task hierarchy (SA-D4, task 2.9).",
|
||||
"owner": "r2-owner \u2014 orchestration governor (task 2.9 first build)",
|
||||
"minimal_schema_ref": "schemas/seams/task_management.schema.json",
|
||||
"forbidden_authority": "No hidden task state outside the tree; owner reorder recorded and propagated; no scheduling bypass of blocked-by dependencies.",
|
||||
"consumer_fixture": "tests/fixtures/seams/task_management.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[task_management]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[task_management]",
|
||||
"security_boundary": "Full history/audit on every task; assignment to Rune or agents bridges through sub-agent orchestration with attributable end-to-end audit.",
|
||||
"evidence_ids": [
|
||||
"SA-D4"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16",
|
||||
"socket_rule": "SA-D6 sockets-first: stable versioned contract + deletion test at the subsystem's first build; the building subsystem is the first consumer.",
|
||||
"first_build_task": "2.9"
|
||||
},
|
||||
{
|
||||
"id": "thread_orchestration",
|
||||
"kind": "socket",
|
||||
"problem": "Threads as the universal unit of conversation and work (origin, parent, inherited-context slice, status, result destination) render in one tree holding human splits and agent children alike (thread addendum, task 2.10).",
|
||||
"owner": "r2-owner \u2014 orchestration governor (task 2.10 first build)",
|
||||
"minimal_schema_ref": "schemas/seams/thread_orchestration.schema.json",
|
||||
"forbidden_authority": "Originless threads impossible; no silent sibling leakage at splits; divergence permanently marked; agent workspaces are threads in the same single tree \u2014 no parallel hierarchy.",
|
||||
"consumer_fixture": "tests/fixtures/seams/thread_orchestration.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[thread_orchestration]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[thread_orchestration]",
|
||||
"security_boundary": "Explicit thread starting; context carries to fork; result destination on every node; origin traceable node-to-root in one view.",
|
||||
"evidence_ids": [
|
||||
"thread-addendum",
|
||||
"SA-D6"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16",
|
||||
"socket_rule": "SA-D6 sockets-first: stable versioned contract + deletion test at the subsystem's first build; the building subsystem is the first consumer.",
|
||||
"first_build_task": "2.10"
|
||||
},
|
||||
{
|
||||
"id": "messaging_promotion",
|
||||
"kind": "socket",
|
||||
"problem": "Importance-aware suggestions and steer/queue promotion controls accept human, agent, and task origins without new message semantics (task 2.2 + SA-D3).",
|
||||
"owner": "r2-owner \u2014 lanes/trust governor (task 2.2 first build)",
|
||||
"minimal_schema_ref": "schemas/seams/messaging_promotion.schema.json",
|
||||
"forbidden_authority": "Agent/task-originated messaging adds no new semantics; promotion never bypasses the decision+evidence+effect package; edit-forward semantics only.",
|
||||
"consumer_fixture": "tests/fixtures/seams/messaging_promotion.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[messaging_promotion]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[messaging_promotion]",
|
||||
"security_boundary": "Every promotion carries decision + evidence + effect; Steer-now vs Queue-for-Task is an owner-visible choice; outside-network origins always ask.",
|
||||
"evidence_ids": [
|
||||
"SA-D3"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16",
|
||||
"socket_rule": "SA-D6 sockets-first: stable versioned contract + deletion test at the subsystem's first build; the building subsystem is the first consumer.",
|
||||
"first_build_task": "2.2"
|
||||
},
|
||||
{
|
||||
"id": "cognition_reserved",
|
||||
"kind": "socket",
|
||||
"problem": "The cognitive-architecture socket reserves name and contract shape so a future cognition subsystem plugs in without redesign \u2014 while its design stays an explicit future problem with no architecture spec now (SA-D5/D24).",
|
||||
"owner": "r2-owner \u2014 cognition reservation holder (SA-D5 owner directive)",
|
||||
"minimal_schema_ref": "schemas/seams/cognition_reserved.schema.json",
|
||||
"forbidden_authority": "No cognitive architecture is specified or implied; no cognitive code path ships; no future-memory API hides behind this reservation (memory_invariant record governs that boundary).",
|
||||
"consumer_fixture": "tests/fixtures/seams/cognition_reserved.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[cognition_reserved]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[cognition_reserved]",
|
||||
"security_boundary": "Reservation is name + contract shape only, marked design deferred (SA-D5); any expansion requires a governed review with a named live consumer.",
|
||||
"evidence_ids": [
|
||||
"SA-D5"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16",
|
||||
"socket_rule": "SA-D6 sockets-first: stable versioned contract + deletion test at the subsystem's first build; the building subsystem is the first consumer.",
|
||||
"first_build_task": "deferred (SA-D5)",
|
||||
"reservation_basis": "SA-D5/D24 owner directive: reserved day-one for name + contract shape only, design explicitly deferred \u2014 the no-consumer-no-seam rule is satisfied by this recorded owner decision, not by invented consumers."
|
||||
},
|
||||
{
|
||||
"id": "connectors",
|
||||
"kind": "socket",
|
||||
"problem": "Named outside services (first: the draft/calendar-browser handoff) connect through governed connectors without connector features leaking into core (SA-D6 + R2-D4).",
|
||||
"owner": "r2-owner \u2014 connectors governor (R2-D4 named-connector + jurisdiction gates)",
|
||||
"minimal_schema_ref": "schemas/seams/connectors.schema.json",
|
||||
"forbidden_authority": "No connector feature exists in core; finance effects stay a deferral record until a connector AND jurisdiction are named; relay boundary stays a separate record \u2014 connectors never mint trust.",
|
||||
"consumer_fixture": "tests/fixtures/seams/connectors.json",
|
||||
"versioning_rule": "Contract is semver via the schema's x-contract-version. A breaking contract change bumps the major version and updates this record's compatibility test in the same change; registry_version tracks registry edits, never the contract.",
|
||||
"contract_version": "1.0.0",
|
||||
"compatibility_test": "tests/test_seam_compat.py::test_seam_compat[connectors]",
|
||||
"deletion_test": "tests/test_seam_deletion.py::test_seam_deletion[connectors]",
|
||||
"security_boundary": "Provider sandbox suite runs first when a connector is named; literal URL/account shown; immutable content digest; never claims success; no credential in URL (R2-D8).",
|
||||
"evidence_ids": [
|
||||
"I-R2-019",
|
||||
"R2-D4",
|
||||
"R2-D8"
|
||||
],
|
||||
"review_date": "2027-01-14",
|
||||
"deletion_trigger": "Delete when a review ends in delete (consumerless at review, or disguised scope exposed by the deletion test); deletion must break nothing claimed. A named live consumer reopens the seam through a governed review (fast expansion path) \u2014 never automatically.",
|
||||
"created_at": "2026-10-16",
|
||||
"socket_rule": "SA-D6 sockets-first: stable versioned contract + deletion test at the subsystem's first build; the building subsystem is the first consumer.",
|
||||
"first_build_task": "1.2 (handoff wiring) / governed deferral (R2-D4)"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
# chat_bubble.py — Behavior Contract
|
||||
|
||||
Source: `src/rune/chat_bubble.py` (401 lines). Consumers: `rune_tui.py:102,1256` (MessageBox per message),
|
||||
`rune_tui.py:3854` (ThinkingBubble per thinking block).
|
||||
Test harness: `BubbleTestApp` (`chat_bubble.py test`) — one sample per role on bg `rgb(30,30,40)`.
|
||||
|
||||
## 1. PURPOSE
|
||||
|
||||
The chat transcript surface: role-styled message bubbles (8 roles), a right-click copy
|
||||
context menu, a collapsible thinking block, and the alignment container that places each
|
||||
bubble left/right/center in the channel log. This is the module a Bubble Tea port renders
|
||||
for every line of conversation history.
|
||||
|
||||
## 2. BEHAVIOR CONTRACTS
|
||||
|
||||
Contract IDs `CB-*`. Every literal (color, string, threshold) below is from source.
|
||||
|
||||
### Role taxonomy & routing
|
||||
|
||||
- **CB-01 Role set**: `user`, `assistant`, `system`, `confirm`, `thinking`, `research`,
|
||||
`error`, `tool`. Entry point is `MessageBox(role, content, metadata=None)`; it composes
|
||||
exactly one child: `ThinkingBubble(content)` for `role == "thinking"`, else
|
||||
`ChatBubble(role, content, metadata)`.
|
||||
- **CB-02 Alignment map** (MessageBox CSS class → `align` inside full-width row):
|
||||
| role | CSS class | alignment |
|
||||
|---|---|---|
|
||||
| `user` | `user-bubble` | right top |
|
||||
| `assistant` | `assistant-bubble` | left top |
|
||||
| `system` | `system-bubble` | center top |
|
||||
| `confirm` | `system-bubble` | center top (shares system's class) |
|
||||
| `thinking` | `thinking-bubble` | left top |
|
||||
| `research` | `research-bubble` | left top |
|
||||
| `error` | `error-bubble` | center top |
|
||||
| `tool` | `tool-bubble` | left top |
|
||||
|
||||
Unknown role falls back to `assistant-bubble` (left). MessageBox itself: `width: 1fr,
|
||||
height: auto` — one full-width row per message.
|
||||
- **CB-03 Bubble width**: hug content (`width: auto`) capped by per-role `max-width`
|
||||
as a fraction of the container (the channel log viewport): all roles **75%** except
|
||||
`system` 90%, `confirm` 90%, `research` 90%, `tool` 70% (and the unreachable
|
||||
`ChatBubble.thinking` 50%). Short messages render a small bubble; long ones grow to
|
||||
the cap and wrap.
|
||||
|
||||
### Box geometry & color system
|
||||
|
||||
- **CB-04 Base geometry (ChatBubble)**: `padding: 0 1` (one column each side, none
|
||||
vertical), `margin: 0 0 1 0` (one blank row between messages), rounded border for
|
||||
every role except `confirm` (thick). Border + padding cost 4 columns of the wrapped
|
||||
text width.
|
||||
- **CB-05 Role color table** (exact values):
|
||||
|
||||
| role | background | border | text color | extra |
|
||||
|---|---|---|---|---|
|
||||
| `user` | `rgb(0,80,120)` | round `rgb(0,140,200)` | theme `$text` | |
|
||||
| `assistant` | `rgb(30,60,30)` | round `rgb(60,140,60)` | `$text` | |
|
||||
| `system` | theme `$surface` | round `$secondary` | `$text-muted` | max-width 90% |
|
||||
| `confirm` | `rgb(40,30,10)` | **thick** `rgb(200,160,40)` | `rgb(255,220,100)` | max-width 90%, **padding 1 2** (overrides base) |
|
||||
| `research` | `rgb(20,40,80)` | round `rgb(60,100,180)` | `$text` | max-width 90% |
|
||||
| `error` | `rgb(100,20,20)` | round `rgb(200,50,50)` | `$text` | |
|
||||
| `tool` | `rgb(80,70,10)` | round `rgb(160,140,30)` | `$text` | max-width 70% |
|
||||
|
||||
Theme tokens (`$surface`, `$secondary`, `$text`, `$text-muted`) come from the app theme,
|
||||
not this module.
|
||||
- **CB-06 Dead style**: `ChatBubble.thinking` CSS (surface bg, 50% max-width) can never
|
||||
apply because MessageBox routes `thinking` to ThinkingBubble (CB-01). A ChatBubble
|
||||
constructed directly with role `thinking` would hit its `render()` branch (CB-09) and
|
||||
this CSS.
|
||||
|
||||
### Header & content rendering (ChatBubble.render)
|
||||
|
||||
- **CB-07 Header labels** (first line, bold, role-specific): `user`→`You` (bold cyan),
|
||||
`assistant`→`Rune` (bold green), `confirm`→`⚠ Confirm` (bold), `research`→`Research`
|
||||
(bold blue), `error`→`Error` (bold red), `tool`→`Tool` (bold yellow). A literal `\n`
|
||||
follows the header block; content starts on line 2.
|
||||
- **CB-08 Inline metadata**: only keys `model`, `tokens`, `latency` (in that order) are
|
||||
shown, appended after the label as one dim segment ` {model} | {tokens} tok |
|
||||
{latency}ms` — only the parts whose keys exist. Shown only for headered roles (not
|
||||
system/thinking). Separators are `" | "`; units are ` tok` and `ms` with no space
|
||||
before `ms`.
|
||||
- **CB-09 System/thinking special-casing**: `system` renders the *entire* content dim
|
||||
italic with no header and no metadata; `thinking` (dead path) renders content dim, no
|
||||
header.
|
||||
- **CB-10 Content literalism**: message content is appended via `Text.append()` —
|
||||
**never interpreted as Rich markup, never markdown-parsed**. `Text("plain")` +
|
||||
`text.append(content)` is the port's safety contract: brackets, asterisks, backticks
|
||||
in model output must render verbatim. (Contrast TW/ThinkingBubble — CB-19.)
|
||||
- **CB-11 Wrapping/reflow**: content wraps at the bubble's inner width (container ×
|
||||
max-width fraction − border − padding). Rich/Textual word-wrap: break at spaces,
|
||||
fold over-long unbroken tokens character-wise to the next line. Empty content →
|
||||
header-only bubble (for headered roles) or zero-width body. No horizontal scroll.
|
||||
Widths are recomputed on terminal resize (auto-reflow).
|
||||
|
||||
### CopyMenu (right-click)
|
||||
|
||||
- **CB-12 Trigger**: `MouseDown` with `event.button == 3` anywhere inside a ChatBubble
|
||||
pushes `CopyMenu(self.content)` as a modal screen.
|
||||
- **CB-13 Menu layout**: full-screen scrim `rgba(0,0,0,0.5)`; centered `.menu-box`
|
||||
`width/height: auto`, `max-width: 40` cols, `$surface` bg, solid `$accent` border,
|
||||
padding `1 2`. Three full-width buttons (margin-bottom 1, last-child 0):
|
||||
`📋 Copy to clipboard` (id `copy`), `📋 Copy as plain text` (id `copy-plain`),
|
||||
`Cancel` (id `cancel`).
|
||||
- **CB-14 Copy semantics**: `copy` strips bracket tags via `re.sub(r"\[.*?\]", "",
|
||||
content)` (non-greedy, per bracket pair) then `app.copy_to_clipboard` + notify
|
||||
"Copied to clipboard". `copy-plain` copies the **raw** string including any markup
|
||||
(notify "Copied raw content"). `dismiss()` runs unconditionally after any of the
|
||||
three buttons — including Cancel and both copy actions.
|
||||
- **CB-15 Regex data loss**: the strip regex cannot distinguish markup from literal
|
||||
brackets — `array[0] and list[1:2]` copies as `array and list`. Verified behavior;
|
||||
any port must reproduce or consciously fix (see Quirks Q3).
|
||||
|
||||
### ThinkingBubble
|
||||
|
||||
- **CB-16 Initial state**: always **collapsed** (`_expanded = False`, class
|
||||
`collapsed`). Height is then 1 row (header only) — the content region is
|
||||
`display: none`.
|
||||
- **CB-17 Collapsed header**: `💭 {summary} (click to expand)` where summary = first
|
||||
**80** chars of content, all `\n` replaced by spaces, `"..."` appended iff
|
||||
`len(content) > 80`. Styling: whole line `dim`; the hint `(click to expand)` is
|
||||
`dim italic`. Header row height fixed at 1.
|
||||
- **CB-18 Toggle**: click anywhere on the widget OR keybinding `ctrl+x` (when focused)
|
||||
flips expansion. Expanded: header becomes `💭 Thinking (click to collapse)` (summary
|
||||
hidden), content region shown (`display: block`, `height: auto`). Collapsed: header
|
||||
regenerates the 80-char summary. Box style: bg `rgb(35,35,45)`, round border
|
||||
`rgb(60,60,70)` → **`rgb(80,80,100)` on hover**, muted text, max-width 75%,
|
||||
padding `0 1`, margin-bottom 1, inner regions padding `0 1`.
|
||||
- **CB-19 Content markup**: ThinkingBubble renders its header and content as
|
||||
**markup strings** (e.g. `f"[dim]{content}[/dim]"`) — unlike ChatBubble (CB-10),
|
||||
square brackets in thinking text *are* parsed by Rich here. The multi-attribute
|
||||
closing tags (`[/dim italic]`) are valid Rich markup (verified).
|
||||
- **CB-20 Tooltip**: 200-char preview (`content[:200]`, newlines→spaces, `"..."` iff
|
||||
> 200) wrapped in `[dim]`. ChatBubble's tooltip (when metadata present) is
|
||||
`Model: X | Tokens: Y | Latency: Zms` joined by `" | "`, tooltip absent when no
|
||||
metadata keys match.
|
||||
|
||||
## 3. INTERACTION MAP
|
||||
|
||||
| Widget | Event / input | Effect |
|
||||
|---|---|---|
|
||||
| ChatBubble | MouseDown button 3 | push `CopyMenu` modal |
|
||||
| CopyMenu | Button `copy` | strip-markup copy + notify + dismiss |
|
||||
| CopyMenu | Button `copy-plain` | raw copy + notify + dismiss |
|
||||
| CopyMenu | Button `cancel` | dismiss (no copy) |
|
||||
| ThinkingBubble | click | toggle expand/collapse |
|
||||
| ThinkingBubble | `ctrl+x` (focused) | toggle expand/collapse |
|
||||
| MessageBox | — | inert layout container |
|
||||
| Host app | `add_message(role, content, metadata)` (`rune_tui.py:1245`) | mount MessageBox, scroll log to end (no animation), append to transcript + message graph |
|
||||
|
||||
No keyboard interaction on ChatBubble; no focus ring; selection is terminal-native,
|
||||
not widget state.
|
||||
|
||||
## 4. RECONSTRUCTION NOTES
|
||||
|
||||
**Textual-specific (replace with Bubble Tea idioms):**
|
||||
- `Static` + DEFAULT_CSS cascade (classes, `:hover`, `display: none` collapse,
|
||||
`align` in Horizontal) → lipgloss styles + conditional rendering in a `Update`/
|
||||
`View` recompute. Collapsed/expanded is just a bool that swaps the body block.
|
||||
- `ModalScreen` scrim/modal → tea overlay in the model's view stack.
|
||||
- `app.copy_to_clipboard` / `app.notify` → OSC 52 or clipboard util + a transient
|
||||
notification line; Textual tooltips have no standard BT equivalent (drop or render
|
||||
the 200-char preview elsewhere).
|
||||
- `Binding("ctrl+x", ...)` needs focus in Textual; in BT make it a global key (e.g.
|
||||
toggle the *last* thinking block) — closer to what users expect anyway.
|
||||
|
||||
**Rune logic that must survive verbatim:**
|
||||
- CB-01/02 role taxonomy + alignment map; CB-05 color table; CB-07/08 header label and
|
||||
metadata formats; CB-09 system dim-italic special case; CB-10 content literalism;
|
||||
CB-16–18 collapse semantics incl. the 80/200 thresholds and `💭` glyph strings;
|
||||
CB-14 copy-mode duality (strip vs raw).
|
||||
|
||||
## 5. KNOWN QUIRKS
|
||||
|
||||
1. **Q1 — Button labels inverted from behavior**: "Copy to clipboard" strips markup;
|
||||
"Copy as plain text" copies the *raw* string with markup tags intact. The names
|
||||
read backwards relative to what they do.
|
||||
2. **Q2 — Confirm rides system-bubble alignment**: `confirm` is centered (CB-02), so
|
||||
the ⚠ Confirm bubble appears mid-column while its ChatBubble styling (thick gold
|
||||
border) differs from system's.
|
||||
3. **Q3 — Copy regex eats literal brackets** (CB-15): code snippets with `x[i]` lose
|
||||
indexing on "copy". Reproduce or fix in the port; flag if fixed.
|
||||
4. **Q4 — Dead thinking CSS** (CB-06) and a dead `render()` thinking branch (CB-09):
|
||||
never reachable through MessageBox.
|
||||
5. **Q5 — Metadata invisible for system/thinking** despite tooltip construction
|
||||
accepting it: `render()` returns before the metadata header for those roles.
|
||||
6. **Q6 — Markup inconsistency**: ChatBubble content is literal (CB-10) but
|
||||
ThinkingBubble content is markup-parsed (CB-19); the CopyMenu strip regex only
|
||||
makes sense for the literal case.
|
||||
7. **Q7 — Tooltips are the only metadata surface for model/tokens/latency beyond the
|
||||
inline header**; a BT port without tooltips loses CB-20's preview for thinking
|
||||
blocks entirely unless surfaced elsewhere.
|
||||
@@ -0,0 +1,791 @@
|
||||
{
|
||||
"frozen_now": "2026-10-14T00:00:00",
|
||||
"generator": "scripts/gen_tui_validator_truth.py",
|
||||
"oracle": "src/rune/tui_validator.py",
|
||||
"python": "3.14.8",
|
||||
"pins": {
|
||||
"PYTHONHASHSEED": "0 (suggestion ties iterate the VALID_CSS_PROPERTIES set)",
|
||||
"RUNE_SCRIPTS_DIR": "scripts/tui_validator_fixtures/scripts_home",
|
||||
"cwd": "repo root (all fixture paths repo-relative)"
|
||||
},
|
||||
"note": "Frozen via the shared harness (scripts/freeze_truth.py) against fixture trees under scripts/tui_validator_fixtures/ — the decision logic is the parity surface, not the live tree's current CSS. Verify: python3 scripts/gen_tui_validator_truth.py --verify",
|
||||
"cases": [
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
""
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
".foo {\n width: 100%;\n}\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"width",
|
||||
2,
|
||||
"width: 100%;"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"/* header\n spanning */\nwidth: 1\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"width",
|
||||
2,
|
||||
"width: 1"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"width: 1 /* trailing */\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"width",
|
||||
1,
|
||||
"width: 1"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"#id {\n* star\n&:hover {\n@media screen\n{\n}\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"Width: 10\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"wIdth: 10\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"width:1\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"width",
|
||||
1,
|
||||
"width:1"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"width : 1\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"width",
|
||||
1,
|
||||
"width : 1"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"grid-size: 3 3\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"grid-size",
|
||||
1,
|
||||
"grid-size: 3 3"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
".a {\n width: 1;\n height: 2;\n hatch: /;\n}\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"width",
|
||||
2,
|
||||
"width: 1;"
|
||||
],
|
||||
[
|
||||
"height",
|
||||
3,
|
||||
"height: 2;"
|
||||
],
|
||||
[
|
||||
"hatch",
|
||||
4,
|
||||
"hatch: /;"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"color: red; border: solid\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"color",
|
||||
1,
|
||||
"color: red; border: solid"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"width: 1\r\nheight: 2\r\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"width",
|
||||
1,
|
||||
"width: 1"
|
||||
],
|
||||
[
|
||||
"height",
|
||||
2,
|
||||
"height: 2"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"3d-depth: 1\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"background: url(data:image/png;base64,AAA)\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
[
|
||||
"background",
|
||||
1,
|
||||
"background: url(data:image/png;base64,AAA)"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "extract_css_properties",
|
||||
"args": [
|
||||
"-webkit-transform: none\n"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".foo {\n width: 100%;\n height: 1fr;\n background: $surface;\n border: solid $primary;\n padding: 1 2;\n margin: 0 1;\n}\n",
|
||||
"selftest1-valid"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".foo {\n width: 100%;\n selection-background: rgb(60, 60, 80);\n selection-foreground: $text;\n}\n",
|
||||
"selftest2-richisms"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"selftest2-richisms:3: Invalid CSS property 'selection-background'",
|
||||
"selftest2-richisms:4: Invalid CSS property 'selection-foreground'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".foo {\n backgroud: red;\n}",
|
||||
"suggestion-dist1"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"suggestion-dist1:2: Invalid CSS property 'backgroud'. Did you mean 'background'?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".a {\n layerz: 1;\n}"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"unknown:2: Invalid CSS property 'layerz'. Did you mean 'layer'?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".a {\n zzzzzz: 1;\n}",
|
||||
"no-suggestion-far"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"no-suggestion-far:2: Invalid CSS property 'zzzzzz'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".a {\n bor: 1;\n}",
|
||||
"no-suggestion-dist3"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"no-suggestion-dist3:2: Invalid CSS property 'bor'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".a {\n Width: 100%;\n}",
|
||||
"uppercase-escape"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".a {\n zzzz: 1;\n}",
|
||||
"mymod:L5"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"mymod:L5:2: Invalid CSS property 'zzzz'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".a {\n colr: 1;\n colr: 2;\n}\n",
|
||||
"dup-lines"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"dup-lines:2: Invalid CSS property 'colr'. Did you mean 'color'?",
|
||||
"dup-lines:3: Invalid CSS property 'colr'. Did you mean 'color'?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".a {\n width: nonsense-value;\n}",
|
||||
"values-unchecked"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".foo:focus {\n border: solid;\n}",
|
||||
"pseudo-selector"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
"/*\ncolr: red\n*/\nwidth: 1\n",
|
||||
"comment-swallow"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
".a {\n hatch: -;\n keyline: heavy;\n scrollbar-corner-color: red;\n text-opacity: 0.5;\n}\n",
|
||||
"exotic-valid"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_css",
|
||||
"args": [
|
||||
"",
|
||||
"empty"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "_find_closest_property",
|
||||
"args": [
|
||||
"backgroud"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "background"
|
||||
},
|
||||
{
|
||||
"fn": "_find_closest_property",
|
||||
"args": [
|
||||
"layerz"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "layer"
|
||||
},
|
||||
{
|
||||
"fn": "_find_closest_property",
|
||||
"args": [
|
||||
"zzzzzz"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": null
|
||||
},
|
||||
{
|
||||
"fn": "_find_closest_property",
|
||||
"args": [
|
||||
"border"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "border"
|
||||
},
|
||||
{
|
||||
"fn": "_find_closest_property",
|
||||
"args": [
|
||||
""
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": null
|
||||
},
|
||||
{
|
||||
"fn": "_find_closest_property",
|
||||
"args": [
|
||||
"colr"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "color"
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"",
|
||||
""
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 0
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"a",
|
||||
""
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 1
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"",
|
||||
"abc"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 3
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"same",
|
||||
"same"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 0
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"kitten",
|
||||
"sitting"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 3
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"backgroud",
|
||||
"background"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 1
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"layerz",
|
||||
"layer"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 1
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"layerz",
|
||||
"layers"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 1
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"abc",
|
||||
"yabd"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 2
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"flaw",
|
||||
"lawn"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 2
|
||||
},
|
||||
{
|
||||
"fn": "_edit_distance",
|
||||
"args": [
|
||||
"intention",
|
||||
"execution"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": 5
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
".",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "relative"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
".sibling",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "relative"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"os",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "stdlib"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"__future__",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "stdlib"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"sys.path",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "stdlib"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"textual",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "external"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"textual.widgets",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "external"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"textual_autocomplete",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "external"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"rich",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "external"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"rune",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "first-party"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"rune.tui_transport",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "first-party"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"memory_env",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "first-party"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"bare_helper",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "scripts"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"mystery_mod",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "unknown"
|
||||
},
|
||||
{
|
||||
"fn": "_classify",
|
||||
"args": [
|
||||
"os,",
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": "unknown"
|
||||
},
|
||||
{
|
||||
"fn": "validate_module_css",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/mod_css_good.py"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_module_css",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/mod_css_bad.py"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"mod_css_bad.py:L1:3: Invalid CSS property 'backgroud'. Did you mean 'background'?",
|
||||
"mod_css_bad.py:L6:3: Invalid CSS property 'colr'. Did you mean 'color'?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_module_css",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_clean/src/rune/rune_tui.py"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_module_css",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_dirty/src/rune/chat_bubble.py"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"chat_bubble.py:L1:3: Invalid CSS property 'backgroud'. Did you mean 'background'?",
|
||||
"chat_bubble.py:L1:4: Invalid CSS property 'selection-background'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_module_css",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/missing.py"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"Could not read scripts/tui_validator_fixtures/missing.py: [Errno 2] No such file or directory: 'scripts/tui_validator_fixtures/missing.py'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_imports",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_clean"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_imports",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_dirty"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"rune_tui.py:2: Import 'os,' — unclassified (unknown): not stdlib, external-allowlist, first-party (scripts/tui_validator_fixtures/tree_dirty/src/rune listing), or scripts/",
|
||||
"rune_tui.py:4: Import 'mystery_mod' — unclassified (unknown): not stdlib, external-allowlist, first-party (scripts/tui_validator_fixtures/tree_dirty/src/rune listing), or scripts/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_imports",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_no_tui"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"Could not read rune_tui.py: [Errno 2] No such file or directory: 'scripts/tui_validator_fixtures/tree_no_tui/src/rune/rune_tui.py'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_all_modules",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_clean"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_all_modules",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_dirty"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"chat_bubble.py:L1:3: Invalid CSS property 'backgroud'. Did you mean 'background'?",
|
||||
"chat_bubble.py:L1:4: Invalid CSS property 'selection-background'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "validate_all_modules",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_no_tui"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_composition",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_clean"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "validate_composition",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_dirty"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "run_full_validation",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_clean"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": []
|
||||
},
|
||||
{
|
||||
"fn": "run_full_validation",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_dirty"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"chat_bubble.py:L1:3: Invalid CSS property 'backgroud'. Did you mean 'background'?",
|
||||
"chat_bubble.py:L1:4: Invalid CSS property 'selection-background'",
|
||||
"rune_tui.py:2: Import 'os,' — unclassified (unknown): not stdlib, external-allowlist, first-party (scripts/tui_validator_fixtures/tree_dirty/src/rune listing), or scripts/",
|
||||
"rune_tui.py:4: Import 'mystery_mod' — unclassified (unknown): not stdlib, external-allowlist, first-party (scripts/tui_validator_fixtures/tree_dirty/src/rune listing), or scripts/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "run_full_validation",
|
||||
"args": [
|
||||
"scripts/tui_validator_fixtures/tree_no_tui"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"Could not read rune_tui.py: [Errno 2] No such file or directory: 'scripts/tui_validator_fixtures/tree_no_tui/src/rune/rune_tui.py'"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
# message_widgets.py — Behavior Contract
|
||||
|
||||
Source: `src/rune/message_widgets.py` (104 lines). Consumers: `rune_tui.py:103,3864,4009`
|
||||
(mount per tool event, deduped by `(channel, call_id)`); decision handler at
|
||||
`rune_tui.py:3988`.
|
||||
|
||||
## 1. PURPOSE
|
||||
|
||||
The tool-work surface: each model-requested tool call becomes one typed, statusful
|
||||
widget in the chat log — never assistant prose. It carries a status line (glyph +
|
||||
name + kind), a details body (JSON arguments/result), and an Approve/Reject action row
|
||||
for approval-gated calls. This is Rune's human-in-the-loop gate rendered as UI.
|
||||
|
||||
## 2. BEHAVIOR CONTRACTS
|
||||
|
||||
Contract IDs `TW-*`.
|
||||
|
||||
### Layout & style
|
||||
|
||||
- **TW-01 Geometry**: `width: 90%` of the channel log, `height: auto`, margin
|
||||
`0 0 1 1` (one row below — separates from next entry; one col right), padding
|
||||
`0 1` (one column each side). Background: theme `$surface`. Border: **left side
|
||||
only**, solid 1-col, theme `$secondary` — a vertical rail, not a full box.
|
||||
Visually distinct from ChatBubble (no rounded box, no role colors).
|
||||
- **TW-02 Three-region stack**: `.tool-summary` (status line), `.tool-details`
|
||||
(body), `.tool-actions` (buttons, only when pending). All `width: 1fr,
|
||||
height: auto`; details text color `$text-muted`.
|
||||
|
||||
### Summary line
|
||||
|
||||
- **TW-03 Format**: `{glyph} {name}` in **bold**, then two spaces, then `{kind}` in
|
||||
**dim** where `kind = event["kind"].replace("_", " ")` (e.g. `pending_approval` →
|
||||
`pending approval`). One line (wraps if long).
|
||||
- **TW-04 Glyph map by event kind**: `pending_approval`→`?`, `completed`→`✓`,
|
||||
`error`→`✗`, `failed`→`✗`, anything else (incl. `unknown`)→`·`.
|
||||
- **TW-05 Default chain**: `name` from `event["request"]["name"]`, default
|
||||
`"unknown tool"`; kind default `"unknown"`; `call_id` property =
|
||||
`str(event["request"]["call_id"])`, default `"unknown"`. Missing `request` key is
|
||||
tolerated everywhere (`.get` chains).
|
||||
|
||||
### Details body
|
||||
|
||||
- **TW-06 Arguments format**: if `request.arguments` is truthy: dim label
|
||||
`arguments: ` then one **single-line** JSON dump — `json.dumps(arguments,
|
||||
sort_keys=True, ensure_ascii=False)` (keys sorted, unicode kept raw).
|
||||
- **TW-07 Result format**: if `event.result` is truthy: dim label `result: ` then
|
||||
`json.dumps(result, sort_keys=True, ensure_ascii=False, default=str)` —
|
||||
non-JSON-serializable values fall back to `str()`. When both present, a `\n`
|
||||
separates them (result on its own line).
|
||||
- **TW-08 Empty body**: with no arguments and no result, details is an empty Text —
|
||||
the Static still renders as a blank row inside the padded area.
|
||||
- **TW-09 Wrapping**: JSON is emitted unbroken; long dumps word-wrap inside the
|
||||
widget width and fold long tokens (Rich fold), i.e. reflow on resize, no
|
||||
truncation, no ellipsis, no internal scroll.
|
||||
|
||||
### Actions & decision protocol
|
||||
|
||||
- **TW-10 Action row**: rendered **iff `event["kind"] == "pending_approval"`**: a
|
||||
`.tool-actions` Vertical with `Approve` (Textual success variant — green) and
|
||||
`Reject` (error variant — red), class names `approve-tool` / `reject-tool`.
|
||||
- **TW-11 Decision message**: pressing either button posts
|
||||
`ToolCallWidget.Decision(widget=self, approved: bool)` — a Textual Message that
|
||||
bubbles to ancestors. It is the *only* outbound signal; the widget never resolves
|
||||
the approval itself.
|
||||
- **TW-12 Host handler contract** (observed at `rune_tui.py:3988-3993`): handler
|
||||
calls `event.stop()`, then disables **all** buttons on the widget (no further
|
||||
clicks), then resolves asynchronously; the transport's returned `tool_events` are
|
||||
routed back either as `apply()` on the existing widget (keyed
|
||||
`(channel, call_id)`) or as a freshly mounted widget.
|
||||
- **TW-13 `apply(event)` state replacement**: swaps `self.event`, re-renders summary
|
||||
and details in place (same widget — the log entry morphs rather than duplicating),
|
||||
and sets every button's `display = (kind == "pending_approval")` (hides the row on
|
||||
resolution). Buttons are hidden, not removed; mounting never *adds* an action row
|
||||
post-hoc.
|
||||
- **TW-14 Identity/tagging**: constructor takes keyword-only `channel: str` — stored
|
||||
but unused inside the widget; the host keys widgets by `(channel, call_id)` and
|
||||
uses `widget.channel` to route the decision back to the right channel's transport.
|
||||
A `call_id` is only unique *within* a channel.
|
||||
|
||||
## 3. INTERACTION MAP
|
||||
|
||||
| Source | Event | Effect |
|
||||
|---|---|---|
|
||||
| Approve button | press | post `Decision(self, approved=True)` |
|
||||
| Reject button | press | post `Decision(self, approved=False)` |
|
||||
| Host | `on_tool_call_widget_decision` | stop event, disable buttons, async `resolve_pending_approval(approved)`, apply/mount result events, consume runtime objects, maybe emit assistant bubble |
|
||||
| Host | new tool event, key unseen | mount `ToolCallWidget(event, channel=ch)`, scroll log to end |
|
||||
| Host | new tool event, key seen | `widget.apply(event)` in place |
|
||||
| Widget | resize | reflow per TW-09 |
|
||||
|
||||
No hover states, no collapse, no focus behavior beyond default button focus.
|
||||
|
||||
## 4. RECONSTRUCTION NOTES
|
||||
|
||||
**Textual-specific (replace):**
|
||||
- `Vertical` + DEFAULT_CSS + variant buttons → lipgloss border-left style + bubbletea
|
||||
list re-render; buttons are just key/mouse targets (`Approve`/`Reject`, or `y`/`n`
|
||||
style keys).
|
||||
- `Message` bubbling / `post_message` → a typed Go message (`DecisionMsg{callID,
|
||||
channel, approved}`) returned from `Update`; parent goroutine does the transport
|
||||
resolve and returns an `apply` message.
|
||||
- `Static.update` in-place morph → recompute the entry in the model slice keyed by
|
||||
`(channel, call_id)` and re-render — the *in-place morph, no duplicate entry*
|
||||
behavior (TW-13) is the essential part.
|
||||
|
||||
**Rune logic that must survive verbatim:**
|
||||
- TW-03/04 status line format + glyph map; TW-06/07 exact JSON dump flags
|
||||
(`sort_keys=True, ensure_ascii=False, default=str` on result only); TW-10
|
||||
pending-only action row; TW-11/12/13 decision protocol incl. disable-on-decision
|
||||
and in-place apply; TW-14 channel+call_id identity scheme.
|
||||
|
||||
## 5. KNOWN QUIRKS
|
||||
|
||||
1. **Q1 — `channel` is inert data**: never read inside the widget; purely a routing
|
||||
tag the host relies on. Ports must keep it attached to the widget identity.
|
||||
2. **Q2 — `apply()` cannot add an action row**: if a non-pending widget's event later
|
||||
becomes `pending_approval` (host doesn't do this today), the buttons stay absent —
|
||||
display-toggle only works on mounted buttons.
|
||||
3. **Q3 — Blank details row** when a tool event has neither arguments nor result
|
||||
(TW-08): the muted empty line is visible padding.
|
||||
4. **Q4 — Truthiness not presence**: `if arguments:` / `if result:` — falsy JSON
|
||||
values (`0`, `""`, `false`, `null`, `[]`) render *no* section even though the key
|
||||
exists. A `"result": null` and a missing result are indistinguishable.
|
||||
5. **Q5 — Glyph set ≠ closed kind set**: any future kind not in TW-04's map silently
|
||||
renders `·`; `failed` and `error` intentionally share `✗`.
|
||||
6. **Q6 — Token estimate upstream**: host passes `tokens = len(response)//4`
|
||||
(`rune_tui.py:3881`) — metadata shown in the *assistant* bubble header is an
|
||||
estimate, not real usage; the widget here never shows tokens.
|
||||
@@ -0,0 +1,118 @@
|
||||
# rune_graphic.py — Behavior Contract
|
||||
|
||||
Source: `src/rune/rune_graphic.py` (145 lines). No TUI consumers in-tree; runtime
|
||||
entry is `rune_graphic.py test` (standalone terminal loop). Not a Textual widget —
|
||||
a Pillow asset generator plus a Rich console playback loop.
|
||||
|
||||
## 1. PURPOSE
|
||||
|
||||
Rune's spinner: a purple diamond rotating about its center, pre-rendered as 12 PNG
|
||||
frames and displayed in the terminal via Rich's half-block image renderer, with a
|
||||
"Rune thinking..." caption. It is the loading/thinking indicator. The visual asset
|
||||
and frame timing — not any widget framework — are the portable contract.
|
||||
|
||||
## 2. BEHAVIOR CONTRACTS
|
||||
|
||||
Contract IDs `RG-*`.
|
||||
|
||||
### Asset generation (Pillow)
|
||||
|
||||
- **RG-01 Asset contract**: `NUM_FRAMES = 12` PNGs, `FRAME_SIZE = 64` px square,
|
||||
RGBA, saved to `~/.rune/frames/` (expanduser) as `rune_00.png` … `rune_11.png`
|
||||
(zero-padded 2 digits). Returns the path list in order.
|
||||
- **RG-02 Diamond geometry**: canvas center `cx = cy = size // 2` (32 for 64). Base
|
||||
shape is an axis-aligned rhombus with half-width `w = int(size * 0.35)` = **22**
|
||||
and half-height `h = int(size * 0.45)` = **28** — vertices (0,−h), (w,0), (0,h),
|
||||
(−w,0) relative to center; taller than wide.
|
||||
- **RG-03 Rotation math**: each vertex rotated by standard 2D matrix
|
||||
`x' = cx + x·cosθ − y·sinθ`, `y' = cy + x·sinθ + y·cosθ`, then **truncated with
|
||||
`int()`** (toward zero, not rounded) — sub-pixel jitter at some angles is inherent
|
||||
to the original look.
|
||||
- **RG-04 Frame sampling**: frame *i* uses `angle = 2π·i / 12` — even 30° steps,
|
||||
i = 0…11. Frame 0 is the upright diamond (θ=0); frame 12 ≡ frame 0, so looping
|
||||
0→11 is seamless. One full revolution per cycle.
|
||||
- **RG-05 Color constants**: diamond fill `DIAMOND_COLOR = (128,0,255)` (purple);
|
||||
background `BG_COLOR = (30,30,40)` (matches the TUI dark bg), opaque (alpha 255).
|
||||
Derived: highlight = channel-wise `+60/+30/+20` clamped at 255 → **(188,30,255)**
|
||||
(pink-shifted, since +60 red vs +30 green); shadow = `−40/−20/−30` floored at 0 →
|
||||
**(88,0,225)**.
|
||||
- **RG-06 Shading layout** (fixed relative to *rotated* vertices, so facets rotate
|
||||
with the diamond — the light source spins with it): highlight polygons
|
||||
`[t, l, center]` and `[t, center, mid_top]` cover the top-left face (midpoints
|
||||
integer-floored `//2`); shadow polygons `[r, b, center]` and `[b, center,
|
||||
mid_right]` cover the bottom-right face.
|
||||
- **RG-07 Edge lines** (width 1): top edges `t→r` and `t→l` in `(200,150,255)`
|
||||
(light); bottom edges `r→b` and `l→b` in `(80,0,160)` (dark).
|
||||
- **RG-08 Lazy generation trigger**: `get_frame_paths()` regenerates iff the frame
|
||||
dir is missing **or** `len(os.listdir(FRAME_DIR)) < 12` — counting *any* files,
|
||||
not just `rune_*.png`. Returns the 12 constructed paths without verifying the
|
||||
files exist.
|
||||
|
||||
### Terminal playback (`rune_graphic.py test`)
|
||||
|
||||
- **RG-09 Render pipeline**: per frame — `console.clear()` (full clear each frame,
|
||||
no diffing), then Rich `Image(path, width=32)`: the 64 px image renders 32
|
||||
terminal columns wide (2 px per column) using Unicode half-blocks → effective
|
||||
grid 32 cols × 32 rows (64 px tall at 2 px per row).
|
||||
- **RG-10 Caption**: below the image, `console.print("[dim purple] Rune
|
||||
thinking...[/dim purple]")` — two leading spaces, dim purple, trailing ellipsis.
|
||||
- **RG-11 Timing/loop**: `time.sleep(0.15)` between frames ≈ **6.67 fps**, index
|
||||
`i % 12`, runs forever; `KeyboardInterrupt` (Ctrl+C) exits cleanly printing a
|
||||
newline + success line. Frame 0 renders immediately at start (no warmup delay).
|
||||
|
||||
## 3. INTERACTION MAP
|
||||
|
||||
| Interface | Trigger | Effect |
|
||||
|---|---|---|
|
||||
| `generate_diamond_frame(angle, size=64)` | call | returns PIL RGBA Image (no I/O) |
|
||||
| `generate_frames(dir, n=12)` | call | writes 12 PNGs, returns path list |
|
||||
| `get_frame_paths()` | call | ensure-generated, return 12 paths |
|
||||
| `main() generate` | CLI | writes frames, prints count |
|
||||
| `main() test` | CLI | RG-09/10/11 playback loop |
|
||||
| Ctrl+C | playback | clean exit |
|
||||
|
||||
No mouse, no keys, no resize handling (fixed 32-col render regardless of terminal
|
||||
width).
|
||||
|
||||
## 4. RECONSTRUCTION NOTES
|
||||
|
||||
**Textual-specific — there is none**: module uses no Textual. The framework surface
|
||||
is Rich (`Console`, `rich.image.Image` half-block renderer) + PIL + a blocking sleep
|
||||
loop.
|
||||
|
||||
**Rune logic that must survive verbatim:**
|
||||
- The asset parameters: RG-01 file/count/size, RG-02 proportions (0.35/0.45
|
||||
half-extents), RG-04 12-frame 30° cycle, RG-05 color constants incl. the derived
|
||||
highlight/shadow channels, RG-06/07 facet layout and edge line colors. These make
|
||||
the frames pixel-faithful — a BT port can simply ship the same 12 PNGs.
|
||||
- RG-10 caption string/styling and RG-11 150 ms cadence.
|
||||
- RG-08 lazy-generation semantics (or replace with an explicit embedded-asset
|
||||
strategy — document the choice).
|
||||
|
||||
**Bubble Tea port strategy**: decode each PNG to RGBA once at startup; for each
|
||||
frame emit rows of half-block (`▀`) cells at 2 px/col × 2 px/row (32×32 cells),
|
||||
foreground = upper px, background = lower px; use `lipgloss` for the dim-purple
|
||||
caption; drive with a `tea.Tick` at 150 ms; any key or the stop condition quits.
|
||||
Alternatively pre-bake the 12 frames as 32-row half-block string tables at build
|
||||
time — cheapest and pixel-identical.
|
||||
|
||||
## 5. KNOWN QUIRKS
|
||||
|
||||
1. **Q1 — `mid_left` is computed and never used** (`rune_graphic.py:67`): dead
|
||||
variable; highlight uses `mid_top` instead. Keep for diff-parity or drop — no
|
||||
visual effect.
|
||||
2. **Q2 — Regeneration heuristic counts foreign files** (RG-08): ≥12 unrelated files
|
||||
in `~/.rune/frames` suppress regeneration, and the returned paths may then not
|
||||
exist — playback would crash loading frame 0. Also no version/size check: frames
|
||||
generated with different `FRAME_SIZE` are considered valid.
|
||||
3. **Q3 — Self-lit shading**: because facets are defined in rotating vertex space
|
||||
(RG-06), the light source rotates with the diamond instead of staying fixed in
|
||||
world space — the "specular" face is always top-left of the shape. A naive port
|
||||
that shades by fixed world-space normal will look different.
|
||||
4. **Q4 — `int()` truncation** (RG-03) toward zero introduces asymmetric vertex
|
||||
jitter; `round()` would produce visibly smoother frames at some angles.
|
||||
5. **Q5 — Full `console.clear()` per frame** (RG-09): flicker-prone on slow
|
||||
terminals; a BT port gets smooth redraws for free via the renderer diff.
|
||||
6. **Q6 — PIL import is deferred** inside functions (`ImageDraw` at call time) with
|
||||
only a TYPE_CHECKING top-level import — the module imports cleanly without Pillow
|
||||
installed until generation is attempted.
|
||||
@@ -0,0 +1,677 @@
|
||||
# rune_tui — Behavior Contracts (design-captured for Bubble Tea reconstruction)
|
||||
|
||||
Source: `src/rune/rune_tui.py` (4170 lines), with exact render rules from
|
||||
`src/rune/chat_bubble.py` (bubble widgets) and `src/rune/message_widgets.py`
|
||||
(tool-approval widgets), and the operator seam from `src/rune/tui_transport.py`.
|
||||
|
||||
Audience: the Bubble Tea reconstruction. Every section below is an
|
||||
**observable contract** — what the user sees, what state persists, what
|
||||
fires when. Internal helper names are given only as anchors; the native
|
||||
TUI must reproduce the *behavior*, not the call graph.
|
||||
|
||||
---
|
||||
|
||||
## 1. PURPOSE
|
||||
|
||||
`rune_tui` is the **primary interactive surface of Rune**: a full-screen
|
||||
terminal application (built on Textual) that is the only intended way a
|
||||
human talks to the system. It is launched via `python3 -m rune`
|
||||
(`src/rune/__main__.py` → `RuneApp().run()`), with an optional
|
||||
`--model <model>`-style CLI flag for the initial model selection.
|
||||
|
||||
It provides, observably:
|
||||
|
||||
- **Two independent conversation channels** — TASK (focused work) and
|
||||
CHAT (free conversation) — side by side, each with its own scrollback
|
||||
log and its own input line.
|
||||
- **A mode system** (`chat | task | build | research | plan | sleep`)
|
||||
that routes submitted text to different handling and is reflected in
|
||||
the status bar.
|
||||
- **A subject header** (`/project <name>`) — a free-text label for the
|
||||
current project, shown in status.
|
||||
- **A command surface** of ~41 slash-commands (`/model`, `/theme`,
|
||||
`/goal`, `/cron`, `/night`, `/build`, `/work`, `/queue`, `/pull`,
|
||||
subsystem inspectors like `/pressure`, `/graph`, `/health`, …) plus a
|
||||
searchable command palette and a model selector popup.
|
||||
- **An operational index pane (WORK)** showing the active channel's
|
||||
runtime objects (tool calls, memory queries, observations,
|
||||
verifications, outcomes) newest-first, as a complement to the narrative
|
||||
chat log.
|
||||
- **Shared durable state**: input history per channel (persisted to
|
||||
disk), last-used model (persisted), every rendered message also
|
||||
appended to a durable message graph, and conversation windows projected
|
||||
from a canonical conversation ledger at mount.
|
||||
|
||||
Consumers / dependents:
|
||||
|
||||
- **The human operator** — the TUI is the body; everything else is
|
||||
reachable through it.
|
||||
- **`rune_operator` (via `tui_transport`)** — the TUI never calls a
|
||||
model directly. Every user turn goes through
|
||||
`tui_transport.get_transport().send_task(text, model=…, channel=…)`,
|
||||
which returns a response envelope. The transport defaults to
|
||||
in-process; a `v1` mode exists behind the `RUNE_TUI_TRANSPORT` env var.
|
||||
The reconstruction must keep this seam: the TUI owns *rendering and
|
||||
routing*, the operator owns *cognition and context*.
|
||||
- **Downstream modules invoked by slash-commands** (see §3.3) — the TUI
|
||||
is a thin command shell over them; each command is "render the module's
|
||||
report as system messages, never crash on failure".
|
||||
- **Background loops** (`cognitive_loop`, `dream_cycle`,
|
||||
`autonomy_loop`) — instantiated at startup and ticked on a 240-second
|
||||
idle timer; their only UI effect is dim system lines in the chat
|
||||
channel.
|
||||
|
||||
Non-goals (things the module explicitly does NOT do anymore): per-message
|
||||
cognitive pipelines were removed — no warm-router thread, no
|
||||
auto-context/auto-task/behavior fan-out on send. Model-facing context
|
||||
assembly belongs to the ContextBroker inside the operator. The TUI sends
|
||||
text and renders the envelope.
|
||||
|
||||
---
|
||||
|
||||
## 2. BEHAVIOR CONTRACTS
|
||||
|
||||
### 2.1 Screen layout (the whole frame)
|
||||
|
||||
```
|
||||
┌ Header (Textual Header: app title + clock) ─────────────────┐
|
||||
│ CHAT (left) │ TASK (right) │ WORK (3rd, task only)│
|
||||
│ header (1 line) │ header (1 line) │ header (1 line) │
|
||||
│ scrollback │ scrollback │ runtime objects │
|
||||
│ input (3 lines) │ input (3 lines) │ │
|
||||
├ Version │ Status bar (1 line) ................. │ 🔄 refresh ┤
|
||||
│ (hidden popups: command palette, model selector) │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Contracts:
|
||||
|
||||
- **Channel order is CHAT-left, TASK-right.** (Deliberate, dated
|
||||
2026-10-05: "conversation first, focused work second".)
|
||||
- **WORK is the third column**, visible only when the active mode is
|
||||
`task` (see §2.5). On `chat` mode it is hidden, not merely empty.
|
||||
- **Footer is exactly one line**, left to right: semver label
|
||||
(`v<version>`, read live from the canonical version at render time —
|
||||
never a baked-in string) │ status bar (mode, model summary, providers,
|
||||
recall/memory, token count, latency, loading spinner) │ a `🔄` refresh
|
||||
glyph at the far right.
|
||||
- **Channel focus is visible**: the focused channel's border switches
|
||||
from the primary color to the accent color, and its header background
|
||||
switches to accent. Focus follows input focus and clicks.
|
||||
- **Channel split is resizable**: `task_width` defaults to 50 (%),
|
||||
adjustable in 5-point steps, clamped to [20, 80]. The chat pane always
|
||||
takes the remainder. The ratio persists for the session only (not
|
||||
written to disk).
|
||||
- **Popups overlay, never replace**: the command palette (60×20) and
|
||||
model selector (65×25) appear centered above the channels; closing
|
||||
either returns focus to the input of the currently active mode's
|
||||
channel.
|
||||
|
||||
### 2.2 ChannelView (one conversation channel)
|
||||
|
||||
Each channel is an independent unit with:
|
||||
|
||||
- A **1-line centered header**: `[b]CHAT[/b] — free conversation` /
|
||||
`[b]TASK[/b] — focused work`.
|
||||
- A **scroll region** holding message widgets (see §2.7), newest at the
|
||||
bottom.
|
||||
- A **3-line input** at the bottom with placeholder
|
||||
`Type in <Label>... (/ for commands, ↑↓ history)` and inline `/`
|
||||
autocomplete (filters the full command list by substring over both
|
||||
command and description; with empty input or bare `/` it shows all
|
||||
commands; with zero matches it falls back to showing all commands).
|
||||
|
||||
Render rules:
|
||||
|
||||
- **Appending a message always scrolls to the bottom** (`scroll_end`,
|
||||
no animation). There is no "stick to bottom vs. free scroll" state —
|
||||
any new content yanks the viewport to the newest message.
|
||||
- **Mount-time projection**: when a channel mounts, it (a) loads its
|
||||
persisted input history, and (b) projects the **last 8 messages** of
|
||||
the canonical conversation ledger for that channel ID into the display
|
||||
list, with timestamps truncated to `HH:MM` (`ts[11:16]`). This
|
||||
projection is best-effort: ledger failure means an empty start, never a
|
||||
failed mount.
|
||||
- **Every rendered message is dual-written**: the bubble mounts in the
|
||||
scroll region AND an entry appends to `channel.messages` (the display
|
||||
projection: `{role, content, time, metadata}`) AND to the durable
|
||||
**message graph** (stable ID + typed kind; the kind comes from
|
||||
`metadata["kind"]` when it is a known kind, else `"message"`; the
|
||||
assigned `message_id` is written back into the metadata dict). Graph
|
||||
failure is swallowed — the bubble is already on screen.
|
||||
- **Clicking anywhere in a channel focuses its input** and updates the
|
||||
focused-channel indicator (`🎯 task` / `💬 chat`) plus the status bar.
|
||||
- **Startup seeds**: on mount the task channel shows
|
||||
`Task channel. Type here for focused work. Ctrl+2 to switch to chat.`
|
||||
and the chat channel shows the mirror line, both as system messages.
|
||||
A cascade of dim system lines follows (module count, Ollama status,
|
||||
providers, version, persona/registry/tasks results, model profile +
|
||||
authority, loop activations, conditional night/work/build hints) — see
|
||||
§2.9 for the ordering contract.
|
||||
|
||||
### 2.3 Input, history, and submission routing
|
||||
|
||||
- **History per channel**: up/down arrows walk that channel's history
|
||||
(max 200 entries, persisted to `~/.rune/input_history.json` keyed by
|
||||
channel ID). Semantics:
|
||||
- Up with no browsing starts browsing at the most recent entry,
|
||||
stashing the current input.
|
||||
- Up at the oldest entry is a no-op (stays). Down past the newest
|
||||
restores the stashed input and exits browsing.
|
||||
- Left/right/home/end/shift-arrows do NOT disturb browsing state; any
|
||||
other key exits browsing and discards the stash.
|
||||
- Adding is deduplicated (re-add moves to most-recent) and blank input
|
||||
is never recorded. History is written to disk on every submit.
|
||||
- **Submit routing** (`Enter` in `input-task` vs `input-chat`):
|
||||
- The input clears immediately on submit; the text is recorded in
|
||||
history first.
|
||||
- Submitting in the task input sets mode to `task`; submitting in the
|
||||
chat input sets mode to `chat`. Typing location wins over any prior
|
||||
`/mode` setting.
|
||||
- Text starting with `/` is a command (handled, never sent to the
|
||||
model, never echoed as a user bubble).
|
||||
- **Chat-mode interception**: before sending, chat text is screened
|
||||
for task-addition intent. On a hit the text is queued (not sent):
|
||||
confidence ≥ 0.8 auto-approves with a dim receipt naming the
|
||||
confidence and pointing at `/queue execute`; lower confidence queues
|
||||
as pending pointing at `/queue list` / `/queue approve`. Either way
|
||||
the turn ends there.
|
||||
- **Task-mode hint**: task text is classified for construction mode;
|
||||
`PROJECT_BUILD` / `EXISTING_PROJECT_CHANGE` produces a one-line
|
||||
yellow hint suggesting `/build start` — advisory only, the turn
|
||||
still goes to the model.
|
||||
- **Idea capture**: task text matching idea phrasing ("i was thinking",
|
||||
"what if we", "btw", "by the way", "also we could", "maybe we
|
||||
should", "i had an idea", "wouldn't it be", "what about", "random
|
||||
thought") is pinned as a note and answered only with
|
||||
`Idea captured. Task continues.` — it is NOT sent to the model.
|
||||
- **Planning trigger (chat only)**: if planning is not already active
|
||||
and the text matches the planning threshold (explicit phrases like
|
||||
"let's plan" / "how should we" / "help me think through"; OR ≥2
|
||||
step-markers like "first…then…next"; OR any design word like
|
||||
"architecture" / "tradeoff" / "strategy"; OR any research word like
|
||||
"investigate" / "look into"), planning mode starts instead of sending
|
||||
(see §2.6). Otherwise the text is echoed as a user bubble and sent.
|
||||
- **Model fan-out is exclusive**: only one model turn runs at a time
|
||||
(`exclusive` worker). A second submit while a turn is in flight queues
|
||||
behind it; it does not cancel or interleave.
|
||||
|
||||
### 2.4 The model turn (send → thinking → response)
|
||||
|
||||
Observable sequence for a normal (non-command, non-intercepted) submit:
|
||||
|
||||
1. User bubble mounts; viewport jumps to bottom.
|
||||
2. **Thinking indicator mounts** at the bottom of the *same* channel's
|
||||
log: a 7-line spinning diamond (4 Unicode frames cycling every
|
||||
120 ms, medium-purple) with `Rune thinking` + 0–3 dots underneath,
|
||||
plus `⏳` in the status bar (`is_loading`). If pre-generated PNG
|
||||
frames exist under `~/.rune/frames/`, they render as pixels instead
|
||||
(same cadence, same caption) — purely a fidelity upgrade, same
|
||||
contract.
|
||||
3. The operator is called: `send_task(text, model=<resolved>, channel=…)`.
|
||||
4. On envelope return, in order:
|
||||
a. Thinking indicator stops and is removed; `is_loading` clears.
|
||||
b. Any `degraded` subsystem names each produce one dim line:
|
||||
`⚠ <name> unavailable (operator path)`.
|
||||
c. If `recall_count > 0`: status bar shows `🧠 <n>` for exactly 3
|
||||
seconds, then clears (timer-driven; a newer turn's indicator
|
||||
overwrites).
|
||||
d. Each non-blank thinking block mounts as a **collapsed**
|
||||
ThinkingBubble (summary = first 80 chars, newlines flattened,
|
||||
+ `...`; header reads `💭 <summary> (click to expand)`; click or
|
||||
Ctrl+X toggles to full content with `(click to collapse)`).
|
||||
e. Each tool event either updates the existing ToolCallWidget for
|
||||
`(channel, call_id)` in place or mounts a new one (see §2.8), and
|
||||
mirrors a one-line entry into the WORK pane (see §2.5).
|
||||
f. The assistant response mounts as an assistant bubble with metadata
|
||||
`{model, tokens=len/4}`; status bar accumulates `tokens_used +=
|
||||
len/4` and `messages_count += 1`.
|
||||
g. **Task/build modes only**: on-complete cron triggers fire
|
||||
(`⏰ Fired <n> on-complete triggers`), then approved queue messages
|
||||
execute into the task channel as user bubbles. Chat mode skips
|
||||
both.
|
||||
h. **Exactly one collapsed post-response line** (or silence):
|
||||
budget pressure (>80% used), degraded-claims count, growth delta,
|
||||
and every-10th-message self-improvement tick are joined with `·`
|
||||
into a single dim line. Empty means nothing rendered — never a
|
||||
placeholder dash.
|
||||
5. Non-success envelopes: `RESEARCH_REQUIRED` renders a yellow
|
||||
`🔬 Research required — task routed to the deep-research path…`
|
||||
system line (a routing disposition, not an error); any other error
|
||||
renders `Error: <error>` dim.
|
||||
6. Any exception in the turn renders **two** dim system lines —
|
||||
`⚠ <ErrorType>: <first 200 chars>` and `💡 Fix: <heuristic>` (the
|
||||
heuristic maps error text to advice: non-async/worker → thread hint,
|
||||
timeout → model hint, connection → Ollama hint, import → canary hint,
|
||||
sqlite → DB hint, JSON → corrupted-file hint, attribute → contracts
|
||||
hint, etc.) — and is journaled both as a teachable moment
|
||||
(`~/.rune/teachable_moments.json`, capped at 200) and in the
|
||||
subsystem-health registry. The session never crashes.
|
||||
|
||||
Model-name resolution (`auto | local | cloud | <literal>`):
|
||||
|
||||
- `auto` → router result for the current mode's task family, else
|
||||
`qwen2.5:3b` on any failure.
|
||||
- `local` → `qwen2.5:3b`. Any other literal passes through unchanged.
|
||||
- `cloud` → the router's top-ranked non-local candidate **only if a
|
||||
live cloud adapter exists**; otherwise the "model" is the literal
|
||||
sentence `cloud-unavailable-fallback: no verified cloud candidate
|
||||
(router ranked none; local remains authoritative)` — passed through as
|
||||
the model name, failures journaled. (Known quirk — see §5.2.)
|
||||
|
||||
### 2.5 WorkPane (the WORK column)
|
||||
|
||||
- **Visibility policy**: visible iff mode is `task` AND the
|
||||
`work_pane_visible` toggle is on (default on). Switching to chat hides
|
||||
it; switching back to task restores the toggle state. Ctrl+3 flips the
|
||||
toggle. The pane object always exists; visibility is display-only.
|
||||
- **Content**: the runtime objects of the **active channel only**
|
||||
(channel tag defaults to `task`; chat objects never bleed into the
|
||||
task view). Sections in fixed order — OUTCOMES ◆, VERIFICATION ✓,
|
||||
OBSERVATIONS ◌, MEMORY ☰, TOOL CALLS ⚙ — each newest-first; empty
|
||||
sections are omitted; fully empty renders `(no runtime objects)` dim.
|
||||
- **Classification** (by key presence): `status`+`outcome_id` →
|
||||
outcome; `method`+`verification_id` → verification; `observation_id`
|
||||
→ observation; `query_id` → memory query; anything else → tool call.
|
||||
One-line formats: outcome → its status; verification → `pass|fail
|
||||
<method>: <evidence>`; observation → `<kind>: <summary>`; memory query
|
||||
→ `<tokens> tok (<n> blocks, dropped: <names|none>)`; tool call →
|
||||
`<name> <kind with _ → space>`. An optional `detail` string renders
|
||||
indented on the next line. Each line is prefixed by its stable object
|
||||
id.
|
||||
- **Upsert + bound**: re-adding a known id updates in place (returns
|
||||
"updated"); new ids append. Beyond **120 entries** the oldest entry
|
||||
evicts (insertion-ordered — the pane is an index, not a log).
|
||||
- Tool events appear **twice by design** (two-view principle): the
|
||||
interactive ToolCallWidget in the chat log (narrative record) and the
|
||||
one-line mirror in the pane (operational index). Approval-resume
|
||||
results also feed the pane. Pane failures never disturb the chat turn.
|
||||
|
||||
### 2.6 Planning mode (plan ledger)
|
||||
|
||||
- Triggered by `/plan [topic]` or automatically in chat per §2.3. Sets
|
||||
`planning_active=true`, mode=`plan`, status refreshes.
|
||||
- Creates ledger entry `PLAN-<n>` (1-based, monotonic within the
|
||||
session): `{id, topic (200 chars), started, channel, status=planning,
|
||||
questions, research_tasks, task_list}` and renders three system lines:
|
||||
`📋 Planning mode: PLAN-<n>`, `Topic: <first 100 chars>`, and the
|
||||
standing invitation (`I'll poke holes in this design… Say 'start' when
|
||||
you want to execute.`).
|
||||
- `/start` (the command, or plan completion path) marks the plan `ready`
|
||||
with timestamp, lists its tasks, and says `Say 'start' to execute, or
|
||||
continue refining.` Executing flips the plan to `executing`, clears
|
||||
`planning_active`, sets mode=`task`, echoes the task list into the
|
||||
task channel under `🎯 Executing plan: PLAN-<n>`, and notes the move in
|
||||
chat. With no `ready` plan, `/start`-equivalent reports `No plan ready
|
||||
to execute.`
|
||||
|
||||
### 2.7 Message bubbles (exact render rules)
|
||||
|
||||
Role → rendering (content is always literal text — markup in user content
|
||||
is NOT interpreted for headers; the header label is styled, the body is
|
||||
plain):
|
||||
|
||||
| Role | Label / style | Alignment | Notes |
|
||||
|---|---|---|---|
|
||||
| `user` | `You` bold cyan + dim ` model | N tok | Nms` suffix when metadata present | right | metadata also becomes hover tooltip |
|
||||
| `assistant` | `Rune` bold green + same dim suffix | left | tokens ≈ `len/4` |
|
||||
| `system` | no label; whole body dim italic | center | startup, receipts, reports |
|
||||
| `thinking` | no label; dim | left (collapsible bubble) | collapsed by default |
|
||||
| `confirm` | `⚠ Confirm` bold | center | e.g. audit follow-up |
|
||||
| `research` | `Research` bold blue | left | |
|
||||
| `error` | `Error` bold red | center | |
|
||||
| `tool` | `Tool` bold yellow | left | |
|
||||
|
||||
- Bubbles are width-capped (chat ~70%, thinking ~75%, tool 90%) with
|
||||
rounded borders; user/assistant get distinct border colors.
|
||||
- Right-click on any bubble opens a copy menu (modal) with the full
|
||||
content. Thinking bubbles expose their first 200 chars as tooltip.
|
||||
- Assistant/tool metadata tooltip: `Model: … | Tokens: … | Latency: …ms`
|
||||
(only present parts).
|
||||
|
||||
### 2.8 ToolCallWidget (approval-gated tool calls)
|
||||
|
||||
- One widget per `(channel, call_id)`; later events for the same id
|
||||
**update in place** (`apply`), never mount a second widget.
|
||||
- Summary line: `<glyph> <tool name> <kind with _→space>` where glyph
|
||||
is `?` pending_approval, `✓` completed, `✗` error/failed, `·`
|
||||
otherwise. Details below in dim: `arguments: <sorted-keys JSON>` then
|
||||
`result: <sorted-keys JSON>` when present.
|
||||
- `pending_approval` uniquely renders **Approve** (success-green) and
|
||||
**Reject** (error-red) buttons. Clicking either immediately disables
|
||||
all buttons on that widget (no double-vote) and posts the decision;
|
||||
the app resolves via `resolve_pending_approval(bool)`, then applies
|
||||
the returned events to the same widget (buttons disappear for
|
||||
non-pending kinds), mirrors runtime objects to the WORK pane, and
|
||||
appends the follow-up response or an `error`-role bubble on failure.
|
||||
Approval is **click-only** — there is no keyboard path and no
|
||||
auto-resume; the turn stays paused until the user clicks.
|
||||
|
||||
### 2.9 Startup sequence (observable order)
|
||||
|
||||
1. Channels mount (history + 8-message ledger projection each), welcome
|
||||
lines, then in parallel: persona compile, registry scan, task+pin
|
||||
load — each reporting exactly one dim line (`✓ Persona compiled` /
|
||||
`⚠ Persona compile error: …`, same for registry; tasks as `<n>
|
||||
active tasks:` + up to 5 `[status] summary(60)` lines or `No active
|
||||
tasks.`, plus `📌 <n> pinned items` which also sets the status-bar
|
||||
pin count). Chat input takes focus.
|
||||
2. `_startup_check` lines in fixed order: `◆ <n> modules loaded`
|
||||
(+ `⚠ <n> modules failed: <first 5>` if any), `◆ Ollama connected (<n>
|
||||
models: <first 3>)` or `⚠ Ollama not connected`, `◆ <n> cloud
|
||||
providers configured` (only if any), `Rune v<ver> | Textual TUI | <n>
|
||||
native modules`.
|
||||
3. Model profile + authority (chat): `◆ Model: <name> (<context>)` and
|
||||
`◆ Authority: <🔓 full authority (uncensored) | 🛡 tier N override
|
||||
active | ✓ standard> | Patterns: <n> | Requests: <n>`; any failure →
|
||||
`⚠ Model profile load failed: …`.
|
||||
4. Loop activations: `◆ Cognitive loop active…`, `◆ Dream cycle
|
||||
active…`, `◆ Autonomy loop active…` (each with its own `⚠ … failed:`
|
||||
variant). A fire-and-forget background POST preloads the current
|
||||
model into VRAM (`keep_alive: -1`); its outcome is never shown.
|
||||
5. Conditional hints (only when true): `🌙 Night mode available
|
||||
(/night)` in evening/night hours; `📋 <n> pending work batches
|
||||
(/work)`; `🔨 <n> builds available (/build)`.
|
||||
6. Idle cognition every **240 s** on a background thread: cognitive
|
||||
`think()` each tick (only non-error action types announced as
|
||||
`◆ think: <types>`), full dream consolidation every 3rd tick
|
||||
(`◆ dream: consolidation cycle complete`). UI is touched only via
|
||||
thread-safe post-back of those dim lines; failures are silent.
|
||||
|
||||
### 2.10 Status bar (exact composition)
|
||||
|
||||
` │ `-joined, left to right, omitting absent parts:
|
||||
|
||||
1. Focused channel (`💬 chat` / `🎯 task`).
|
||||
2. Model summary: `<family> [<size>B] [🔓] <💻|☁ provider>` — family is
|
||||
the first match of a fixed list (qwen3.6 … davidau→qwen3.6, qwen,
|
||||
llama, mistral, gemma, phi, deepseek), size from `(\d+\.?\d*)[bB]`,
|
||||
`🔓` when `uncensored`/`davidau` appears, `☁ <prefix>` when the name
|
||||
contains `/` (except `hf.co/`), else `💻`. Full model id + profile
|
||||
tips live in the hover tooltip.
|
||||
3. `+<n-1> providers` only when >1 provider connected.
|
||||
4. `🧠 <recall_count>` for 3 s after a turn with recall.
|
||||
5. `<tokens> tok` (session cumulative, only when >0), `<latency>ms`
|
||||
(only when >0), ` ⏳` while a turn is in flight.
|
||||
|
||||
### 2.11 Keybindings (complete table)
|
||||
|
||||
| Key | Action | Contract |
|
||||
|---|---|---|
|
||||
| `Ctrl+1` | focus task | mode=task, focus task input, status refresh, WORK re-evaluated (shows) |
|
||||
| `Ctrl+2` | focus chat | mode=chat, focus chat input, status refresh, WORK re-evaluated (hides) |
|
||||
| `Ctrl+3` | toggle WORK pane | flips the persisted toggle, re-applies policy (§2.5) |
|
||||
| `Ctrl+Left` | shrink chat | task_width +5, clamp 80 |
|
||||
| `Ctrl+Right` | grow chat | task_width −5, clamp 20 |
|
||||
| `Ctrl+P` | command palette | opens 60×20 overlay w/ cleared search focused |
|
||||
| `Ctrl+C` | clear / quit | non-empty input → clear it (and arm quit timer); empty input → quit only if a previous Ctrl+C landed <500 ms ago, else arm the timer |
|
||||
| `Ctrl+X` | toggle thinking | only inside a ThinkingBubble: expand/collapse |
|
||||
| `Up/Down` | history / navigate | in channel inputs: history walk (§2.3); in palette/selector lists: move selection |
|
||||
| `Enter` | submit / select | in inputs: submit; in palette: run highlighted command; in model selector: expand category or select model |
|
||||
| `Esc` | cancel popup | closes palette/selector, refocuses active channel input |
|
||||
| `Click` | focus / copy / buttons | channel click focuses its input; refresh glyph restarts; bubble right-click copies; Approve/Reject resolve tools |
|
||||
|
||||
Notes: there is no `Ctrl+D`, no vim mode, no multiline-submit key —
|
||||
`Enter` always submits (no newline escape exists). The stale
|
||||
`action_toggle_mode` (task↔chat flip) has **no binding** — dead surface,
|
||||
do not resurrect it as a key.
|
||||
|
||||
### 2.12 Command palette & model selector (overlay contracts)
|
||||
|
||||
- **Palette** (`Ctrl+P` or `/` autocomplete): header
|
||||
`⌘ Command Palette (type to search, Enter to select, Esc to cancel)`,
|
||||
search input, results list. Typing filters by substring over command
|
||||
AND description, selection resets to top on each keystroke. Up/down
|
||||
moves the `▸` marker (clamped; at the edges the list scrolls instead
|
||||
of wrapping). Enter echoes `Command: <cmd>` as a system message in the
|
||||
active channel and dispatches it; Esc closes. Also exposed through the
|
||||
framework command system (same command set).
|
||||
- **Model selector** (`/model` bare or `/model list`): header
|
||||
`Model Selector (↑↓ navigate, Enter expand/select, Esc cancel)`,
|
||||
search input (matches display name, name, or tier), results grouped
|
||||
under exactly two collapsible categories — `💻 Local Models (n)` and
|
||||
`☁ Cloud Providers (n)` — of which **exactly one is expanded at a
|
||||
time** (default: local). Toggling a category resets selection to 0
|
||||
and rebuilds. Rows render `<▸| > <☑|☐> <display> [<tier>] <size>GB[ ◆
|
||||
if MoE]` for local, `<▸| > <☑|☐> <display> (<provider>)` for cloud;
|
||||
`☑` marks the current model, `▸` the cursor. Enter on a model selects
|
||||
it (mode-channel receipt + authority line per §2.9.3, persists to
|
||||
`~/.rune/model_state.json` with timestamp); Enter with no visible
|
||||
models expands the other category. Esc closes. Rows carry profile
|
||||
tooltips (family/size/strengths/prompting tips) when available.
|
||||
|
||||
### 2.13 Slash-command surface (dispatch contract)
|
||||
|
||||
- Parsing: first whitespace-separated token, lowercased, is the command;
|
||||
the rest is the raw arg string. Unknown commands → `Unknown command:
|
||||
<cmd>. Type /help for options.` in the active channel. Aliases:
|
||||
`/models`→`/model`, `/abs`→`/abstraction`.
|
||||
- Every handler renders into the **active channel** (`_ch()`: task
|
||||
channel when mode is task, else chat) — except queue-execution and
|
||||
plan-execution echoes, which target the task channel explicitly.
|
||||
- Every handler degrades, never crashes: module failure renders a dim
|
||||
`Error …: <e>` / `⚠ …` line in-channel. This is load-bearing (see
|
||||
§5.1).
|
||||
- Behaviorally distinct commands (all others are read-report renderers
|
||||
of a module's status object — preserve their exact line formats from
|
||||
the `_cmd_*` handlers when rebuilding help/report output):
|
||||
- `/project <name>` — sets the subject header (free text, may be
|
||||
empty); status refreshes; receipt `Project set to: <arg>`.
|
||||
- `/mode <task|chat|build|research|plan>` — switches mode + status;
|
||||
receipt `Mode set to: <arg>` (else the usage line). Switching to
|
||||
`task` auto-executes approved queue messages into the task channel
|
||||
(receipt pair `📋 Executing <n>…` / `✅ Executed <n>…`); switching to
|
||||
`build` reports resumable builds or the `/build start` hint.
|
||||
- `/model [list|auto|local|cloud|<name>]` — bare/`list` opens the
|
||||
selector; `auto` stores the literal `auto` (resolved per-turn);
|
||||
anything else stores the literal string verbatim (including
|
||||
`cloud`); receipt `Model set to: <arg>`; persists immediately.
|
||||
- `/theme <name>|list` — allowlist of 10 fixed themes (nord, dracula,
|
||||
monokai, solarized-light/dark, catppuccin-mocha/latte, tokyo-night,
|
||||
flexoki, gruvbox); default `dracula`; bare arg reprints current +
|
||||
list + usage.
|
||||
- `/goal`, `/cron`, `/night`, `/build`, `/work`, `/queue`, `/connect`,
|
||||
`/disconnect`, `/cycle`, `/pull`, `/testing`, `/audit` — stateful
|
||||
subcommand trees; preserve each subcommand's usage string and
|
||||
receipt wording exactly (they are user-visible API).
|
||||
- `/build start <desc>` is **fail-closed**: classification via the
|
||||
canonical construction interface first (`NOT_CONSTRUCTION`/`UNKNOWN`
|
||||
refuses with probe questions; classifier unavailable refuses);
|
||||
then a canonical construction contract is minted and transitioned
|
||||
`approved → building` — mint failure refuses the build. The build
|
||||
manager runs only under a governing contract. `/build resume|abort|
|
||||
ledger` report `Build <id> not found` for unknown ids
|
||||
(resume of a complete build says it cannot be resumed).
|
||||
- `/pull <model>` mounts a live progress bar + label in the active
|
||||
channel log (`📥 <model>: <done>/<total> MB (<pct>%)`, status-only
|
||||
lines when no totals), runs `ollama pull` on a worker thread,
|
||||
reports success/failure dim, refreshes the model profile on success,
|
||||
and names `ollama command not found` explicitly.
|
||||
- `/help` prints the fixed command summary (canonical list — keep it
|
||||
in sync with the dispatch table when adding commands).
|
||||
- `/health` — subsystem failure ledger (`failures/tier/last_error`
|
||||
per subsystem, or `all subsystems healthy`) + event-journal count.
|
||||
- `/rune` — identity/role/current-task/active-model + task counts via
|
||||
the operator transport.
|
||||
|
||||
### 2.14 Edge cases (observed, must survive)
|
||||
|
||||
- Empty submit (whitespace-only) is a silent no-op.
|
||||
- `/` alone in chat triggers history recording + command dispatch of the
|
||||
bare `/` → unknown-command receipt (it is NOT swallowed).
|
||||
- Submitting while a turn is in flight queues behind the exclusive
|
||||
worker — no cancellation, no interleave, no spinner stacking contract
|
||||
(second spinner mounts only when its turn starts).
|
||||
- Backend-import failure at startup (`BACKEND_AVAILABLE=false`) renders
|
||||
`⚠ Backend not available. Some features disabled.` and every
|
||||
backend-touching path degrades to empty/disabled rather than raising.
|
||||
- Model selector with zero models: Enter toggles the other category
|
||||
rather than selecting.
|
||||
- `selected_index` is always clamped after rebuild; an empty filtered
|
||||
list yields no cursor and no crash.
|
||||
- Timestamps: channel display list uses local `HH:MM` at render;
|
||||
ledger-projected messages use `ts[11:16]`; persisted model state uses
|
||||
UTC ISO.
|
||||
- `/queue execute` with nothing approved → `No approved messages to
|
||||
execute.` (not silence, not an error).
|
||||
- `/plan` with no arg plans topic `"New plan"`.
|
||||
|
||||
---
|
||||
|
||||
## 3. INTERACTION MAP
|
||||
|
||||
### 3.1 Events in (user → TUI)
|
||||
|
||||
| Event | Effect |
|
||||
|---|---|
|
||||
| Text + Enter in either input | History record → persist → route (§2.3): command / queue / hint / idea / planning / model turn |
|
||||
| Click in channel | Focus that input; update focus indicator + status |
|
||||
| Click `🔄` | **Exit process + spawn `rune`** (restart-to-reload — see §5.3) |
|
||||
| Ctrl+1 / Ctrl+2 / Ctrl+3 / Ctrl+Left / Ctrl+Right / Ctrl+P / Ctrl+C | §2.11 |
|
||||
| Palette search/select, selector search/navigate/select | §2.12 |
|
||||
| Thinking-bubble click / Ctrl+X | Expand/collapse (§2.7) |
|
||||
| Bubble right-click | Copy menu modal with full content |
|
||||
| Approve / Reject click | Decision → operator resolve → widget update + pane mirror + follow-up (§2.8) |
|
||||
| Idle 240 s tick | Background think/dream; dim lines at most (§2.9.6) |
|
||||
|
||||
### 3.2 Effects out (TUI → world)
|
||||
|
||||
- `tui_transport.get_transport().send_task(text, model, channel)` — the
|
||||
single model-turn call; returns the envelope consumed in §2.4.
|
||||
- `resolve_pending_approval(bool)` — tool-approval resolution.
|
||||
- `get_conversation_window(channel, n=8)` — mount-time history
|
||||
projection.
|
||||
- `get_status()` — `/rune` report.
|
||||
- Disk writes: `~/.rune/input_history.json` (per-channel input
|
||||
history), `~/.rune/model_state.json` (`{model, timestamp}`),
|
||||
`~/.rune/teachable_moments.json` (capped error journal), message-graph
|
||||
appends, queue/goal/cron/build/work stores via their modules.
|
||||
- Process: `ollama pull` subprocess (`/pull`); `rune` re-exec on `🔄`;
|
||||
background VRAM-preload POST to the Ollama `/api/generate`
|
||||
(`keep_alive: -1`).
|
||||
- Module reads for every `/command` (status/report/query calls only —
|
||||
the TUI never mutates domain state except through queue approve/
|
||||
execute, goal set/achieve/clear, cron add/remove/run, provider
|
||||
connect/disconnect, build start/abort, work model/clear, dream
|
||||
consolidate).
|
||||
|
||||
### 3.3 Hooks into other modules (what it touches)
|
||||
|
||||
- **Operator seam**: `tui_transport` (inprocess default, `v1` via
|
||||
`RUNE_TUI_TRANSPORT`), `rune_operator` (canonical task/conversation
|
||||
state for the task loader + `ConstructionInterface` for `/build
|
||||
start`), `message_graph` (durable transcript), `message_queue`
|
||||
(chat→task queue), `construction_interface` + `build_mode` (build
|
||||
pipeline), `model_routing` (cloud-candidate ranking).
|
||||
- **Model/provider stack**: `model_connect` (list/route/MoE check,
|
||||
Ollama URL + `/api/tags` health), `provider_registry` (list/connect/
|
||||
cycle/disconnect), `model_profiles` + `safety_override` (profile +
|
||||
authority lines), `provider_budget` (post-response signal),
|
||||
`token_optimizer` (`/tokens`).
|
||||
- **Memory/cognition**: `memory_store`, `graph_memory`, `warm_neuron_
|
||||
store`, `context_pressure`, `abstraction`, `claim_audit`,
|
||||
`growth_delta`, `self_improve`, `sleep_time` (+ dream/cognitive/
|
||||
autonomy loops), `recall_router`/`voicebox` (startup check only).
|
||||
- **Infrastructure**: `event_bus`, `microkernel`, `hook_engine`,
|
||||
`persona_gate`, `persona_definition`, `prompt_sync`, `system_registry`,
|
||||
`subsystem_health` (failure ledger + logging), `time_awareness`,
|
||||
`night_mode`, `cron_engine`, `goal`, `pins`, `task_persist` (legacy
|
||||
fallback for the task loader only), `cache`, `witness`,
|
||||
`capability_broker`, `warm_router`, `parallel_processor` (startup fan-
|
||||
out), `memory_env` (paths), `drift_ledger`, `behavior`, `auto_context`,
|
||||
`auto_tasks`, `identity_anchors`, `voice_*`, `work_manager`,
|
||||
`testing_mode` + `behaviour_ledger` + `project_registry` (`/testing`),
|
||||
`chat_bubble`/`message_widgets` (rendered widgets).
|
||||
|
||||
---
|
||||
|
||||
## 4. RECONSTRUCTION NOTES (Textual-specific dies / Rune-logic survives)
|
||||
|
||||
**Dies (Textual framework mechanics — reimplement natively in Bubble
|
||||
Tea):** the `App`/`compose`/`query_one` widget tree; reactive fields
|
||||
(`mode`, `model`, `task_width`, …) — become Bubble Tea model state;
|
||||
CSS (borders, focus colors, popup sizes, bubble widths) — re-express as
|
||||
Lip Gloss styles with the same observable values (§2.1, §2.7, §2.12);
|
||||
`run_worker`/`call_from_thread`/`set_interval`/`set_timer` — become
|
||||
Bubble Tea commands/subscriptions; `Binding` table — becomes key-msg
|
||||
handling with the §2.11 semantics; `RichLog`/`VerticalScroll` auto-
|
||||
scroll widgets — become viewport models honoring the always-scroll-to-
|
||||
bottom rule; `AutoComplete` dropdown and `Provider` command-palette
|
||||
integration — become a filtered-list model with the §2.12 fallback
|
||||
(show-all-on-no-match); `ProgressBar` — becomes a progress model fed by
|
||||
JSON-line parsing; tooltips modals (`CopyMenu`, hover tips) — become
|
||||
native overlays/help views; `scroll_visible(animate=False)` and
|
||||
`focus-within` styling — become explicit cursor/focus state.
|
||||
|
||||
**Survives (Rune logic — must be preserved verbatim in behavior):**
|
||||
channel duality + chat-left/task-right order + per-channel history (200,
|
||||
dedup, stash/restore) and persistence format; mode state machine
|
||||
(submit-location-wins, `/mode` switching, planning_active latch, plan
|
||||
ledger `PLAN-<n>` lifecycle); the full §2.3 routing precedence
|
||||
(command > queue-intercept (chat) > build-hint (task) > idea-capture
|
||||
(task) > planning-trigger (chat) > send); exclusive single-flight model
|
||||
turns; the §2.4 response-consumption order (degraded lines → recall
|
||||
badge → thinking bubbles → tool widgets + pane mirrors → assistant
|
||||
bubble → task-only triggers/queue → single collapsed signals line);
|
||||
WORK pane policy/classification/one-line formats/120-entry bound;
|
||||
approval-gating that is click-explicit with button-disabling and
|
||||
in-place `apply`; fail-closed `/build start`; degrade-not-crash on every
|
||||
handler; the transport seam (`send_task` / `resolve_pending_approval` /
|
||||
`get_conversation_window` / `get_status`) — the native TUI must NOT grow
|
||||
its own model client; startup line ordering and conditional hints; the
|
||||
240 s idle-cognition cadence with thread-safe UI post-back; all usage/
|
||||
receipt/error strings named in §2.13–§2.14 (they are user-visible API).
|
||||
|
||||
---
|
||||
|
||||
## 5. KNOWN QUIRKS (accidental-looking, load-bearing)
|
||||
|
||||
1. **The parallel startup block lives inside `on_static_clicked`.**
|
||||
Persona compile, registry scan, and task/pin loading execute inside
|
||||
the refresh-button click handler, not in `on_mount` — they run on
|
||||
every `🔄`-area click event dispatch that reaches past the id check
|
||||
(and, by the same placement, are the reason startup reporting shares
|
||||
a code path with the restart button). A reconstruction that
|
||||
"cleans" this into a tidy `Init()` must still emit the same receipt
|
||||
lines exactly once at startup and keep `🔄` = exit + re-exec.
|
||||
2. **`cloud` resolves to an English sentence.** With no live cloud
|
||||
adapter, `_resolve_model_name("cloud")` returns the literal string
|
||||
`cloud-unavailable-fallback: no verified cloud candidate (router
|
||||
ranked none; local remains authoritative)` AS the model name, passed
|
||||
all the way into `send_task`. Do not "fix" this into a silent local
|
||||
fallback — downstream logging and the status bar depend on the
|
||||
explicit marker.
|
||||
3. **`ResearchStatusLabel` is dead code.** Defined at module bottom
|
||||
(reads `research/issue-*/task_tree.json` on a 30 s timer) but never
|
||||
mounted or referenced. Do not reconstruct it.
|
||||
4. **`action_toggle_mode` is unbound.** The task↔chat flip exists but has
|
||||
no key; Ctrl+3 is the WORK-pane toggle (a previous Ctrl+3 mode-toggle
|
||||
was deliberately retired as redundant). Do not rebind it.
|
||||
5. **`_run_warm_router` is a documented no-op.** The ~390-line
|
||||
per-message pipeline was removed; the method remains as a comment
|
||||
pointing at the ContextBroker (`#19`), on-demand `/commands`, and
|
||||
the meaningful-event trigger owner (`#48`). The idle tick's
|
||||
`task.failed` seed is the first endogenous trigger — preserve that
|
||||
pointer, not the method.
|
||||
6. **Every handler swallows everything.** The blanket
|
||||
`except Exception → dim line` pattern (plus `record_failure` in the
|
||||
worker path) is the reliability contract, not laziness: a Bubble Tea
|
||||
port must route ALL backend errors to in-channel dim lines and never
|
||||
let a failed fetch kill the session.
|
||||
7. **`_cmd_sleep` ignores its backend.** It prints a static
|
||||
module/function list, not live status. Reproduce the static text;
|
||||
do not "improve" it into a real query (that changes the contract).
|
||||
8. **Audit uses role `confirm`.** `_cmd_audit`'s work-manager follow-up
|
||||
renders with role `confirm` (`⚠ Confirm`, centered) — the only
|
||||
producer of that role. Keep the role; it is how the follow-up is
|
||||
visually distinguished.
|
||||
9. **Task-loader authority order.** The startup task list reads the
|
||||
canonical `TaskStateRepository` (`created`/`running`) first and uses
|
||||
the `task_persist` export ledger only as a degraded fallback (with a
|
||||
stdout notice). The two sources use different status vocabularies
|
||||
(`created|running` vs `open|in_progress`) — preserve both filters on
|
||||
their respective paths.
|
||||
10. **Version label reads at render time.** The footer semver is fetched
|
||||
from the canonical version on every render, not cached at startup —
|
||||
a restarted backend under a live TUI shows the new version. Keep it
|
||||
dynamic.
|
||||
@@ -0,0 +1,111 @@
|
||||
# tui_backlog — behavior contract (TUI design capture)
|
||||
|
||||
Oracle: `src/rune/tui_backlog.py` (125 lines). SCAFFOLD-TRANCHE-007 §22–25.
|
||||
Pure ledger over a `structural_store` doc (`{"entities": [...]}`); the only
|
||||
impurity is the `now()` timestamp (`structural_store.now()`,
|
||||
`%Y-%m-%dT%H:%M:%SZ` UTC).
|
||||
|
||||
## 1. PURPOSE
|
||||
|
||||
Durable TUI Upgrade Backlog: every meaningful backend feature is checked
|
||||
for whether it needs a TUI/user-surface projection or operator control, and
|
||||
the decision is recorded so the user-facing system never falls behind the
|
||||
backend. Standing architectural requirement, not a one-off list.
|
||||
|
||||
## 2. BEHAVIOR CONTRACTS
|
||||
|
||||
### 2.1 Authority (§25) — the load-bearing rule
|
||||
|
||||
The backlog is a maintenance/product-surface LEDGER (outstanding work
|
||||
only). It is NOT TUI structural authority: implemented TUI structure stays
|
||||
under Layout Contracts / the project's structural authority. A Bubble Tea
|
||||
reconstruction must NOT treat backlog rows as layout truth.
|
||||
|
||||
### 2.2 Projection types (§24)
|
||||
|
||||
`PROJECTIONS = {"NONE","INSPECTION","CONTROL","BOTH","UNKNOWN"}`.
|
||||
`STATUSES = {"OPEN","PLANNED","IN_PROGRESS","DONE","WONT_DO"}`.
|
||||
Defaults: `projection="INSPECTION"`, `status="OPEN"`,
|
||||
`priority="normal"` (free-form string, never validated),
|
||||
`authority_source="product-surface requirement"`.
|
||||
|
||||
### 2.3 `add_backlog_item(doc, item_id, *, feature, desired_behavior, why, …) → dict`
|
||||
|
||||
| Rule | Behavior |
|
||||
|---|---|
|
||||
| `projection ∉ PROJECTIONS` | `raise ValueError("projection must be one of […]")` (sorted list in message) |
|
||||
| `status ∉ STATUSES` | `raise ValueError("status must be one of […]")` |
|
||||
| entity id | `f"tui-backlog:{item_id}"`, `kind="tui_backlog_item"` |
|
||||
| identities | `feature, desired_behavior, why, projection, dependencies (sorted, default []), priority, status, authority_source` — `feature`+`projection` retained permanently |
|
||||
| duplicate id | REFRESH IN PLACE via `add_entity` — identities merged over, `evidence_refs` unioned + sorted, `observed_at` bumped, maturity never downgraded. The ledger does not fork. |
|
||||
| `evidence_refs=None` | defaults to `[f"backlog-added:{authority_source}"]` |
|
||||
|
||||
### 2.4 `get_backlog_item(doc, item_id) → dict | None`
|
||||
|
||||
`entity(doc, f"tui-backlog:{item_id}")` — direct id hit, else alias
|
||||
resolution, else `None`.
|
||||
|
||||
### 2.5 `list_backlog(doc, *, status=None, projection=None) → [dict]`
|
||||
|
||||
Filters `kind == "tui_backlog_item"` only; `status`/`projection` filters are
|
||||
exact-match on `identities` (falsy filter = no filtering); result sorted by
|
||||
entity `id` ascending.
|
||||
|
||||
### 2.6 `update_backlog_status(doc, item_id, status, *, evidence_ref="") → dict`
|
||||
|
||||
| Rule | Behavior |
|
||||
|---|---|
|
||||
| unknown `item_id` | `raise ValueError(f"no backlog item: {item_id}")` |
|
||||
| `status ∉ STATUSES` | `raise ValueError("status must be one of […]")` |
|
||||
| on success | `identities["status"] = status` (no other field touched), `observed_at = now()`; non-empty `evidence_ref` is unioned into sorted `evidence_refs` |
|
||||
|
||||
### 2.7 `record_tui_projection_decision(doc, feature_id, *, projection, rationale, source_contract="") → dict`
|
||||
|
||||
§24 AUTO-CAPTURE SEAM: records a "TUI projection required?" decision for a
|
||||
backend feature — INCLUDING a truthful `NONE`. Accounting, not UI design;
|
||||
nothing is auto-designed. `projection ∉ PROJECTIONS` → `ValueError`; entity
|
||||
id `f"tui-projection:{feature_id}"`, `kind="tui_projection_decision"`;
|
||||
`rationale` TRUNCATED to 200 chars (`rationale[:200]`); `evidence_refs =
|
||||
[f"tui-projection-decision:{projection}"]` (always exactly one, derived
|
||||
from the verdict). Duplicate `feature_id` refreshes in place like §2.3.
|
||||
|
||||
## 3. INTERACTION MAP
|
||||
|
||||
```
|
||||
tui_backlog.py
|
||||
├── imports: rune.structural_store.{add_entity, entity, now}
|
||||
├── imported by: rune_tui.py (`.tui_backlog` relative import — first-party per validator)
|
||||
├── callers: construction-contract closure ("TUI projection required?" auto-capture seam)
|
||||
├── state: caller's doc dict ({"entities": [...]}) — module holds NO state
|
||||
└── writes: entities of kind tui_backlog_item / tui_projection_decision only
|
||||
```
|
||||
|
||||
## 4. RECONSTRUCTION NOTES
|
||||
|
||||
Rune-logic (MUST port exactly): id schemes (`tui-backlog:`,
|
||||
`tui-projection:`) and kinds; the `NONE`-is-truthful rule; in-place refresh
|
||||
(no forking); `rationale[:200]`; default evidence refs; sorted
|
||||
dependencies/evidence; id-sorted listing; the exact `ValueError` boundaries.
|
||||
`update_backlog_status` mutates the live entity in place (callers holding
|
||||
the old reference see the change) — preserve reference semantics or
|
||||
document the copy.
|
||||
|
||||
Framework-specific: nothing — no Textual, no I/O. The Bubble Tea port is a
|
||||
straight data-ledger port; the only clock is `observed_at` (freeze `now()`
|
||||
in tests like the shared harness does).
|
||||
|
||||
## 5. KNOWN QUIRKS
|
||||
|
||||
1. **`priority` is unvalidated.** Any string sticks; only projection/status
|
||||
are gated.
|
||||
2. **No status-transition guard.** Any status → any status (e.g.
|
||||
`DONE → OPEN`) is allowed; the state machine is convention, not code.
|
||||
3. **`update_backlog_status` with empty `evidence_ref` leaves evidence
|
||||
untouched** — no default evidence is added (unlike `add_backlog_item`).
|
||||
4. **Rationale truncation is silent.** 200+ char rationales lose their tail
|
||||
with no marker.
|
||||
5. **`list_backlog` sorts by entity id, not priority/recency** — no
|
||||
priority ordering exists.
|
||||
6. **Projection-decision evidence is single-valued and verdict-derived** —
|
||||
re-recording with a different projection unions the old evidence value
|
||||
(`add_entity` unions), so a flipped decision keeps both evidence refs.
|
||||
@@ -0,0 +1,112 @@
|
||||
# tui_transport — behavior contract (TUI design capture)
|
||||
|
||||
Oracle: `src/rune/tui_transport.py` (108 lines). Contract
|
||||
`BUILDER-2026-10-04T10:32:00Z-cli` (H4 slice 1, T4.5). Stdlib only
|
||||
(`json os urllib.error urllib.request typing`). Battery:
|
||||
`src/rune/canary_tui_transport.py` (four-method equivalence + dead-core
|
||||
honesty, against `core_server` + a stub operator).
|
||||
|
||||
## 1. PURPOSE
|
||||
|
||||
The TUI transport seam: the operator surface is exactly four public calls,
|
||||
runnable IN-PROCESS (today) or over the `/v1` wire (the migration path)
|
||||
behind one interface. Selected by `RUNE_TUI_TRANSPORT=inprocess|v1`
|
||||
(DEFAULT `inprocess` — zero behavior change on merge; the wave opts in
|
||||
surface by surface).
|
||||
|
||||
## 2. BEHAVIOR CONTRACTS
|
||||
|
||||
### 2.1 The four-call surface (both transports answer all four)
|
||||
|
||||
| Call | InProcess | V1 (wire) |
|
||||
|---|---|---|
|
||||
| `get_status() → dict` | `operator.get_status()` | `GET /v1/status` (body returned as-is) |
|
||||
| `send_task(description, model=None, channel="operator") → dict` | `operator.send_task(description, model, channel)` positionally | `POST /v1/chat/completions` with `{"messages":[{"role":"user","content":description}]}` (+`"model"` ONLY when `model is not None`); returns `out["rune"]` — the full operator result rides the rune block (H2 contract) |
|
||||
| `get_conversation_window(channel="operator", n=8) → [dict]` | `operator.conversation_window(channel, n=n)` (NOTE: different method name) | `GET /v1/conversation?channel={channel}&n={n}` (raw f-string interpolation, no URL-encoding); returns `out["messages"]` |
|
||||
| `resolve_pending_approval(approved: bool) → dict` | `operator.resolve_pending_approval(approved)` (value passed through uncoerced) | `POST /v1/approvals/resolve` with `{"approved": bool(approved)}` (COERCED to bool); returns body as-is |
|
||||
|
||||
### 2.2 `get_transport(mode=None)` — the seam entry point
|
||||
|
||||
Decision table:
|
||||
|
||||
| `mode` arg | `RUNE_TUI_TRANSPORT` | Result |
|
||||
|---|---|---|
|
||||
| `None` | unset | `InProcessTransport()` (default `inprocess`) |
|
||||
| `None` | `"inprocess"` | `InProcessTransport()` |
|
||||
| `None` | `"v1"` | `V1Transport()` (base from `RUNE_TUI_CORE_URL` or default) |
|
||||
| `"v1"` / `"inprocess"` | anything | arg WINS (env not consulted) |
|
||||
| anything else | anything | `raise ValueError("unknown RUNE_TUI_TRANSPORT mode: {mode!r} (expected inprocess\|v1)")` |
|
||||
|
||||
### 2.3 `V1Transport(base_url=None, timeout=130.0)`
|
||||
|
||||
`self._base = (base_url or os.environ.get("RUNE_TUI_CORE_URL",
|
||||
DEFAULT_CORE_URL)).rstrip("/")` — `DEFAULT_CORE_URL =
|
||||
"http://127.0.0.1:8377"` (the Go front door's default port).
|
||||
`_http(method, path, body=None)`: `body is not None` → JSON-encoded with
|
||||
`Content-Type: application/json`; `body=None` → bare GET with NO content
|
||||
header. Response JSON-decoded and returned. `HTTPError` → `RuntimeError(f"v1
|
||||
transport: {method} {path} -> {e.code}: {detail}")` (body detail truncated
|
||||
to 200 chars, chained `from e`). Any OTHER exception (connection refused,
|
||||
timeout, non-JSON body) propagates UNWRAPPED.
|
||||
|
||||
### 2.4 `InProcessTransport(operator=None)`
|
||||
|
||||
`operator=None` → lazy `import rune_operator; rune_operator.get_operator()`
|
||||
at CONSTRUCTION time (import is inside `__init__`, so the module imports
|
||||
cleanly without the operator present). Explicit operator (tests, canary
|
||||
stub) is used as-is.
|
||||
|
||||
### 2.5 Dead-core law (canary §5)
|
||||
|
||||
Dead core RAISES — never a silent empty answer. `V1Transport` against a
|
||||
dead port raises (connection error propagates); the canary asserts the
|
||||
raise, not the subtype.
|
||||
|
||||
## 3. INTERACTION MAP
|
||||
|
||||
```
|
||||
rune_tui.py --get_transport()--> get_transport()
|
||||
├── inprocess → InProcessTransport → rune_operator.get_operator()
|
||||
│ ├── get_status / send_task / conversation_window / resolve_pending_approval
|
||||
└── v1 → V1Transport → HTTP → Go front door :8377 (or core sidecar)
|
||||
├── GET /v1/status
|
||||
├── POST /v1/chat/completions → out["rune"]
|
||||
├── GET /v1/conversation?channel=&n= → out["messages"]
|
||||
└── POST /v1/approvals/resolve → body as-is
|
||||
canary_tui_transport.py: equivalence battery (stub operator + core_server on ephemeral port)
|
||||
```
|
||||
|
||||
## 4. RECONSTRUCTION NOTES
|
||||
|
||||
Rune-logic (MUST port exactly): the four-call surface with EXACT defaults
|
||||
(`channel="operator"`, `n=8`, `model=None`, `timeout=130.0`); `model`
|
||||
omitted-from-body vs `approved` bool-coerced asymmetry; `out["rune"]` /
|
||||
`out["messages"]` envelope unwraps (KeyError on malformed core replies is
|
||||
contract behavior, not a bug to smooth over); `get_transport` decision
|
||||
table including the `ValueError` text; `rstrip("/")` on base; 200-char HTTP
|
||||
detail truncation; dead-core-raises.
|
||||
|
||||
Framework-specific: `urllib` → Go `net/http`; env-var selection stays env
|
||||
vars in Go (`RUNE_TUI_TRANSPORT`, `RUNE_TUI_CORE_URL`); the lazy
|
||||
`rune_operator` import becomes a constructor-injected interface (the Go
|
||||
port should keep the "explicit operator bypasses global lookup" seam —
|
||||
the canary depends on it). The Bubble Tea side owns ONLY this seam's
|
||||
client; the `/v1` server surface belongs to the core side.
|
||||
|
||||
## 5. KNOWN QUIRKS
|
||||
|
||||
1. **Asymmetric None-handling.** `model=None` OMITS the key; `approved`
|
||||
is always coerced with `bool()`. InProcess passes `approved` through
|
||||
uncoerced — the two transports differ on truthy non-bools.
|
||||
2. **Unencoded query string.** `channel`/`n` are f-string-interpolated;
|
||||
a channel with `&`/`?` breaks the URL. Port must reproduce or
|
||||
deliberately fix (fix = divergence from frozen behavior).
|
||||
3. **Method-name mismatch.** `get_conversation_window` calls
|
||||
`operator.conversation_window` — the operator method is NOT named after
|
||||
the transport method.
|
||||
4. **Only HTTPError is wrapped.** Connection/timeout/JSON errors propagate
|
||||
raw; callers cannot catch a single transport-error type.
|
||||
5. **`timeout=130.0`s default** is tuned for slow model calls — do not
|
||||
"sensibly" lower it in the port.
|
||||
6. **`get_transport` truthiness trap.** `mode=""` falls through to the env
|
||||
var (falsy arg = unset); only non-empty strings override.
|
||||
@@ -0,0 +1,240 @@
|
||||
# tui_validator — behavior contract (TUI design capture)
|
||||
|
||||
Oracle: `src/rune/tui_validator.py` (514 lines). Deterministic, pure except
|
||||
two filesystem reads and one env-dependent directory listing.
|
||||
Frozen parity surface: `docs/tui-design-capture/frozen_tui_validator_truth.json`
|
||||
(78 rows, via `scripts/gen_tui_validator_truth.py`; verify with
|
||||
`python3 scripts/gen_tui_validator_truth.py --verify`).
|
||||
CLI: `tui_validator.py [check|css|test]` (exit 0 pass / 1 fail).
|
||||
|
||||
## 1. PURPOSE
|
||||
|
||||
Pre-flight checks for Textual CSS and widget composition. Catches invalid
|
||||
CSS properties, unknown selectors, and composition errors before they reach
|
||||
the user. Runs as part of the canary or standalone.
|
||||
|
||||
The Bubble Tea reconstruction needs this module's boundary only: a
|
||||
CSS-lint verdict function (property allowlist + suggestion), an
|
||||
import-classification verdict function, and a file-scanner harness.
|
||||
`validate_composition` is a stub returning `[]` — no Bubble Tea port needed.
|
||||
|
||||
## 2. BEHAVIOR CONTRACTS
|
||||
|
||||
### 2.1 `extract_css_properties(css_text) → [(prop, line_no, full_line)]`
|
||||
|
||||
Line-level decision table. Comments (`/* … */`, DOTALL) are stripped FIRST,
|
||||
so every line number below is post-strip (a comment spanning lines N lines
|
||||
shifts later lines up). Each remaining line is stripped, then:
|
||||
|
||||
| Input line (after strip) | Verdict | Reason |
|
||||
|---|---|---|
|
||||
| `""` (empty) | skip | |
|
||||
| starts with `/*`, `*`, `#`, `.`, `&`, `@`, `{`, `}` | skip | selector/comment/brace forms |
|
||||
| matches `^([a-z][a-z0-9-]*)\s*:` | EMIT `(name, line, stripped_line)` | |
|
||||
| anything else (`Width:`, `3d-depth:`, `-webkit-`, `wIdth:`) | skip | regex requires lowercase-first |
|
||||
| `color: red; border: solid` | EMIT `color` only | only the FIRST property per line |
|
||||
| `width: 1 /* trailing */` | EMIT `width` | comment stripped before matching |
|
||||
| `width: 1\r\nheight: 2` | both emitted | `split("\n")` leaves `\r`, caught by strip |
|
||||
| `background: url(data:image/png;base64,AAA)` | EMIT `background` | value colons ignored (match is prefix) |
|
||||
| `width:1` / `width : 1` | EMIT | `\s*:` tolerates missing/extra space |
|
||||
|
||||
Frozen rows: `".foo {\n width: 100%;\n}\n"` → `[("width", 2, "width: 100%;")]`;
|
||||
`"#id {\n* star\n&:hover {\n@media screen\n{\n}\n"` → `[]`;
|
||||
`"Width: 10\n"`, `"wIdth: 10\n"`, `"3d-depth: 1\n"`,
|
||||
`"-webkit-transform: none\n"` → `[]`.
|
||||
|
||||
### 2.2 `validate_css(css_text, source="unknown") → [error_str]`
|
||||
|
||||
For each extracted property NOT in `VALID_CSS_PROPERTIES`
|
||||
(85 names; see §2.3), emit:
|
||||
|
||||
```
|
||||
{source}:{line_no}: Invalid CSS property '{prop}'
|
||||
```
|
||||
|
||||
plus, when `_find_closest_property(prop)` returns non-None:
|
||||
|
||||
```
|
||||
. Did you mean '{suggestion}'?
|
||||
```
|
||||
|
||||
Validation decision table (input → verdict):
|
||||
|
||||
| CSS input | Verdict |
|
||||
|---|---|
|
||||
| all six props valid (`width height background border padding margin`) | `[]` |
|
||||
| `selection-background` + `selection-foreground` (Rich-isms) | 2 errors, NO suggestion (frozen: `selftest2`) |
|
||||
| `backgroud: red` | 1 error + `Did you mean 'background'?` (dist 1) |
|
||||
| `layerz: 1` | 1 error + `Did you mean 'layer'?` — TIE (`layer`/`layers` both dist 1); winner is set-iteration order, pinned by `PYTHONHASHSEED=0` (KNOWN QUIRK §5) |
|
||||
| `zzzzzz: 1` | 1 error, NO suggestion (all distances ≥ 3) |
|
||||
| `bor: 1` | 1 error, NO suggestion (best dist is exactly 3; threshold is strict `< 3`) |
|
||||
| `Width: 100%` | `[]` — never extracted, silently passes (KNOWN QUIRK §5) |
|
||||
| `width: nonsense-value` | `[]` — VALUES ARE NEVER CHECKED, only names |
|
||||
| `.foo:focus { border: solid; }` | `[]` — pseudo-classes are not validated at all |
|
||||
| `colr` on lines 2 AND 3 | 2 errors, one per line, each suggesting `color` |
|
||||
| `/*\ncolr: red\n*/\nwidth: 1` | `[]` — props inside comments are stripped |
|
||||
| `hatch keyline scrollbar-corner-color text-opacity` | `[]` — all in the allowlist |
|
||||
| `""` (empty) | `[]` |
|
||||
| source label | passed through verbatim: `source="mymod:L5"` → `"mymod:L5:2: …"` |
|
||||
|
||||
### 2.3 `VALID_CSS_PROPERTIES` — the 85-name allowlist (verbatim)
|
||||
|
||||
Layout & sizing (23): `align box-sizing content-align display dock height
|
||||
layer layers layout margin max-height max-width min-height min-width offset
|
||||
overflow padding position scrollbar-gutter scrollbar-size
|
||||
scrollbar-visibility width`.
|
||||
Colors & appearance (5): `background background-tint color opacity tint`.
|
||||
Border (13): `border border-left border-right border-top border-bottom
|
||||
border-subtitle-align border-subtitle-background border-subtitle-color
|
||||
border-subtitle-style border-title-align border-title-background
|
||||
border-title-color border-title-style`.
|
||||
Outline (5): `outline outline-left outline-right outline-top outline-bottom`.
|
||||
Text (5): `text-align text-opacity text-overflow text-style text-wrap`.
|
||||
Grid (6): `column-span grid-columns grid-gutter grid-rows grid-size row-span`.
|
||||
Hatch (1): `hatch`. Keyline (1): `keyline`.
|
||||
Links (6): `link-background link-background-hover link-color
|
||||
link-color-hover link-style link-style-hover`.
|
||||
Pointer (1): `pointer`.
|
||||
Scrollbar colors (7): `scrollbar-background scrollbar-background-active
|
||||
scrollbar-background-hover scrollbar-color scrollbar-color-active
|
||||
scrollbar-color-hover scrollbar-corner-color`.
|
||||
Visibility (1): `visibility`.
|
||||
(`VALID_PSEUDO_CLASSES` — 12 names — is DEAD DATA: nothing reads it.)
|
||||
|
||||
### 2.4 `_find_closest_property(prop) → name | None`
|
||||
|
||||
`best_dist = 3` initial; STRICT `<` comparison, so only edit-distance 0–2
|
||||
suggests. Iteration is over the SET, so ties resolve by hash order.
|
||||
Frozen: `backgroud→background`, `layerz→layer`, `zzzzzz→None`,
|
||||
`border→border` (exact, dist 0), `""→None`, `colr→color`.
|
||||
|
||||
### 2.5 `_edit_distance(a, b) → int`
|
||||
|
||||
Classic Levenshtein DP; shorter string is swapped to `b`; `len(b)==0`
|
||||
returns `len(a)`. Frozen spot values: `("", "")→0`, `("a","")→1`,
|
||||
`("","abc")→3`, `("kitten","sitting")→3`, `("flaw","lawn")→2`,
|
||||
`("intention","execution")→5`.
|
||||
|
||||
### 2.6 `_classify(module_name, src_dir) → verdict`
|
||||
|
||||
Decision order is FIXED — first match wins:
|
||||
|
||||
1. starts with `"."` → `relative` (even bare `"."`)
|
||||
2. dotted top-level in `sys.stdlib_module_names` → `stdlib`
|
||||
(`__future__` and `sys.path` both land here)
|
||||
3. top-level in `EXTERNAL_MODULES` (`textual`, `textual_autocomplete`,
|
||||
`rich`) → `external` (`textual.widgets` → `external` via top-level)
|
||||
4. top-level == `basename(src_dir)` (`rune`) or a `*.py` stem in `src_dir`
|
||||
(excluding `__init__.py`; package basename added iff `__init__.py`
|
||||
exists) → `first-party`
|
||||
5. `<memory_env.scripts_dir()>/<top>.py` exists → `scripts`
|
||||
(env-dependent: `RUNE_SCRIPTS_DIR`)
|
||||
6. else → `unknown`
|
||||
|
||||
Frozen: `.`→relative, `os`/`__future__`/`sys.path`→stdlib,
|
||||
`textual`/`textual.widgets`/`textual_autocomplete`/`rich`→external,
|
||||
`rune`/`rune.tui_transport`/`memory_env`→first-party,
|
||||
`bare_helper`→scripts, `mystery_mod`→unknown, **`os,`→unknown**
|
||||
(comma-capture quirk, §5).
|
||||
|
||||
### 2.7 `validate_module_css(module_path) → [error_str]`
|
||||
|
||||
Unreadable file → single-element degrade: `["Could not read {path}: {e}"]`
|
||||
(any exception, `noqa: BLE001` deliberate). Otherwise finds
|
||||
`(?:DEFAULT_CSS|CSS)\s*=\s*"""(.*?)"""` (DOTALL, non-greedy) and validates
|
||||
each literal with source `{basename}:L{def_line}`. Literal-harvest table:
|
||||
|
||||
| Fixture | Verdict |
|
||||
|---|---|
|
||||
| valid `DEFAULT_CSS` only | `[]` |
|
||||
| `DEFAULT_CSS` + `CSS` both bad | 2 errors (`backgroud→background`, `colr→color`), `L1`/`L6` |
|
||||
| `DEFAULT_CSS_ALT = """…zzz…"""` | IGNORED — name must be exactly `DEFAULT_CSS`/`CSS` |
|
||||
| `CSS = f"""…zzzz…"""` | IGNORED — `f` prefix breaks the literal match |
|
||||
| `SINGLE = '''…zzzzz…'''` | IGNORED — single-quote triples not matched |
|
||||
| docstring `"""…"""` before imports | IGNORED — not assigned to a CSS name |
|
||||
| missing file | `["Could not read …: [Errno 2] …"]` |
|
||||
|
||||
### 2.8 Tree-level scanners
|
||||
|
||||
`validate_all_modules(rune_dir)`: scans EXACTLY
|
||||
`src/rune/{rune_tui.py, chat_bubble.py, rune_spinner.py}` (hardcoded list),
|
||||
skipping absent files silently. Frozen: clean tree `[]`; dirty tree the 2
|
||||
`chat_bubble.py` errors; tree without `rune_tui.py` `[]`.
|
||||
|
||||
`validate_imports(rune_dir)`: reads `src/rune/rune_tui.py` ONLY
|
||||
(missing → `["Could not read rune_tui.py: …"]`); regex
|
||||
`^(?:from\s+(\S+)\s+import|import\s+(\S+))` MULTILINE — `import` must be the
|
||||
first token on the line. Verdicts `relative|stdlib|external|first-party|
|
||||
scripts` pass; `unknown` emits
|
||||
`rune_tui.py:{line}: Import '{mod}' — unclassified (unknown): not stdlib,
|
||||
external-allowlist, first-party ({src_dir} listing), or scripts/`.
|
||||
Frozen dirty tree: exactly 2 errors (`os,` line 2; `mystery_mod` line 4).
|
||||
|
||||
`validate_composition(rune_dir)`: STUB — always `[]` (frozen on 2 trees).
|
||||
|
||||
`run_full_validation(rune_dir=None)`: concatenation
|
||||
`all_modules + imports + composition`, in that order (frozen dirty tree: 4
|
||||
errors in that order; `rune_dir=None` derives the grandparent of
|
||||
`src/rune/`).
|
||||
|
||||
### 2.9 `self_test()` / CLI
|
||||
|
||||
`self_test()` runs 5 checks (valid CSS passes; Rich-ism pair → exactly 2
|
||||
errors; `backgroud→background` suggestion; every allowlist name validates;
|
||||
full validation runs without crash), prints `✅/❌` lines, returns
|
||||
`failed == 0`. CLI: `check` → full validation; `css` → module CSS only;
|
||||
`test` → self-test; no args → usage + exit 1; unknown command → exit 1.
|
||||
Exit 0 on pass, 1 on any error.
|
||||
|
||||
## 3. INTERACTION MAP
|
||||
|
||||
```
|
||||
tui_validator.py
|
||||
├── reads: src/rune/{rune_tui,chat_bubble,rune_spinner}.py (CSS literals)
|
||||
├── reads: src/rune/rune_tui.py (import lines)
|
||||
├── reads: memory_env.scripts_dir() listing (scripts verdict)
|
||||
├── reads: sys.stdlib_module_names (stdlib verdict)
|
||||
├── reads: os.listdir(src_dir) (first-party verdict)
|
||||
├── callers: canary harness (check/css), tests/test_tui_validator_imports.py (#139 pins)
|
||||
└── writes: NOTHING (stdout + exit codes only)
|
||||
```
|
||||
|
||||
## 4. RECONSTRUCTION NOTES
|
||||
|
||||
Rune-logic (MUST port exactly): the 85-name allowlist VERBATIM (any
|
||||
addition/removal changes verdicts); the line-skip prefix set; the
|
||||
lowercase-first regex (including its blind spots); strict `< 3` suggestion
|
||||
threshold; the six-way classification ORDER; the hardcoded 3-file scan list;
|
||||
the `DEFAULT_CSS|CSS` triple-double-quote harvest; error string formats.
|
||||
|
||||
Framework-specific (adapt freely): nothing Textual-rendered — this module
|
||||
never touches the TUI runtime. A Bubble Tea port reimplements the same
|
||||
string/regex logic in Go; only the suggestion-tie order needs a defined
|
||||
rule (Go map iteration is also random — SORT candidates or take
|
||||
lexicographically-first on ties; note the frozen `layerz→layer` row pins
|
||||
lexicographic order, which Go must reproduce).
|
||||
|
||||
## 5. KNOWN QUIRKS
|
||||
|
||||
1. **Suggestion ties are hash-ordered.** `_find_closest_property` iterates a
|
||||
set with strict `<`; equidistant candidates resolve by `PYTHONHASHSEED`.
|
||||
The truth pins `PYTHONHASHSEED=0` (`layerz→layer`).
|
||||
2. **Case-blind extractor.** `Width:`/`wIdth:` never match the regex → pass
|
||||
silently. A reconstruction that normalizes case would DIVERGE.
|
||||
3. **Comma capture.** `import os, sys` yields module name `os,` (regex takes
|
||||
`\S+`) → `unknown` → flagged. Frozen in truth.
|
||||
4. **Indented imports invisible.** The import regex is `^`-anchored; imports
|
||||
inside functions are never classified (frozen: `indented_invisible`
|
||||
produces no row).
|
||||
5. **Values never validated.** `width: nonsense` passes; pseudo-classes
|
||||
never checked; `VALID_PSEUDO_CLASSES` is dead data.
|
||||
6. **Only double-quote triples harvested.** `f"""`, `'''`, renamed
|
||||
variables (`DEFAULT_CSS_ALT`) all escape validation.
|
||||
7. **First-property-per-line.** `color: red; border: bogus` checks `color`
|
||||
only.
|
||||
8. **`validate_composition` is a stub.** Always `[]`; the "composition
|
||||
validation" CLI claim is aspirational.
|
||||
9. **Comment-strip shifts line numbers.** Multi-line comments move reported
|
||||
line numbers up relative to the source file.
|
||||
10. **Best-effort degrade.** Unreadable files yield `Could not read …`
|
||||
rows, never exceptions.
|
||||
@@ -48,6 +48,7 @@ henxels:
|
||||
- ./docs/v1/**
|
||||
- ./docs/wiki/**
|
||||
- ./docs/gitea_issue_reconciliation.json
|
||||
- ./docs/seams/**
|
||||
- ./docs/rebase/**
|
||||
- henxel: "The known giant stays visible \u2014 file-size debt is tracked, not silent"
|
||||
in: ./src/rune/*
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"default_area": "",
|
||||
"law": "one language verdict per tree area; enforcement is scripts/check_language_verdict.py (phase 0B task 0.10; I-R2-011, GAP-GR-003; runner member language_verdict_fixtures). A generator may propose areas but never certify its own manifest; the verifier never scans its own quarantine territory.",
|
||||
"notes": [
|
||||
"No generated/data exemption exists: every file inside a native area is scanned identically. Only governed exceptions (owner+expiry+reason) exempt, and they actually expire.",
|
||||
"Exact-token enforcement (NEG-003/NEG-004 lessons): violations are exact artifact extensions, exact shebangs, command-position invocations in orchestration files, and exact remote-artifact tokens — never bare substrings or prose mentions.",
|
||||
"Precedence: the most specific matching entry (area, quarantine, exception) wins; path '' is the whole-tree default. The default is python quarantine because the build/test toolchain itself (unified_check, CI bootstrap, scripts/, tests/) is Python during migration."
|
||||
],
|
||||
"areas": [
|
||||
{
|
||||
"path": "tui/",
|
||||
"language": "go",
|
||||
"policy": "native-only",
|
||||
"note": "Go territory — the Bubble Tea face; python regrowth here is RED"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/",
|
||||
"language": "rust",
|
||||
"policy": "native-only",
|
||||
"note": "Rust core crates; the dichotomy-era python parity twins remain as governed exceptions below until retirement"
|
||||
},
|
||||
{
|
||||
"path": "schemas/",
|
||||
"language": "data",
|
||||
"policy": "native-only",
|
||||
"note": "declarative boundary schemas + recorded corpus — nothing executable"
|
||||
}
|
||||
],
|
||||
"quarantine": [
|
||||
{
|
||||
"path": "",
|
||||
"language": "python",
|
||||
"owner": "nick",
|
||||
"expires": "2027-04-16",
|
||||
"note": "root default: the toolchain (scripts/, tests/, pyproject, Makefile, unified runner) is python during migration; expires with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/",
|
||||
"language": "python",
|
||||
"owner": "nick",
|
||||
"expires": "2027-04-16",
|
||||
"note": "the python runtime core — retirement waves 7.3 remove it slice by slice"
|
||||
},
|
||||
{
|
||||
"path": "scripts/",
|
||||
"language": "python",
|
||||
"owner": "nick",
|
||||
"expires": "2027-04-16",
|
||||
"note": "tooling scripts including this gate's verifier home"
|
||||
},
|
||||
{
|
||||
"path": "tests/",
|
||||
"language": "python",
|
||||
"owner": "nick",
|
||||
"expires": "2027-04-16",
|
||||
"note": "pytest suite — the one test world is python-run today"
|
||||
},
|
||||
{
|
||||
"path": ".gitea/",
|
||||
"language": "python",
|
||||
"owner": "nick",
|
||||
"expires": "2027-04-16",
|
||||
"note": "N3 (targeted-go-rust-evidence.json): ci.yml still installs uv into a python3 venv — recorded here as quarantined debt with a real expiry instead of an invisible fact"
|
||||
}
|
||||
],
|
||||
"exceptions": [
|
||||
{
|
||||
"path": "src/rust/capability_scoring/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/cognition_core/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/construction_depth/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/evidence_provenance/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity harness (7 files) — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/impact_preview/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/memory_retrieval/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/memory_scoring/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/memory_storage/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/query_memory/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/rune_spine/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/salience_gate/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/scaffold_core/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/structural_store/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/token_counting/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
},
|
||||
{
|
||||
"path": "src/rust/voice_sync/",
|
||||
"owner": "migration",
|
||||
"expires": "2027-04-16",
|
||||
"reason": "dichotomy-era python parity twin / oracle loader — retire with the migration waves (tasks 7.x)"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
## [2026-10-17T07:12:30] AT1: created
|
||||
- **Details**: fix the release pipeline scripts today
|
||||
- **Channel**: chat
|
||||
- **Status**: created
|
||||
|
||||
Loaded 100 of 4446 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user