Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
141 commits
Select commit Hold shift + click to select a range
4429b15
feat(L0): pulse-designer interview spine in AGENTS.md
aarontrowbridge Jul 3, 2026
7c4351d
test(L0): interview e2e slow suite — tiered A/B/C (registration / plu…
aarontrowbridge Jul 3, 2026
735f425
feat(L0): amicode_* tool pack v0 + config registration
aarontrowbridge Jul 3, 2026
3288162
test(L0): tier C live — full A/B/C green vs the branded binary
aarontrowbridge Jul 3, 2026
5dabb87
test(L0): tier D — full chain interview→launched solve (MVP DoD) PASSES
aarontrowbridge Jul 3, 2026
f941a7e
feat(L0): hardware/calibrate guided stubs — the pack covers all 8 stages
aarontrowbridge Jul 3, 2026
7eac2b5
feat(L0): Amico identity + proactive interview kickoff
aarontrowbridge Jul 3, 2026
9d28f02
feat(L0): amicode_ask — multiple-choice questions as button payloads
aarontrowbridge Jul 3, 2026
1a5b2a8
fix(L0): amicode_ask discipline + optional per-option details
aarontrowbridge Jul 3, 2026
5c83b38
brand: digi Harmoniqs H-robot mark replaces the amico smile
aarontrowbridge Jul 3, 2026
4eca7bb
feat: fixed opencode port (amicode.opencodePort, default 43117) + awa…
aarontrowbridge Jul 3, 2026
ac04730
feat(scores): manifest schema + additive validation
aarontrowbridge Jul 3, 2026
3f9ea59
feat(scores): SCORE.md loader with per-score error isolation
aarontrowbridge Jul 3, 2026
e8b1927
fix(scores): loader test targeted schema_version by accident
aarontrowbridge Jul 3, 2026
1ac3496
feat(scores): repertoire lint (templates, memory hooks, lineage, enti…
aarontrowbridge Jul 3, 2026
e2e55b8
feat(scores): score #0 — pulse-designer interview as data (+ vsix pac…
aarontrowbridge Jul 3, 2026
555fadb
feat(scores): entitlement gating + onset router section
aarontrowbridge Jul 3, 2026
9db4367
feat(scores): interview_state [score] slice with version pinning (JSO…
aarontrowbridge Jul 3, 2026
4d6f55e
feat(scores): usage capture module (funnel, off-path, gate events)
aarontrowbridge Jul 3, 2026
d832b75
feat(scores): compiler + router spliced into the injection seam, scor…
aarontrowbridge Jul 3, 2026
114f4e8
feat(scores): stage-order + gate guard enforced in the amicode_* tool…
aarontrowbridge Jul 3, 2026
c3259f1
test(scores): live e2e — router → score #0 → pinned state + usage fun…
aarontrowbridge Jul 3, 2026
71d74f5
docs(scores): repertoire authoring README
aarontrowbridge Jul 3, 2026
11d5a7c
scores: mirror 778e1bb ask-discipline into score #0 + anchor-on-recor…
aarontrowbridge Jul 3, 2026
7cf3923
test(scores): version-agnostic prep assertions + banner/manifest vers…
aarontrowbridge Jul 3, 2026
33bc3e2
feat(L0): consolidate on opencode's NATIVE question tool; amicode_ask…
aarontrowbridge Jul 3, 2026
51e8b4b
feat(L0): curated 'What can Amicode do?' answer — no webfetch, no eng…
aarontrowbridge Jul 3, 2026
bedc549
feat: private-mirror vendoring + team testing guide + Rydberg CZ temp…
aarontrowbridge Jul 3, 2026
42b6418
docs: root agent-forward setup guide (AGENTS.md + CLAUDE.md pointer)
aarontrowbridge Jul 3, 2026
f6a0e67
chore: vendor from the harmoniqs/opencode private release (v1.17.3-am…
aarontrowbridge Jul 3, 2026
d81e63f
test: align buildOpencodeConfigContent call sites with rebased runsRo…
aarontrowbridge Jul 3, 2026
0ff9813
feat(spec-a): opened entity model, canonical json, slug, diff + senti…
aarontrowbridge Jul 3, 2026
8bb9436
feat(spec-a): hashes.ts — sha256 over canonical entity json (#64 seam)
aarontrowbridge Jul 3, 2026
2ccae6b
feat(spec-a): problems.ts — workspace fs ops, active pointer, events,…
aarontrowbridge Jul 3, 2026
b4eff4b
feat(spec-a): score_guard two-dir split + cross-score fresh-state rule
aarontrowbridge Jul 3, 2026
9dd41a5
feat(spec-a): tools → active problem workspace, AMICODE_DIFF sentinel…
aarontrowbridge Jul 3, 2026
3bea357
feat(spec-a): one-shot legacy _entities migration (injectable roots)
aarontrowbridge Jul 3, 2026
1901974
feat(spec-a): extension grant + score-manifest → problems root; AMICO…
aarontrowbridge Jul 3, 2026
ad6fc4b
docs(spec-a): interview opens a Problem; solve params recorded via am…
aarontrowbridge Jul 3, 2026
5c4e3e5
test(spec-a): plugin_exercise — direct end-to-end tool drive on temp …
aarontrowbridge Jul 3, 2026
d2e2536
docs(agents): announce the problem rail once when the first entity re…
aarontrowbridge Jul 4, 2026
fe7e458
feat(schema): SolveSpec + run.toml v2 — executor/tier/env/source/hash…
aarontrowbridge Jul 4, 2026
93ecc87
feat(amico-run): authoring config + Julia import scan (spec C gate in…
aarontrowbridge Jul 4, 2026
1b22449
feat(amico-run): masked baseline + template/exemplar catalog + shape …
aarontrowbridge Jul 4, 2026
3788a8f
feat(amico-run): the --spec launch gate — schema, import scan, tier/e…
aarontrowbridge Jul 4, 2026
b79c511
feat(amico-run): --spec gated launch — solvespec.json persisted, run.…
aarontrowbridge Jul 4, 2026
3d4bf71
feat(amico-run): resolve + sandbox subcommands (mechanical tier resol…
aarontrowbridge Jul 4, 2026
c73cd34
feat(amico-run): free-tier post-FINISHED re-rollout invoke + verifica…
aarontrowbridge Jul 4, 2026
96d06a6
feat(extension): authoring assets — template registry, tier-3 skeleto…
aarontrowbridge Jul 4, 2026
b34c6f8
feat(extension): issimo package entitlements + authoring.json session…
aarontrowbridge Jul 4, 2026
97c1fd5
feat(extension): amicode_verify tool + tiered authoring workflow in A…
aarontrowbridge Jul 4, 2026
d5c073b
feat(extension): gate auto-promote on free-tier verification (reader …
aarontrowbridge Jul 4, 2026
1186339
test(extension): live golden for verify_rollout.jl (agree true/false)…
aarontrowbridge Jul 4, 2026
19c3c59
feat(inspector): RunStatus 'stopped' + detectStopped, relabel on both…
aarontrowbridge Jul 4, 2026
e9c8321
feat(solve): cooperative STOP-file check in PulseEmitCallback (all 4 …
aarontrowbridge Jul 4, 2026
18c5f13
feat(inspector): run-controls helpers (stop/save/catalog) + getActive…
aarontrowbridge Jul 4, 2026
0508258
feat(inspector): stop/save/openRunDir commands + webview control rout…
aarontrowbridge Jul 4, 2026
54a6b8d
feat(inspector): control-row buttons (stop/save/open) + pure enableme…
aarontrowbridge Jul 4, 2026
94ece31
baseline: rydberg honest-tier narrative fix (carried into package-ski…
aarontrowbridge Jul 4, 2026
50f94ac
feat(inspector): amicode.inspector.autoOpen (default off) gates auto-…
aarontrowbridge Jul 4, 2026
2d517e6
fix(entitlements): resolve package allowlist from scores/entitlements…
aarontrowbridge Jul 4, 2026
2cdd2ee
feat(inspector): status-bar warming/stopped states + click opens insp…
aarontrowbridge Jul 4, 2026
b9bb870
feat(schema): additive result.toml fields for spline/free-phase verif…
aarontrowbridge Jul 4, 2026
fe3e9ba
feat(inspector): pure timing helpers (parseMaxIter/computeEta/formatE…
aarontrowbridge Jul 4, 2026
acbb82e
feat(inspector): elapsed/rate/ETA strip — buffered timing, wall_secon…
aarontrowbridge Jul 4, 2026
1c46952
test(inspector): add postTiming to watcher-statemachine mock inspecto…
aarontrowbridge Jul 4, 2026
8b6e595
fix(sandbox): stdlibs load from @stdlib — filter them from generated …
aarontrowbridge Jul 4, 2026
bac1d7d
feat(inspector): metric-row layout — ITER counter + hero + small tile…
aarontrowbridge Jul 4, 2026
879035e
feat(skills): dual-source skill-index resolver — public platform libr…
aarontrowbridge Jul 4, 2026
8f0ab70
feat(inspector): log-y convergence sparkline on the objective hero (T…
aarontrowbridge Jul 4, 2026
6c9a87d
feat(inspector): JuliaMono @font-face + font-src CSP + accent alignme…
aarontrowbridge Jul 4, 2026
22dbf14
feat(skills): splice dual-source skill index at session prep + least-…
aarontrowbridge Jul 4, 2026
3bd39e4
feat(interview): author-first open intake — retire the decline, route…
aarontrowbridge Jul 4, 2026
9724a54
feat(verify): spline-aware precedence + free-phase post-hoc harness (§6)
aarontrowbridge Jul 4, 2026
e9e55d8
merge: consolidate slice-1 package-skills into problem-workspaces (lo…
aarontrowbridge Jul 5, 2026
793dd0a
fix(skills): register resolved skills as opencode-native skills (invo…
aarontrowbridge Jul 5, 2026
a6f070d
fix(interview): de-rigidify pulse-designer for state prep + bosonic s…
aarontrowbridge Jul 5, 2026
2fea3f1
feat(substrate): vault store + distill queue with global lock (L0 tas…
aarontrowbridge Jul 5, 2026
1fa793e
feat(substrate): personalized splice + headless distiller chain (L0 t…
aarontrowbridge Jul 5, 2026
342b994
feat(substrate): amicode_profile tool + onboarding entity stream (L0 …
aarontrowbridge Jul 5, 2026
a948b43
feat(substrate): overture onboarding score + chained routing + interv…
aarontrowbridge Jul 5, 2026
9c6196d
feat(substrate): batch retro-ingest runner + distiller working-root g…
aarontrowbridge Jul 5, 2026
09abaa8
feat(L1): derived recommendations — confidence + provenance + demo co…
aarontrowbridge Jul 5, 2026
60839df
feat(L2): Amico Veloce — confident autonomy on high-confidence recomm…
aarontrowbridge Jul 5, 2026
2383b4a
feat(L1): grant distiller read access to ~/harmoniqs/demos for demo-i…
aarontrowbridge Jul 5, 2026
eb87067
amicode(chat): bridge in-app palette ops to allowlisted vscode commands
aarontrowbridge Jul 5, 2026
b478db1
Merge main (1.1–1.4a: Scheduler, RunsManager, multi-run Inspector, sm…
Rchari1 Jul 6, 2026
83c7998
feat: theme-calculated Harmoniqs yellow — OKLCH-solved brand accent (…
kateebonner Jul 6, 2026
9634f86
workbench: run picker + pane-ticker pause + runId-tagged controls (+ …
Rchari1 Jul 6, 2026
9d2a564
workbench: wire catalog what-next — tune/warm-start stage a concrete …
Rchari1 Jul 6, 2026
96f5e44
workbench: chat theme bridge — iframe boots with ?colorScheme= from t…
Rchari1 Jul 6, 2026
a675836
docs: one-spine mirror breadcrumb — readTerminalState semantics are m…
Rchari1 Jul 6, 2026
bcfe93e
workbench: animated H-robot mark in the Run Inspector (replaces the <…
Rchari1 Jul 7, 2026
20f10b6
workbench: inspector mark = the house silhouette glyph (AmicoSpinner …
Rchari1 Jul 7, 2026
30cdac4
workbench: chat auto-opens when the server is ready (amicode.chat.aut…
Rchari1 Jul 7, 2026
ce384b1
workbench: inspector never reveals during the boot index replay — onl…
Rchari1 Jul 7, 2026
e0efb93
workbench: boot-quiet test coverage — fresh-run warming asserts the p…
Rchari1 Jul 7, 2026
1e2a1b8
workbench: clipboard bridge — the framed app requests paste over the …
Rchari1 Jul 7, 2026
2b5c9c4
workbench: open-external bridge — framed app opens https links via vs…
Rchari1 Jul 7, 2026
f1a0792
status bar: stalled-run gate — FINISHED-less run with run.log silent …
Rchari1 Jul 7, 2026
a795cd4
score: never leak the 'issimo' entitlement codename into chat (read a…
Rchari1 Jul 7, 2026
3321733
stop always terminates: escalation ladder (cooperative STOP → stalled…
Rchari1 Jul 7, 2026
d0d97c6
fix(stop): probe lsof at /usr/sbin (macOS) and /usr/bin (Linux) — har…
Rchari1 Jul 7, 2026
8c93477
fix(stop): realpath both sides of the cwd ownership proof — lsof repo…
Rchari1 Jul 7, 2026
05a0acf
fix(stop): forceStop yields to a FINISHED that appeared during the TE…
Rchari1 Jul 7, 2026
aeb174f
fix(stop): re-prove pid ownership before the SIGKILL sweep — a pid fr…
Rchari1 Jul 7, 2026
6ecd3e9
fix(stop): JSON-decode script_path from run.toml — values are written…
Rchari1 Jul 7, 2026
e1862cc
test(stop): pin JSON-decoding of escaped script_path values
Rchari1 Jul 7, 2026
16c5bd8
fix(stop): every stop toast and the force-stop dialog name the run — …
Rchari1 Jul 7, 2026
ab1048a
fix(stop): double-stop guard + escalation-timer disposal — a second S…
Rchari1 Jul 7, 2026
758a3c2
fix(stop): tolerate a deleted run dir — STOP write and finalize are b…
Rchari1 Jul 7, 2026
04c3211
perf(runs): 2s TTL cache on liveStatus — boot replay of a long run.lo…
Rchari1 Jul 7, 2026
0c5e09c
fix(runs): the poll backstop downgrades the selected run to 'stalled'…
Rchari1 Jul 7, 2026
3d467ca
fix(runs): selectRun consults liveStatus instead of hardcoding 'runni…
Rchari1 Jul 7, 2026
e681591
chore: remove empty runs_manager_boot.test.ts accidentally created by…
Rchari1 Jul 7, 2026
eb9f6bf
fix(picker): stalled runs show '$(warning) stalled · iter N' — the pi…
Rchari1 Jul 7, 2026
7741dec
fix(bridge): https scheme check is case-insensitive (RFC 3986); clipb…
Rchari1 Jul 7, 2026
2776d0a
test(scores-e2e): version-agnostic score-marker assertion — hardcoded…
Rchari1 Jul 7, 2026
bfc3448
refactor(runs): one STALL_AFTER_MS (runs_manager imports run_controls…
Rchari1 Jul 7, 2026
c36652f
refactor(runs): one STALL_AFTER_MS (runs_manager imports run_controls…
Rchari1 Jul 7, 2026
cc8c3f0
style: prettier 3.6.2 over the branch's touched files + a minimal .pr…
Rchari1 Jul 7, 2026
754bf08
style: prettier over the branch's touched amico-run files (missed in …
Rchari1 Jul 7, 2026
56910af
style: prettier repo-wide (new .prettierrc/.prettierignore) — one-tim…
Rchari1 Jul 7, 2026
ed7923c
fix(config): fallback-only model pin — without one, opencode's defaul…
Rchari1 Jul 7, 2026
51da46c
ci(vsix-gate): fix by splitting package step — explicit build + fetch…
Copilot Jul 7, 2026
cd95560
test(slow): live-turn extractor is model-agnostic — Gemini opens with…
Rchari1 Jul 7, 2026
ec44848
feat(bridge): save-file lane — the run-card gallery's PNG export rout…
Rchari1 Jul 7, 2026
fbb3d31
fix(config): google pin moves to gemini-2.5-flash — the newest flash …
Rchari1 Jul 7, 2026
d6d099b
fix(config): creds-free default model — opencode/deepseek-v4-flash-fr…
Rchari1 Jul 7, 2026
512cb82
ci: authenticate the private-fork opencode fetch (GH_TOKEN)
jack-champagne Jul 7, 2026
ded0ae2
chore(extension): bump opencode.lock to v1.17.3-amicode.2
jack-champagne Jul 7, 2026
bb5c8e7
workbench: main (1.1–1.4a) into problem-workspaces + bridges, stalled…
Rchari1 Jul 7, 2026
91e3382
fetch:opencode: build from local fork clone pinned by git ref
jack-champagne Jul 7, 2026
9cec7da
opencode.lock: release fallback → v1.17.3-amicode.3 (UI gate on)
jack-champagne Jul 8, 2026
46ec1fc
opencode.lock: ref → 6f104ff (fork doc fix merged, harmoniqs/opencode#3)
jack-champagne Jul 8, 2026
130f612
Merge pull request #92 from harmoniqs/rchari/workbench
jack-champagne Jul 8, 2026
829bb2e
Merge pull request #91 from harmoniqs/jack/opencode-local-source
jack-champagne Jul 8, 2026
2d7477e
ci: tag-triggered VSIX release (v* tags)
jack-champagne Jul 8, 2026
e2d13cb
Merge pull request #93 from harmoniqs/jack/release-workflow
jack-champagne Jul 8, 2026
02f67eb
opencode.lock: → v1.17.3-amicode.4 + ref e9b6951 (workflow-built rele…
jack-champagne Jul 8, 2026
8dff613
Merge pull request #94 from harmoniqs/jack/lock-amicode4
jack-champagne Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
13 changes: 12 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ jobs:
# self-skipping — the skip was the #25 CI-level false-green (an injection or
# config-merge regression would pass CI because the only test for it skipped).
- run: pnpm --filter amicode-v2 fetch:opencode
env:
# gh release download pulls the vendored binary from the PRIVATE
# harmoniqs/opencode release; the default GITHUB_TOKEN is scoped to
# this repo only, so gh needs a cross-repo token (repo secret).
GH_TOKEN: ${{ secrets.OPENCODE_FETCH_TOKEN }}
- run: pnpm -r run test # amico-run suite (incl. S31 grep rule) + extension unit suite (incl. the opencode inject/merge integration) + @amicode/schema conformance
- name: amico-validate — shipped configs conform + linked bin works (0.1c gate)
run: |
Expand Down Expand Up @@ -70,7 +75,11 @@ jobs:
- uses: actions/setup-node@v4
with: { node-version: 20, cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: pnpm --filter amicode-v2 package # amico-run build + ext build + fetch:opencode + vsce
- run: pnpm -r run build # amico-run + extension (same as fast — ensures dist/ is in place before vsce)
- run: pnpm --filter amicode-v2 fetch:opencode # vendor the opencode binary (explicit step, matches fast/boot-smoke)
env:
GH_TOKEN: ${{ secrets.OPENCODE_FETCH_TOKEN }} # private-fork release fetch (see fast)
- run: pnpm --filter amicode-v2 exec vsce package --no-dependencies --allow-missing-repository -o amicode.vsix
- run: AMICODE_REQUIRE_VSIX=1 pnpm --filter amicode-v2 exec vitest run test/packaging.test.ts
boot-smoke:
strategy:
Expand All @@ -84,4 +93,6 @@ jobs:
with: { node-version: 20, cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: pnpm --filter amicode-v2 fetch:opencode
env:
GH_TOKEN: ${{ secrets.OPENCODE_FETCH_TOKEN }} # private-fork release fetch (see fast); one step covers both matrix legs
- run: pnpm --filter amicode-v2 test:smoke
53 changes: 53 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Tag-triggered VSIX release. Push a `v*` tag → full package chain (amico-run +
# extension + exemplars + vendored opencode via fetch:opencode), channel-gate
# check on the vendored binary, packaging-manifest gate, then a GitHub release
# with amicode.vsix attached.
name: release
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Existing tag to package and release"
required: true

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
contents: write

jobs:
vsix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.tag || github.ref_name }}
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: pnpm --filter amicode-v2 run package # amico-run + build + exemplars + fetch:opencode + vsce
env:
GH_TOKEN: ${{ secrets.OPENCODE_FETCH_TOKEN }} # private-fork release fetch (see ci.yml)
- name: Gate check — vendored binary ships the amicode UI ON
run: |
set -euo pipefail
BIN=packages/extension/vendor/opencode/linux-x64/opencode
cat packages/extension/vendor/opencode/linux-x64/.source
# minified shape: `...general?.newLayoutDesigns,<VAR>)` with `<VAR>=!0` (on) / `=!1` (off)
VAR=$(grep -aoh 'newLayoutDesigns,[A-Za-z$_]\{1,8\})' "$BIN" | head -1 | sed 's/newLayoutDesigns,//; s/)//')
test -n "$VAR" || { echo "FAIL: gate pattern not found (minifier drift? update this check)"; exit 1; }
grep -aq "[^A-Za-z0-9_\$]${VAR}=!0" "$BIN" \
|| { echo "FAIL: channel gate OFF (${VAR}=!1) — vendored binary hides the amicode UI"; exit 1; }
echo "OK: gate ON (${VAR}=!0)"
- run: AMICODE_REQUIRE_VSIX=1 pnpm --filter amicode-v2 exec vitest run test/packaging.test.ts
- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ inputs.tag || github.ref_name }}"
gh release create "$TAG" --title "amicode $TAG" --generate-notes \
packages/extension/amicode.vsix
8 changes: 8 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# generated / vendored — never hand-formatted
node_modules
dist
packages/extension/vendor
packages/extension/exemplars
*.vsix
pnpm-lock.yaml
CHANGELOG.md
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"printWidth": 120,
"semi": true
}
80 changes: 80 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Amicode — agent setup & testing guide

You are (probably) an agent asked to set up, develop, or test Amicode on this machine.
This file is the canonical path. The human-oriented test script is
`packages/extension/TESTING.md`; design authority lives in the `harmoniqs/amico` vault
(see `README.md`). **Do not confuse this file with `packages/extension/AGENTS.md`** —
that one is the product's runtime instruction set for the in-app agent, not for you.

## What this is

A VS Code extension (`packages/extension`, pnpm workspace) for conversational quantum
pulse design: chat (an opencode server we vendor, branded as Amicode) → guided interview
→ LLM-authored Julia solve (Piccolo) via `packages/amico-run` → live Run Inspector.
Interview flows are data (`packages/extension/scores/`). Schemas: `packages/schema`.

## Prerequisites (verify each; do not assume)

1. `node >= 20`, `corepack enable` (repo pins `pnpm@9.15.9` — never install pnpm globally)
2. Julia ≥ 1.12 via juliaup: `curl -fsSL https://install.julialang.org | sh`
3. `gh auth status` succeeds AND `gh repo view harmoniqs/opencode` succeeds
(private fork mirror — the vendored binary downloads from its release; if 404, stop
and tell the human to request access from Aaron)
4. An LLM provider for the chat: `opencode auth login` after the binary is vendored
(or `ANTHROPIC_API_KEY` in the environment). Without one, the free anonymous tier is
used — functional but flaky; do not judge interview-quality bugs on the free tier.

## Setup (in order; each step has a check)

```bash
git clone git@github.com:harmoniqs/amicode.git && cd amicode
git checkout aaron/night-l0-pulse-designer # the testing branch (PR #75) until merged
corepack enable && pnpm install # check: exits 0, lockfile untouched
pnpm -r build # check: packages/extension/dist/extension.js exists
pnpm --filter amicode-v2 run fetch:opencode # check: vendor/opencode/<platform>/opencode exists
# (lock source=local: builds from a harmoniqs/opencode
# clone at the locked ref — sibling ../opencode, or
# AMICODE_OPENCODE_SRC / --local <path>; needs bun.
# No clone → falls back to the gh release download)
pnpm --filter amicode-v2 test # check: 200+ tests pass, 0 fail
bash packages/extension/scripts/install.sh # Julia project (~15 min first precompile) + VSIX + lab.toml
node packages/extension/scripts/healthcheck.mjs # check: 4/4 ✓ (julia, opencode, amico-run, creds)
```

macOS note: the vendored binary is unsigned — if Gatekeeper blocks it:
`xattr -d com.apple.quarantine packages/extension/vendor/opencode/darwin-arm64/opencode`

## Verification gates (run before claiming anything works)

| Gate | Command | Expect |
|---|---|---|
| Fast suite | `pnpm --filter amicode-v2 test` | all pass |
| Boot smoke | `pnpm --filter amicode-v2 run test:smoke` | `[smoke] PASS` |
| Julia solve E2E | `AMICO_TEST_JULIA_PROJECT=$HOME/.amico/julia pnpm --filter amicode-v2 run test:slow` | template vet passes, F > 0.999 |
| Live interview E2E | `cd packages/extension && AMICODE_E2E_LIVE=1 npx vitest run test/slow/interview_e2e.test.ts` | tiers A/B/C pass (C needs a provider; flaky on free tier) |
| Full chain (opt-in, ~3 min) | add `AMICODE_E2E_FULLCHAIN=1` | tier D: interview → real solve → F > 0.99 |

## Development facts you need

- **Dev host**: open this repo in VS Code, F5 ("Run Extension (amicode-v2)"). The opencode
server runs on **fixed port 43117** (`amicode.opencodePort`); Remote-SSH users forward it once.
- **The vendored binary is a build artifact** — never edit it; it comes from
`harmoniqs/opencode` (thin fork, patch stack in its `AMICODE-PATCHES.md`). Rebrand/UI work
happens THERE, product logic lives HERE in config/plugin/scores (Layer 0).
- `packages/extension/opencode-plugin/` executes inside opencode's Bun runtime — it is NOT
part of the extension bundle; keep it dependency-free; exactly one export.
- `packages/extension/scores/` — interview flows as data. New user path = new `SCORE.md`
(see `scores/README.md`); lint gate: `pnpm --filter amicode-v2 test -- repertoire_lint`.
- Run artifacts land in `~/.amico/runs/default/<runId>/` (contract: `run.toml`, `AMICODE_ITER`
lines, `iter_*.png`, `result.toml`, `pulse.jld2`, `FINISHED`). Validate files with
`packages/schema/launcher/amico-validate <file>`.
- Never commit to `main`; branch + PR. Testing feedback → PR #75 thread.

## Known sharp edges

- `test:slow` without `AMICO_TEST_JULIA_PROJECT` silently skips the Julia gates.
- The vendor `.sha256` stamp is the ACTUAL binary hash; `.source` records provenance
(`local <ref>` or `release <repo>@<tag>`). Local-source installs always rebuild; a
release-mode run re-downloads whenever the stamp differs from the lock manifest.
- Free-tier live e2e tiers are non-deterministic; a single tier-C failure is sampling noise.
- Julia 1.12.x minor-version drift vs the pinned Manifest prints a warning and proceeds.
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CLAUDE.md

Read `AGENTS.md` in this directory — it is the canonical agent-forward setup, development,
and testing guide for this repo. Follow it exactly (prerequisites → setup → verification
gates). Human test script: `packages/extension/TESTING.md`.
Binary file added packages/.DS_Store
Binary file not shown.
Binary file added packages/amico-run/.DS_Store
Binary file not shown.
22 changes: 11 additions & 11 deletions packages/amico-run/esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { build } from 'esbuild'
import { chmodSync } from 'node:fs'
import { build } from "esbuild";
import { chmodSync } from "node:fs";

await build({
entryPoints: ['src/cli.ts'],
entryPoints: ["src/cli.ts"],
bundle: true,
platform: 'node',
target: 'node20',
platform: "node",
target: "node20",
// ESM, not CJS: the package is "type": "module", so node executes dist/amico-run.js
// as ESM — a CJS bundle would die on `require is not defined in ES module scope`.
format: 'esm',
outfile: 'dist/amico-run.js',
banner: { js: '#!/usr/bin/env node' },
format: "esm",
outfile: "dist/amico-run.js",
banner: { js: "#!/usr/bin/env node" },
sourcemap: true,
logLevel: 'info',
})
chmodSync('dist/amico-run.js', 0o755)
logLevel: "info",
});
chmodSync("dist/amico-run.js", 0o755);
12 changes: 8 additions & 4 deletions packages/amico-run/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,25 @@
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"bin": { "amico-run": "./launcher/amico-run" },
"engines": { "node": ">=20" },
"bin": {
"amico-run": "./launcher/amico-run"
},
"engines": {
"node": ">=20"
},
"scripts": {
"build": "node esbuild.config.mjs",
"typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests --exclude '**/slow/**'",
"test:slow": "vitest run test/slow"
},
"dependencies": {
"@amicode/schema": "workspace:*"
"@amicode/schema": "workspace:*",
"smol-toml": "^1.3.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"esbuild": "^0.24.0",
"smol-toml": "^1.3.0",
"typescript": "^5.6.0",
"vitest": "^2.1.0"
}
Expand Down
67 changes: 67 additions & 0 deletions packages/amico-run/src/authoring.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Authoring config (spec C) — the extension→amico-run seam. Session prep
// writes ~/.amico/authoring/authoring.json (allowlist resolved from
// entitlements + absolute paths to the bundled registry/exemplars/harness
// assets); the gate reads it here. Absent file → conservative built-in
// defaults (public base ∪ support set) so a bare-but-spec'd dev invocation
// still gates sanely. $AMICO_AUTHORING_FILE overrides the path (tests).
import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";

// NOTE (spec-20260704-113005 §3): session prep ALSO writes an additive
// `skills: [{source: "library"|"package", package?, name, description, path}]`
// array — the dual-source skill index the agent was given this session. It is a
// record for provenance/UI; amico-run does not consume it (unknown fields are
// ignored here), so it is intentionally NOT in this interface.
export interface AuthoringConfig {
allowlist: string[]; // entitlement-resolved Harmoniqs packages
support_set: string[]; // fixed support packages the run-dir contract itself needs
registry?: string; // abs path to templates/registry.toml
exemplars?: string; // abs path to exemplars/index.json
verify_harness?: string; // abs path to julia/verify_rollout.jl
verify_tolerance: number; // tier-3 re-rollout agreement (absolute)
}

export const DEFAULT_ALLOWLIST = ["Piccolo", "Legato", "Intonato", "NamedTrajectories", "DirectTrajOpt"];
export const DEFAULT_SUPPORT = ["JLD2", "CairoMakie", "Makie", "TOML", "Printf"];
const DEFAULT_TOLERANCE = 0.001;

function defaults(): AuthoringConfig {
return {
allowlist: [...DEFAULT_ALLOWLIST],
support_set: [...DEFAULT_SUPPORT],
verify_tolerance: DEFAULT_TOLERANCE,
};
}

export function authoringFile(): string {
const env = process.env.AMICO_AUTHORING_FILE;
if (env && env.trim() !== "") return env;
return join(homedir(), ".amico", "authoring", "authoring.json");
}

export function readAuthoring(): { config: AuthoringConfig; warning?: string } {
const file = authoringFile();
if (!existsSync(file)) return { config: defaults() };
let raw: unknown;
try {
raw = JSON.parse(readFileSync(file, "utf8"));
} catch {
return { config: defaults(), warning: `malformed authoring.json at ${file} — using built-in defaults` };
}
if (typeof raw !== "object" || raw === null)
return { config: defaults(), warning: `authoring.json at ${file} is not an object — using built-in defaults` };
const data = raw as Record<string, unknown>;
const strings = (v: unknown): string[] | undefined =>
Array.isArray(v) ? v.filter((s): s is string => typeof s === "string") : undefined;
return {
config: {
allowlist: strings(data.allowlist) ?? [...DEFAULT_ALLOWLIST],
support_set: strings(data.support_set) ?? [...DEFAULT_SUPPORT],
registry: typeof data.registry === "string" ? data.registry : undefined,
exemplars: typeof data.exemplars === "string" ? data.exemplars : undefined,
verify_harness: typeof data.verify_harness === "string" ? data.verify_harness : undefined,
verify_tolerance: typeof data.verify_tolerance === "number" ? data.verify_tolerance : DEFAULT_TOLERANCE,
},
};
}
42 changes: 42 additions & 0 deletions packages/amico-run/src/baseline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Masked baseline (spec C tier-2 demotion detection) — sha256 of a script
// with every line strictly BETWEEN the fill-point markers replaced by
// "#MASKED" (the marker lines themselves are kept). Fill-point edits are
// invariant; any edit outside them changes the hash, which the gate treats
// as "no longer the exemplar's physics". Default markers are the template
// convention's `# ── FILL IN` / `# ─────` pair; an index entry may override
// with fill_begin/fill_end regex sources. Unterminated blocks mask to EOF
// (conservative: an attacker deleting the end marker can't unmask anything).
import { createHash } from "node:crypto";

const DEFAULT_BEGIN = "^# ── FILL IN";
const DEFAULT_END = "^# ─────";

export function maskFillPoints(text: string, beginSource?: string, endSource?: string): string {
const begin = new RegExp(beginSource ?? DEFAULT_BEGIN);
const end = new RegExp(endSource ?? DEFAULT_END);
const out: string[] = [];
let inside = false;
for (const line of text.split("\n")) {
if (!inside && begin.test(line)) {
inside = true;
out.push(line);
continue;
}
if (inside && end.test(line)) {
inside = false;
out.push(line);
continue;
}
out.push(inside ? "#MASKED" : line);
}
return out.join("\n");
}

export function maskedHash(text: string, beginSource?: string, endSource?: string): string {
return (
"sha256:" +
createHash("sha256")
.update(maskFillPoints(text, beginSource, endSource))
.digest("hex")
);
}
Loading
Loading