Skip to content

feat(spec): remote-shell kickoff sign-off - #97

Merged
inkatze merged 5 commits into
mainfrom
planwright/remote-shell/spec
Aug 5, 2026
Merged

feat(spec): remote-shell kickoff sign-off#97
inkatze merged 5 commits into
mainfrom
planwright/remote-shell/spec

Conversation

@inkatze

@inkatze inkatze commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Kickoff sign-off for specs/remote-shell. Walkthrough complete, all four files
flipped Draft → Ready.

Spec specs/remote-shell
Brief specs/remote-shell/kickoff-brief.md
Scope Full bundle (first activation)
Validator spec-validate.sh specs/remote-shell — 0 errors, 0 warnings, re-run after the Ready flip
Anchor 8a7b5f18fd515f08a413ff7b492cbb4c82b692c9scripts/spec-anchor.sh specs/remote-shell

Lens pass

All nine Discovery-Rigor lenses walked inline (sub-agent fan-out was unavailable
this session; the substitution is declared in the brief rather than silent).
Every finding dispositioned, none declined or deferred. The kickoff altitude
check passes: a trigger fired, D-1 is the altitude record, it is cited from the
goal, and the task decomposition matches the claimed altitude.

The one finding needing its own fix was a commit blocker: this bundle's
edits tripped internal-hostname eleven times, every match a repo-owned
filename rather than a machine name. Fixed with three anchored allowlist
entries in a separate commit, verified in both directions — the staged hook
passes, and a real <host>.local still fires.

What the walkthrough changed

Four corrections where the spec disagreed with the repo:

  • REQ-D re-scoped from build to declare-and-verify. The ~/.ssh/auth_sock
    indirection already exists — confirmed against the repo, against commit
    8990a23 (which postdates the observation the requirement was drafted from),
    and against the live host. It also resolves to the host's own 1Password
    agent, so the stale-forwarded-agent failure the bundle was framed around does
    not arise on this route.
  • Task 6 narrowed to the Linux baseline, resolving a contradiction between
    its Done-when and the bundle's own macOS exclusion.
  • D-2's mosh rejection re-grounded on port forwarding, after the
    agent-forwarding premise it led with turned out to be false. Conclusion
    unchanged; the reasoning is now true.
  • The Task 4 → Task 5 dependency dropped as a leftover of the pre-walkthrough
    framing, shortening the critical path.

One scope addition on request: phone client access as REQ-G, D-10 and Task 8,
reusing the tailnet route and the repo's existing separate-authorized-keys-file
pattern, with the key injected from 1Password rather than committed.

Bundle now carries 27 REQs against 27 test-spec entries (one-to-one, re-derived
mechanically), 10 design decisions, 8 tasks, and a 13-row risk register — three
rows of which are catalogued decision domains the spec touches but deliberately
does not decide.

Next

Merge is the second key. Nothing dispatches until this merges; Active is then
derived from the first task to start.

inkatze added 5 commits August 5, 2026 11:57
The internal-hostname rule matches any dotted <label>.local-style string and
cannot tell a machine name from a path this repo declares itself. config.local,
config.lan (roles/ssh/files/config.lan.tpl) and planwright.local
(.claude/planwright.local.yml) are files, not hosts, and every commit that so
much as names them was being blocked.

Anchored to the exact matched strings, so a real host called
<something>-config.local still fires. Verified in both directions: the staged
hook now exits 0 on the remote-shell bundle, and a negative control
(Hostname my-server.local) still trips the rule.
Section-by-section walkthrough to mutual understanding, signed off; all four
files flipped Draft -> Ready.

Six recorded decisions. Two ambiguous terms pinned: the tailnet address rides
the 1Password server_host field, in MagicDNS form. Four corrections where the
spec disagreed with the repo -- REQ-D re-scoped from build to declare-and-verify
after three checks proved the ~/.ssh/auth_sock indirection already exists
(8990a23); Task 6 narrowed to the Linux baseline, resolving a contradiction with
the macOS exclusion; D-2's mosh rejection re-grounded on port forwarding after
its agent-forwarding premise fell; the Task 4 -> Task 5 dependency dropped as a
leftover of the old framing. One scope addition on request: phone client access
as REQ-G, D-10 and Task 8.

Bundle now 27 REQs / 27 test-spec entries (one-to-one, re-derived
mechanically), 10 design decisions, 8 tasks, and a 13-row risk register
carrying three catalogued decision domains the spec touches but does not
decide.
The kickoff sign-off is complete and anchored; only the terminal ready-flip is
outstanding. Its gate requires a green CI rollup on the PR head commit, and the
repo pipeline is not green for reasons outside this bundle, so PR #97 stays a
draft. Re-entry is the flip alone.

Recorded as a plain bullet rather than a task reference bullet: it is a
bundle-level publication step, and parking a real task to express it would
distort the derived status render. That tension between the refusal arm and the
format-version 2 tasks.md contract is filed as a skill-drift observation.
@inkatze
inkatze marked this pull request as ready for review August 5, 2026 21:25
@inkatze
inkatze merged commit 58facc7 into main Aug 5, 2026
1 check passed
@inkatze
inkatze deleted the planwright/remote-shell/spec branch August 5, 2026 21:25
inkatze added a commit that referenced this pull request Aug 6, 2026
…hestration observations (#99)

* chore(observations): record dev-services orchestration step findings

Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9

* chore(observations): record planwright worker-permission seam findings

Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9

* chore(observations): record the silent stream-json recover hang

Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9

* chore(observations): record worker LC_ALL=C inheritance breaking project CI

Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9

* feat(tooling): gate shell scripts on shellcheck

The repo lints YAML and Ansible and scans for secrets, but its 13 tracked
shell scripts had no static analysis at all. Surfaced when a dev-services
Task 1 worker reached for shellcheck to check the scripts it was writing
and found the repo did not ship it.

Pinned in mise.toml alongside the other tools, and wired into lefthook so
it applies to every shell script rather than one task's.

--severity=warning rather than the default: the tracked scripts carry
info-level SC2015 notes (the deliberate 'A && B || C' assertion idiom in
the *-test.sh harnesses) which would block every commit. Warning-level is
green across the repo today, so the gate catches regressions instead of
demanding a cleanup first.

The one warning-level blocker was module-setup.sh, where SC2154 fires on
initdir/moddir/systemdsystemunitdir. Those are dracut's module API,
injected into the script by dracut itself, so the finding is a false
positive and is silenced file-wide with the reason recorded inline.

Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9

* chore(gitleaks): exempt mise.local.toml as a repo-owned filename

Adding `mise.local.toml` to .gitignore was blocked by the internal-hostname
rule, which matched the literal `mise.local` inside the filename. The rule
cannot tell a repo-owned path from a machine name, which is the exact case
#97 already carved out a second exemption class for; this is its fourth
member, not a new precedent.

Anchored like its siblings, so the backstop still catches a real host:
verified that `mise.local.toml` passes clean while `buildbox-mise.local`
still fires.

The file itself is the machine-local mise env at the repo root, carrying
this host's absolute CLAUDE_PLUGIN_DATA and CLAUDE_PLUGIN_ROOT so
planwright's fleet scripts can resolve their state home. Untracked for the
usual reason: every value in it is specific to one machine.

Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9

* chore: gitignore planwright's per-spec orchestration runtime state

Every /orchestrate dispatch writes a timestamped marker under
specs/<spec>/.orchestrate/, which is runtime state by design — the tower
rebuilds it from git and process evidence rather than reading it back from
a commit. Untracked but unignored, it left the working tree dirty after
every run and showed up as an uncommitted-change warning when opening a PR.

Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant