Cobra surfaced several command-line validation failures as plain errors.
Classifying them by message text could report correctable input as
internal/unknown, misleading agents and returning the wrong exit code.
Classify errors at the boundary that produces them. Args and residual
Cobra validation become validation/invalid_argument, while raw execution
hooks and plugin failures become internal/unknown. Preserve typed errors,
causes, bare exits, and partial failures.
Make final-tree instrumentation stateless, type pre-callback framework
failures at their source, and keep rendering and Shutdown lifecycle
observations consistent. A Shutdown handler receives the error the command
returned with its wrapping intact, so errors.Is still reaches the
producer's sentinels, and it cannot change what the user was told because
that is decided before the event fires. The envelope is still written
after the event, keeping it the trailing content of stderr where readers
look for it even when a failing hook warns on the same stream.
Guard the error copier against a typed error being added without it, so
handlers cannot silently start sharing a producer's value again. Add
regression coverage for repeated execution, late help, lazy completion,
writer failures, shortcut diagnostics, credential-provider
classification, lifecycle isolation, and stderr write order.
Both are registered, reachable business domains — `lark-cli attendance
user_tasks query` and `lark-cli mindnotes nodes create` run, and both appear in
`auth login --domain` — but neither had an entry in
service_descriptions.json. GetServiceTitle/GetServiceDescription returned "",
so buildDomainMeta fell back to the typed service spec, which carries one
string for both languages. The result was visible in `--help`: mindnotes
rendered its Chinese description in the English domain list, and attendance
rendered "attendance record query", a lowercase fragment shown in both locales.
Both keep their own scope namespaces (attendance:task:readonly,
mindnote:node:create/read) and stay independent auth domains, so no
auth_domain is set. whiteboard remains the only domain folded into docs.
TestGetDomainMetadata_HasTitleAndDescription could not catch this: it asserts
on buildDomainMeta's output, which the fallback had already made non-empty.
The new reconciliation test walks EmbeddedServicesTyped plus AllShortcuts and
asserts both languages resolve from the config itself, before any fallback.
EmbeddedServicesTyped is the overlay-free parse, so the test does not depend on
what remote_meta.json happens to hold on the machine — a domain that only ever
arrives via remote overlay stays out of its reach.
* refactor: add output emitter contract and differential harness
Introduce a leaf Emitter in internal/output that composes the existing
output primitives (content-safety scan, envelope, jq, format rendering,
notice) behind a single command-scoped port. The emitter is unwired: no
production caller is migrated, so CLI output stays byte-for-byte unchanged.
A differential test harness drives the real legacy entry points
(RuntimeContext.Out/OutRaw/OutFormat/..., WriteSuccessEnvelope and the
pagination formatter) and asserts byte-identical stdout/stderr plus typed
errors, locking behavior before later slices migrate callers.
* refactor: tighten emitter API and cover pagination with real tests
- split Emitter.Success/PartialFailure and drop EmitOptions.OK so a
missing ok flag can no longer silently emit ok:false
- give StreamPage its own StreamOptions (format + pretty) instead of
reusing EmitOptions, making "jq needs aggregation" a compile-time fact
- pin the Emitter jq-error contract (returns error, writes no stderr);
the caller adapter re-emits the legacy stderr line on migration
- add in-package tests driving the real apiPaginate/servicePaginate over
a mock transport: multi-page aggregation, empty-result fallback,
MarkRaw handling, and the business-error raw-response red line
* test: use standard TestFactory harness for pagination tests
Replace the hand-rolled RoundTripper + APIClient construction in the
apiPaginate/servicePaginate tests with cmdutil.TestFactory and its
httpmock.Registry, and isolate LARKSUITE_CLI_CONFIG_DIR to t.TempDir(),
matching the repo's standard HTTP-mocked test convention. Assertions and
coverage (multi-page aggregation, empty-result fallback, MarkRaw, and the
business-error raw-response red line) are unchanged.
* refactor: route success output through the single Emitter port
Migrate the success-output surfaces onto internal/output's Emitter,
byte-for-byte identical (proven by frozen golden diffs and the real
paginate/HandleResponse tests):
- RuntimeContext.Out/OutRaw/OutFormat/OutFormatRaw/OutPartialFailure now
build an Emitter and call Success/PartialFailure; emit and outFormat are
removed. An adapter maps the returned error back to the legacy
outputErrOnce / jq-error stderr / exit-code behavior.
- WriteSuccessEnvelope degrades to a thin Emitter.Success delegate; its 8
callers are unchanged.
- apiPaginate/servicePaginate stream pages via Emitter.StreamPage; the
aggregate and business-error raw-response branches are untouched.
- HandleResponse routes its non-JSON structured-response branch through
Emitter.Success.
Frozen golden fixtures replace the runtime legacy oracles so the
differential harness cannot go self-referential after migration.
* fix: keep _notice on struct payloads in Emitter's unknown-format fallback
printLegacyDataJSON now normalizes via toGeneric first (matching FormatValue), so a struct / named-map payload retains its injected _notice on the unknown-format -> JSON fallback rather than dropping it silently. Add a regression test that fails against the pre-fix path.
* refactor: make the Emitter own write failures and stop mutating inputs
Route every Emitter stdout path through a render-to-buffer-then-copy helper so a marshal/render failure leaves stdout empty and surfaces a typed internal error (with cause), and a stdout write failure is propagated instead of silently swallowed. Leaf writers gain error-returning Write* cores; the legacy Print*/FormatValue wrappers keep their exact behavior for unmigrated callers.
- handleEmitterError now captures every error, not only the jq/safety branches; flip OutRaw's write-error test to assert propagation.
- Clone the map before injecting _notice so a caller's payload is never mutated and an existing _notice is never overwritten.
- Preserve jq's own typed error (validation/api) on a bad expression or runtime failure; only wrap genuine stdout write failures.
- Split tests: normative emitter_contract_test.go vs frozen emitter_legacy_compat_test.go (base SHA recorded, self-update env vars removed).
* fix: satisfy license-header and forbidigo lint on the emitter changes
- Move the base-SHA note below the copyright header in the renamed legacy-compat test so the license-header check sees a valid header at the top.
- Route the leaf wrappers' marshal/format stderr messages through a single legacyStderrf helper (one //nolint:forbidigo) instead of bare os.Stderr, preserving exact legacy behavior for unmigrated direct callers while passing forbidigo; drop the now-unused os imports.
* fix: stop legacy CSV wrappers reporting write failures to stderr
Align FormatAsCSV/FormatAsCSVPaginated and FormatValue/FormatPage's CSV branch with the other leaf wrappers: report only marshal failures, swallow write failures. Previously they emitted a 'csv write error' for the (empty) line and the JSON-fallback write failures that the pre-refactor code ignored, and mislabeled a JSON write failure as a CSV one. Failure-path only; success output is unchanged (golden double-diff still byte-for-byte).
Register approval.instance.status_changed_v4 and approval.task.status_changed_v4 with custom flattened schemas and user-auth pre-consume subscription setup.
Handle approval subscription_type as optional multi-value pre-registration metadata: omitted values register both involved and managed relations, explicit values can be single, comma-separated, or JSON array, and consumers do not unsubscribe on exit.
Report partial approval subscription registration failures with registered and failed relation context while preserving the underlying typed error classification.
Document approval event output fields and subscription semantics, and refresh approval skill references from API metadata.
* fix: unify dry-run output contract
* fix: address dry-run review feedback
* fix(dryrun): tighten preview contract and unify data shape
- transcribe HTTP method verbatim in previews (HEAD/OPTIONS were
reported as GET); reject an empty method in api with a typed error
- unify the dry-run data payload across api/service/shortcut paths:
{api, context?: {app_id, user_open_id}}; drop data.as — the envelope
top-level identity is the single identity source
- mark pretty dry-run stdout with '# dry-run: request not sent' so logs
that drop stderr still show it was a preview
- extract the shared preview builder, collapse PrintDryRunWithFile's
loose params into FileUploadMeta, and fail loudly on nil previews
- revert description-marker identity parsing: stale prose must not
override corrected accessTokens (blocks legal user calls on
images.create); identity gating keys off accessTokens only
- pin the new contracts with tests: verbatim method, three-way context
parity, nil-preview error, empty-context omission, marker line
* docs(agents): add typed-data, faithful-transcription, and contract-test conventions
- typed struct at the boundary over map[string]interface{} threading;
distinct types where values could swap silently (internal/meta.Token)
- transcribe input verbatim in previews/transformations; reject
unhonorable flag combinations with typed errors instead of silently
substituting behavior
- contract tests must fail when the implementation is reverted
* test: migrate dry-run tests grown on main to the envelope format
main gained raw-format dry-run readers while the PR was in flight
(wiki drive export #1802, drive list comments #1845, slash commands,
sheets history, docs fetch, mail draft-send/triage, vc meeting events).
Migrate them to the envelope accessors (clie2e.DryRunGet / data-wrapped
decoders) and drop the now-redundant DryRunData extractions in files
unified on DryRunGet.
---------
Co-authored-by: guokexin.02 <264159873+Tantanz20020918@users.noreply.github.com>
This PR improves the im.message.receive_v1 event output by exposing structural
metadata fields (reply context, sender type, mentions) that were previously only
available in the raw V2 envelope. It also syncs the same structural fields to the legacy
+subscribe --compact pipeline.
* chore: bump oapi-sdk-go/v3 to v3.7.2 for filename-aware multipart upload
* fix: preserve original filename in multipart file upload
BuildFormdata read local files into a bytes.Reader before handing them
to the SDK, so the SDK's part-filename detection (which only reads
*os.File) fell back to "unknown-file" for every local --file upload.
Use AddFileWithName with the file's basename instead.
* fix(schema): fall back to runtime catalog when no embedded metadata
Binaries built from the bare Go module (plugin builds) embed only the
empty meta_data_default.json stub because meta_data.json is gitignored
and fetched at build time. The schema command, its completion, and the
affordance command-form resolver read the embedded-only catalog, so
every schema lookup failed with "Unknown service" even though the
runtime registry had already sync-fetched full metadata.
Add registry.SchemaCatalog(): embedded when compiled in (official
builds unchanged, still deterministic), otherwise the merged runtime
catalog seeded from cache or remote fetch. When neither source has
data (offline plugin build with a cold cache), schema now returns a
failed_precondition error with an actionable hint instead of
"Unknown service" with an empty candidate list.
* fix(registry): gate cached meta overlay on version newer than embedded
The cached remote meta was overlaid onto the embedded meta_data.json
unconditionally, so after a CLI upgrade an equal- or older-version
cache kept shadowing the freshly shipped embedded definitions until a
later refresh happened to rewrite it.
Only overlay when the cache version is strictly newer than the
embedded baseline. The bare-module stub baseline is "0.0.0", so plugin
builds without compiled metadata still take any real cached version
(TestOverlayGate_StubEmbedded_OverlaysRealCache) and the schema
runtime fallback keeps working offline from a warm cache.
Ports #1376 onto the typed meta model.
---------
Co-authored-by: liangshuo-1 <266696938+liangshuo-1@users.noreply.github.com>
Add docs +history-list, +history-revert, and +history-revert-status backed by docs_ai history OpenAPI endpoints.
Document the safe history workflow and extend dry-run/live E2E coverage for the new shortcuts.
* refactor: retire legacy error envelopes and enforce typed contract
Consolidate all command error reporting onto the typed errs.* contract, remove
the legacy error surface that predated it, and tighten the lint guards so the
contract holds across the whole repository going forward.
Every failure now reaches stderr as one envelope shape: a category, an
optional subtype, a human- and agent-readable message, and a recovery hint,
with invalid parameters listed under `params`. The legacy ExitError envelope,
its constructors, and the boundary bridge that promoted untyped config and
authorization errors are deleted, leaving a single path from error to wire.
Predicate commands keep their silent-exit behavior through a dedicated signal
that carries only an exit code.
Infrastructure paths that still emitted ad-hoc envelopes — flag parsing,
unknown commands and subcommands, plugin and policy guards, confirmation
prompts, and auth/config failures — now classify into the same taxonomy.
Business, API, auth, and config exit codes are preserved; the one behavioral
change is that Cobra usage failures (missing required flag, unknown command,
bad arguments) now emit the typed validation envelope and exit 2, matching the
explicit flag and subcommand guards, instead of Cobra's plain-text exit 1.
Enforcement is repo-wide rather than per-path:
- The errscontract guards run by default everywhere instead of through a
migration allowlist, so legacy envelopes cannot be reintroduced anywhere.
- errorlint runs across the whole repository: every error wrap must use %w and
every comparison must use errors.Is/errors.As, so interior wraps stay legal
but can no longer break the chain the typed boundary relies on.
- The errs-no-bare-wrap guard is keyed by structural prefix instead of an
explicit per-domain allowlist, so new shortcut domains are covered without
editing a list. It runs where forbidigo is enabled (the shortcut domains and
the auth/config/service command groups); repo-wide chain integrity for the
remaining command paths is carried by errorlint above.
* test: align cli_e2e success assertions to the ok envelope
The api and service success path now emits the {"ok":true} envelope, so the
cli_e2e workflow assertions that still expected the old {"code":0} shape via
AssertStdoutStatus(t, 0) fail once they run with live credentials. Switch those
workflow assertions to AssertStdoutStatus(t, true); the fake-payload helper test
in core_test.go keeps its code-shape assertion.
* feat: add --json flag support to auth subcommands
* feat(auth/logout): add json output support for logout command
* feat(auth/list): add json output support for auth list command
Add note shortcuts for note detail and unified transcript retrieval, route vc note detail parsing through the note domain, and update note/vc/minutes skill guidance for normal versus unified transcript handling.
Includes dry-run E2E coverage for the new note shortcuts and documents the remaining live E2E fixture gap.
Framework support for resource-scoped event subscriptions, so one
EventKey can fan out into independent per-resource subscription scopes:
- KeyDefinition gains SubscriptionKey / NormalizeParams / Match hooks
- ComputeSubscriptionID derives a dedup identity from (EventKey, sub-key
params); plumbed through bus Hub, consume loop, and the
Hello / PreShutdownCheck / ConsumerInfo protocol messages
- add a synchronous Match filter stage before Process
- change PreConsume cleanup to func() error and surface cleanup
(unsubscribe) failures as WARN with an idempotency note
- adapt minutes/vc/whiteboard PreConsume to the new cleanup signature
- render SubscriptionID / SubscriptionKey in event status & schema output
No domain wires these hooks yet; covered by unit tests using bus/protocol
doubles. (Mail, the original exerciser, is intentionally not included.)
Change-Id: Ifc743f1aa0bc4dff0c8a1e35da24883694fe7699
Replace every command-facing error path in the event domain — the
consume/schema command layer, the +subscribe shortcut, EventKey
definitions, and the consume orchestration — with typed errs.*
envelopes, so consumers get stable type, subtype, param, hint, and
missing_scopes metadata for classification and recovery instead of
free-form message text.
- Input validation (--jq, --param, --output-dir, --filter, --route,
unknown EventKey, EventKey params) reports validation /
invalid_argument with the offending flag in param and an actionable
hint.
- Scope preflight reports authorization / missing_scope with the
machine-readable missing_scopes list; console-subscription and
single-bus preconditions report failed_precondition with recovery
hints.
- The consume API boundary passes already-typed errors through and
classifies transport, non-JSON HTTP, and unparsable responses; the
vc note-detail retry now matches the not-found code on typed errors
(it silently never fired against the legacy envelope shape).
- Previously-bare failures exited 1 with a plain-text "Error:" line
and now exit with their category code (validation 2, auth 3,
network 4, internal 5) alongside the typed stderr envelope.
- forbidigo and errscontract guards now cover the event paths so
regressions fail lint; AGENTS.md and the lark-event skill document
the typed contract for agent consumers.
Validation: make unit-test (race) green; event unit and e2e suites
assert category/subtype/param/hint and cause preservation against the
real binary; errscontract and golangci lint clean.