Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 2 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,27 +95,20 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Full history so the back-edge-ceiling ratchet can read the baseline
# at the merge-base and reject a same-PR raise. Mirrors the Fallow job.
fetch-depth: 0

- name: Setup toolchain
uses: ./.github/actions/setup-node-pnpm
with:
install-deps: false

- name: Check import-direction DAG and ratchets
- name: Check import-direction DAG
# Generalizes the former inline commands/-import grep into a structured
# import-direction lint over the resolved graph. See scripts/layering/check.ts
# and CONTEXT.md (Architecture: folder DAG + layering lint). Invoked directly
# (no deps needed) so the job stays fast and does not require a pnpm install.
# LAYERING_BASE pins the baseline-monotonicity comparison to the PR base.
env:
LAYERING_BASE: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
run: |
node --experimental-strip-types --test scripts/layering/model.test.ts
node --experimental-strip-types scripts/layering/check.ts --base "$LAYERING_BASE"
node --experimental-strip-types scripts/layering/check.ts

affected-selector:
name: Affected-check Selector
Expand Down
18 changes: 6 additions & 12 deletions .github/workflows/perf-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,12 @@ jobs:
profile: pixel_7
target: google_apis_playstore
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -no-metrics
script: |
set -e
# Disable animations up front so accessibility dumps don't time out (the harness
# also runs `settings animations off`, this is belt-and-suspenders).
adb -s emulator-5554 shell settings put global window_animation_scale 0 || true
adb -s emulator-5554 shell settings put global transition_animation_scale 0 || true
adb -s emulator-5554 shell settings put global animator_duration_scale 0 || true
node --experimental-strip-types scripts/perf/run.ts \
--platform android \
--serial emulator-5554 \
--n "$PERF_ROUNDS" --warmup 1 \
--out-dir "$GITHUB_WORKSPACE/perf-results"
script: >-
set -e;
adb -s emulator-5554 shell settings put global window_animation_scale 0 || true;
adb -s emulator-5554 shell settings put global transition_animation_scale 0 || true;
adb -s emulator-5554 shell settings put global animator_duration_scale 0 || true;
node --experimental-strip-types scripts/perf/run.ts --platform android --serial emulator-5554 --n "$PERF_ROUNDS" --warmup 1 --out-dir "$GITHUB_WORKSPACE/perf-results"

- name: Upload Android perf report
if: always()
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ This repo encodes invariants as self-declaring gates. The correct response to a
- every command declares a timeout policy on its descriptor: the timeout-policy completeness test
- TS/Swift rule parity: golden tables under `contracts/fixtures/` consumed by vitest and the gated XCTest
- cross-command apple-leak guard, folder DAG/import lint (including zero value-import cycles and
down-only exact target-spine back-edge identities), fallow (dead code, duplication, complexity)
zero target-spine back-edges), fallow (dead code, duplication, complexity)

## Hard Rules
- Use process helpers from `src/utils/exec.ts` for TypeScript process execution: `runCmd`, `runCmdStreaming`, `runCmdSync`, `runCmdBackground`, and `runCmdDetached`. Do not import raw `spawn`/`spawnSync` outside `src/utils/exec.ts`; add or extend an exec helper instead. Plain `.mjs` packaging fixtures that cannot import TypeScript helpers should keep child-process usage local and prefer `execFile`/`execFileSync` over spawn.
Expand Down
3 changes: 1 addition & 2 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ The perfect-shape refactor is complete and merged. Its end-state:
- Folder DAG + layering lint. `kernel`/`remote`/`metro`/`client`/`snapshot`/`screenshot-diff`/
`replay`/`cli-parser`/`daemon-client`+`server`/`sdk` are arranged as an import-direction DAG
(imports point down toward the kernel sink), enforced in CI by `scripts/layering/check.ts`.
The gate rejects production value-import cycles and enforces the ranked target-spine's exact
back-edge identities as a down-only baseline in `scripts/layering/back-edge-baseline.json`.
The gate rejects production value-import cycles and every ranked target-spine back-edge.
- Agent-cost. Responses carry a cost block and MCP `outputSchema`, rendered through a leveled
`ResponseView`.

Expand Down
90 changes: 81 additions & 9 deletions docs/agents/contract-projection-output-economy-spike.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

Issues: #1185, #1186

This report keeps the two experiments bounded: one typed command family for executable contract
projection and one opt-in response family for digest expansion.
This report records two completed, bounded experiments: one typed command family for executable
contract projection and one opt-in response family for digest expansion. Neither result starts a
repository-wide migration.

## Selection evidence

Expand All @@ -27,10 +28,10 @@ The baseline has three independent command-to-surface declarations per selected
2. the public client method signature;
3. the keyed MCP output-schema entry.

That is 15 independent projection declarations around five executable definitions. The spike will
make each executable definition the single command-to-client/MCP projection declaration and derive
the client surface and MCP schema map from those five definitions. Success is five declarations
instead of 15, without changing CLI, daemon, client, or MCP behavior.
That was 15 independent projection declarations around five executable definitions. The experiment
made each executable definition the single command-to-client/MCP projection declaration and
derived the client surface and MCP schema map from those five definitions. It reached five
declarations instead of 15 without changing CLI, daemon, client, or MCP behavior.

The experiment stops at these five commands. It must not continue if it needs generated source, a
schema DSL, a new dependency, a parallel registry, a new import back-edge, weaker parity checks,
Expand Down Expand Up @@ -63,7 +64,7 @@ seven commands, zero fallback observations, exactly one retry, and all in-sessio
preserved. The network addition must keep those measurements unchanged and separately prove that a
failed request remains identifiable and actionable from the digest.

## Measurement plan
## Measurement method

- Declaration count: count independent selected-command projection declarations before and after.
- Runtime: compare median cold imports of the MCP command-tool projection at the merge base and
Expand Down Expand Up @@ -92,8 +93,11 @@ from those projections, `AgentDeviceCommandClient` derives the five navigation m
- Layering/Fallow: green; no new value back-edge (type-only import from client, value import only
from the already-command-importing MCP schema map).

Recommendation: #1185 should CONTINUE incrementally, one already-typed closed-contract family at a
time, re-measuring declarations and bundle each step and stopping on any stop condition.
The selected five-command experiment is complete. Other already-typed closed-contract families are
candidates only, not a migration queue. Any follow-up must be proposed and measured independently,
must reduce declarations without weakening ownership or parity checks, and must stop on any of the
conditions above. Generated source, a schema DSL, a new dependency, a parallel registry, a new
back-edge, or a material runtime/bundle regression remains a stop signal.

## Digest expansion results (#1186)

Expand Down Expand Up @@ -124,3 +128,71 @@ Defaults are unchanged; the digest is strictly opt-in via a non-default response
risk: the dropped-field set is intentionally specific to the known verbose network payload fields;
if a backend introduces a new large per-entry field it would remain until added to the drop list
(safe by default — unknown fields are preserved, never silently narrowed).

The selected network experiment is complete. Events, debug symbols, recording, and any new family
remain candidates only. A follow-up requires paired evidence:

1. deterministic byte/shape reduction on representative fixtures; and
2. unchanged or better actionability, including follow-up command choice, fallback observation
count, retry count, identity, refs, warnings, artifacts, and in-session recovery.

Byte reduction alone is not a successful digest.

## Post-merge closeout evidence

The deterministic suite was rerun after both implementations merged:

- `pnpm test:output-economy`: 4 files and 26 tests passed.
- Network default/digest: 26,371 / 1,665 bytes; all eight entries and failed-request identity
remain available.
- MCP snapshot result: 1,173 bytes with four refs in the combined text + structured result.
- Routine workflow: 2,276 bytes across seven commands, zero fallback observations, one retry, and
`recoveryPreservesSession=true` with code, session, refs generation, retriable flag, and hint.

`pnpm bench:help-conformance --dry-run` generated eight prompts for the four output/recovery cases
across the configured Haiku and GPT mini runners. This proves case selection and prompt plumbing
only. No scored small-model result is recorded here because authenticated `claude` and `codex`
runners were unavailable; future scored runs remain informational actionability oracles, not CI
gates.

No new live-device result is recorded in this closeout. The local environment had no simulator,
emulator, or attached device. A one-round `perf-nightly` dispatch on the PR branch was also
unavailable because the GitHub integration could not create workflow-dispatch events. Historical
Android nightly logs did expose a deterministic workflow defect: the emulator action executes each
`script` line independently, so shell continuation backslashes reached `scripts/perf/run.ts` as a
literal `\` argument. The action now receives one folded shell script, so `set -e` governs the
benchmark and no continuation tokens cross action boundaries. The next scheduled run after merge
is the live validation point. Deterministic fixtures are not presented as live reliability
evidence.

The six-month churn × current-size refresh continues to identify bounded candidates rather than a
split mandate:

| File | Commits | LOC | Churn × LOC |
|---|---:|---:|---:|
| `src/cli/parser/cli-help.ts` | 34 | 1,227 | 41,718 |
| `src/core/dispatch-interactions.ts` | 31 | 1,123 | 34,813 |
| `src/client/client-types.ts` | 21 | 1,139 | 23,919 |

Higher-churn files below 1,000 lines include `src/core/dispatch.ts`, `src/cli.ts`, and several
daemon handlers. These are seam-stability signals, not automatic split targets. A future split must
answer one ownership question, improve locality, preserve dependency direction, and mirror source
and test topology. The refresh deliberately avoids a blanket LOC gate; path-based commit counts
also undercount renamed files unless inspected with `git log --follow`.

Repeat the broad ranking with:

```sh
SINCE="$(node -e "const d = new Date(); d.setUTCMonth(d.getUTCMonth() - 6); process.stdout.write(d.toISOString().slice(0, 10))")"
git log --since="$SINCE" --name-only --pretty=format: -- 'src/**' \
| rg -v '^$|__tests__|\.test\.' | sort | uniq -c | sort -rn \
| while read -r commits file; do
test -f "$file" || continue
lines="$(wc -l < "$file")"
printf '%d\t%d\t%d\t%s\n' "$((commits * lines))" "$commits" "$lines" "$file"
done \
| sort -rn
```

`src/utils` contains 106 TypeScript files: 71 production files and 35 tests. That is a standing
opportunistic ownership policy, not a mass relocation backlog.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@
"check:affected": "node --experimental-strip-types scripts/check-affected/run.ts",
"check:affected:test": "node --experimental-strip-types --test scripts/check-affected/model.test.ts scripts/check-affected/run.test.ts",
"check:layering": "node --experimental-strip-types --test scripts/layering/model.test.ts && node --experimental-strip-types scripts/layering/check.ts",
"check:layering:baseline": "node --experimental-strip-types scripts/layering/check.ts --update-baseline",
"check:production-exports": "fallow dead-code --config fallow-production-exports.json --production --unused-exports --baseline fallow-baselines/production-unused-exports.json --fail-on-issues",
"check:production-exports:baseline": "fallow dead-code --config fallow-production-exports.json --production --unused-exports --save-baseline fallow-baselines/production-unused-exports.json --summary",
"check:bundle-owner-files": "node --experimental-strip-types scripts/check-bundle-owner-files.ts",
Expand Down
1 change: 0 additions & 1 deletion scripts/layering/back-edge-baseline.json

This file was deleted.

Loading
Loading