Skip to content

Allow contributors to run app-hosted tests locally#436

Merged
FuJacob merged 1 commit into
mainfrom
fix/local-test-signing
May 30, 2026
Merged

Allow contributors to run app-hosted tests locally#436
FuJacob merged 1 commit into
mainfrom
fix/local-test-signing

Conversation

@FuJacob
Copy link
Copy Markdown
Owner

@FuJacob FuJacob commented May 30, 2026

Summary

App-hosted unit tests (CotabbyTests) were unrunnable for any contributor who does not hold the G946M8K23B Apple Developer certificate. The root cause is a combination of two settings on the Cotabby target:

  • ENABLE_HARDENED_RUNTIME: YES — macOS enforces that bundles dynamically loaded into a Hardened Runtime process must be signed, and by default requires a matching Team ID.
  • CODE_SIGNING_ALLOWED: NO on the test bundle — prevented even ad-hoc signing, so macOS rejected the injection.

Fix: add Cotabby-Debug.entitlements with com.apple.security.get-task-allow = true and wire it into the app target's Debug config. This entitlement tells macOS to relax Hardened Runtime injection restrictions for debug/test builds. Also remove CODE_SIGNING_ALLOWED: NO from CotabbyTests so the bundle receives ad-hoc signing and can be loaded by the relaxed host.

Release/Archive builds are unaffected: the entitlements file is only applied in Debug config, and CI continues to sign with the full team certificate.

Validation

xcodebuild -project Cotabby.xcodeproj -scheme Cotabby \
  -destination 'platform=macOS' build-for-testing
# ** TEST BUILD SUCCEEDED **

swiftlint lint --quiet
# exit 0

xcodegen generate
# Created project at Cotabby.xcodeproj  (no drift)

End-to-end test action requires the team certificate locally; build-for-testing confirms the test bundle compiles and links correctly against the host app.

Linked issues

Refs the note in CLAUDE.md: "If app-hosted tests fail because of local signing or Team ID mismatch, report the exact failure and still run build-for-testing."

Risk / rollout notes

  • Cotabby-Debug.entitlements is only applied to Debug builds via the configs.Debug.CODE_SIGN_ENTITLEMENTS key in project.yml. Release/Archive builds have no entitlements file change.
  • get-task-allow is a standard Apple-blessed entitlement for test hosts; Xcode adds it automatically when managing entitlements for app-hosted tests. We are making it explicit so XcodeGen propagates it correctly.
  • Removing CODE_SIGNING_ALLOWED: NO from CotabbyTests allows ad-hoc signing (CODE_SIGN_IDENTITY: "-") to proceed, which is benign for a test bundle that is never distributed.

Greptile Summary

This PR fixes app-hosted test execution for contributors without the G946M8K23B team certificate by adding a Cotabby-Debug.entitlements file with com.apple.security.get-task-allow and wiring it into the Debug build config, while also removing CODE_SIGNING_ALLOWED: NO from the test bundle so ad-hoc signing can proceed.

  • Cotabby-Debug.entitlements: Minimal entitlements plist containing only com.apple.security.get-task-allow = true, scoped exclusively to the Debug configuration via project.yml and project.pbxproj.
  • project.yml / project.pbxproj: CODE_SIGN_ENTITLEMENTS is set under configs.Debug for the Cotabby app target only; the Release config remains unchanged. CODE_SIGNING_ALLOWED: NO is removed from CotabbyTests base settings, allowing ad-hoc signing (CODE_SIGN_IDENTITY: \"-\") to proceed for the test bundle in both Debug and Release.

Confidence Score: 5/5

Safe to merge; changes are correctly scoped to Debug builds and do not touch Release or Archive signing.

The entitlements file contains only the single get-task-allow key Apple explicitly designates for debug/test hosts, and it is wired exclusively to the Debug build configuration in both project.yml and the generated project.pbxproj. The Release XCBuildConfiguration block for the Cotabby target has no CODE_SIGN_ENTITLEMENTS entry, so distribution builds are untouched. Removing CODE_SIGNING_ALLOWED: NO from the test bundle is safe because CODE_SIGNING_REQUIRED: NO remains and the bundle is never distributed. The project.yml and project.pbxproj are in sync.

No files require special attention.

Important Files Changed

Filename Overview
Cotabby-Debug.entitlements New file containing only com.apple.security.get-task-allow; minimal, correct, and scoped to Debug builds only.
project.yml Adds CODE_SIGN_ENTITLEMENTS under configs.Debug for the app target (correctly scoped) and removes CODE_SIGNING_ALLOWED: NO from CotabbyTests base settings; both changes look correct.
Cotabby.xcodeproj/project.pbxproj Reflects project.yml changes: entitlements file added only in the Debug XCBuildConfiguration block for Cotabby; CODE_SIGNING_ALLOWED: NO removed from both Debug and Release blocks of CotabbyTests.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[xcodebuild test / build-for-testing] --> B{Build Config?}
    B -- Debug --> C[Cotabby app signed with Cotabby-Debug.entitlements]
    C --> D[get-task-allow = true
Relaxes Hardened Runtime injection restrictions]
    D --> E[CotabbyTests bundle ad-hoc signed via CODE_SIGN_IDENTITY = '-']
    E --> F[macOS loads test bundle into app process]
    B -- Release / Archive --> G[Cotabby app signed with team certificate only]
    G --> H[Full Hardened Runtime enforcement]
Loading

Reviews (1): Last reviewed commit: "Allow contributors to run app-hosted tes..." | Re-trigger Greptile

Add a Debug-only entitlements file with get-task-allow so macOS relaxes
Hardened Runtime restrictions during test injection. Remove
CODE_SIGNING_ALLOWED=NO from CotabbyTests so the bundle is ad-hoc signed
and can be loaded into the debug-signed host app.
@FuJacob FuJacob merged commit 5730bbc into main May 30, 2026
4 checks passed
FuJacob added a commit that referenced this pull request May 31, 2026
Locks the anti-regression invariant behind the chat-template fix: the user
turn must be exactly the prefix with no "Text before caret:" / "Final
instruction:" label scaffolding (the strings small instruct models echoed
into ghost text), the prefix must not leak into the system turn, and
profile/rules/clipboard/screen context must land in the system turn (and be
omitted when absent). Six pure-function cases.

Full app-hosted suite: 320 tests, 0 failures (run with CODE_SIGNING_ALLOWED=NO
per the local-test setup added in #436).
FuJacob added a commit that referenced this pull request May 31, 2026
Locks the anti-regression invariant behind the chat-template fix: the user
turn must be exactly the prefix with no "Text before caret:" / "Final
instruction:" label scaffolding (the strings small instruct models echoed
into ghost text), the prefix must not leak into the system turn, and
profile/rules/clipboard/screen context must land in the system turn (and be
omitted when absent). Six pure-function cases.

Full app-hosted suite: 320 tests, 0 failures (run with CODE_SIGNING_ALLOWED=NO
per the local-test setup added in #436).
FuJacob added a commit that referenced this pull request May 31, 2026
Locks the anti-regression invariant behind the chat-template fix: the user
turn must be exactly the prefix with no "Text before caret:" / "Final
instruction:" label scaffolding (the strings small instruct models echoed
into ghost text), the prefix must not leak into the system turn, and
profile/rules/clipboard/screen context must land in the system turn (and be
omitted when absent). Six pure-function cases.

Full app-hosted suite: 320 tests, 0 failures (run with CODE_SIGNING_ALLOWED=NO
per the local-test setup added in #436).
FuJacob added a commit that referenced this pull request May 31, 2026
Locks the anti-regression invariant behind the chat-template fix: the user
turn must be exactly the prefix with no "Text before caret:" / "Final
instruction:" label scaffolding (the strings small instruct models echoed
into ghost text), the prefix must not leak into the system turn, and
profile/rules/clipboard/screen context must land in the system turn (and be
omitted when absent). Six pure-function cases.

Full app-hosted suite: 320 tests, 0 failures (run with CODE_SIGNING_ALLOWED=NO
per the local-test setup added in #436).
FuJacob added a commit that referenced this pull request May 31, 2026
Locks the anti-regression invariant behind the chat-template fix: the user
turn must be exactly the prefix with no "Text before caret:" / "Final
instruction:" label scaffolding (the strings small instruct models echoed
into ghost text), the prefix must not leak into the system turn, and
profile/rules/clipboard/screen context must land in the system turn (and be
omitted when absent). Six pure-function cases.

Full app-hosted suite: 320 tests, 0 failures (run with CODE_SIGNING_ALLOWED=NO
per the local-test setup added in #436).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant