mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-23 11:03:41 +00:00
c4d429d14c94de941ac761333cdac0a33895a05f
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2580c2c8bb |
fix(cli): gate pad init's Step-4 login on canPromptForConfig() (BUG-2592) (#1115)
* fix(cli): gate pad init's Step-4 login on canPromptForConfig() (BUG-2592) A configured-but-unauthenticated non-interactive `pad init` fell into doBrowserLogin and blocked on the poll wait (wall-clock-bounded since BUG-2572, still minutes of hang nobody can complete) instead of failing fast — Step 3 has had this exact gate since init.go:205, and cmd_workspace.go got it in PR #1111 (BUG-2538). The gate sits AFTER the saved-credentials check so a headless run with valid stored credentials proceeds untouched. Remedy text per the corrected trail ruling (the r1 constraint was refuted by r2): piped `pad auth login --interactive` IS a working non-interactive login (doInteractiveLogin reads a plain bufio.Reader, piped-bytes-safe since BUG-1886), so the message points there — and deliberately not at pad init's --email/--name/--password, which only fire when SetupRequired. Claude-Session: https://claude.ai/code/session_018qREYgDd6Ag1X1SDmqhyFM * docs(plugin): pad init no longer hangs in the session-expired case — update the three claims + plugin 0.2.1 The BUG-2592 gate makes three plugin-skill passages stale (same shape as PR #1111's codex r3 self-invalidation): capture and onboard said `pad init` can still hang on the browser flow when configured-but- unauthenticated, and the pad skill's whoami-guidance said the same at its "not a safer probe" sentence. All three now state the fixed truth, live-verified this session: fixed binary fails fast in 0.1s with the piped-login remedy; pre-fix control binary hangs to the timeout kill in the identical sandbox state; the remedy itself (piped `pad auth login --interactive`) logs in and restores credentials. Plugin 0.2.1 — text reaches nobody without a bump (version-pinned at install). Claude-Session: https://claude.ai/code/session_018qREYgDd6Ag1X1SDmqhyFM |
||
|
|
ac05d8a2b1 |
fix(cli): fail fast and quiet on non-interactive workspace init (BUG-2538, BUG-2577) (#1111)
* Fail fast and quiet on non-interactive `pad workspace init` BUG-2538: initCmd drove runBrowserSetup/doBrowserLogin unconditionally when the instance needed first-run setup or login, blocking a non-interactive caller (script, CI, headless agent) on a browser handoff nobody can complete. Gate both branches on canPromptForConfig(), mirroring the precedent already used by `pad init` (init.go:205-206), and fail fast with a hint pointing at `pad init --email/--name/--password` or `pad auth setup`/`pad auth login`. BUG-2577: offerSkillInstall (shared by workspace init and workspace link) printed a "(Y/n): " prompt even when the answer would be auto-defaulted rather than read, because it gated on cli.IsTerminal() (stdin only). Switch to canPromptForConfig() (stdin AND stdout), which is the same predicate now used for BUG-2538 and the more robust of the two checks already in the codebase. Claude-Session: https://claude.ai/code/session_01BhQoeaWXxJbvw86ezzK8dt * Fix wrong remedy in BUG-2538's !Authenticated error message codex r1: the !Authenticated branch suggested `pad init --email/--name/--password`, but those headless flags only bootstrap the first admin account and only fire when SetupRequired — for an already-set-up-but-unauthenticated instance, `pad init` falls through to its own ungated Step 4 re-auth (BUG-2592), so the suggestion relocated the hang instead of avoiding it. Drop the pad-init suggestion in this branch only; point at `pad auth login` and note there's no non-interactive login path yet. SetupRequired branch is unchanged — its pad-init suggestion is correct for that state. Claude-Session: https://claude.ai/code/session_01BhQoeaWXxJbvw86ezzK8dt * Fix two more inaccurate remedies flagged by codex r2 1. SetupRequired branch: `pad init --email/--name/--password` silently eats the caller's workspace name/--template — pad init creates its own CWD-named workspace as a side effect, so a re-run of the original `pad workspace init <name> --template <t>` short-circuits on the link pad init just made with no signal <name>/<t> were ignored. Switch the remedy to `pad auth setup --email/--name/--password`, which bootstraps the admin account only (no workspace side effects), then re-run the original command. 2. !Authenticated branch: the "no non-interactive login path exists" claim was false — `pad auth login --interactive` reads email/password off a plain, TTY-ungated bufio.Reader (doInteractiveLogin, cmd_auth.go:554+; BUG-1886 made it piped-bytes-safe), so it works fine when credentials are piped in. Reworded to point at it and dropped the incorrect BUG-2592 reference (that bug tracks pad init's ungated Step 4, not a missing login mechanism). TestWorkspaceInitNonTTYSetupRequired's assertion updated from "pad init" to "pad auth setup" to match; TestWorkspaceInitNonTTYNotAuthenticated needed no change (still asserts "pad auth login"). Claude-Session: https://claude.ai/code/session_01BhQoeaWXxJbvw86ezzK8dt * Update skill docs invalidated by the non-interactive fast-fail fix codex r3: BUG-2538/BUG-2577 made this diff's own docs stale. Four files (skills/pad/SKILL.md, plugin/skills/pad/SKILL.md, plugin/skills/onboard/SKILL.md, plugin/skills/capture/SKILL.md) still say non-interactive `pad workspace init` on a configured-but- unauthenticated machine "blocks for minutes with no non-interactive fallback" — that was true pre-fix (per BUG-2541's verification) and is false now. Reworded the WHY without dropping the underlying do-not-run-blind guidance: an agent's tool call is always non-interactive, so it now gets a fast, actionable error instead of a hang, but the error still just says a human needs an interactive terminal — `pad auth whoami` remains the right check to run instead. Where the docs' `pad init` claims are about the still-unfixed session-expired path (BUG-2592, this diff's Step-4 sibling, left untouched), those claims are unchanged and now cite BUG-2592 explicitly. skills/INSTALL.md:24 updated separately (P3): notes the non-interactive silent-install branch of `pad workspace init`'s skill offer, alongside the existing interactive-prompt description. Docs only, no Go changes — go build/test and embed.go's //go:embed skills/pad/SKILL.md still resolve; no test asserts the old wording. Claude-Session: https://claude.ai/code/session_01BhQoeaWXxJbvw86ezzK8dt |
||
|
|
d8627968d0 |
feat(plugin): pad Claude Code plugin — nested layout, monitors, skills (PLAN-2469 Phase 2) (TASK-2534) (#1085)
* feat(plugin): assemble Phase-0 scaffold v3 into a nested plugin layout — PLAN-2469 Phase 2 (TASK-2534)
Drops scaffold v3 (plugin.json, marketplace.json, monitors.json, the pad/
status/capture/onboard skills) in under plugin/, verbatim, rather than at
repo root. Repo root already embeds skills/pad/SKILL.md into the binary
as the surface-agnostic `pad agent install` source (embed.go); loading
the plugin flat at repo root would register BOTH that copy and the
plugin's Claude-Code-only trimmed copy as separate skills in the same
session (confirmed empirically via --plugin-dir) and would ship the
whole repo as the marketplace install payload (confirmed via a real
marketplace add + install + cache inspection). Nesting under plugin/
with marketplace.json's source: "./plugin" scopes both discovery and
the install payload to exactly the plugin's own files, with zero
content changes to the scaffold itself.
The stale minimal .claude-plugin/plugin.json at repo root is removed —
superseded by the real one at plugin/.claude-plugin/plugin.json; repo
root's .claude-plugin/ now holds only marketplace.json, which is what
`/plugin marketplace add PerpetualSoftware/pad` needs to find there.
`claude plugin validate --strict` passes clean on both the repo root
(marketplace) and plugin/ (plugin manifest) independently.
* fix(plugin): round-1 review fixes — invocation reality, degradation ordering, confirm-by-default, license (TASK-2534)
Six findings from codex round 1 + dispatcher's shallow pass:
- pad/SKILL.md's "How This Works" claimed a literal `/pad <anything>`
command, but under plugin namespacing this skill registers as
`pad:pad`, not a bare `/pad`. Rewrote to state the true DR-1 model
(contextual trigger by description, not typed) and defined the
document's own `/pad <anything>` notation as shorthand for "what the
user said," not literal syntax. Fixes the earlier /pad:pad cosmetic
note at its root and the two "onboarding" sections that repeated the
same wrong dual-syntax framing.
- status/SKILL.md and capture/SKILL.md invoked `pad` before stating
their missing-CLI fallback; reordered so the failure modes are
handled as part of the same instruction, not an afterthought.
monitors.json's command had no guard at all — a padless machine
would hit a raw spawn error, not silence. Added a `command -v pad`
shell guard (verified live, both branches: silent when pad is
absent, invokes correctly when present) and re-verified the two
original degradation legs (no .pad.toml, unreachable padd) through
the exact guarded command string, not just the bare binary.
- `pad bootstrap` hard-exits ("no workspace linked...") when unlinked
(cmd/pad/main.go's getWorkspace()), but the skill assumed it always
succeeds and the onboarding NL routing tried to load the onboard
PLAYBOOK before a workspace — and therefore that playbook — could
exist. Added the unlinked-workspace branch to Context Loading and
taught onboarding routing to run `pad workspace init` first in that
case, matching what skills/onboard/SKILL.md already did correctly.
- capture/SKILL.md's "no ceremony" immediate-create contradicted the
main skill's "always confirm before creating/modifying." Made
confirm-first the default, with an explicit opt-out for workspaces
whose conventions declare autonomous capture.
- Two `open "$IMG"` examples were macOS-only; added the Linux
(xdg-open) alternative and a describe-the-path fallback.
- plugin.json declared MIT; the repo is Apache-2.0 (LICENSE,
web/package.json). Corrected.
`claude plugin validate --strict` clean on both the repo root
(marketplace) and plugin/ (plugin manifest) after the fixes.
* fix(plugin): make DR-4's no-write default unmistakable (TASK-2534)
Second independent dogfood run found the etiquette text underdetermined
behavior: one run correctly parked a context nudge, another interpreted
"fold it in" as license to run pad item comments and post a real reply
to the watched item — a write action from a context notification, which
DR-4 never intended and the plugin's noise-discipline promise depends on
never happening. Rewrote the section: read-only one-line park-and-summarize
is now the explicit default for every watch notification and any addressed-
to-you event the model isn't certain about; writing to Pad in reaction to a
notification is called out as prohibited by name (no more "fold it in"
euphemism), with a single narrow exception (an assignment/ask explicitly
addressed to the session's user, where acting is unambiguously expected)
and an explicit "when in doubt, park" tiebreaker.
claude plugin validate --strict clean on both repo root (marketplace) and
plugin/ (plugin manifest).
* fix(plugin): loop the pad-missing monitor guard instead of a one-shot sleep (TASK-2534)
Verifying the finding-2 guard through Claude Code's REAL monitor spawn
path (--bg --plugin-dir, not a manual `sh -c` test) surfaced a real gap
the manual test couldn't: the runner does NOT relaunch a monitor command
after it exits. The round-1 guard (`command -v pad && exec ... || sleep
3600`) is a single check-then-sleep-then-exit — once that one hour
elapses, the process exits and, per this observed behavior, is never
restarted, leaving the session permanently monitor-less for the rest of
its life on a machine where pad wasn't yet on PATH at session start.
That silently breaks DOC-2479's "sleep-retry hourly" contract, which the
Go binary's own `pad watch --stream --for-session` loop honors correctly
for its two conditions (no .pad.toml, padd unreachable) via an actual
internal retry loop — this shell guard needs the same shape for its one
condition (pad binary absent).
Wrapped the check in `while ! command -v pad; do sleep 3600; done` so it
keeps re-checking indefinitely instead of dying after one interval, then
falls through to the same `exec pad watch --stream --for-session` once
pad becomes resolvable. Verified live through the real spawn path with a
shortened sleep interval on a throwaway plugin copy: a fresh sleep child
appears every cycle (confirms the loop keeps running, not one-shot), then
re-verified the actual committed command (real sleep 3600) resolves a
live `pad watch --stream --for-session` process when pad is present and
sits in a healthy, silent, no-error `sleep 3600` loop when it's absent —
both through claude --bg --plugin-dir, not manual shell invocation.
claude plugin validate --strict clean on both repo root (marketplace)
and plugin/ (plugin manifest).
* fix(plugin): probe watch capability, not just binary existence, in the monitor guard (TASK-2534)
Codex round 2, P1: the round-1 fix looped on `command -v pad`, but an
old pad binary predating `pad watch` resolves on PATH fine and then
dies non-zero the moment `exec pad watch --stream --for-session` runs
— and the previous commit already established the runner never
relaunches an exited monitor command, so that death is permanent for
the session. `command -v` only proves the binary exists, not that it
has the subcommand this guard needs.
Changed the loop condition to `pad watch --help`, which probes the
actual capability in one call: covers both missing-binary (no such
command) and old-binary-without-watch (unknown subcommand, non-zero
exit) with a single condition. Verified first that `--help` is safe to
call as a probe — it succeeds (exit 0) with no `.pad.toml` anywhere
and padd unreachable, so it can't deadlock a legitimate unlinked-
project or padd-down user behind the guard.
claude plugin validate --strict clean on both repo root (marketplace)
and plugin/ (plugin manifest).
* fix(plugin): round-2 doc fixes — capture routing, confirm scope, invocation wording, CLI accuracy (TASK-2534)
Findings 2-8 from codex round 2:
- capture/SKILL.md: no unlinked-workspace branch — would fail opaquely
on `pad collection list` without .pad.toml. Mirrors the same routing
fix the main skill already has: run `pad workspace init` first.
- pad/SKILL.md's notification write-exception didn't say whether the
confirm-always principle (Key Principles #3) still applies inside it.
It does — the exception only lifts never-write, not confirm-first —
now stated explicitly where the exception lives, same rule as
capture's autonomous-capture opt-out.
- The `/pad` shorthand disclaimer scoped itself to "the playbook-routing
and examples sections below," missing Context Loading's "on every
`/pad invocation`" a few lines above it. Widened to cover the whole
document. plugin.json's own description repeated the bare "/pad
conversational surface" claim; reworded to name the real typed
shortcuts instead, and synced marketplace.json's plugin-entry
description to match (was already a paraphrase, now identical).
- Ideation example passed both `--content` and `--stdin` to the same
`pad item create` call — verified --stdin wins and hangs on ReadAll
with no piped input. Dropped --stdin, kept --content.
- Plan-retro example was missing the mandatory `--comment` this same
doc teaches elsewhere. Key Principles #2's status enumeration listed
"blocked" as a task status — verified against
internal/collections/templates.go: the tasks schema's actual options
are open/in-progress/done/cancelled, no "blocked". Fixed both.
- Two role-board pointers claimed `pad server open` lands on
`/{workspace}/roles` directly; verified cmd/pad/cmd_server.go's open
command only appends the workspace slug, never a sub-path. Reworded
both to describe navigating to Roles from the opened UI rather than
asserting an unverified deep-link.
claude plugin validate --strict clean on both repo root (marketplace)
and plugin/ (plugin manifest).
* fix(plugin): safe (non-hanging) unlinked-workspace self-heal, onboarding offer in status, ask-events wording (TASK-2534)
Codex round 3, findings 1-3:
- The routing added in rounds 1-2 told the skill to run bare
`pad workspace init` whenever bootstrap reported "no workspace
linked." Verified live this can HANG INDEFINITELY: with pad
configured (mode/URL set) but no admin account created yet /
session not authenticated, `pad workspace init` calls a
browser-based setup/login flow with no TTY guard and no
non-interactive fallback — unlike `pad init`, which has an explicit
`!canPromptForConfig()` check at the same step and fails fast with a
headless-bootstrap hint instead. Bootstrap's own two failure
signatures ("Pad is not configured" vs "no workspace linked") don't
fully disambiguate this either — verified live that "no workspace
linked" fires even when the deeper problem is "no admin account
exists yet," which is exactly the state that hangs.
Fixed by adding a `pad auth whoami` preflight (verified live: fast,
safe, never blocks, in all three states — unconfigured, configured-
but-unauthenticated, and fully set up) before ever attempting
`pad workspace init`. Only self-heal when whoami reports a real
user; otherwise tell the user to run `pad init` themselves in an
interactive terminal (Claude Code: suggest `! pad init`). Applied
consistently across pad/SKILL.md's Context Loading and Onboarding
routing, onboard/SKILL.md, and capture/SKILL.md's unlinked-workspace
branch (round 2 addition, same hang risk).
- status/SKILL.md ran `pad project dashboard --format json` but never
checked its `needs_onboarding` field (verified present on that
response too, not just bootstrap's — internal/server/handlers_
dashboard.go's NeedsOnboarding), so the mandated onboarding offer
couldn't fire via `/pad:status`. Added the same offer wording the
main skill uses.
- monitors.json's description claimed the stream delivers "asks for
your role" alongside assignment; verified against
internal/watchevents/watchevents.go: KindAsk is contract-reserved
with no Phase 1 producer. Reworded to describe what ships today,
noting ask-events as reserved-not-yet-emitted.
Filed docapp TASK-2537 for the fourth finding (embed-source
skills/pad/SKILL.md has the same --content/--stdin bug and has
drifted from these corrections) — selective port, out of this diff's
scope per the two skills' by-design framing divergence.
claude plugin validate --strict clean on both repo root (marketplace)
and plugin/ (plugin manifest).
* fix(plugin): --full on body-promising queries, capture loads always-on conventions, onboard's already-set-up path (TASK-2534)
Codex round 4, findings 1-3:
- Every `pad item list conventions|playbooks --format json` example whose
surrounding prose promises "bodies" was missing `--full` — verified
against cmd/pad/cmd_item.go: the flag exists specifically because JSON
output defaults to a token-light summary shape with no content.
Without it, all 8 of these calls (the Context Loading bootstrap-field
description at :41, the 7 trigger-query examples under "Before
Performing Work") would return exactly what the surrounding text says
they're loading bodies to avoid: metadata with no content. Added
--full to all 8, not just the block the finding cited — the "Template"
lines carry the identical "pull their bodies" promise as the
"Concrete examples" lines a few lines below them, so leaving one set
fixed and the other not would just move the inconsistency.
Swept the rest of the file's `item list ... --format json` examples
(Ideation load-context, Status Check role queue, Daily Standup) —
left those alone; their surrounding prose asks for enumeration/counts
("3 items in your queue", "Yesterday/Today/Blockers"), not body
content. The one exception found: Retrospective's task-load line
promises "what shipped, what was deferred, lessons learned" — that
needs actual task content to synthesize, so it got --full too, with
a one-line note explaining why (the other fixed lines didn't need
one; their promise was already explicit in the surrounding prose).
- capture/SKILL.md never loaded conventions at all. Per the dispatcher's
ruling: low ceremony means skipping bootstrap's dashboard/playbook/
role weight, not skipping mandatory project rules (trigger=always
conventions exist precisely to be small and always-applied). Capture
now runs the always-on conventions query (with --full) before
creating and applies whatever it returns; the autonomous-capture
exception now checks the SAME load instead of running its own
separate (previously --full-less) query. One sentence documents the
tradeoff as deliberate: always-on rules yes, full bootstrap no.
- onboard/SKILL.md only defined the needs_onboarding=true branch. Added
the false branch: say the workspace is already set up, summarize what
exists (collection/convention counts from the bootstrap payload
already in hand), and offer extend/audit instead of re-running
first-time setup — same confirm-first rule as everywhere else.
claude plugin validate --strict clean on both repo root (marketplace)
and plugin/ (plugin manifest).
|