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
89 changes: 84 additions & 5 deletions .github/workflows/five-platform-live-qa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@
# FSD/ONE_CLIENT_N_NODES.md describes and is why no Android node binary is
# needed — CIRISServer publishes none.
#
# A SECOND NODE, ONLY WHEN A FLOW ASKS. A flow with `fixture: two_node` (a
# receipt to open, a peer to pick, a room with a real message) gets a second
# ciris-server on the same runner — the binary this leg already downloaded,
# started natively on 5242/5243 with its own --home under $RUNNER_TEMP and a
# unique --key-id — claimed, announced, peered with the leg's node, and seeded
# with a contact each way and one chat message (testing/gate/two_node.py, the
# Docker-free port of CIRISServer harness/mesh-repro/scenarios/chat.sh). Every
# leg passes --node-binary; a run with no such flow never starts it. Each job
# tears it down under always(), because the flow runner's `finally` does not
# survive a SIGKILL.
#
# :4243 AND NOT :8080. A bare ciris-server has no brain and never binds the
# agent's port; see .github/actions/ciris-node. This comment said 8080 for the
# whole life of the gate, and so did every probe and forward under it.
Expand Down Expand Up @@ -241,7 +252,37 @@ jobs:
python3 -m testing.gate.run_platform --platform desktop --xvfb \
--jar "${{ steps.art.outputs.jar }}" \
--node-version "${{ steps.node.outputs.version }}" \
--shots shots --report reports/linux.json --flows testing/flows
--shots shots --report reports/linux.json --flows testing/flows \
--node-binary node/ciris-server --peer-work "$RUNNER_TEMP/two-node-linux"

# THE ANDROID LEG SHARES THIS NODE, AND THE FIXTURE CHANGED IT. A
# `fixture: two_node` flow on the desktop leg leaves the node with a
# contact and a room; the Android leg's bare-node flows ("People with no
# contacts yet") would then fail for the desktop's reason. So when the
# fixture ran (it writes values.json), the Android leg gets a fresh node
# on a fresh home, and claims it through its own wizard like any first run.
- name: Was the node seeded by the two-node fixture?
id: seeded
if: always()
run: |
python3 -m testing.gate.two_node down --work "$RUNNER_TEMP/two-node-linux" || true
if [ -f "$RUNNER_TEMP/two-node-linux/values.json" ]; then
echo "seeded=true" >> "$GITHUB_OUTPUT"
for port in 4242 4243; do
pids=$(sudo lsof -ti "tcp:$port" -sTCP:LISTEN 2>/dev/null || true)
[ -n "$pids" ] && { echo "stopping the seeded node on :$port ($pids)"; sudo kill $pids || true; }
done
sleep 2
else
echo "seeded=false" >> "$GITHUB_OUTPUT"
fi

- name: A fresh node for the Android leg
if: steps.seeded.outputs.seeded == 'true'
uses: ./.github/actions/ciris-node
with:
home: ${{ runner.temp }}/ciris-home-android
log: node-android.log

# WITHOUT THIS THE EMULATOR RUNS IN SOFTWARE AND DIES.
#
Expand Down Expand Up @@ -301,7 +342,17 @@ jobs:
#
# after the emulator had booted and the whole leg had been paid for.
# It reads worse on one line and it is the form that runs.
script: python3 -m testing.gate.run_platform --platform android --apk "${{ steps.art.outputs.apk }}" --node-version "${{ steps.node.outputs.version }}" --shots shots --report reports/android.json --flows testing/flows; rc=$?; adb logcat -d > logcat.txt 2>&1; exit $rc
script: python3 -m testing.gate.run_platform --platform android --apk "${{ steps.art.outputs.apk }}" --node-version "${{ steps.node.outputs.version }}" --shots shots --report reports/android.json --flows testing/flows --node-binary node/ciris-server --peer-work "$RUNNER_TEMP/two-node-android"; rc=$?; adb logcat -d > logcat.txt 2>&1; exit $rc

# THE PEER DIES WITH THE JOB, WHATEVER KILLED IT. The runner's `finally`
# and its SIGTERM handler cover a failed flow and a cancelled step; this
# covers the rest, by the pidfile each leg's fixture wrote.
- name: Tear down the two-node peers
if: always()
run: |
for leg in linux android; do
python3 -m testing.gate.two_node down --work "$RUNNER_TEMP/two-node-$leg" || true
done

# ALWAYS. A failure you cannot diagnose from the artifact costs a re-run
# to learn what this run already knew.
Expand All @@ -313,8 +364,11 @@ jobs:
shots/
reports/
node.log
node-android.log
*-app.log
logcat.txt
${{ runner.temp }}/two-node-*/peer.log
${{ runner.temp }}/two-node-*/values.json

macos-ios:
name: macos desktop + ios simulator
Expand Down Expand Up @@ -373,7 +427,8 @@ jobs:
python3 -m testing.gate.run_platform --platform desktop \
--jar "$(python3 -m testing.gate.candidate_artifacts --kind desktop | tail -1)" \
--node-version "${{ steps.node.outputs.version }}" \
--shots shots --report reports/macos.json --flows testing/flows
--shots shots --report reports/macos.json --flows testing/flows \
--node-binary node/ciris-server --peer-work "$RUNNER_TEMP/two-node-macos"

# ── THE iOS BUNDLE, MATERIALIZED THE WAY THE AGENT'S GATE DOES IT ──────
#
Expand Down Expand Up @@ -655,10 +710,17 @@ jobs:
echo "simulator: $UDID"
xcrun simctl boot "$UDID" || true
xcrun simctl bootstatus "$UDID" -b
# The macOS leg's peer, if its runner was killed before `finally`:
# 5242/5243 must be free for this leg's own.
python3 -m testing.gate.two_node down --work "$RUNNER_TEMP/two-node-macos" || true
rc=0
# The simulator shares the runner's loopback, so the node the app
# embeds is 127.0.0.1:4243 from here too — the fixture's --node-url
# default — and the peer on 5242/5243 does not collide with it.
python3 -m testing.gate.run_platform --platform ios --app "$app" --udid "$UDID" \
--node-version "${{ steps.node.outputs.version }}" \
--shots shots --report reports/ios.json --flows testing/flows || rc=$?
--shots shots --report reports/ios.json --flows testing/flows \
--node-binary node/ciris-server --peer-work "$RUNNER_TEMP/two-node-ios" || rc=$?

# THE APP'S OWN ACCOUNT, EITHER WAY. Run 35359571538 got the iOS app
# to a real screen — "Engine Failed to Start: server did not become
Expand Down Expand Up @@ -709,6 +771,13 @@ jobs:
fi
exit $rc

- name: Tear down the two-node peers
if: always()
run: |
for leg in macos ios; do
python3 -m testing.gate.two_node down --work "$RUNNER_TEMP/two-node-$leg" || true
done

- if: always()
uses: actions/upload-artifact@v4
with:
Expand All @@ -719,6 +788,8 @@ jobs:
node.log
*-app.log
ios-logs/
${{ runner.temp }}/two-node-*/peer.log
${{ runner.temp }}/two-node-*/values.json
client/iosApp/app_packages_native_sim/MANIFEST.txt
client/iosApp/substrate.lock.json

Expand Down Expand Up @@ -762,7 +833,13 @@ jobs:
python3 -m testing.gate.run_platform --platform desktop \
--jar "$(python3 -m testing.gate.candidate_artifacts --kind desktop | tail -1)" \
--node-version "${{ steps.node.outputs.version }}" \
--shots shots --report reports/windows.json --flows testing/flows
--shots shots --report reports/windows.json --flows testing/flows \
--node-binary node/ciris-server --peer-work "$RUNNER_TEMP/two-node-windows"

- name: Tear down the two-node peer
if: always()
shell: bash
run: python3 -m testing.gate.two_node down --work "$RUNNER_TEMP/two-node-windows" || true

- if: always()
uses: actions/upload-artifact@v4
Expand All @@ -773,6 +850,8 @@ jobs:
reports/
node.log
*-app.log
${{ runner.temp }}/two-node-*/peer.log
${{ runner.temp }}/two-node-*/values.json

gallery:
name: screenshot gallery
Expand Down
2 changes: 1 addition & 1 deletion FSD/CSD/CSD-005-people.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ again. On a fresh node with no contacts → `card_contacts_add` and no

## 5. QA plan

Spec complete and flow written (`testing/flows/drafts/csd-005-people.yaml`, floor `>=0.5.225`); promotes to `testable` when the floor is no longer `unreleased` and the flow runs on the matrix (#97).
Spec complete and flow written (`testing/flows/drafts/csd-005-people.yaml`, floor `>=0.5.225`); promotes to `testable` when the floor is no longer `unreleased` and the flow runs on the matrix (#97) The populated list and the receipt step are no longer optional: `fixture: two_node` seeds the contact, and on the Linux desktop leg (2026-09-28) the row, its trust chip, its hamburger and the five-fact receipt all passed. The flow then failed at `the_add_card_opens_with_paste_and_scan`: the desktop add card shows `btn_scan_contact_code_status`, not `btn_scan_contact_code` — a defect in that step, unrelated to the fixture.

**Platforms.** All five. The Contacts entry screen is what CIRISAgent's
five-platform gate leans on; no tag it drives has changed.
Expand Down
2 changes: 1 addition & 1 deletion FSD/CSD/CSD-006-receipt.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ Bound per surface; CSD-005 §4 is the first instance.

## 5. QA plan

**Flow not complete.** `testing/flows/drafts/csd-006-receipt.yaml` (floor `>=0.5.225`) never opens a receipt: the hamburger's tag is `btn_receipt_<keyId>`, a flow `click:` takes one literal tag, and no fixture seeds a contact whose key id the flow could name. Every fact step is therefore gated on `sheet_receipt` and always skips. It is complete when a seeded contact (or a runner that can click the first match of `btn_receipt_*`) lets it open a concrete receipt; until then this card is not ready to promote.
Spec complete and flow written (`testing/flows/drafts/csd-006-receipt.yaml`, floor `>=0.5.225`, `fixture: two_node`). The two-node fixture (`testing/gate/two_node.py`) seeds a contact, and the flow opens `btn_receipt_${PEER_KEY_ID}` and asserts all five facts, the wire dimension and the wire rule (`chat:`). Run locally on the Linux desktop leg 2026-09-28 (candidate 0.5.224 checked as 0.5.225, node v0.5.217): 5/6 passed, the grant-less step skipped as designed because the node sends the grant. Not yet run on the other four legs; promotes when the floor is met and it runs on the matrix.

A card CSD that binds this template asserts `visible:` on all five `receipt_*`
tags after clicking its `btn_receipt_<id>`; a card whose rows are furniture
Expand Down
2 changes: 1 addition & 1 deletion FSD/CSD/CSD-047-network-content.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ expect:

## 5. QA plan

**Flow not complete.** `testing/flows/drafts/csd-047-network-content.yaml` (floor `unreleased`) never reaches the digest step: that needs a peer picked by `peer_pick_row_<keyId>`, a `click:` takes one literal tag, and no fixture seeds a peer whose key id the flow could name. The digest steps are gated on `input_content_id` and always skip, so the flow is green without driving the half this card is about. It is complete when a seeded peer lets it pick one. Until then this card is not ready to promote.
Spec complete and flow written (`testing/flows/drafts/csd-047-network-content.yaml`, floor `unreleased`, `fixture: two_node`): it enters from the hub's `tile_federation_content` and picks `peer_pick_row_${PEER_NODE_KEY_ID}`, the peer the fixture admitted. It has NOT run: besides the floor, the runner cannot reach its first screen on this build — nav_map's hop to LayerGlobalCommons (`circle_global_commons -> tab_rules -> nav_epistemic_layer_global_commons`) stops on CircleTab with the last tag never appearing (Linux desktop, 2026-09-28). A real fetch still needs a digest the peer holds, which the fixture does not seed.

**Platforms.** All five, as the node's owner. A real fetch needs a second node
holding a known digest; the matrix stands one up.
Expand Down
2 changes: 1 addition & 1 deletion FSD/CSD/CSD-091-user-chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ and §5 disclaims it for the matrix.

## 5. QA plan

**Flow not complete.** `testing/flows/drafts/csd-091-user-chat.yaml` (floor `>=0.5.225`) cannot go green on an ordinary matrix run as written: `a_room_with_history` is gated only on `chat_transcript`, which also renders for a room holding nothing but a system note (the single-node `awaiting_peer` room, `ChatScreen.kt`) and for a refusal over an empty room; `SystemNoteRow` carries no tag, so `count: chat_msg_* min 1` fails on the ordinary run and nothing on screen can gate it. It is complete when system notes are tagged or a two-node fixture seeds a message. Until then this card is not ready to promote.
Spec complete and flow written (`testing/flows/drafts/csd-091-user-chat.yaml`, floor `>=0.5.225`, `fixture: two_node`): it enters the room from People by `btn_contacts_chat_${PEER_KEY_ID}` and asserts the peer's message by its attestation id (`chat_msg_${MESSAGE_ATTESTATION_ID}`), not a class count a system note could satisfy. It CANNOT go green on the released line: two unconferred v0.5.217 nodes never key the pair room (peers admitted ADVISORY, frames fail the SignedTransportDestination check), so no message crosses and `a_room_with_history` fails naming why (`evidence/blocked_upstream.tsv`). Linux desktop, 2026-09-28: the entry, composer and refresh steps passed; history failed as stated.

**Platforms.** All five for the transcript and the refusals; **two nodes** for
anything that involves the other side, which `testing/gate/node_fixture.py` does
Expand Down
1 change: 1 addition & 0 deletions evidence/blocked_upstream.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ issue repo scan_root glob needle files lines kind predicate
471 CIRISAgent . requirements*.txt ciris-client 0 0 absence THE MIGRATION ITSELF. Neither consumer depends on the client package yet; both carry their own ~200k-line copy. The extraction is not done when this repo has the source — it is done when this needle scores 1 in CIRISAgent AND in CIRISServer, and their client/ directories are deleted. Until then this repo is a THIRD tree, which is the cost AGENTS.md warned about and the reason it is worth paying only if it ends. Adoption issues filed 2026-08-20: CIRISServer#471 and CIRISAgent#1089.
379 CIRISServer client *.gradle.kts proguard|minifyEnabled 0 0 absence MEASURED in CIRISClient#1: the desktop uber-jar is 66.48 MiB and compresses to a 65,488,254-byte wheel - 62.5% of PyPI's 104,857,600-byte limit on its own, because ProGuard is blocked on ktor 3.x. That is why a node build and an agent build could not ship in ONE wheel — two ~63 MiB desktop bundles do not fit — and it is half the argument CIRISServer#479 settled by deleting the flavor outright: one artifact ships and narrows itself against the probed node. The localization bundles inside are the product and are never cut for size. packaging/check_wheel_size.py measures it every build.
574 CIRISServer src *.rs run_without_ai 0 0 absence A NODE INSTALLED TO RUN WITHOUT AI STILL REPORTS `agent.folded=true`. /v1/system/health carries data.agent.{folded,reachable}, and on a run-without-AI install the fold is reported present and never becomes reachable — so clientModeFrom() returns undetermined forever and the client retries for its whole StartupBudget (60s on Android, measured as 62s of nothing drivable before Login; CIRISClient#48). The client CANNOT contradict it: /v1/setup/status returns only {setup_required, has_env_file, has_admin_user}, and run_without_ai is accepted by /v1/setup/complete but never read back, so nothing on the wire distinguishes 'a brain that is slow to answer' from 'a brain that was never going to'. CIRISClient mitigated the COST (the retry no longer blocks startup routing — CIRISApp.kt commitGate) but cannot fix the SIGNAL. The obligation lands when the node either reports folded=false for a run-without-AI install or exposes run_without_ai on /v1/setup/status; either makes this needle score >0 in the server tree. Filed as CIRISServer#574; either fix closes it (report folded=false for a run-without-AI install, or expose run_without_ai on /v1/setup/status). Counts are 0/0 by inspection of the shipped SetupStatusData contract, NOT measured against a CIRISServer checkout: that tree is not present here.
698 CIRISServer - - - - - untestable TWO RELEASED NODES CANNOT KEY A PAIR ROOM. Measured 2026-09-28 with testing/gate/two_node.py on v0.5.217 (the binary every live-QA leg downloads): claim, announce, peering both ways (production self-key-record), owner-key replication and POST /v1/contacts all succeed, but the room stays chat.state.awaiting_peer for 480 s — each node admits the other ADVISORY (not conferred), inbound frames fail 'no hybrid-verified SignedTransportDestination binds this (peer, dest) pair' (CIRISEdge#393 item 2), and the joiner's KeyPackage never replicates. CIRISServer's harness/mesh-repro chat ladder is green only on a test-anchor build with a test trust root (test-blessed-self-record / test-admit-peer are cfg(feature=test-anchor)). So CSD-091's 'a room with a real message' cannot pass on the matrix. Needs filing upstream: a way for two unconferred released nodes to key a pair room, or a released test-root knob for harnesses. Closes when two_node.py reports message_arrived=true against a released binary.
Loading
Loading