Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
17bf0f4
feat(1.1): Scheduler — serial run queue built to the ratified Executo…
Rchari1 Jul 3, 2026
b9ed9fe
fix(1.1): scheduler review — microtask-deferred re-pump + enforce the…
Rchari1 Jul 3, 2026
22f664d
Merge pull request #68 from harmoniqs/rchari/56-scheduler
Rchari1 Jul 3, 2026
6c0c6fc
feat: catalog entry card — components, save-to-catalog flow, session …
kateebonner Jul 3, 2026
84cbc73
1.2 — RunsManager: multi-run engine keyed on the append-only runs/ind…
Rchari1 Jul 4, 2026
d393e3e
1.3: Run Inspector single→multi-run (runId-keyed protocol, per-run pa…
Rchari1 Jul 6, 2026
5986581
1.4a: smoke corpus — Scheduler → executor → RunsManager → Inspector e…
Rchari1 Jul 6, 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
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
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
}
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);
60 changes: 30 additions & 30 deletions packages/amico-run/src/authoring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,64 @@
// 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'
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)
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
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')
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
const file = authoringFile();
if (!existsSync(file)) return { config: defaults() };
let raw: unknown;
try {
raw = JSON.parse(readFileSync(file, 'utf8'))
raw = JSON.parse(readFileSync(file, "utf8"));
} catch {
return { config: defaults(), warning: `malformed authoring.json at ${file} — using built-in defaults` }
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>
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
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,
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,
},
}
};
}
39 changes: 22 additions & 17 deletions packages/amico-run/src/baseline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,37 @@
// 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'
import { createHash } from "node:crypto";

const DEFAULT_BEGIN = '^# ── FILL IN'
const DEFAULT_END = '^# ─────'
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')) {
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
inside = true;
out.push(line);
continue;
}
if (inside && end.test(line)) {
inside = false
out.push(line)
continue
inside = false;
out.push(line);
continue;
}
out.push(inside ? '#MASKED' : line)
out.push(inside ? "#MASKED" : line);
}
return out.join('\n')
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')
return (
"sha256:" +
createHash("sha256")
.update(maskFillPoints(text, beginSource, endSource))
.digest("hex")
);
}
Loading
Loading