21 KiB
Maintainer Automation Notes
This document keeps agent- and maintainer-specific workflow notes out of the repository root while preserving repo-local guidance for automation.
Read Order
README.mdCONTRIBUTING.mddocs/architecture.md- This document
Project Snapshot
dwsis a Go-based DingTalk Workspace CLI and MCP runtime bridge.- Product commands are loaded dynamically via
internal/pluginfrom bundled descriptors. - Command handlers live in
internal/helpers; runtime execution flows throughinternal/executorandinternal/transport.
Repository Map
cmd: public CLI entrypointinternal/app: root command wiring, static utility commands, plugin loadinginternal/helpers: product command handlers (dev, chat, calendar, contact, etc.)internal/plugin: plugin-based dynamic command loaderinternal/cli: catalog types and static endpoint loaderinternal/executor: invocation dispatch and result handlinginternal/transport: MCP HTTP client and request signinginternal/auth: login, token management, agent-code detectioninternal/audit: user operation audit loginternal/errors: structured error model with categories and hintsinternal/keychain: OS keychain integration for credential storageinternal/security: endpoint allowlist and domain trustinternal/pat: PAT (Personal Access Token) authorization flowdocs/: public architecture and reference docsscripts/: build, test, lint, packaging, and policy checkstest/: CLI, integration, contract, unit, and skill E2E test suites
Task Routing
- Add or fix a command path: start from
internal/helpers(handler implementations) orinternal/app(command tree wiring) - Protocol or transport issues: inspect
internal/transport - Auth or login issues: inspect
internal/auth,internal/pat,internal/keychain - Error message or category issues: inspect
internal/errors - Audit log issues: inspect
internal/audit - Plugin loading or command surface: inspect
internal/plugin - Failure or degraded mode: inspect
internal/errors
Policy Checks
When command surface or plugin descriptors change, run:
./scripts/policy/check-command-surface.sh --strict./scripts/policy/check-open-source-assets.sh
Common Commands
make build
make test
make lint
./scripts/dev/ci-local.sh
git diff --check
Reviewer Router GitHub App
Reviewer requests and merge authority intentionally use different identities.
The base-owned pull_request_target workflow may use its built-in
GITHUB_TOKEN to request reviewers, but it must mint a dedicated GitHub App
installation token before enabling auto-merge. GitHub suppresses most workflow
events created by the built-in token; using it for auto-merge prevents the
merge commit's push workflows from running and leaves the exact-SHA Coverage
baseline without a trusted main-scoped producer.
Configure the dedicated App before merging a workflow revision that requires it:
- install it only on
DingTalk-Real-AI/dingtalk-workspace-cli; - grant only
Contents: read and writeandPull requests: read and write; - set repository variable
REVIEWER_ROUTER_APP_CLIENT_IDto its client ID; - set
REVIEWER_ROUTER_APP_SLUGto its exact lowercase slug; - set repository secret
REVIEWER_ROUTER_APP_PRIVATE_KEYto its private key; - create one active repository branch ruleset named
main-merge-writers, targeting onlyrefs/heads/main, with exactly oneRestrict updatesrule (update_allows_fetch_and_merge: false). GitHub may project that strict value through the read APIs as{type: "update"}withparametersomitted; consumers accept only that exact omission or a one-fieldparametersobject containing explicit booleanfalse, and reject every other present shape or value. They then bind the same ruleset node through GraphQL and require its non-nullupdateAllowsFetchAndMergevalue to be exactlyfalse; - give that ruleset exactly three bypass actors: the Reviewer Router App as an
Integrationinpull_requestmode, plushaofeng0705(ID30925823) andPeterGuy326(ID47820304) inalwaysmode for Formula publication and break-glass recovery; - never give the App bypass on
main-protection,main-quality, or any other ruleset, and never reuseHOMEBREW_PR_TOKEN,RELEASE_GOVERNANCE_TOKEN, or a personal token for Reviewer Router.
The workflow limits each minted token to the current repository, requests the
two permissions explicitly, and lets the token action revoke it at job end.
It also requires the minted App slug to equal the reviewed repository variable;
there is no GITHUB_TOKEN fallback. Before reading App credentials, the
base-owned workflow revalidates the event's exact base/head and uses its
built-in token only to disable an existing request owned by
github-actions[bot] or one whose title or merge metadata requests that GitHub
skip workflows. A mint or permission failure therefore leaves that PR
manual-merge only. The built-in token's Contents: write permission is
isolated to this trusted cleanup job and is never used to enable auto-merge;
review routing keeps Contents: read. Existing requests owned by a human or
another non-built-in identity are replaced with the exact dedicated-App
request after token minting. Only an already App-owned request with the fixed
headline/body is preserved. The required Test context reads the live
repository settings and applied rulesets, verifies the exact writer-rule
shape, and requires its own built-in Actions identity to report
current_user_can_bypass: never. Before enabling or reconciling auto-merge,
the minted App independently requires pull_requests_only on that writer rule
and never on every other active main ruleset. These identity-relative checks
remain available to low-privilege tokens; GitHub deliberately hides the full
bypass_actors list from callers without ruleset-write access. Operators must
therefore inspect that list during rollout and keep it at the exact three actors
above. The required Test context then briefly waits for the concurrent
router takeover and accepts only a null request or the configured App owner
with exact fixed metadata. A null request is safe for this failure mode because
the built-in Actions identity cannot pass the writer rule; other permitted
identities emit either a protected-main push or the trusted closed-PR repair.
Draft PRs skip this identity check; the explicit ready_for_review trigger
reruns admission when they become merge-eligible,
while edited and auto_merge_enabled rerun both workflows when the PR title
or merge request changes. A human auto_merge_disabled event reruns CI without
silently re-enabling the request, leaving it available only to the designated
break-glass identity. The required Test context rejects GitHub workflow-skip
directives in the PR title or an existing auto-merge request and verifies the
repository's reviewed MERGE_MESSAGE title plus PR_TITLE or BLANK body
defaults. GitHub does not expose those merge-related settings to the read-only
admission token: the classifier accepts only both exact reviewed values or the
complete omission of both properties, and rejects partial omission, null, or
any other value. Before any enable, reconcile, or merge mutation, the dedicated
App's current-repository token (which has Contents: write) must observe both
exact reviewed values. The dedicated App binds each mutation to the exact head
OID and supplies a fixed safe headline and body, so GitHub cannot copy an unsafe
PR title into its merge commit.
After enabling, the workflow requires the owner to equal the token action's
exact <app-slug>[bot] output. If the event base/head changes during the
mutation window, it removes only that App-owned request and fails the run.
The App-owned native auto-merge request is the reviewed automation intent, not
the sole executor: GitHub's deferred auto-merge path does not reliably apply a
GitHub App's pull-request-only ruleset bypass. A zero-permission approval-signal
workflow converts submitted or dismissed reviews into workflow_run; completed
admission workflows use the same trusted default-branch trigger. The serialized
reconcile job treats workflow_run only as a wake-up signal: it never reads the
triggering run's pull-request payload or artifacts and never checks out code
from that run. It enumerates open main PRs again through the API, then
revalidates the safe App owner, metadata, and ruleset boundary immediately
before calling the synchronous PR merge endpoint with the exact current head
SHA. The preflight requires exactly one repository-owned main-protection
ruleset with one latest-head approval and exactly one repository-owned
main-quality ruleset with the reviewed nine strict checks. The App must report
never on both and on every other non-writer ruleset. Every required context
must be bound to the GitHub Actions App (integration_id=15368); a missing,
different, or duplicate context/source entry fails closed together with
deletion or weakening of either gate. HTTP 405 means the PR is not ready,
while 409 means its revision
changed; either remains open for the next event. Other failures make
reconciliation red. A concurrent native merge is accepted only after the final
PR state proves the exact head, App identity, and non-empty merge SHA.
A staggered twice-hourly schedule provides eventual recovery if a webhook or
workflow completion is delayed, and workflow_dispatch remains the on-demand
repair path.
The break-glass publisher must preserve a safe final commit message;
[skip ci], [ci skip], [no ci], [skip actions],
[actions skip], and a skip-checks: true trailer are forbidden outside the
release-controlled Formula-only path below.
GitHub may suppress pull_request_target entirely for security-sensitive head
branch names, including names that look like commit SHAs. Such a PR receives
neither App takeover nor the closed-event repair. Rename the head branch for
the normal path; if break-glass merge is unavoidable, preserve a safe final
message so the protected-main push CI remains the authoritative producer.
After installing the App, the protected-main push that deploys this workflow
runs reconciliation automatically. Approval-signal and admission-workflow
completions run the same serialized recovery path. The job enumerates open,
ready main PRs
with any non-App owner, unsafe App metadata, or workflow-skip metadata. It
revalidates each base/head, converges a safe request to the exact dedicated-App
owner and fixed message, and leaves a workflow-skipping request disabled for
manual correction. It never enables auto-merge where the request was already
null. Every exact safe App request is then attempted through the synchronous,
SHA-bound merge endpoint; a server-declared not-ready result remains open for
the next event. A mid-migration failure leaves the affected PR disabled for a
fresh routing event or break-glass merge. One PR failure is recorded
without preventing later legacy owners from being attempted; the batch ends
red with a per-PR summary. Manually dispatch Reviewer routing from main
until the failed count is zero.
Disabling the App-owned auto-merge request before the reconcile job's final PR
read leaves that PR manual-only. That final read is the cancellation
linearization point: GitHub's merge API can condition atomically on the head SHA
but not on the auto-merge request itself, so a disable racing after that read may
lose to an already-issued merge request. To stop an in-flight attempt
before the merge endpoint accepts it, close the PR or change its head; if the
server observes that state first, it rejects the state/SHA-bound merge. No
client-side action can revoke a merge that GitHub has already accepted.
The endpoint has no equivalent expected-base parameter. The workflow therefore
checks base=main and the repository before and after merge and fails any
retargeted result, but a retarget racing after the final read cannot be made
atomic client-side. Never retarget a PR while its App-owned intent is active:
disable the request, wait until all running Reviewer routing reconciliation
jobs finish, and only then change the base. Preventing a malicious same-instant
retarget requires a GitHub-side branch/ruleset control rather than workflow
code.
A PR that introduces or rotates this identity still runs the old base-owned
router. Install/configure the App and activate the exact writer ruleset first;
this blocks its legacy github-actions[bot] request from writing main. After
the governance PR's final push, disable that old request, confirm the live
settings/ruleset contract and all required checks are green for the exact head,
then have only haofeng0705 or PeterGuy326 merge that head with the
repository-generated safe merge message. Verify the resulting merge SHA has a
CI run with event=push,
a successful Coverage context, and an exact-SHA baseline cache under
refs/heads/main. Confirm automatic reconciliation reports zero failures and
zero non-App owners. Finally use a normal canary PR to verify that the dedicated
App is both enabledBy and mergedBy, and that the same post-merge chain
repeats before declaring the rollout complete.
Homebrew Formula Delivery
Official releases use the designated HOMEBREW_PR_TOKEN identity to update
exactly one tracked Formula after the immutable GitHub assets and their
checksums have passed verification. The publisher validates the rendered Ruby,
commits only the configured Formula path, never force-pushes main, and retries
from a fresh clone up to three times when main advances concurrently. Normal
stable and beta releases do not create a Formula PR or run a permission
canary. The workflow uses the existing repository-scoped
HOMEBREW_PR_TOKEN release identity because GitHub does not allow its built-in
Actions App to bypass this repository's rulesets. Its owner is the designated
always-bypass actor for controlled Formula publication and break-glass recovery,
including on main-merge-writers. The workflow creates the
nine Code Admission checks for the Formula-only commit only after proving its
sole parent already has all nine successful checks and the committed Formula
exactly matches this release's verified bytes. Formula commits retain
[skip ci], so the sealing step exposes only the reviewed commit identity to
an independent confirmation job. That job creates the
Coverage Baseline Cache acknowledgement and emits the reviewed
coverage-baseline-promote repository dispatch. The default-branch
Coverage Baseline Promotion workflow independently verifies the exact
single-parent Formula commit, both parent and target admission contexts, and
default-branch containment before checking out the target. It restores only
the exact parent profile, recomputes the complete profile if that cache is
absent, and saves the Formula SHA under the main cache scope. Because the
cache save action treats upload errors as warnings, a second lookup must report
cache-hit=true for the exact target key before the producer succeeds. The
promotion completes the unique acknowledgement, and the confirmation job
waits for that exact check-run ID. npm and mirror publication depend only on
the immutable release job, so a transient
cache-service failure cannot strand an otherwise valid release between
channels; the final release-delivery gate still fails until the exact cache is
confirmed. Once Formula sealing exposes the target SHA, the confirmation job
also runs when a later immutable-package recheck fails, so a post-push failure
cannot orphan the producer. Rerun the failed promotion/confirmation path after
repairing the producer. Never add a prefix restore-keys fallback to this path.
Coverage Baseline Repair is the independent safety net for every merged PR.
Its base-owned pull_request_target: closed job never checks out or executes PR
content: it binds the closed event's PR number and stable head SHA to the
current merged-PR facts (merged_at, base.ref, and merge_commit_sha) and
proves that merge commit is contained in main. It deliberately does not
compare REST base.sha, because that field follows the live base branch and
can move after the merge. Only then does it emit a
coverage-baseline-repair repository dispatch. Workflow-skip directives alone
do not suppress pull_request_target, subject to GitHub's separate
security-sensitive branch-name restriction described above. The low-trust
trigger is forbidden from writing the default-branch cache directly. Before
dispatching, it gives Actions event delivery one minute to expose a run from
the exact protected .github/workflows/ci.yml workflow and exits if that normal producer already
owns the SHA, avoiding a duplicate full-suite run. A successful CI producer
must hard-verify its exact cache key. If that run instead completes with any
non-success conclusion, a separate base-owned workflow_run dispatcher binds
the exact workflow ID/path, run ID/attempt, conclusion, repository, branch, and
head SHA before requesting repair. workflow_run also has read-only
default-branch cache access, so both dispatchers use the reviewed
repository_dispatch exception. The dispatched default-branch producer
revalidates the corresponding merged-PR or failed-CI identity before checkout,
restores only the exact target key, recomputes the complete profile on a miss,
and verifies cache-hit=true after saving. An hourly schedule refreshes the
event-time main SHA after direct break-glass pushes or cache eviction;
workflow_dispatch provides the same current-main repair on demand. The
dedicated App identity remains mandatory because events created by the built-in
GITHUB_TOKEN can suppress both the main push and the closed-PR event.
Keep HOMEBREW_PR_TOKEN repository-scoped with Contents: write and
Pull requests: write (the latter remains necessary for withdrawal rollback),
keep its owner as the designated ruleset bypass actor, and do not reuse
RELEASE_GOVERNANCE_TOKEN. The workflow and publisher provide the Formula-only
path restriction; GitHub rulesets do not infer that restriction from the token.
Release Governance and Recovery
Store RELEASE_GOVERNANCE_TOKEN as a dedicated Actions secret with only
repository Administration: read. The immutable-releases REST endpoint is an
administration setting and cannot be read by the workflow's built-in
GITHUB_TOKEN. Both the default-branch governance preflight and the tag
contract use this same credential so a missing or expired identity is detected
before an irreversible tag is created.
Recovery is restricted to an existing annotated tag whose exact tag object, commit, sealed metadata, original failed run/attempt, requester identity and Release state all match; it then reuses the normal release jobs without a second-person environment approval. A same-run “Re-run failed jobs” is even lighter: the seal job may adopt an existing tag only when its complete authority matches that run and its original attempt is not newer than the current attempt. Do not put publication secrets in temporary branches or create ad-hoc recovery workflows.
Cloud-sealed releases mirror to OSS only when the repository variable
ENABLE_OSS_MIRROR is exactly true. Leave the variable unset while no Bucket
is provisioned; GitHub, npm, and Homebrew delivery can then complete without
running the OSS step. Once enabled, missing credentials, an invalid Bucket, or
an upload failure remains fail-closed. The cloud tag immutably records the
decision as OSS-Mirror: enabled|deferred; publication and withdrawal consume
that sealed value instead of the variable's later state. Deferred releases
cannot use repair_oss_version; enabling OSS applies to later release tags
until an audited immutable repair marker is implemented.
If an immutable GitHub Release and npm package were delivered but an enabled
downstream China mirror failed, dispatch the normal Release workflow from the
protected default branch with exactly one of repair_gitee_version or
repair_oss_version. Channel repair accepts a fully successful exact release,
or a failed exact-tag run only when its latest attempt completed the release
contract, build, Apple signature, immutable GitHub publication, and npm
delivery checks for the exact tagged commit. OSS repair additionally requires
the tag's sealed policy to be enabled. It then downloads and re-verifies the
immutable assets before invoking only the selected mirror. For a failed
release, an OSS repair requires the OSS step itself to be the recorded failure.
A Gitee repair accepts either a failed Gitee job or a Gitee job that was
skipped behind that OSS failure; the latter is an explicit Gitee backfill and
does not claim that OSS has been repaired. Gitee repair requires GITEE_TOKEN,
GITEE_USER, and GITEE_REPO; OSS repair requires OSS_ACCESS_KEY_ID,
OSS_ACCESS_KEY_SECRET, OSS_ENDPOINT, and OSS_BUCKET (with optional
OSS_PREFIX) as Actions secrets. Missing credentials fail the selected repair
closed.
Handoff Checklist
Before handoff, include:
- Changed files and why
- Verification commands run and outcomes
- Known risks or follow-up work