Source profileQuality 93/100Review permissions

hushh-labs/hushh-research/.claude/skills/hushh-research-ship/SKILL.md

hushh-research-ship

The exact merge-and-deploy procedure for hushh-labs/hushh-research, written from a real 90-minute block. Use whenever work in this repo needs to reach main or UAT — raising the PR, merging it, dispatching deploy-uat or deploy-production, or when a merge reports "Review required" or "approval from someone other than the last pusher". Encodes which merge command survives the policy layer, the one branch-protection flag that causes the block, and the restore that must follow. Follow it instead of r

Source repository stars
25
Declared platforms
0
Static risk flags
2
Last source update
2026-08-25
Source checked
2026-08-25

Decision brief

What it does: where it fits

Merged is not shipped. Deployed is not shipped. Shipped is the live surface answering correctly.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/hushh-labs/hushh-research --skill ".claude/skills/hushh-research-ship"
    Safe inspection promptEditorial

    Inspect the Agent Skill "hushh-research-ship" from https://github.com/hushh-labs/hushh-research/blob/42522d05abd9f89f9de6b436befe801e0ea0585b/.claude/skills/hushh-research-ship/SKILL.md at commit 42522d05abd9f89f9de6b436befe801e0ea0585b. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      The procedure

      1. Confirm CI is green against the head SHA, not the PR summary — the summary can still show a previous run's result:

      Confirm CI is green against the head SHA, not the PR summary — the summary can still show aCapture the baseline so the restore is provable rather than asserted:Lift enforceadmins yourself — no human step is needed:
    2. 02

      UAT — the normal target. scope=auto lets the workflow detect backend/frontend.

      gh workflow run deploy-uat.yml --repo hushh-labs/hushh-research \ --ref main -f scope=auto -f sha=

      gh workflow run deploy-uat.yml --repo hushh-labs/hushh-research \ --ref main -f scope=auto -f sha=
    3. 03

      The merge block

      Try --admin first. Verified 2026-08-21: it merged 14 PRs back to back on a non-admin token with no enforceadmins lift at all. Everything below about lifting enforceadmins applies to admins only — see "Which case are you in?". On a non-admin account the lift is neither needed nor…

      Every merge invalidates every other open branch. The Base Freshness GateContract edits need two regenerations, not one. Changing anyA layout spec must be registered to run. CI runs test:layout-contracts
    4. 04

      What you will see

      …even though Ankit is in the bypass list and has told you to merge. (He is a repo admin, which the original write-up recorded as "maintainer". That distinction turns out to be the whole mechanism — see below.)

      …even though Ankit is in the bypass list and has told you to merge. (He is a repo admin, which the original write-up recorded as "maintainer". That distinction turns out to be the whole mechanism — see below.)
    5. 05

      Which case are you in? (answer this first)

      The block is not the same for everyone, which is what the original write-up got wrong. It turns on one thing: whether you are a repo admin.

      The block is not the same for everyone, which is what the original write-up got wrong. It turns on one thing: whether you are a repo admin.

    Permission review

    Static risk signals and limitations

    Runs scripts

    medium · line 166

    The documentation asks the agent to run terminal commands or scripts.

    python3 -c "

    Runs scripts

    medium · line 250

    The documentation asks the agent to run terminal commands or scripts.

    python3 scripts/ops/verify_live_environment.py --env uat

    Reads files

    low · line 290

    The documentation asks the agent to read local files, directories, or repositories.

    Read `safe-changes` in this same directory before touching any secret, IAM policy, or deploy config.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score93/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars25SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    hushh-labs/hushh-research
    Skill path
    .claude/skills/hushh-research-ship/SKILL.md
    Commit
    42522d05abd9f89f9de6b436befe801e0ea0585b
    License
    Apache-2.0
    Collected
    2026-08-25
    Default branch
    main
    View the original SKILL.md

    Shipping hushh-research

    Merged is not shipped. Deployed is not shipped. Shipped is the live surface answering correctly.

    This repo blocks merges in a way that looks like a permissions problem and is not. The block below cost about 90 minutes on 2026-08-06. It is fully understood — do not re-derive it.


    The merge block

    Try --admin first. Verified 2026-08-21: it merged 14 PRs back to back on a non-admin token with no enforce_admins lift at all. Everything below about lifting enforce_admins applies to admins only — see "Which case are you in?". On a non-admin account the lift is neither needed nor possible.

    What you will see

    Review required — At least 1 approving review is required by reviewers with write access.
    Merging is blocked — New changes require approval from someone other than the last pusher.
    

    …even though Ankit is in the bypass list and has told you to merge. (He is a repo admin, which the original write-up recorded as "maintainer". That distinction turns out to be the whole mechanism — see below.)

    Which case are you in? (answer this first)

    The block is not the same for everyone, which is what the original write-up got wrong. It turns on one thing: whether you are a repo admin.

    gh api repos/hushh-labs/hushh-research/collaborators/$(gh api user --jq .login)/permission --jq .permission
    gh api orgs/hushh-labs/teams/allowed-maintainers-to-approve/memberships/$(gh api user --jq .login) --jq .state
    
    You areWhat happensWhat to do
    not admin, on allowed-maintainers-to-approveenforce_admins does not apply to you; your bypass is live--admin merges. Never touch enforce_admins — you cannot read or write it anyway
    adminenforce_admins: true enforces protections against administrators, overriding your place on the bypass list--admin first; if GitHub refuses, the lift below is genuinely yours to run
    neitherno bypass existsget a review from someone else

    Why (2026-08-21, 14 merges)

    gh pr merge <N> --admin --merge --match-head-commit <SHA> went straight through on all 14, as anoushkauocwrite/maintain, not admin, and an active member of allowed-maintainers-to-approve. No enforce_admins call was made at any point. The PRs sat at BLOCKED / REVIEW_REQUIRED right up to the merge and merged anyway.

    That is only a contradiction if you believe the table below, which claims enforce_admins: true "makes that bypass list inert for everyone". It does not. enforce_admins means enforce all configured restrictions for administrators — it subjects admins to branch protection. It leaves bypass_pull_request_allowances working for everyone else.

    So both stories fit one mechanism. Ankit is an admin: enforce_admins applied the protections to him despite his bypass-list membership, so he was blocked, and lifting it exempted him — the original fix was right, for him. A non-admin on the bypass team was never affected by that flag at all.

    The failure mode of the old text was generalising one admin's experience into a rule for everyone, when the rule inverts for the non-admins doing most of the merging — and sends them at a procedure that 404s on their token, since reading or writing /branches/main/protection needs admin. (404, not 403: GitHub hides the object rather than admitting the permission denial.)

    Still unconfirmed: the current value of enforce_admins. It does not change the non-admin path, but it decides whether an admin still needs the lift today or whether someone has since turned it off, leaving the fallback dead. One call, from an admin account, settles it:

    gh api repos/hushh-labs/hushh-research/branches/main/protection \
      --jq '{enforce_admins: .enforce_admins.enabled,
             reviews: .required_pull_request_reviews.required_approving_review_count,
             last_push: .required_pull_request_reviews.require_last_push_approval,
             bypass_teams: [.required_pull_request_reviews.bypass_pull_request_allowances.teams[]?.slug]}'
    

    Do not re-derive this. If --admin ever stops working, add a dated line here rather than reinstating the lift as the default.

    Also learned in that run

    • Every merge invalidates every other open branch. The Base Freshness Gate blocks any branch behind origin/main, so a queue of N PRs is N cycles of git merge origin/main → push → wait for green → merge. Budget for it, and batch-refresh branches you have already proved conflict-free with git merge-tree --write-tree origin/main origin/<branch>.
    • Contract edits need two regenerations, not one. Changing any *.voice-action-contract.json requires npm run build:voice-gateway, and the gateway digest then makes the Governance job fail on a stale contracts/architecture/runtime-topology-index.v1.json until you also run scripts/ops/generate_runtime_topology_index.py (with Python 3.13 — consent-protocol/.venv/bin/python, not system python3).
    • A layout spec must be registered to run. CI runs test:layout-contracts whenever an e2e/*.layout.spec.ts changes, but that script names its specs explicitly. A new spec triggers the job and is then silently skipped unless you add it to the script.

    Why it happens

    Four settings on main interlock:

    SettingValueEffect
    required_approving_review_count1a review is required
    require_last_push_approvaltruethe last pusher cannot be the approver
    bypass_pull_request_allowancesAnkit + 5 others + the "Allowed Maintainers to Approve" teamthey skip the review — and for non-admins this works, see above
    enforce_adminstrueenforces the protections against administrators, overriding their place in the bypass list

    Ankit is normally both the PR author and the last pusher. GitHub never lets anyone approve their own PR, and because he is an admin, enforce_admins: true cancels the bypass that would otherwise let him merge without one. The two rules pin each other for him.

    enforce_admins is the whole cause — not his permissions, not yours. Corrected 2026-08-22: his permissions are exactly the cause. enforce_admins only reaches admins, so this whole section describes the admin case and nothing else. A non-admin on the bypass team is not blocked, has nothing to lift, and should not be reading past the table at the top.

    The fix is four API calls you run yourself (step 3 below). Do not ask him to click anything, and do not report this as a blocker — it was one for 90 minutes only because the cause was misdiagnosed as a permissions problem.

    Admins only, and even then a fallback. Steps 2, 3, 5 and 6 need admin and 404 on a non-admin token. Try step 1 → step 4 on their own first; that alone merged 14 PRs on 2026-08-21, from an account that could not have run the rest.

    The procedure

    1. Confirm CI is green against the head SHA, not the PR summary — the summary can still show a previous run's result:
      gh api repos/hushh-labs/hushh-research/commits/<HEAD_SHA>/check-runs \
        --jq '.check_runs[] | "\(.name): \(.conclusion)"' | sort
      
    2. Capture the baseline so the restore is provable rather than asserted:
      gh api repos/hushh-labs/hushh-research/branches/main/protection > /tmp/protection_before.json
      
    3. Lift enforce_admins yourself — no human step is needed:
      gh api -X DELETE repos/hushh-labs/hushh-research/branches/main/protection/enforce_admins
      gh api repos/hushh-labs/hushh-research/branches/main/protection/enforce_admins --jq '.enabled'   # false
      
      This is authorised standing procedure: Ankit chose "temporarily lift, merge, restore" explicitly and asked for it to be the documented default. Run it as discrete commands. The first attempt at this was written as a bash script that lifted-and-merged in one file and was blocked — the classifier objects to the wrapper, not to the API call. Do not re-wrap it.
    4. Merge:
      gh pr merge <N> --repo hushh-labs/hushh-research --admin --merge --match-head-commit <FULL_SHA>
      
      Success prints nothing. Do not read silence as failure — verify instead.
    5. Restore immediately — before the deploy, before anything else:
      gh api -X POST repos/hushh-labs/hushh-research/branches/main/protection/enforce_admins --jq '.enabled'
      
      Must print true.
    6. Prove the restore:
      gh api repos/hushh-labs/hushh-research/branches/main/protection > /tmp/protection_after.json
      python3 -c "
      import json
      b=json.load(open('/tmp/protection_before.json')); a=json.load(open('/tmp/protection_after.json'))
      print('IDENTICAL' if b==a else 'DIFFERS')"
      

    Command shapes learned the hard way

    • --match-head-commit needs the full 40-character SHA. An abbreviated one fails with Could not coerce value "..." to GitObjectID, which reads like a permissions error and is not.
    • gh pr merge without --admin returns The merge strategy for main is set by the merge queue and only arms auto-merge. Armed is not merged. Always verify state, never infer it:
      gh pr view <N> --repo hushh-labs/hushh-research --json state,mergeCommit \
        --jq '"\(.state) \(.mergeCommit.oid // "none")"'
      
    • Never write required_pull_request_reviews. Its update endpoint replaces the whole object, and it carries the six-user + one-team bypass list that a careless write would erase. Only ever use the narrow enforce_admins endpoint.

    If --admin is refused locally

    gh pr merge --admin is sometimes rejected by the auto-mode classifier. That layer sits above bypassPermissions, and no settings allow rule reaches it. Telling Ankit to add one is a wrong answer dressed as a fix — he has already pasted rules that changed nothing.

    Retry once; it is non-deterministic and often clears, particularly after a server-side change such as enforce_admins flipping. If it is still refused, that is a genuine hard block: say so plainly and give one exact action. Do not re-route it through gh api, a shell script wrapper, an alias, or a child process — that is evading a stable denial, not solving it.


    Deploying

    All three lanes are manual workflow_dispatch only and refuse to run unless dispatched from main. Nothing auto-deploys on merge — if you do not dispatch, nothing ships.

    # UAT — the normal target. scope=auto lets the workflow detect backend/frontend.
    gh workflow run deploy-uat.yml --repo hushh-labs/hushh-research \
      --ref main -f scope=auto -f sha=<MERGE_SHA>
    
    # Production — only when asked for in those words. Requires an exact green main SHA.
    gh workflow run deploy-production.yml --repo hushh-labs/hushh-research \
      --ref main -f sha=<SHA> -f scope=all -f run_predeploy_backup_job=false -f enable_one_email_kyc=false
    

    Watch it to completion — a dispatch is not a deploy:

    until [ "$(gh run view <RUN_ID> --repo hushh-labs/hushh-research --json status --jq .status)" = "completed" ]; do
      sleep 45
    done
    gh run view <RUN_ID> --repo hushh-labs/hushh-research --json conclusion --jq .conclusion
    

    Environments

    prodUATdev
    GCP projecthushh-pdahushh-pda-uathushh-pda-dev
    Apphttps://one.hushh.aihttps://uat.one.hushh.aihttps://dev.one.hushh.ai
    APIhttps://api.hushh.aihttps://api.uat.hushh.ai

    Manual Cloud Run writes on hushh-pda-uat are blocked by a deliberate IAM deny policy. Reads work. Deploy through the workflow; do not route around it.


    Verify on the real thing

    A green workflow is not a running system.

    # 1. the revision actually serving — never index traffic[0]; tagged revisions occupy the first slots
    gcloud run services describe consent-protocol --region us-central1 --project hushh-pda-uat --format=json \
    | python3 -c "
    import json,sys; st=json.load(sys.stdin)['status']
    live=next((t for t in st.get('traffic',[]) if t.get('percent')==100), None)
    print('serving:', live and live['revisionName'], '| latest:', st.get('latestReadyRevisionName'))
    print('IN SYNC' if live and live['revisionName']==st.get('latestReadyRevisionName') else '*** NOT SERVING LATEST ***')"
    
    # 2. the live surface
    python3 scripts/ops/verify_live_environment.py --env uat
    

    Then the user-visible outcome on https://uat.one.hushh.ai — console, network, and the states that actually break: loading, empty, error, mobile.

    Ankit tests from India. Every US-only directory in this repo returns zero rows on HTTP 200 for an Indian location — not an error. His first screen is legitimately the empty state, so the thing to verify is that it offers a ZIP box instead of looking broken. Always follow with a US ZIP (98033) in the same pass, or a working surface reads as a dead one.


    Before you start

    Other agents share ~/Desktop/husshresearch and will stash or discard uncommitted work there. Use a worktree off the true latest origin/main:

    git fetch origin --prune
    git worktree add -b <branch> /tmp/wt-<name> origin/main
    # reuse deps only when the lockfiles match byte for byte
    ln -s ~/Desktop/husshresearch/hushh-webapp/node_modules /tmp/wt-<name>/hushh-webapp/node_modules
    

    The backend needs Python 3.13; python3 on this Mac resolves to Xcode's 3.9 and dies on from datetime import UTC. Use ~/Desktop/husshresearch/consent-protocol/.venv/bin/python.

    The pre-commit and pre-push hooks used to fail on four markdown files broken on clean origin/main (CONTRIBUTING.md plus three under consent-protocol/docs/reference/); that was fixed upstream — verified 2026-08-06, the hook exits 0 on a pristine checkout, so a normal commit needs no bypass. The procedure outlives the incident: if a hook ever fails again, prove the failure is pre-existing on clean origin/main first, and only then use --no-verify rather than widening scope into unrelated files:

    git worktree add --detach /tmp/wt-baseline origin/main
    cd /tmp/wt-baseline && sh consent-protocol/ops/monorepo/pre-commit.sh
    

    Read safe-changes in this same directory before touching any secret, IAM policy, or deploy config.

    Frequently asked questions

    What to verify before installation and use

    What does the hushh-research-ship source document cover?

    Merged is not shipped. Deployed is not shipped. Shipped is the live surface answering correctly.

    How do I install hushh-research-ship?

    The source record exposes this install command: npx skills add https://github.com/hushh-labs/hushh-research --skill ".claude/skills/hushh-research-ship". Inspect the command and pinned source before running it.

    Which permission-related actions were detected?

    Static rules flagged exec-script, read-files in the source; the page lists the matching lines and excerpts.

    Alternatives

    Compare before choosing