Skip to content

Commit 2039bd2

Browse files
committed
Add node lab debug status and attach UX
1 parent 0125a69 commit 2039bd2

10 files changed

Lines changed: 550 additions & 8 deletions

File tree

.blitz/test_state_v1/indexes/task_states.ndjson

Lines changed: 53 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Distributed Cleanup Runbook
2+
3+
Use this runbook after local distributed StackLab proofs, especially after a
4+
failed run or interrupted terminal session.
5+
6+
## Normal Cleanup
7+
8+
```bash
9+
mix stack_lab.gn_ten.node_lab.down --json
10+
```
11+
12+
The command removes the node-lab run-state file. Peer-mode proofs also stop
13+
peers before the original command returns and record cleanup receipts.
14+
15+
## Verify EPMD State
16+
17+
```bash
18+
epmd -names
19+
```
20+
21+
No expected `stack_lab` proof node should remain after cleanup. If a local
22+
debug session is still attached, close it before treating the cleanup as
23+
complete.
24+
25+
## Inspect Status
26+
27+
```bash
28+
mix stack_lab.gn_ten.node_lab.status --json
29+
```
30+
31+
A clean state reports `no_active_run`. If a run state remains, inspect the
32+
status summary for log paths, cleanup posture, and node ids, then rerun
33+
`down`.
34+
35+
## Worktree Hygiene
36+
37+
```bash
38+
git status --short
39+
```
40+
41+
Generated runtime output belongs under ignored or temporary paths. A proof run
42+
must not create tracked source, fixture, or docs changes unless the phase
43+
explicitly updates deterministic fixtures or proof-matrix rows.

docs/runbooks/distributed_node_lab.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,33 @@ retention. That claim belongs to a later daemon or release-path controller.
3434
```bash
3535
mix stack_lab.gn_ten.node_lab.status --json
3636
mix stack_lab.gn_ten.node_lab.probe --node fixture_profile_0 --json
37+
mix stack_lab.gn_ten.node_lab.attach --node fixture_profile_0 --json
3738
mix stack_lab.gn_ten.node_lab.down --json
3839
```
3940

40-
`status` reads the latest run-state receipt. `probe` reads one logical node
41-
receipt from that state. `down` removes the run-state file and is idempotent.
41+
`status` reads the latest run-state receipt and reports a debug summary:
42+
logical node ids, profiles, peer node names, started OTP apps, owner `:pg`
43+
membership, facade readiness, current connection posture, log path, latest
44+
receipt refs, cleanup posture, and artifact hygiene. `probe` reads one logical
45+
node receipt from that state. `attach` prints a redacted local debug-shell
46+
recipe for the node. `down` removes the run-state file and is idempotent.
47+
48+
## Attach
49+
50+
```bash
51+
mix stack_lab.gn_ten.node_lab.attach --node fixture_profile_0 --json
52+
```
53+
54+
The attach receipt names the node and profile and prints only a redacted
55+
manual shell shape:
56+
57+
```text
58+
iex --sname debug_shell_<run> --cookie <redacted_run_cookie> --remsh <node>
59+
```
60+
61+
Phase 15 does not store or print cookie values. It also does not claim durable
62+
cross-command peer retention; current peer-mode proofs clean up peers before
63+
returning unless a future retained controller owns that stronger behavior.
4264

4365
## Safety Notes
4466

guides/distributed_debugging.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Distributed Debugging
2+
3+
StackLab distributed debugging is a local proof-harness workflow. It helps
4+
inspect peer-mode runs without turning Erlang distribution into a production
5+
security model or a platform business API.
6+
7+
## Status
8+
9+
```bash
10+
mix stack_lab.gn_ten.node_lab.status --json
11+
```
12+
13+
The status receipt reports:
14+
15+
- run id and topology ref;
16+
- logical node id, profile, and BEAM node name;
17+
- started OTP apps;
18+
- owner-defined `:pg` membership;
19+
- facade readiness;
20+
- current connection posture;
21+
- log path;
22+
- latest receipt refs;
23+
- cleanup posture;
24+
- artifact hygiene for run-state and log paths.
25+
26+
It must not include Erlang cookie values. The word `cookie` may appear in
27+
posture fields, but the secret value is never printed.
28+
29+
## Attach
30+
31+
```bash
32+
mix stack_lab.gn_ten.node_lab.attach --node mezzanine_workflow_0 --json
33+
```
34+
35+
The attach command prints a redacted local shell recipe for the selected
36+
logical node:
37+
38+
```text
39+
iex --sname debug_shell_<run> --cookie <redacted_run_cookie> --remsh <node>
40+
```
41+
42+
This is a development convenience. It is not tenant authority, not product
43+
authority, and not a production operations model. Current peer-mode runs clean
44+
up peers before returning, so attach receipts can be `not_attached` while still
45+
documenting the safe recipe and node target.
46+
47+
## Logs
48+
49+
Node-lab logs are written under generated or temporary paths such as:
50+
51+
```text
52+
tmp/stack_lab/<run_id>/logs/distributed.log
53+
```
54+
55+
Each line includes timestamp, profile, logical node id, BEAM node name, stream,
56+
correlation ref, and a redacted message. Cookies, credentials, raw prompts, raw
57+
memory, and raw provider payloads are not valid log output.
58+
59+
## Local Inspection
60+
61+
Observer and low-level Erlang tracing are local-only debugging aids:
62+
63+
```elixir
64+
:observer.start()
65+
```
66+
67+
Automated proof assertions use owner receipts, StackLab receipts, and AITrace
68+
exports. They do not depend on Observer, remote PIDs, or ad hoc `:dbg` output.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
defmodule Mix.Tasks.StackLab.GnTen.NodeLab.Attach do
2+
@moduledoc "Prints a redacted debug attach receipt for a gn-ten node-lab node."
3+
4+
use Mix.Task
5+
6+
alias StackLab.GnTenNodeLab.{RunState, Runner}
7+
8+
@shortdoc "Prints a redacted gn-ten node-lab attach recipe"
9+
10+
@impl Mix.Task
11+
def run(args) do
12+
Mix.Task.run("app.start")
13+
14+
{opts, _argv, invalid} =
15+
OptionParser.parse(args,
16+
strict: [
17+
node: :string,
18+
state: :string,
19+
json: :boolean
20+
]
21+
)
22+
23+
if invalid != [], do: Mix.raise("invalid options: #{inspect(invalid)}")
24+
25+
node_id = Keyword.get(opts, :node) || Mix.raise("expected --node <logical_node_id>")
26+
runner_opts = [state_path: Keyword.get(opts, :state, RunState.default_path())]
27+
28+
case Runner.attach(node_id, runner_opts) do
29+
{:ok, receipt} ->
30+
print(receipt, Keyword.get(opts, :json, false), :info)
31+
32+
{:error, receipt} ->
33+
print(receipt, Keyword.get(opts, :json, false), :error)
34+
exit({:shutdown, 1})
35+
end
36+
end
37+
38+
defp print(receipt, true, level) do
39+
encoded = Jason.encode!(receipt, pretty: true)
40+
if level == :error, do: Mix.shell().error(encoded), else: Mix.shell().info(encoded)
41+
end
42+
43+
defp print(receipt, false, :info) do
44+
Mix.shell().info("stack_lab.gn_ten.node_lab.attach #{receipt["status"]}")
45+
Mix.shell().info("node=#{receipt["node"]}")
46+
Mix.shell().info("cookie=<redacted>")
47+
end
48+
49+
defp print(receipt, false, :error) do
50+
Mix.shell().error("stack_lab.gn_ten.node_lab.attach failed")
51+
Mix.shell().error("failures=#{inspect(receipt["failures"])}")
52+
end
53+
end

mix.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,14 @@ defmodule StackLab.Workspace.MixProject do
158158
"guides/generalized_stack.md",
159159
"guides/qc_and_operations.md",
160160
"guides/code_smell_remediation.md",
161+
"guides/distributed_debugging.md",
161162
"docs/gn_ten_proof_matrix.md",
162163
"docs/review/gn_ten_batch_review.md",
163164
"docs/review/shared_library_governed_adapter_review.md",
164165
"docs/runbooks/up_single.md",
165166
"docs/runbooks/up_multi.md",
166167
"docs/runbooks/distributed_node_lab.md",
168+
"docs/runbooks/distributed_cleanup.md",
167169
"docs/runbooks/faults.md",
168170
"docs/runbooks/tre_lane_acceptance.md",
169171
"CHANGELOG.md",
@@ -181,6 +183,7 @@ defmodule StackLab.Workspace.MixProject do
181183
"guides/generalized_stack.md",
182184
"guides/qc_and_operations.md",
183185
"guides/code_smell_remediation.md",
186+
"guides/distributed_debugging.md",
184187
"docs/gn_ten_proof_matrix.md",
185188
"docs/review/gn_ten_batch_review.md",
186189
"docs/review/shared_library_governed_adapter_review.md"
@@ -189,6 +192,7 @@ defmodule StackLab.Workspace.MixProject do
189192
"docs/runbooks/up_single.md",
190193
"docs/runbooks/up_multi.md",
191194
"docs/runbooks/distributed_node_lab.md",
195+
"docs/runbooks/distributed_cleanup.md",
192196
"docs/runbooks/faults.md",
193197
"docs/runbooks/tre_lane_acceptance.md"
194198
],

support/gn_ten_node_lab/README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ It owns local test-harness mechanics:
1515
- checked-in topology fixture loading;
1616
- required app boot probes;
1717
- owner-defined facade host and `:pg` readiness checks;
18-
- JSON admin receipts for `preflight`, `up`, `status`, `probe`, and `down`;
18+
- JSON admin receipts for `preflight`, `up`, `status`, `probe`, `attach`, and
19+
`down`;
1920
- distributed envelope scanning for tenant, authority, trace, idempotency,
2021
payload-mode, redaction, local-term, raw-payload, cross-tenant, stale-schema,
2122
and direct-lower-import defects;
@@ -48,6 +49,7 @@ mix stack_lab.gn_ten.node_lab.up \
4849
--json
4950
mix stack_lab.gn_ten.node_lab.status --json
5051
mix stack_lab.gn_ten.node_lab.probe --node fixture_profile_0 --json
52+
mix stack_lab.gn_ten.node_lab.attach --node fixture_profile_0 --json
5153
mix stack_lab.gn_ten.node_lab.down --json
5254
```
5355

@@ -57,6 +59,13 @@ and recorded as an explicit intent, but cross-command peer retention is not a
5759
v2 Phase 6 claim. A later daemon or release-path controller must own that
5860
stronger claim.
5961

62+
`status` returns a debug summary with node ids, profile names, node names,
63+
started apps, owner `:pg` membership, facade readiness, log path, latest
64+
receipt refs, cleanup posture, and artifact hygiene. `attach` prints a
65+
redacted local debug-shell recipe for a logical node. It intentionally omits
66+
the Erlang cookie value and records that Phase 15 does not prove production
67+
security or cross-command peer retention.
68+
6069
## Distributed Envelope Scanner
6170

6271
The package exposes `StackLab.GnTenNodeLab.scan_envelope/2` and

0 commit comments

Comments
 (0)