ios-sync
The facts
Its description
Regenerate the iOS debug bridge against the latest upstream gstack templates. (gstack) Triggers: resync the ios debug bridge; regenerate ios accessors; update the gstack ios instrumentation
The description the agent reads to decide when to use it. Phrases the author says should start it.
Tools it may use, or may not
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion
Tools it may use while it runs without asking you first.
Runs scripts
bin/gstack-skill-start, scripts/question-registry.ts, bin/gstack-question-preference, bin/gstack-question-log, bin/gstack-skill-end (first 5 of 6 lines)
Scripts the text names. Found by matching words, so treat them as pointers.
Stops for a person
4 lines stop to ask a person
Lines that stop and wait for a person before going on. Found by matching words, so treat them as pointers.
What leads to it
- gstack hands off to this PROACTIVE all, or user names it gstack, line 253
- ios-fix calls this on 409 schema_mismatch after rebuild ios-fix, line 167
Other skills that hand off to this one, call it, require it or offer it instead, as a model read them.
Licence
MIT, from LICENSE
The terms its author shares it under, which let it be shown here.
Other files in this skill (1) Close
The text
Most marks come from matching words in the file, so treat them as pointers.
<!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly --> <!-- Regenerate: bun run gen:skill-docs -->
Raw lines 1 to 21Boilerplate, lines 22 to 78. The same text appears in 45 other files here. Close
Telemetry (run last)
After workflow completion, log telemetry with ONE command. OUTCOME is success/error/abort/unknown; SESSION_ID and TEL_START are the values the preamble's skill-start output echoed. It also drains the artifacts-sync queue (the former skill-end sync step — do not run gstack-brain-sync separately).
PLAN MODE EXCEPTION — ALWAYS RUN: This writes telemetry to $GSTACK_STATE_ROOT/analytics/, matching preamble analytics writes.
~/.claude/skills/gstack/bin/gstack-skill-end --skill "ios-sync" --outcome OUTCOME \
--session-id "SESSION_ID" --tel-start "TEL_START" --used-browse USED_BROWSE \
--error-message "ERROR_MESSAGE" --failed-step "FAILED_STEP" 2>/dev/null || trueReplace OUTCOME and USED_BROWSE (yes/no) before running; substitute SESSION_ID/TEL_START from the skill-start echoes. ERROR_MESSAGE/FAILED_STEP are "" unless outcome is error. If the command is missing (stale install), skip telemetry — it never blocks the workflow.
Resync the iOS debug bridge
After /ios-qa is installed in an app, the user may:
- Add new
@Observableclasses or properties that need accessor coverage. - Upgrade gstack to a newer version with hardening fixes.
- Move the
// @Snapshotablegenerator marker comment to a different field.
This skill regenerates the relevant artifacts in place.
Templates live in upstream gstack. The installed gstack-ios-qa-regen launcher resolves its own gstack root and copies only the supported bridge files from ios-qa/templates/.
Phase 1: Detect installed version
- Read
<app>/DebugBridgeGenerated/.gstack-version(written by /ios-qa during install). If missing, treat the install as "unknown old version". - Read upstream version from
$GSTACK_ROOT/VERSION. - If versions match AND no new
@Observableclasses were added, exit early with "already up to date".
Phase 2: Regenerate codegen output
Run the deterministic regenerator once. --app-source is the directory the accessor scanner should inspect; --bridge-dir is the local Swift package that the app links in Debug builds:
~/.claude/skills/gstack/bin/gstack-ios-qa-regen \
--app-source "$APP_SOURCE_DIR" \
--bridge-dir "$APP_SOURCE_DIR/DebugBridge"The command removes only the known obsolete generated files from the former flat DebugBridgeGenerated/ layout before emitting the current accessor. Generation accepts file-scope observable classes and JSON-native scalar, array, String-keyed dictionary, and Optional field types. It rejects custom types, implicitly unwrapped Optionals, nested observable classes, and duplicate snapshot keys before writing a completion marker.
The composite-hash cache key handles whether anything actually needs regenerating; if Swift version, generator git rev, lockfile, source content, and platform triple all match the cache, this is a ~50ms no-op.
Raw lines 122 to 144Phase 3: Review the generated diff
- Review changes under
<app>/DebugBridge/and<app>/DebugBridgeGenerated/StateAccessor.swift. - Confirm the command did not modify the app's handwritten Swift files.
- Keep app-specific wiring in the app target; canonical bridge package files are regenerated from upstream and should not be hand-edited.
Phase 4: Verify
swift buildsucceeds against the app's package.xcodebuild -scheme <SchemeName>succeeds.- Re-launch the app on the device; daemon connects + rotates token.
GET /state/snapshotreturns the new accessor schema hash.
Failure modes
| Symptom | Action |
|---|---|
| Swift compile fails after regen | Revert via git restore + AskUserQuestion: surface the compile error |
| Codegen reports an invalid marked declaration | Use a file-scope observable class and a writable instance var with an explicit JSON-native type, internal/public setter, and a key unique across models; otherwise remove the // @Snapshotable marker. |
| Schema hash unchanged after adding new @Observable | No field has the standalone // @Snapshotable marker comment — codegen excludes unmarked state correctly. Add the comment immediately above each field that should be snapshotted. |
| Scanner sees generated bridge sources | Pass the narrow app source directory; the regenerator automatically excludes DebugBridgeGenerated and StateAccessor.swift. |
From garrytan/gstack at 3f79d81, 2026-11-01. Not read by a model yet. Summary and techniques by Claude Opus 5.5 on 2026-11-06.