Merge remote-tracking branch 'origin/main' into capy/seed-recorder

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

# Conflicts:
#	scripts/harness-version.json
This commit is contained in:
garrytan committed 2026-10-18 00:38:59 +00:00
commit ffe95d7cb5
78 files changed
+4126 -241

No files matched your search

+12
View File
@@ -294,6 +294,18 @@ requalified catalog or withdraws the catalog's runtimes in the same pull
request. Independently, the helper refuses to select a runtime recorded under
another isolation policy.
The daily precision gate (`dailyPrecision95` in
[`cso-eval.ts`](../scripts/cso-eval.ts)) divides true positives by reported
findings. Hypotheses count in neither term. A finding the evaluator marks
`instrumentation` also counts in neither term: it targets the evaluator's
declared fixture instrumentation, such as the canary overlay files or the egress
probe route. The seeded issue adds one true positive per cell, however many
times it is restated. A finding the evaluator marks `incidental` is a different
issue that a unanimous judge panel confirmed is real in that source. It adds one
true positive per distinct `incidentalKey` in its cell, never counts toward
recall, and needs no seeded match. Recall still measures only the seeded issue,
and the thresholds are unchanged.
The judge observes held-out attacks in one boot: a `VerificationRequest`'s
`security` field is either one assertion or an array of up to eight assertions
with distinct names, judged in a single before/after pair. The before phase is
+1
View File
@@ -44,6 +44,7 @@ cause, the held files and the fix.
| `error` | The drain stopped before committing and kept the queue | `status=error. The last sync stopped before committing; the queue is kept and retried. See the cause: <bin> --status` | The cause `--status` names: a git index lock, a commit hook, a `git add` failure |
| `unknown` | The status file holds a status code outside this table | `status=unknown. The sync status file is unreadable. See it: <bin> --status` | Run `<bin> --once` to rewrite it |
| stale-push | No push for 24 hours while the drain's `drainable` count is above zero | `stale-push. Files are ready to sync but nothing has been pushed for over 24 hours. See why: <bin> --status` | The waiting reason `--status` names (a coupled group waiting for a consistent generation, a `git add` failure) |
| case-collision | `case_names` is set: artifact paths differ only by letter case, so the drain holds queued files under them | `case-collision held=<N> names=<names>. Artifact paths differ only by letter case, which a Mac or Windows disk stores as one file; sync holds <N> queued file(s) under them and syncs everything else. Repair: https://github.com/garrytan/gstack/blob/main/docs/troubleshooting.md#artifact-case-collision. See the paths: <bin> --status` | Keep one spelling: the manual repair in [troubleshooting](troubleshooting.md#artifact-case-collision) |
| stale-drain | `last_drain_at` older than 24 hours while the queue holds records | `stale-drain. The sync has not run for over 24 hours while files wait in the queue. See why: <bin> --status` | If no gstack process is running, remove `~/.gstack/.brain-sync.lock.d`, then run `<bin> --once` |
`<bin>` is the absolute path, for example
+5 -2
View File
@@ -90,8 +90,11 @@ On machine B:
gstack-brain-sync --status
```
Shows: the status (`ok`, `idle`, `held`, `blocked`, `push_failed`,
`error`), files held back by the secret scan with their fixes, the
Shows: the status (`ok`, `idle`, `held`, `collision`, `blocked`,
`push_failed`, `error`), files held back by the secret scan with their
fixes, files held because their path differs only by letter case from
another path (`case_held`, `case_names`; see
[troubleshooting](troubleshooting.md#artifact-case-collision)), the
`drainable` count, the last sync and push times, pending queue depth, and
the current privacy mode.
+72
View File
@@ -630,6 +630,78 @@ has nothing to calibrate from. It no longer says "calibrated".
**Fix.** Answer more registered questions, then `gstack-developer-profile --derive`.
<a id="artifact-case-collision"></a>
### `ARTIFACTS_SYNC: attention: case-collision held=<N> names=<names>. Artifact paths differ only by letter case, ...`
**Meaning.** The artifacts repo holds paths that differ only by letter case,
such as `projects/MyApp/` and `projects/myapp/`. A Mac or Windows disk stores
them as one file, so git there would stage a write under the other spelling,
and sync would report success while one spelling stays modified and the other
machines read stale data (#3220). Sync now holds every queued file under such a
path instead of staging it: the file stays queued, everything else keeps
syncing, and the status is `collision` rather than `ok`. When only one
spelling is tracked, that spelling keeps syncing and only the new alias is
held; when neither is tracked yet, both wait. `<names>` are the colliding folder or file names; `gstack-brain-sync
--status` lists the held paths in `message`. A local commit that would push a
new alias to the remote (two machines created both spellings at once) is
refused with status `error` and kept locally.
Nothing is repaired automatically. Slug resolution and skill start are
unaffected; only the sync holds files.
**Fix.** Keep one spelling, by hand, on one machine:
1. Choose the spelling to keep: the one `gstack-slug` prints in that project
today. If the project resolves to different spellings (a remote URL typed
two ways, a folder reached as `~/code/MyApp` and `~/code/myapp`), make it
stable first: fix the remote URL's case, or set `GSTACK_PROJECT_SLUG`.
2. Fold the other spelling's tracked files into the kept one. Read both
spellings from git, because a Mac's working tree shows only one of them,
and the working files too, because they hold the held, unsynced writes.
This loop covers the dropped spelling's tracked files and its untracked
ones (held writes that never synced). It folds JSONL logs (concatenated,
duplicate lines dropped) and prints every other file for you to decide:
```bash
cd ~/.gstack
keep=projects/MyApp; drop=projects/myapp
{ git -c core.ignorecase=false ls-files -- "$drop"; git -c core.ignorecase=false ls-files --others -- "$drop"; } | sort -u | while IFS= read -r f; do
rel=${f#"$drop"/}
case "$rel" in *.jsonl) ;; *) echo "decide by hand: $f"; continue ;; esac
{ git show ":$keep/$rel"; git show ":$f"; cat "$keep/$rel" "$f"; } 2>/dev/null | awk '!seen[$0]++' > "$HOME/.gstack-fold.tmp"
mkdir -p "$keep/$(dirname "$rel")" && mv "$HOME/.gstack-fold.tmp" "$keep/$rel"
git -c core.ignorecase=false add -f -- "$keep/$rel"
git -c core.ignorecase=false rm -q --cached --ignore-unmatch -- "$f"
echo "folded: $keep/$rel"
done
```
For each file it prints (Markdown, JSON snapshots such as
`decisions.active.json`), compare `git show ":$keep/<file>"` with
`git show ":$drop/<file>"`, write the content to keep to `"$keep/<file>"`,
then run the same `git -c core.ignorecase=false add -f -- "$keep/<file>"`
and `git -c core.ignorecase=false rm -q --cached --ignore-unmatch -- "$drop/<file>"`. Never
concatenate a JSON snapshot. Then commit and push:
```bash
git -c core.ignorecase=false commit -m "repair: fold $drop into $keep"
git push origin HEAD
```
On Linux the two folders are separate on disk: after the commit, check
that `git -c core.ignorecase=false ls-files --others -- "$drop"` lists
nothing you have not folded or moved, then remove the dropped folder
(`rm -r "$drop"`). On a Mac or Windows they are the same folder, so do
not remove anything.
3. Clear the held records of the dropped spelling and queue the kept files:
`gstack-brain-sync --skip-file <dropped path>` for each held path that
`--status` lists, then `gstack-brain-enqueue <kept path>` for each
`folded:` line and each file you moved by hand, and
`gstack-brain-sync --once`.
4. On every other machine, let the next skill start pull the repair (or run
`git -C ~/.gstack pull`). The line disappears once no tracked path
differs only by case.
<a id="skill-start-former-bucket"></a>
### `gstack: earlier data for this project is in projects/<slug> (a worktree's old bucket). Merge it: ...`