Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
190 commits
Select commit Hold shift + click to select a range
3858c38
ci(desktop): run one real journey phase against the built app on ever…
0x-copilot-dev Aug 13, 2026
605e7c4
ci(desktop): collect the supervised service logs in the shell, not vi…
0x-copilot-dev Aug 13, 2026
828e596
fix(ai-backend): bound tool results on every store backend, not just …
0x-copilot-dev Aug 13, 2026
f32bc2b
feat(ai-backend): own the per-model-call retry policy
0x-copilot-dev Aug 13, 2026
ed54031
fix(journeys): wait out the supervised boot before asserting the sign…
0x-copilot-dev Aug 13, 2026
5820542
feat(ai-backend): bound runs with a chosen recursion limit and a wall…
0x-copilot-dev Aug 13, 2026
6acfcea
wip(wf_43932912-c46-2): snapshot of stalled agent work, unreviewed an…
0x-copilot-dev Aug 13, 2026
f73f763
wip(wf_43932912-c46-4): snapshot of stalled agent work, unreviewed an…
0x-copilot-dev Aug 13, 2026
352d98c
wip(wf_43932912-c46-7): snapshot of stalled agent work, unreviewed an…
0x-copilot-dev Aug 13, 2026
58907ff
wip(wf_76cb2a1f-451-1): snapshot of stalled agent work, unreviewed an…
0x-copilot-dev Aug 13, 2026
44e32ce
wip(wf_76cb2a1f-451-4): snapshot of stalled agent work, unreviewed an…
0x-copilot-dev Aug 13, 2026
745d584
feat(ai-backend): bound runs with a chosen recursion limit and a wall…
0x-copilot-dev Aug 13, 2026
0629353
feat(tools): gate the sub-module forms of "landed but not wired"
0x-copilot-dev Aug 13, 2026
936e61c
docs(audit): three-way harness comparison vs OpenCode and Hermes
0x-copilot-dev Aug 13, 2026
8c70156
Merge branch 'ci/desktop-journey-per-pr' into claude/harness-opencode…
0x-copilot-dev Aug 13, 2026
50db214
Merge branch 'feat/dark-wiring-gate' into claude/harness-opencode-her…
0x-copilot-dev Aug 13, 2026
d2891af
Merge branch 'fix/tool-result-cap-every-store-backend' into claude/ha…
0x-copilot-dev Aug 13, 2026
bc4d39e
Merge branch 'feat/model-call-retry-policy' into claude/harness-openc…
0x-copilot-dev Aug 13, 2026
a77c9f1
Merge branch 'worktree-wf_43932912-c46-4' into claude/harness-opencod…
0x-copilot-dev Aug 13, 2026
c78b268
wip(wf_43932912-c46-1): snapshot of stalled agent work, unreviewed an…
0x-copilot-dev Aug 13, 2026
a43bf1a
wip: recover stalled compaction-transcript-divider work verbatim
0x-copilot-dev Aug 13, 2026
693d483
feat(ai-backend): wire the host-write undo service onto app state
0x-copilot-dev Aug 13, 2026
73f44b4
style(ai-backend): ruff-format the WIP policy rules module
0x-copilot-dev Aug 13, 2026
38799c9
fix(ai-backend): keep the ask_a_question resume payload byte-identica…
0x-copilot-dev Aug 13, 2026
caec554
test(ai-backend): cover capture and revert through the real deepagent…
0x-copilot-dev Aug 13, 2026
372d191
fix(ai-backend): make MCP tool-name composition injective
0x-copilot-dev Aug 13, 2026
1e9aa72
refactor(ai-backend): rebuild the compaction notice on a seam that ex…
0x-copilot-dev Aug 13, 2026
7c77081
chore(mcp): restore the docstring a mid-edit probe overwrote
0x-copilot-dev Aug 13, 2026
b6c8410
fix(ai-backend): make the decision ledger's coverage test the PDP's o…
0x-copilot-dev Aug 13, 2026
308e3e4
feat(ai-backend): emit a compression_note when a tool result is compa…
0x-copilot-dev Aug 13, 2026
2a22cb7
fix(mcp): repair vendor tool schemas instead of deleting the connector
0x-copilot-dev Aug 13, 2026
00e4f0e
test(ai-backend): pin the two-connectors-one-tool-name case on the so…
0x-copilot-dev Aug 13, 2026
bb053a0
test(ai-backend): pin the destructive rung above BYPASS, and the rule…
0x-copilot-dev Aug 13, 2026
545c321
feat(ai-backend): project the compaction divider's label server-side
0x-copilot-dev Aug 13, 2026
3fec2bb
fix(mcp): attribute a schema repair to the connector that shipped it
0x-copilot-dev Aug 13, 2026
afc3d1f
test(ai-backend): prove the undo routes are reachable through the com…
0x-copilot-dev Aug 13, 2026
ae016f2
docs(mcp): note where the degraded schema stays whole
0x-copilot-dev Aug 13, 2026
843b754
test(ai-backend): cover the rule layer, home expansion, and retroacti…
0x-copilot-dev Aug 13, 2026
84debc9
feat(ai-backend): sweep expired captures at boot, so retention is rea…
0x-copilot-dev Aug 13, 2026
1375044
refactor(mcp): drop the ledger class for the set it wrapped
0x-copilot-dev Aug 13, 2026
1e50c7e
test(ai-backend): pin the policy rule layer at the seam the MCP middl…
0x-copilot-dev Aug 13, 2026
d345ffc
refactor(mcp): delete the message for a rejection that no longer exists
0x-copilot-dev Aug 13, 2026
967f955
fix(ai-backend): project grant_options on the lane that actually emit…
0x-copilot-dev Aug 13, 2026
e37a2a4
test(ai-backend): pin the decision_scope hops from HTTP body to LangG…
0x-copilot-dev Aug 13, 2026
2334868
test(ai-backend): pin the compaction divider through the real worker …
0x-copilot-dev Aug 13, 2026
0cade15
fix(ai-backend): resolve the policy card for a namespaced MCP tool
0x-copilot-dev Aug 13, 2026
593ae2c
test(ai-backend): drive the destructive gate and the always-grant thr…
0x-copilot-dev Aug 13, 2026
43ab51b
refactor(ai-backend): collapse the offloader onto one entry point
0x-copilot-dev Aug 13, 2026
f27bc49
docs(ai-backend): fix the ladder's stale cross-reference to the per-c…
0x-copilot-dev Aug 13, 2026
d3afc61
test(ai-backend): drive the real tools, and correct the WIP's delete …
0x-copilot-dev Aug 13, 2026
3c37f0e
fix(ai-backend): make the MCP catalog advertise the callable tool name
0x-copilot-dev Aug 13, 2026
95961d9
docs(ai-backend): correct the delete comment to what was measured
0x-copilot-dev Aug 13, 2026
866597a
fix(ai-backend): strip the MCP namespace from the projected display t…
0x-copilot-dev Aug 13, 2026
e6c761f
test(ai-backend): drive the worker e2e proofs by the registered tool …
0x-copilot-dev Aug 13, 2026
81a551b
test(ai-backend): follow the namespaced tool through the ledger and f…
0x-copilot-dev Aug 13, 2026
5a24823
test(ai-backend): pin the digested-name boundary the policy stage ref…
0x-copilot-dev Aug 13, 2026
3ae23d3
Merge branch 'worktree-wf_588d585e-b22-1' into claude/harness-opencod…
0x-copilot-dev Aug 13, 2026
7f2b9e4
Merge branch 'worktree-wf_588d585e-b22-2' into claude/harness-opencod…
0x-copilot-dev Aug 13, 2026
e99c4b7
Merge branch 'worktree-wf_588d585e-b22-3' into claude/harness-opencod…
0x-copilot-dev Aug 13, 2026
cea3d62
Merge branch 'worktree-wf_588d585e-b22-5' into claude/harness-opencod…
0x-copilot-dev Aug 13, 2026
45cb757
merge: policy rules + destructive-under-bypass gate
0x-copilot-dev Aug 13, 2026
7cee41a
chore(gates): honour the dark-wiring ratchet on the merged wave
0x-copilot-dev Aug 13, 2026
585234d
wip(ai-backend): import stalled skill-progressive-disclosure snapshot…
0x-copilot-dev Aug 13, 2026
d275f5c
wip: import stalled feat/runtime-hook-seam snapshot verbatim as an au…
0x-copilot-dev Aug 13, 2026
2ff9b72
feat(ai-backend): wire the bounded Skill index into the assembled prompt
0x-copilot-dev Aug 13, 2026
f17668a
refactor(ai-backend): trim the hook seam and remove its shared-state …
0x-copilot-dev Aug 13, 2026
6232604
refactor(ai-backend): fold the hook dispatcher's internals into HookD…
0x-copilot-dev Aug 13, 2026
b4449b3
test(ai-backend): declare list_skills in the context-origin inventory
0x-copilot-dev Aug 13, 2026
e618213
feat(ai-backend): add the run-steering mailbox and its model-step del…
0x-copilot-dev Aug 13, 2026
500a339
feat(ai-backend): pin the steer command, request/response and run_ste…
0x-copilot-dev Aug 13, 2026
f71e30b
test(ai-backend): restructure the hook tests into mixins, and stop ov…
0x-copilot-dev Aug 13, 2026
5e4a9bb
wip(ai-backend): tool_program plan/executor/tool skeleton (checkpoint…
0x-copilot-dev Aug 13, 2026
9b5c806
feat(ai-backend): bound subagent delegation depth on the live task path
0x-copilot-dev Aug 13, 2026
04f3386
feat(ai-backend): carry the steer command through the queue port and …
0x-copilot-dev Aug 13, 2026
41d6fa8
feat(ai-backend): floor a delegated child's posture at its parent's, …
0x-copilot-dev Aug 13, 2026
9c24307
chore: drop tools/check_dark_wiring.py and its baseline from this branch
0x-copilot-dev Aug 13, 2026
d0adcb9
fix(ai-backend): keep the skill card row in factory, where the gate c…
0x-copilot-dev Aug 13, 2026
d89d22c
docs(ai-backend): record why a vetoed tool call opens no lifecycle entry
0x-copilot-dev Aug 13, 2026
954593d
feat(ai-backend): claim a steer above the execution semaphore and del…
0x-copilot-dev Aug 13, 2026
780b2b8
test(ai-backend): cover the four delegation controls, ablation-verified
0x-copilot-dev Aug 13, 2026
863e919
feat(ai-backend): accept a steer at POST /v1/agent/runs/{run_id}/steer
0x-copilot-dev Aug 13, 2026
8969f19
test(ai-backend): prove a steer is claimable under saturation and lan…
0x-copilot-dev Aug 13, 2026
7a49077
test(skills): cover the card hop the conditional-visibility predicate…
0x-copilot-dev Aug 13, 2026
c0000f8
test(ai-backend): drive the steer route end to end, including the seal
0x-copilot-dev Aug 13, 2026
b6bf6b7
feat(ai-backend): add the tool_program hyperparameter section
0x-copilot-dev Aug 13, 2026
269eed7
chore(ai-backend): prune the coordination orphan line and drop a dead…
0x-copilot-dev Aug 13, 2026
9e666ed
refactor(ai-backend): drop the inbox members nothing reads
0x-copilot-dev Aug 13, 2026
8cae3a4
refactor(ai-backend): drop the Fields.VIRTUAL_PATH key this branch st…
0x-copilot-dev Aug 13, 2026
00c73e7
test(ai-backend): prove a steer survives the file queue across a restart
0x-copilot-dev Aug 13, 2026
72113df
fix(ai-backend): make the permission floor true on the live handoff seam
0x-copilot-dev Aug 13, 2026
7777f80
feat(ai-backend): route every tool_program step through the graph's t…
0x-copilot-dev Aug 13, 2026
00e8e81
feat(ai-backend): wire run_tool_program onto the model tool surface
0x-copilot-dev Aug 13, 2026
c382e7a
test(ai-backend): enter the permission floor at the seam, and stop th…
0x-copilot-dev Aug 13, 2026
86fd328
test(ai-backend): pin that a batched step is enforced like a direct one
0x-copilot-dev Aug 13, 2026
6d0f1e1
test(ai-backend): cover tool_program sequencing, references, bounds a…
0x-copilot-dev Aug 13, 2026
31885de
test(ai-backend): restate the pinned inventories that run_tool_progra…
0x-copilot-dev Aug 13, 2026
965b853
test(ai-backend): declare run_tool_program's context-window rent
0x-copilot-dev Aug 13, 2026
65e2523
refactor(ai-backend): spend the step schema's context rent on the mod…
0x-copilot-dev Aug 13, 2026
5ba772d
Merge branch 'worktree-wf_062c65e9-4ab-2' into claude/harness-opencod…
0x-copilot-dev Aug 13, 2026
d7bac52
Merge branch 'worktree-wf_062c65e9-4ab-4' into claude/harness-opencod…
0x-copilot-dev Aug 13, 2026
580d1bf
Merge branch 'worktree-wf_062c65e9-4ab-5' into claude/harness-opencod…
0x-copilot-dev Aug 13, 2026
16d631d
merge: batch tool program
0x-copilot-dev Aug 13, 2026
7b0cf74
merge: runtime hook seam
0x-copilot-dev Aug 13, 2026
a6ca75b
fix(api-types): mirror the run_steered event, and re-ratchet dark wiring
0x-copilot-dev Aug 14, 2026
85ecc7c
chore(gates): baseline the three routes whose UI half is still pending
0x-copilot-dev Aug 14, 2026
28f5603
feat(facade): forward host-writes, host-writes/revert and steer to ai…
0x-copilot-dev Aug 14, 2026
e33b2b9
test(facade): pin the boundary on the three new run forwards
0x-copilot-dev Aug 14, 2026
7836a1e
feat(ai-backend): enforce a Skill's allowed_tools at the tool surface
0x-copilot-dev Aug 14, 2026
6e5959a
feat(chat-surface): render the compaction boundary as a quiet transcr…
0x-copilot-dev Aug 14, 2026
db2016b
test(journeys): a reachability journey for the run-governing limits
0x-copilot-dev Aug 14, 2026
78bda14
test(ai-backend): pin the allowed_tools ceiling through a real turn
0x-copilot-dev Aug 14, 2026
16c6fed
test(chat-surface): pin the compaction divider's wiring, order and la…
0x-copilot-dev Aug 14, 2026
77ebf13
feat(api-types): mirror the steer + host-write undo contracts
0x-copilot-dev Aug 14, 2026
2f84a80
test(facade): drive the three forwards through the real forwarder
0x-copilot-dev Aug 14, 2026
663a4cd
feat(ai-backend): give declared agents a real entry point
0x-copilot-dev Aug 14, 2026
5d86b2d
feat(api-types,facade): the typed contract and boundary tests for dec…
0x-copilot-dev Aug 14, 2026
194ea20
feat(chat-surface): bind the once/always grant control on the approva…
0x-copilot-dev Aug 14, 2026
a304ca6
fix(journeys): assert transport's signature, not its prose
0x-copilot-dev Aug 14, 2026
1ad834c
test: pin the once/always binding — and the backend fact it is scoped to
0x-copilot-dev Aug 14, 2026
cd8166b
Merge branch 'worktree-wf_d5b2a767-fa1-1' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
048871d
Merge branch 'worktree-wf_d5b2a767-fa1-2' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
780ed23
Merge branch 'worktree-wf_d5b2a767-fa1-3' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
41565e9
Merge branch 'worktree-wf_d5b2a767-fa1-4' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
8925d67
Merge branch 'worktree-wf_d5b2a767-fa1-5' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
ea6386e
test(chat-surface): fix the compaction ordering assertion
0x-copilot-dev Aug 14, 2026
22649c0
fix(desktop): forward COPILOT_HP__ hyperparameter overrides to superv…
0x-copilot-dev Aug 14, 2026
a15572f
test(bench): measure what the step-ceiling raise actually bought
0x-copilot-dev Aug 14, 2026
20e1d81
docs(bench): record the measured findings, including the falsified claim
0x-copilot-dev Aug 14, 2026
bc682cb
feat(ai-backend): gate run_tool_program off by default
0x-copilot-dev Aug 14, 2026
9e628fd
test(ai-backend): prove the gate removes run_tool_program's schema, n…
0x-copilot-dev Aug 14, 2026
e522c28
feat(ai-backend): add the tool_surface.artifact_family exposure knob
0x-copilot-dev Aug 14, 2026
5ee1000
feat(ai-backend): honour the artifact-family exposure knob in both wo…
0x-copilot-dev Aug 14, 2026
11b8ba5
feat(ai-backend): make the build-time prompt plan readable at the occ…
0x-copilot-dev Aug 14, 2026
ab081d3
fix(ai-backend): a tool call the run killed is no longer recorded as …
0x-copilot-dev Aug 14, 2026
975fd87
test(ai-backend): pin the saving in the occupancy ledger's own units
0x-copilot-dev Aug 14, 2026
869c65f
test(ai-backend): scope the orphan reproduction to the reconciled call
0x-copilot-dev Aug 14, 2026
01cbb11
test(ai-backend): pin the write_todos sequence the benchmark blamed
0x-copilot-dev Aug 14, 2026
b1e248e
test(ai-backend): measure the artifact-family saving on the real tool…
0x-copilot-dev Aug 14, 2026
7281064
test(ai-backend): pin the build-time plan as the system block's attri…
0x-copilot-dev Aug 14, 2026
8efbf3c
test(ai-backend): pin that the artifact family cannot change mid-task
0x-copilot-dev Aug 14, 2026
8d928eb
feat(ai-backend): declare the tools middleware installs, which no gat…
0x-copilot-dev Aug 14, 2026
4fca3b2
test(ai-backend): pin the artifact-surface suite off the developer's …
0x-copilot-dev Aug 14, 2026
f35c814
Merge branch 'worktree-wf_c5dc6ac4-ebd-2' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
e41d0e2
Merge branch 'worktree-wf_c5dc6ac4-ebd-3' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
192c90f
merge: gate the artifact/rowset tool family
0x-copilot-dev Aug 14, 2026
37a5b90
merge: precise error codes for tool calls orphaned by a run failure
0x-copilot-dev Aug 14, 2026
c31713e
fix(bench): correct the falsified-claim finding — the ceiling WAS bin…
0x-copilot-dev Aug 14, 2026
fb4a0d6
test(bench): record the gated build's measured cold prompt
0x-copilot-dev Aug 14, 2026
80c31d2
feat(chat-surface): project the wire's grant_options/grant_scope into…
0x-copilot-dev Aug 14, 2026
78e9d80
feat(ai-backend): resident summaries + /tools/ guidance catalog domain
0x-copilot-dev Aug 14, 2026
f671260
feat(chat-surface): TcWriteGateRow draws the durable grant arm in its…
0x-copilot-dev Aug 14, 2026
3caf258
feat(ai-backend): read-only /tools/ guidance backend
0x-copilot-dev Aug 14, 2026
be414ad
feat(chat-surface): bind the once/always arm on the ask card
0x-copilot-dev Aug 14, 2026
4681328
feat(ai-backend): mount /tools/ and defer the three heaviest first-pa…
0x-copilot-dev Aug 14, 2026
95d3044
wip(chat-surface): steer note projection + row, not yet wired
0x-copilot-dev Aug 14, 2026
8f36016
test(chat-surface): pin the once/always split at the projection
0x-copilot-dev Aug 14, 2026
5f5bed1
test(chat-surface): pin the durable arm's placement, states and name …
0x-copilot-dev Aug 14, 2026
e5e9054
test(chat-surface): prove the once/always arm reaches the mounted ask…
0x-copilot-dev Aug 14, 2026
2adb9e2
test(chat-surface): hold the grant arm locally so tsc can see it is n…
0x-copilot-dev Aug 14, 2026
cacc2f6
test(ai-backend): measure the /tools/ deferral on the assembled surface
0x-copilot-dev Aug 14, 2026
c83b74f
feat(ai-backend): register the runtime's first in-product hook
0x-copilot-dev Aug 14, 2026
93e01c2
test(ai-backend): pin the composite-mount hop the model actually read…
0x-copilot-dev Aug 14, 2026
0315a13
test(bench): a heavy task set the four short prompts cannot reach
0x-copilot-dev Aug 14, 2026
600c9cc
test(ai-backend): prove the builtin hook fires on the shipped run path
0x-copilot-dev Aug 14, 2026
dcf6365
feat(chat-surface): draw the mid-run steer in the transcript
0x-copilot-dev Aug 14, 2026
64ce23c
test(bench): score what the heavy tasks reach, and state each metric'…
0x-copilot-dev Aug 14, 2026
d6d4af8
test(bench): a gated task must be positively confirmed, never merely …
0x-copilot-dev Aug 14, 2026
d957d4b
feat(chat-surface): project + bind the run's host-write journal
0x-copilot-dev Aug 14, 2026
bdddd85
docs(ai-backend): do not spell a baselined dark symbol out in prose
0x-copilot-dev Aug 14, 2026
a83bbdc
docs(bench): record what the four short prompts cannot reach, and wir…
0x-copilot-dev Aug 14, 2026
c2cbb03
refactor(ai-backend): name the tool tally's fields instead of indexin…
0x-copilot-dev Aug 14, 2026
d89987f
test(bench): pin one task with HEAVY_TASKS so validating the harness …
0x-copilot-dev Aug 14, 2026
79b2a7f
docs(bench): record why the heavy set has not been run, and the recip…
0x-copilot-dev Aug 14, 2026
1a8bce4
feat(chat-surface): draw the run's disk changes on the rail, with a p…
0x-copilot-dev Aug 14, 2026
c397cb1
feat(chat-surface): steer the live run from the composer
0x-copilot-dev Aug 14, 2026
8a91139
test(bench): resolve the task pin before the boot, not inside the phase
0x-copilot-dev Aug 14, 2026
6e22cb7
test(chat-surface): drive the revert affordance, not just its projection
0x-copilot-dev Aug 14, 2026
121cfe6
test(chat-surface): prove the steer reaches the endpoint, not a secon…
0x-copilot-dev Aug 14, 2026
507742b
test(chat-surface): the arm gives way to the confirm, so the prefix r…
0x-copilot-dev Aug 14, 2026
6b1553f
Merge branch 'worktree-wf_b02da768-33c-1' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
e607888
Merge branch 'worktree-wf_b02da768-33c-2' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
5295769
Merge branch 'worktree-wf_b02da768-33c-4' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
7007136
Merge branch 'worktree-wf_b02da768-33c-5' into claude/harness-opencod…
0x-copilot-dev Aug 14, 2026
50c5190
merge: revert affordance + host-writes, dropping a duplicate grant im…
0x-copilot-dev Aug 14, 2026
a26b159
fix(chat-surface): drop the dangling export from the discarded grant …
0x-copilot-dev Aug 14, 2026
41a1b4b
test(bench): measure the disclosure build — cold prompt 22,304 -> 20,547
0x-copilot-dev Aug 14, 2026
e4a95f7
docs(bench): renumber the trajectory section after the heavy-task-set…
0x-copilot-dev Aug 14, 2026
cca7228
merge: origin/dev
0x-copilot-dev Aug 14, 2026
0891dc6
fix(ci): three failures the merge surfaced
0x-copilot-dev Aug 14, 2026
24f8807
style: ruff-format the five files the merge left unformatted
0x-copilot-dev Aug 14, 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
165 changes: 165 additions & 0 deletions .github/workflows/ci-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ on:
- "tools/cli/package.json"
- "tools/cli-testing/package.json"
- "tools/cli-testing/package-lock.json"
- "tools/cli-testing/harness/**"
- "tools/desktop-journeys/**"
- "tools/desktop-runtime/manifest.json"
- "package.json"
- "package-lock.json"
Expand All @@ -43,6 +45,8 @@ on:
- "tools/cli/package.json"
- "tools/cli-testing/package.json"
- "tools/cli-testing/package-lock.json"
- "tools/cli-testing/harness/**"
- "tools/desktop-journeys/**"
- "tools/desktop-runtime/manifest.json"
- "package.json"
- "package-lock.json"
Expand Down Expand Up @@ -148,3 +152,164 @@ jobs:

- name: Test the workspace-fs addon
run: npm run test:native --workspace @0x-copilot/desktop

# ── ONE real user journey against the BUILT app, on every PR ────────────────
#
# tools/desktop-journeys/ drives the real packaged app — supervised Electron +
# embedded PostgreSQL + all three Python services — through the Playwright
# control server, clicking real testIds. It has repeatedly caught
# "unit-tests-pass-but-the-real-app-is-broken" regressions, and until this job
# NOTHING ran it on a PR: `grep -rln desktop-journeys .github/workflows/`
# returned zero files. desktop-supervised-boot-drill.yml is the nearest thing
# and it is schedule + dispatch + tools/desktop-runtime paths only, driving the
# stack as a STAGING process rather than through the shipped app.
#
# Four constraints shape this job. Each is load-bearing; none is style.
#
# 1. EXACTLY ONE PHASE, pinned by JOURNEY_PHASES below. Since e8622a1d the
# phases in a journey file share ONE boot and consume each other's state on
# purpose, so running the whole file per-PR would inherit stale route / rail
# / env / run-history and fail in a LATER phase with a symptom-shaped
# message that reads exactly like a product bug. FR-0 is first, so it
# consumes nothing.
#
# 2. NO SECRETS, NO THIRD-PARTY CALL. FR-0 is the only phase that needs no
# provider key and drives no model: it asserts the keyless BYOK gate and
# that the catalog, read through the app, reports nothing configured.
# NOTE ON THE FAKE MODEL: RUNTIME_FAKE_MODEL=1 is what the boot drill uses,
# and it is deliberately unreachable from here. `buildServiceEnv` starts
# from {} and copies only ENV_PASSTHROUGH_ALLOWLIST
# (apps/desktop/main/services/service-env.ts), which does not contain it —
# that is the fail-closed guarantee stated in
# agent_runtime/execution/fake_model.py ("the shipped desktop never sets
# it"). Setting it in this job's env would be a no-op that reads like
# coverage, so it is not set. The hermetic run->stream tail stays where it
# can actually run: desktop-supervised-boot-drill.yml.
#
# 3. STAGE FRESH. <COPILOT_HOME>/runtime/**/services/** is a COPY of services/*
# taken at stage time and nothing re-syncs it, so a cached stage would test
# code the PR deleted. Only the DOWNLOADS (CPython + PostgreSQL tarballs)
# are cached; the stage itself is rebuilt from this commit's tree.
#
# 4. NOT A REQUIRED CHECK. A new e2e gate needs a few runs of observed
# stability before anything is allowed to block on it. Requiring it also
# means making it unconditional (see the ci-gates.yml header) — this job is
# path-filtered and macOS-billed, so that is a separate, deliberate step.
desktop-journey-first-run:
name: desktop journey (first_run FR-0)
# Apple Silicon: the staged runtime manifest ships darwin/win32 only, and
# this is the arch whose bundle the runner can both download and execute.
runs-on: macos-14
timeout-minutes: 45
env:
# THE ONE-PHASE CONSTRAINT, in one place. _lib.py's JourneyPlan runs only
# the phases named here and drops the rest; naming an id the journey does
# not declare exits 1 rather than passing an empty run, so renaming FR-0
# cannot leave this job green over nothing.
JOURNEY_PHASES: FR-0
# Port 8790 is _lib.py's default and collides with a driver left over from
# a previous run. A hosted runner is ephemeral so nothing is left over, and
# `concurrency:` above cancels a same-ref run before this one starts —
# a distinct port is belt-and-braces, chosen rather than derived so the
# value in a failing log is the value written here.
CTL_PORT: "8791"
# A first boot on a fresh profile runs initdb + migrations + three
# uvicorns. Hosted macOS is slower than a laptop at all three, and this
# number is measured rather than guessed: on a warm M-series laptop, with
# the runtime already staged, launch → the sign-in gate took 110s (94s of
# it after the window existed). 420 leaves a wide margin over that.
#
# It governs BOTH waits, which is the whole reason the job works:
# * _lib.py `_probe()` — how long until the driver's control server
# answers, i.e. until the first WINDOW exists; and
# * `DriverSession.wait_for_app_ready` — how long the app may keep
# showing `boot-gate` (BootProgress.tsx) after that.
# Those are different moments. The window appears in ~9s; the services
# behind it take ~100s more. Before the second wait existed, every journey
# went straight to `wait_for(..., timeout_s=60)` and failed with
# "sign-in gate never appeared" — a box timeout wearing a product
# failure's clothes, and a red gate on this job's very first run.
BOOT_TIMEOUT_S: "420"
DRIVER_LAUNCH_TIMEOUT_MS: "300000"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
cache: npm

# _lib.py needs 3.11+ (StrEnum). Pinned rather than trusting whatever
# `python3` the image happens to ship, because that failure is an import
# error minutes into a 45-minute job.
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"

# DOWNLOAD cache only (CPython + PostgreSQL tarballs), keyed on the pinned
# manifest so a re-pin busts it. The staged tree itself is never cached —
# see constraint 3 above. Same cache the boot drill uses.
- name: Cache desktop runtime downloads
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/enterprise-desktop-runtime
key: desktop-runtime-${{ runner.os }}-arm64-${{ hashFiles('tools/desktop-runtime/manifest.json') }}
restore-keys: |
desktop-runtime-${{ runner.os }}-arm64-

# Installs playwright (driver.mjs launches Electron through it) and the
# electron binary the driver resolves from this checkout's node_modules.
- name: Install Node dependencies
run: npm ci --no-audit --no-fund

# Fresh stage from THIS commit's services/*. Ad-hoc code-signing so the
# freshly extracted mach-o binaries execute without a Developer ID.
- name: Stage self-contained runtime (darwin-arm64, ad-hoc signed)
run: node tools/desktop-runtime/stage.mjs --platform darwin --arch arm64 --adhoc-sign

# The driver launches apps/desktop/out/**, not the TypeScript sources.
- name: Build the desktop bundle
run: npm run build --workspace @0x-copilot/desktop

# THE JOURNEY. Exit 0 only if FR-0 ran and passed: a skip (3) or a blocked
# (2) is non-zero by design, so an absent stage or a vanished phase id
# cannot be mistaken for success.
- name: One live journey phase against the built app
env:
# The `source` target's default is this path, but naming it is what
# keeps the preflight and the launch pointed at the stage built above.
COPILOT_HOME: ${{ github.workspace }}/apps/desktop/resources
run: python3 tools/desktop-journeys/first_run.py

# The four supervised services write their logs under the app's userData
# dir, which is OUTSIDE the workspace. Copy them next to the screenshots so
# one in-workspace path carries every piece of evidence.
#
# Done in the shell on purpose. An upload `path:` beginning with `~` is
# expanded by @actions/glob, not by bash, and `if-no-files-found: warn`
# downgrades a miss to a warning — so getting it wrong loses the service
# logs silently, in precisely the red run that needed them. `$HOME` here is
# expanded by the shell, which is verifiable. No `${{ }}` in the script
# body: a substituted value is a script-injection vector (ci-gates header).
- name: Collect supervised service logs
if: failure()
run: |
for logs in "$HOME/Library/Application Support/0xCopilot"/journey-first-run-*/logs; do
[ -d "$logs" ] || continue
dest="tools/desktop-journeys/runs/services-$(basename "$(dirname "$logs")")"
mkdir -p "$dest"
cp "$logs"/*.log "$dest"/ 2>/dev/null || true
done
find tools/desktop-journeys/runs -type f | sed 's/^/ evidence: /'

# Screenshots, driver.log (Electron main), and the supervised services'
# logs. Without these a red run on a runner nobody can ssh into is
# unreadable.
- name: Upload journey evidence
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: desktop-journey-first-run-evidence
path: tools/desktop-journeys/runs/**
if-no-files-found: warn
retention-days: 7
20 changes: 18 additions & 2 deletions .github/workflows/ci-gates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ jobs:
with:
python-version: "3.13"

# pyyaml: test_desktop_journey_ci.py parses .github/workflows/ci-desktop.yml
# to check the per-PR desktop-journey job against the journey it pins.
- name: Install pytest
run: python -m pip install --upgrade pip pytest
run: python -m pip install --upgrade pip pytest pyyaml

# Each gate is tested before it is trusted: a gate whose own tests fail is
# not evidence about the repository, it is a broken instrument. Every gate
Expand All @@ -84,8 +86,11 @@ jobs:
tools/test_check_llm_provider_imports.py \
tools/test_check_migration_manifest.py \
tools/test_check_reader_methods.py \
tools/test_check_dark_wiring.py \
tools/test_apply_branch_protection.py \
tools/test_cli_release.py
tools/test_cli_release.py \
tools/test_desktop_journey_ci.py \
tools/test_harness_bench_heavy_tasks.py

- name: Dark capabilities
run: python tools/check_dark_capabilities.py
Expand All @@ -101,6 +106,17 @@ jobs:
- name: Route reachability
run: python tools/check_route_reachability.py

# The sub-module sibling of the two gates above. Those catch a whole
# module nobody imports and a whole capability no test turns on; this one
# catches the same defect one level down, where the population actually
# lives -- a symbol only a test reaches, a capability only one store
# backend can serve, a validated field spent on an f-string in a prompt.
# 306 such symbols already exist and are recorded with a reason each in
# tools/dark_wiring_baseline.txt; this fails on the 307th, and on any
# baseline line that has gone stale.
- name: Dark wiring
run: python tools/check_dark_wiring.py

- name: CSS selector shadowing
run: python tools/check_css_selector_shadowing.py

Expand Down
7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ tools/design-parity/surfaces/generative-surfaces-v3/design/copilot.css
# their emission under runner control avoids a second Markdown writer.
tools/design-parity/surfaces/chat-tool-call-shell/out/*.md
tools/design-parity/surfaces/generative-surfaces-v3/out/*.md

# Benchmark arm reports are machine-written by tools/harness-bench/rescore.py
# with `json.dumps(indent=2)`, which always expands arrays. Prettier collapses
# short ones, so every rescore would otherwise land a whitespace-only diff over
# real measurements — and re-formatting evidence is how evidence stops being
# byte-comparable across arms.
tools/harness-bench/runs/*.json
27 changes: 27 additions & 0 deletions apps/desktop/main/services/service-env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,33 @@ describe("buildServiceEnv(backend)", () => {
expect(env.OTEL_SDK_DISABLED).toBe("true");
});

it("forwards a COPILOT_HP__ hyperparameter override to the service", () => {
// The step ceiling was configurable in settings.py and unreachable in the
// packaged app: the allowlist is exact-name and this family is generated.
// A live journey phase could not provoke the ceiling for exactly this
// reason, while every unit test built settings directly and never crossed
// this boundary.
const env = buildServiceEnv(
"ai-backend",
inputs({ COPILOT_HP__EXECUTION__RECURSION_LIMIT: "40" }),
);
expect(env.COPILOT_HP__EXECUTION__RECURSION_LIMIT).toBe("40");
});

it("forwards only the COPILOT_HP__ family, not arbitrary host env", () => {
const env = buildServiceEnv(
"ai-backend",
inputs({
COPILOT_HP__EXECUTION__RUN_DEADLINE_SECONDS: "900",
AWS_SECRET_ACCESS_KEY: "must-not-cross",
COPILOT_NOT_HP: "must-not-cross",
}),
);
expect(env.COPILOT_HP__EXECUTION__RUN_DEADLINE_SECONDS).toBe("900");
expect(env.AWS_SECRET_ACCESS_KEY).toBeUndefined();
expect(env.COPILOT_NOT_HP).toBeUndefined();
});

it("passes GOOGLE_OAUTH_CLIENT_ID through when set", () => {
const env = buildServiceEnv(
"backend",
Expand Down
27 changes: 27 additions & 0 deletions apps/desktop/main/services/service-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ import type { SupervisedServiceName } from "./runtime-paths";
// never the raw desktop process env). PATH/HOME/etc. are baseline process
// plumbing; the Google OAuth vars are the contract's named passthroughs;
// the provider keys are dev conveniences that BYOK supersedes in-product.
/**
* Env families forwarded by PREFIX rather than by exact name.
*
* Deliberately narrow: a prefix widens what crosses into a supervised service,
* so it is justified only where the set of names is generated rather than
* enumerable. Today that is exactly one family — the hyperparameter overrides.
*/
export const ENV_PASSTHROUGH_PREFIXES: readonly string[] = ["COPILOT_HP__"];

export const ENV_PASSTHROUGH_ALLOWLIST: readonly string[] = [
// Baseline process plumbing.
"PATH",
Expand Down Expand Up @@ -362,6 +371,24 @@ export function buildServiceEnv(
env[key] = value;
}
}
// Hyperparameter overrides are a PREFIX FAMILY, not a fixed set of names:
// `COPILOT_HP__EXECUTION__RECURSION_LIMIT` and its siblings are derived from
// the hyperparameter document, so an exact-name allowlist can never stay
// complete as that document grows. `agent_runtime/settings.py:311` calls this
// form "the only surface" for setting them — and nothing forwarded it, so a
// supervised service ALWAYS took the built-in default and no operator could
// change one. The unit suite could not see it: every hyperparameter test
// constructs settings directly and never crosses this boundary. Caught by the
// RL-2 live journey phase, which could not provoke the step ceiling it
// asserts precisely because the ceiling was unsettable in the packaged app.
for (const [key, value] of Object.entries(inputs.processEnv)) {
if (!ENV_PASSTHROUGH_PREFIXES.some((prefix) => key.startsWith(prefix))) {
continue;
}
if (value !== undefined && value !== "") {
env[key] = value;
}
}
env.PYTHONPATH = pythonPathValue(inputs.pathDelimiter);
env.PYTHONUNBUFFERED = "1";
// Preview connectors OFF by default in the desktop app; the passthrough above
Expand Down
Loading