feat(plugin): lua drains, logs --since/--follow, 18 sources [PRO-1896] - #14
Merged
Merged
Conversation
Teaches the lua-agent-builder plugin the lua-cli 3.38.0 log-drain surface and the `lua logs` read window, all verified against lua-core-services `main` (src/cli/command-definitions.ts, src/commands/drains.ts, drains.mutations.ts, logs.ts, src/api/drains.api.service.ts, src/utils/aliases.ts, @lua/shared-types log-drain.types.ts and vm-execution-log.types.ts, @lua/shared-observability drain-scrubber.ts). - /lua-drains: a new slash for all eleven verbs (list, status, deliveries, create, update, delete, test, verify, pause, resume, rotate-secret) with the single-permission pattern, the secret rules (the signing secret is minted server-side and printed once, never stored by the plugin; --header-from-env NAME=ENV_VAR for a vendor key; no secret in the transcript) and the JSON contract (--json on stdout, chrome on stderr). - lib/knowledge/log-drains.md: what a drain is, the six states, the verification handshake (only http echoes X-Lua-Verify), the http / otlp / datadog / betterstack presets, delivery guarantees and the Retry-After contract, the quota ladder, the scrubber and its [redacted:<rule>] markers, logs:read vs the sensitive logs:manage. - /lua-logs, lua-debug and lua-qa now read a window instead of paging: --since / --until / --environment / --follow, the full 18-source --type list, a "watch a promote" recipe, and T0-bounded QA log scans. Corrects the stale "a log entry has no environment field" claim. - Permissions: allow lua drains list|status|deliveries|test, ask create|update|delete|verify|pause|resume|rotate-secret. Deliberately not added to lib/tokenizer.mjs -- a drain ships logs, it does not change what runs in production, so LUA_DEPLOY_CONFIRMED=1 would be the wrong sentence. - lint-cli-flags drops `lua logs --follow` (it exists now) and gains `lua logs --min-severity` and `lua drains update --type`; lint-knowledge-commands learns the drains.action verbs. - 1.4.0 -> 1.5.0, PINNED_MIN_LUA_CLI 3.37.0 -> 3.38.0, MCP bundle rebuilt. Checks: eslint + 16 of 17 lints pass, 428 plugin tests and 147 MCP tests pass with coverage thresholds met. lint-pinned-version is RED by design until lua-cli 3.38.0 is published, the same window 1.4.0 sat in before 3.37.0 shipped. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review follow-up on the same ticket.
- hooks/post-deploy-smoke.mjs was the other half of the ticket's Context
sentence ("the plugin's smoke checks and the lua-debug agent poll logs by
page and reconstruct time windows"). It pulled a 20-row page and filtered
it against THIS machine's clock; it now asks the route for the window with
`--since 1m`, which the server resolves, and trusts it. A lua-cli older
than 3.38.0 exits 1 on the unknown option, so one fallback to the
pre-3.38.0 shape keeps the check working -- the pin only warns, so those
sessions are still in the field. `--environment production` is deliberately
not passed: the step-1 ping goes through `lua chat`, whose rows the A1 call
sites may stamp `sandbox`, and a smoke check that hid its own ping's errors
would be worse than a slightly wider scan. Four new tests cover both paths.
- /lua-drains no longer suggests the bare `lua drains` spelling: the allow
rules are per verb, so a bare invocation matches none of them and would
prompt for a read, breaking the single-permission contract.
- /lua-drains says out loud that the --include-content flow is the one place
two ask prompts can occur, and that the second is a new user-initiated turn.
430 plugin tests pass; hooks stay at 100% coverage.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the plugin half of the Log drains programme: PRO-1896 (I4).
The plugin now knows the lua-cli 3.38.0 log-drain surface and the
lua logsread window. Every claim was read from lua-core-servicesmain—packages/lua-cli/src/cli/command-definitions.ts(thedrainsandlogsdeclarations),src/commands/drains.ts,src/commands/drains.mutations.ts,src/commands/logs.ts,src/api/drains.api.service.ts,src/utils/aliases.ts(drains.action,logs.type),@lua/shared-typeslog-drain.types.ts/vm-execution-log.types.ts,@lua/shared-observabilitydrain-scrubber.ts— never from the public docs, per the repo's "verified against the shipped lua-cli source" rule.What's in it
/lua-drains(new slash) — all eleven verbs (list,status,deliveries,create,update,delete,test,verify,pause,resume,rotate-secret) with the plugin's single-permission pattern: oneAskUserQuestion, then the Bash prompt as the single confirmation for a mutation.create, androtate-secretwithout--finalize), so the slash never copies it into a file, an env var, a commit or its own reply. A header value never reaches a command line:--header <NAME>prompts hidden (and so cannot work under--ci),--header-from-env <NAME>=<ENV_VAR>is the CI form, and for a vendor preset (DD-API-KEY,Authorization: Bearer …) the slash stops at the secret and hands the user the line to run in their own terminal.--jsonon stdout (lua drains create --json | jq -r '.secret'has to work); the verification instructions and the content acknowledgement on stderr (emitAside); a typed envelope rather than the✖line for a refusal that escapes a verb. Per-verb payloads are tabulated.--include-contentis run without--yesfirst: the CLI prints the warning and the acknowledgement and exits non-zero having created nothing, the slash quotes both verbatim, and the user's approval of the re-run is the acknowledgement.lua drains statusexits2both for a usage error and for "some drain isfailing" — the slash decides fromdrains[].state, never from the exit code.test(30 s) /verify(60 s) are 202-then-poll:pending: truewith exit 1 means not yet, not failed.lib/knowledge/log-drains.md(new, the sixth knowledge file) — what a drain is (an organization resource), the eleven verbs and their aliases, the six states, the selectors (19 selectable sources = the 18AGENT_LOG_SOURCESplusexecution), the verification handshake (onlyhttpechoesX-Lua-Verify, with the/.well-known/lua-drain-verifyfallback;token_not_echoedis the common failure; 5 attempts/drain/hour), thehttp/otlp/datadog/betterstackpresets, delivery guarantees and theRetry-Aftercontract (honoured exactly, clamped at an hour; the drain routes answer429 DRAIN_RATE_LIMITEDwith a bareRetry-Afterand noX-RateLimit-*headers), the quota ladder (80 % notify → 100 % dropdebug→ 125 % dropinfo→ 150 % pause with reasonquota), the scrubber and its[redacted:builtin]/[redacted:lua-token]/[redacted:vendor-key]/[redacted:org:<rule-id>]markers, andlogs:readvs the sensitivelogs:manage.--since/--followeverywhere a window was being reconstructed —/lua-logsgains the full 18-source--typelist and the read window;lua-debuggains step 2b and the "watch a promote" recipe;lua-qarecordsT0before the first turn and scans with--since <T0> --environment <target>instead of pulling 100 rows and filtering by timestamp.hooks/post-deploy-smoke.mjs— the other half of the ticket's Context sentence ("the plugin's smoke checks and thelua-debugagent poll logs by page and reconstruct time windows"). It pulled a 20-row page and filtered it against this machine's clock; it now asks the route for the window with--since 1mand trusts it. A lua-cli older than 3.38.0 exits 1 on the unknown option, so one fallback to the pre-3.38.0 shape keeps the check working — the pin only warns, so those sessions are still in the field.--environment productionis deliberately not passed: the step-1 ping goes throughlua chat, whose rows the A1 call sites may stampsandbox, and a smoke check that hid its own ping's errors would be worse than a slightly wider scan. Four new tests cover both paths; the trigger set (SMOKE_LABELS), the warning and the non-blocking behaviour are unchanged.Two corrections worth a reviewer's eye
environmentfield. The plugin asserted "there is noenvironmentfield" in three places;AgentLogMetadatagainedorgId,environment,agentVersion,executionId,executionSeq,traceparentand the truncation pair. It is optional and additive, and a row without it reads asproduction— on the drain matcher and under--environmentalike — so its absence never means sandbox.lua logshas no severity flag.--min-severityis a log-drain selector. The ticket's suggested recipe (lua logs --since … --follow --min-severity warn) would exit 1 on the shipped CLI, solua-debugfilters severity client-side onsubTypeandlint-cli-flagsnow denies the spelling.Permission tiering — a deliberate deviation from the ticket's wording
The ticket asks for the production-gate hook to treat
create/update/delete/rotate-secretas production verbs. This PR puts the seven configuration verbs in theasktier instead, which is how the repo already treats every other org-level mutation (lua env *,lua integrations connect,lua triggers rotate-token, the workflow run-control verbs), and allowslist|status|deliveries|test.Reason:
lib/tokenizer.mjsis documented as the single source of truth for "what changes production", and nothing a drain verb does changes what runs in production — a drain ships a copy of logs. Adding them would force/lua-drainsto emitLUA_DEPLOY_CONFIRMED=1 lua drains create …("the user confirmed a deploy") and would makeconfirm-deploysend the user to/lua-deployfor a log-shipping change. Theaskprompt is a real gate and is/lua-drains's single confirmation. Happy to move them into the tokenizer if reviewers prefer the ticket's literal reading — say so and it's a small follow-up.testis inallowon the ticket's own authority ("list/status/deliveries/testas safe"): it changes no Lua-side configuration — it sends one syntheticlua.drain.testrecord down the delivery path the drain is already using — whereasverifyis inaskbecause it mutates the drain's state. The per-verb spelling is deliberate: a blanketBash(lua drains *)would either prompt for a read or admitrotate-secret, and because the rules are per verb the slash always spells the verb rather than relying on barelua drains.test/lib/permissions-mirror.test.mjsnow pins all eleven.Checks
eslint .lint-knowledge-commandsrun withLUA_CLI_SRCpointed at a lua-cli checkout: 877lua …references validated)scripts/lint-pinned-version.mjsPINNED_MIN_LUA_CLImoves to3.38.0and npm'slatestis still3.37.0. Identical to the window 1.4.0 sat in before 3.37.0 published; the lint was not weakened, and it goes green the moment lua-cli 3.38.0 shipsnpm test -- --coverage+scripts/check-coverage.mjsmcp/lua-platform)dist/server.js, 0.10 MB (budget 5 MB)There is no
claude plugin evalsuite in this repo — CI runs the lint chain plus jest, and the §3.7 rule isscripts/lint-single-permission.mjs(21 slash commands pass).Not in scope
Docs pages (I2, a separate ticket) and the throwaway-org E2E run, which needs a published lua-cli 3.38.0 to exercise
lua drainsat all. Thetail_logsMCP tool is unchanged and stays documented as the pre-3.38.0 fallback.🤖 Generated with Claude Code