@lab43/q 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +17 -12
- package/agents/adversarial-reviewer.md +3 -3
- package/conventions/documentation.md +17 -14
- package/conventions/extensions.md +8 -4
- package/conventions/issue-tracking.md +11 -2
- package/conventions/plans.md +1 -1
- package/conventions/principles.md +2 -2
- package/conventions/pull-requests.md +2 -2
- package/conventions/writing.md +8 -0
- package/hooks/session-start.mjs +9 -6
- package/package.json +17 -3
- package/references/agent-briefing.md +15 -14
- package/references/q-state.md +1 -1
- package/references/run-contract.md +10 -5
- package/skills/address-feedback/SKILL.md +7 -7
- package/skills/create-plan/SKILL.md +4 -4
- package/skills/drive/SKILL.md +1 -1
- package/skills/groom-docs/SKILL.md +9 -9
- package/skills/{tackle → implement}/SKILL.md +11 -11
- package/skills/implement-plan/SKILL.md +11 -11
- package/skills/install/SKILL.md +11 -11
- package/skills/parallelize/SKILL.md +5 -5
- package/skills/review/SKILL.md +3 -3
- package/skills/sync/SKILL.md +2 -2
- package/skills/triage/SKILL.md +11 -11
- package/skills/uninstall-extension/SKILL.md +8 -8
- package/skills/update/SKILL.md +10 -9
- package/skills/update-docs/SKILL.md +12 -12
- package/skills/upstream/SKILL.md +7 -7
package/skills/update/SKILL.md
CHANGED
|
@@ -9,15 +9,15 @@ Follow the run contract — `${CLAUDE_PLUGIN_ROOT}/references/run-contract.md`.
|
|
|
9
9
|
|
|
10
10
|
## Step 1: Take stock
|
|
11
11
|
|
|
12
|
-
Bare invocation covers `@lab43/q` and every installed extension — the direct `devDependencies` whose own `package.json` carries the `q-extension` keyword (source: q conventions/extensions.md). A named target scopes the run: `q` means `@lab43/q`; any other name means that extension. Confirm any target you can't identify as an extension before treating it as one. Below, *package* covers both q and an extension.
|
|
12
|
+
Bare invocation covers `@lab43/q` and every installed extension — the direct `devDependencies` whose own `package.json` carries the `q-extension` keyword (source: @lab43/q conventions/extensions.md). A named target scopes the run: `q` means `@lab43/q`; any other name means that extension. Confirm any target you can't identify as an extension before treating it as one. Below, *package* covers both q and an extension.
|
|
13
13
|
|
|
14
14
|
Read four versions for each package in scope — the watermarks per `${CLAUDE_PLUGIN_ROOT}/references/q-state.md`:
|
|
15
15
|
|
|
16
16
|
| Pinned | Installed | Latest | Watermark |
|
|
17
17
|
| --- | --- | --- | --- |
|
|
18
|
-
| its pin in the project's `package.json`; in a repo authoring an extension, the q pin lives in that extension's own manifest (source: q conventions/extensions.md) | `version` in `node_modules/<package>/package.json` | `npm view <package> version` | its `reconciledAgainst` entry |
|
|
18
|
+
| its pin in the project's `package.json`; in a repo authoring an extension, the q pin lives in that extension's own manifest (source: @lab43/q conventions/extensions.md) | `version` in `node_modules/<package>/package.json` | `npm view <package> version` | its `reconciledAgainst` entry |
|
|
19
19
|
|
|
20
|
-
Alongside the versions, hold each third-party extension's q declaration — its `@lab43/q` devDependency (source: q conventions/extensions.md) — against the project's own q pin, and flag a mismatch either way. A declaration ahead of the pin closes by updating q here; one behind closes only by that extension's release.
|
|
20
|
+
Alongside the versions, hold each third-party extension's q declaration — its `@lab43/q` devDependency (source: @lab43/q conventions/extensions.md) — against the project's own q pin, and flag a mismatch either way. A declaration ahead of the pin closes by updating q here; one behind closes only by that extension's release.
|
|
21
21
|
|
|
22
22
|
A package with no pin and no watermark entry has nothing to update — propose `/q:install` for it and stop.
|
|
23
23
|
|
|
@@ -44,17 +44,18 @@ Run `npm install --save-dev --save-exact --ignore-scripts <package>@<latest>`, v
|
|
|
44
44
|
|
|
45
45
|
## Step 4: Reconcile what the diff touched
|
|
46
46
|
|
|
47
|
-
Work only from the diffs. Each package's diff runs from its watermark to its pin as Step 3 left it. What the diff touched decides which of these applies. A diff may touch
|
|
47
|
+
Work only from the diffs. Each package's diff runs from its watermark to its pin as Step 3 left it. What the diff touched decides which of these applies. A diff may touch more than one:
|
|
48
48
|
|
|
49
49
|
- **Changed `conventions/`** — hold the project's docs against each changed rule:
|
|
50
|
-
- remove an override whose target updated to agree or disappeared — it is spent (source: q conventions/documentation.md, Three tiers of conventions)
|
|
50
|
+
- remove an override whose target updated to agree or disappeared — it is spent (source: @lab43/q conventions/documentation.md, Three tiers of conventions)
|
|
51
51
|
- re-check each "(source: …)" restatement against its changed home
|
|
52
52
|
- prune a project rule the new text now owns — it is duplication now
|
|
53
53
|
- ask about a project rule the new text contradicts, the one call the go-ahead didn't settle: keep it as a recorded deviation (add the overrides marker) or adopt the incoming rule. Adopting can leave code non-conforming — suggest `/q:review` on the affected area; code fixes are out of scope here
|
|
54
54
|
|
|
55
|
-
An extension authored in this repo is part of that surface: re-check its docs the same way. The q pin this run moved is also that extension's shipped written-against declaration, and the re-check is what makes the moved declaration true (source: q conventions/extensions.md).
|
|
55
|
+
An extension authored in this repo is part of that surface: re-check its docs and its own `description` the same way. The q pin this run moved is also that extension's shipped written-against declaration, and the re-check is what makes the moved declaration true (source: @lab43/q conventions/extensions.md).
|
|
56
56
|
|
|
57
|
-
Then sync the briefing's index lines for the package — a doc added or removed changes the list, a changed intro re-draws its blurb (see: q conventions/documentation.md, Taxonomy).
|
|
57
|
+
Then sync the briefing's index lines for the package — a doc added or removed changes the list, a changed intro re-draws its blurb (see: @lab43/q conventions/documentation.md, Taxonomy).
|
|
58
|
+
- **A changed extension `description`** — re-draw that extension's group heading in the briefing's docs index (see: `${CLAUDE_PLUGIN_ROOT}/references/agent-briefing.md`). A release can change the description alone.
|
|
58
59
|
- **A changed plugin** — re-run `/q:install`, scoped to join this run's change: it is idempotent, creating what the new version's scaffold expects and correcting what has drifted from it.
|
|
59
60
|
|
|
60
61
|
After each package's reconciliation, write its watermark per `${CLAUDE_PLUGIN_ROOT}/references/q-state.md`: its `reconciledAgainst` entry to its pinned version.
|
|
@@ -67,8 +68,8 @@ In ship mode, commit first. In both modes, validate the changes (see: ${CLAUDE_P
|
|
|
67
68
|
|
|
68
69
|
## Step 6: Open the PR
|
|
69
70
|
|
|
70
|
-
1. **
|
|
71
|
-
2. **Open the PR**: push the branch and open the PR per the PR-authoring rules (see: q conventions/pull-requests.md).
|
|
71
|
+
1. **The local gate**: run it over the uncommitted changes (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, The local gate).
|
|
72
|
+
2. **Open the PR**: push the branch and open the PR per the PR-authoring rules (see: @lab43/q conventions/pull-requests.md).
|
|
72
73
|
3. Close the session by reporting:
|
|
73
74
|
- Old and new pins, and each catch-up applied without a pin move.
|
|
74
75
|
- What each release changed.
|
|
@@ -14,34 +14,34 @@ Follow the run contract — `${CLAUDE_PLUGIN_ROOT}/references/run-contract.md`.
|
|
|
14
14
|
The change comes from the invocation: named in the prompt, surfaced by the session's own work, or handed over by a delegating skill. On a bare invocation, sweep the session for candidates — decisions made, gotchas hit, corrections taken. Route each by what it is:
|
|
15
15
|
|
|
16
16
|
- **A lesson, pattern, or decision to record — including a decision to change an existing convention** → the conventions path, Step 3.
|
|
17
|
-
- **A fact, instruction, or overview change for the product's readers** → the surface the taxonomy assigns it — a guide, the README, or the briefing, whether or not the invocation named one (see: q conventions/documentation.md, Taxonomy).
|
|
18
|
-
- **A plan change** → the lifecycle's rules — status flips and marked amendments; authoring new plans belongs to `/q:create-plan`, and amendments during an implementation run to `/q:implement-plan`, not here (see: q conventions/plans.md, Lifecycle).
|
|
17
|
+
- **A fact, instruction, or overview change for the product's readers** → the surface the taxonomy assigns it — a guide, the README, or the briefing, whether or not the invocation named one (see: @lab43/q conventions/documentation.md, Taxonomy).
|
|
18
|
+
- **A plan change** → the lifecycle's rules — status flips and marked amendments; authoring new plans belongs to `/q:create-plan`, and amendments during an implementation run to `/q:implement-plan`, not here (see: @lab43/q conventions/plans.md, Lifecycle).
|
|
19
19
|
- **A change that belongs in a q skill's instructions** → not a doc change; flag it as an upstream candidate — suggest `/q:upstream`. (A change that belongs in an extension's doc stays on the conventions path — Step 3 records it locally as a deviation first.)
|
|
20
20
|
|
|
21
21
|
## Step 2: Read the policy
|
|
22
22
|
|
|
23
|
-
Read q's documentation policy (see: q conventions/documentation.md) and its writing rules (see: q conventions/writing.md), plus any installed extension's doc whose topic governs documentation, and the project's `docs/conventions/documentation.md` — its recorded rulings and deviations win on conflict.
|
|
23
|
+
Read q's documentation policy (see: @lab43/q conventions/documentation.md) and its writing rules (see: @lab43/q conventions/writing.md), plus any installed extension's doc whose topic governs documentation, and the project's `docs/conventions/documentation.md` — its recorded rulings and deviations win on conflict.
|
|
24
24
|
|
|
25
25
|
## Step 3: The conventions path — qualify the lesson
|
|
26
26
|
|
|
27
|
-
Read q's principles (see: q conventions/principles.md), plus any installed extension's doc whose topic governs cross-cutting principles, and the project's `docs/conventions/principles.md`. Then hold the lesson to four gates, in order:
|
|
27
|
+
Read q's principles (see: @lab43/q conventions/principles.md), plus any installed extension's doc whose topic governs cross-cutting principles, and the project's `docs/conventions/principles.md`. Then hold the lesson to four gates, in order:
|
|
28
28
|
|
|
29
29
|
1. **Is it a rule?** Would it change what a future reader writes or flags? Narrative, descriptions of current behavior, and code-readable facts don't qualify — the code carries those. What qualifies is the binding form: the constraint, the do/don't, the decision with rationale. No rule in it ends the path — report that, don't force an entry.
|
|
30
|
-
2. **Where will its next reader be standing?** A fact needed only when touching one specific site becomes a comment there, in code or in a doc's markup, not a conventions entry. A lesson the next person would re-trip writing similar code elsewhere is cross-cutting even with one current instance — that one goes in the doc. Genuinely uncertain: comment now, promote on second occurrence (source: q conventions/principles.md, Colocate knowledge with its next reader).
|
|
31
|
-
3. **Is prose the right rung?** A rule that a component or lint could hold shouldn't settle for documentation (source: q conventions/principles.md, Prefer the strongest enforcement rung). If a stronger rung exists, propose *that* as the fix (or schedule it), with the rationale colocated in the component or lint rule.
|
|
32
|
-
4. **Which tier?** A lesson about the project's code belongs in its `docs/conventions/`. A lesson that seems to belong elsewhere — about the q workflow itself, or a topic an installed extension owns — is recorded in the project's `docs/conventions/` too, as a marked project deviation where it contradicts that rule, and flagged to the user in the session as an upstream candidate — suggest `/q:upstream`. Workflow-general versus project-specific is hard to call from inside one project: record and flag, don't withhold (source: q conventions/documentation.md, Three tiers of conventions). The exception is an extension this repo authors: a lesson its docs own is edited directly into them — the author changes the rule; overrides and upstreaming are the consumer's mechanism (source: q conventions/extensions.md).
|
|
30
|
+
2. **Where will its next reader be standing?** A fact needed only when touching one specific site becomes a comment there, in code or in a doc's markup, not a conventions entry. A lesson the next person would re-trip writing similar code elsewhere is cross-cutting even with one current instance — that one goes in the doc. Genuinely uncertain: comment now, promote on second occurrence (source: @lab43/q conventions/principles.md, Colocate knowledge with its next reader).
|
|
31
|
+
3. **Is prose the right rung?** A rule that a component or lint could hold shouldn't settle for documentation (source: @lab43/q conventions/principles.md, Prefer the strongest enforcement rung). If a stronger rung exists, propose *that* as the fix (or schedule it), with the rationale colocated in the component or lint rule.
|
|
32
|
+
4. **Which tier?** A lesson about the project's code belongs in its `docs/conventions/`. A lesson that seems to belong elsewhere — about the q workflow itself, or a topic an installed extension owns — is recorded in the project's `docs/conventions/` too, as a marked project deviation where it contradicts that rule, and flagged to the user in the session as an upstream candidate — suggest `/q:upstream`. Workflow-general versus project-specific is hard to call from inside one project: record and flag, don't withhold (source: @lab43/q conventions/documentation.md, Three tiers of conventions). The exception is an extension this repo authors: a lesson its docs own is edited directly into them — the author changes the rule; overrides and upstreaming are the consumer's mechanism (source: @lab43/q conventions/extensions.md).
|
|
33
33
|
|
|
34
|
-
A lesson through the gates gets a home: the topically-owning doc — grep the surface first, q's conventions and every installed extension's included: a project rule may already exist to refine, and a rule q or an extension already carries is already law — record it only as a marked deviation if the lesson disagrees, never as a copy. A new doc only when no existing topic owns the rule,
|
|
34
|
+
A lesson through the gates gets a home: the topically-owning doc — grep the surface first, q's conventions and every installed extension's included: a project rule may already exist to refine, and a rule q or an extension already carries is already law — record it only as a marked deviation if the lesson disagrees, never as a copy. A new doc is created only when no existing topic owns the rule (source: @lab43/q conventions/documentation.md, Conventions docs). It arrives with its intro and its index line in the same change (source: @lab43/q conventions/documentation.md, Taxonomy).
|
|
35
35
|
|
|
36
36
|
## Step 4: Confirm the scope
|
|
37
37
|
|
|
38
|
-
Confirm what the session derived, in
|
|
38
|
+
Confirm what the session derived, in conversational mode (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, Collaboration modes) — candidates a sweep surfaced, a genuinely contestable home or treatment: state each change, its home, and its treatment — what gets rewritten, deleted, or added. A change the invocation spelled out — the user's prompt or a calling skill's — is already agreed and skips this step: state its classification's small calls rather than asking.
|
|
39
39
|
|
|
40
40
|
In a run invoked directly by the user, ask which review mode — local or ship — the delivery runs under (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, Review modes), even when a spelled-out change skips the rest of this step.
|
|
41
41
|
|
|
42
42
|
## Step 5: Apply per policy
|
|
43
43
|
|
|
44
|
-
Act autonomously once the scope is agreed. In a run that delivers here (Steps 6–7), first pick the delivery branch (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, The delivery branch). Then edit the docs directly rather than proposing wording and waiting for approval — the user reviews the applied changes as a git diff. Draft to each surface's own taxonomy rules (see: q conventions/documentation.md, Taxonomy) and the writing rules (see: q conventions/writing.md). Keep the briefing's docs index in sync if membership or a gloss changed, to the shape the briefing template defines (see: `${CLAUDE_PLUGIN_ROOT}/references/agent-briefing.md`). In a delivering run's ship mode, commit.
|
|
44
|
+
Act autonomously once the scope is agreed. In a run that delivers here (Steps 6–7), first pick the delivery branch (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, The delivery branch). Then edit the docs directly rather than proposing wording and waiting for approval — the user reviews the applied changes as a git diff. Draft to each surface's own taxonomy rules (see: @lab43/q conventions/documentation.md, Taxonomy) and the writing rules (see: @lab43/q conventions/writing.md). Keep the briefing's docs index in sync if membership or a gloss changed, to the shape the briefing template defines (see: `${CLAUDE_PLUGIN_ROOT}/references/agent-briefing.md`). In a delivering run's ship mode, commit.
|
|
45
45
|
|
|
46
46
|
## Step 6: Adversarial review
|
|
47
47
|
|
|
@@ -49,6 +49,6 @@ Changes made for a calling skill end at Step 5: they join the calling run's chan
|
|
|
49
49
|
|
|
50
50
|
## Step 7: Open the PR
|
|
51
51
|
|
|
52
|
-
1. **
|
|
53
|
-
2. **Open the PR**: push the branch and open the PR per the PR-authoring rules (see: q conventions/pull-requests.md).
|
|
52
|
+
1. **The local gate**: run it over the uncommitted changes (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, The local gate).
|
|
53
|
+
2. **Open the PR**: push the branch and open the PR per the PR-authoring rules (see: @lab43/q conventions/pull-requests.md).
|
|
54
54
|
3. Close the session by reporting each change and its home, plus anything swept but not recorded and why.
|
package/skills/upstream/SKILL.md
CHANGED
|
@@ -16,13 +16,13 @@ Follow the run contract — `${CLAUDE_PLUGIN_ROOT}/references/run-contract.md`.
|
|
|
16
16
|
3. The project's marked overrides — grep `docs/conventions/` for "(overrides:", dropping those that target the project's own docs.
|
|
17
17
|
4. The project's unmarked elaborations — read the project docs whose filenames match one of q's conventions or an installed extension's, for rulings that build on a rule there rather than contradict it.
|
|
18
18
|
|
|
19
|
-
Partition the candidates by destination: skill friction and "(overrides: q …)" targets belong to the q repo, `Lab43/q`; a candidate targeting an extension's doc belongs to that extension's repo, read from `repository` in `node_modules/<extension>/package.json`. An extension with no repository recorded can't be PRed — carry its candidates to the report for the user to deliver by hand.
|
|
19
|
+
Partition the candidates by destination: skill friction and "(overrides: @lab43/q …)" targets belong to the q repo, `Lab43/q`; a candidate targeting an extension's doc belongs to that extension's repo, read from `repository` in `node_modules/<extension>/package.json`. An extension with no repository recorded can't be PRed — carry its candidates to the report for the user to deliver by hand.
|
|
20
20
|
|
|
21
21
|
## Step 2: Converge with the user
|
|
22
22
|
|
|
23
|
-
Qualify the candidates against q's documentation policy (see: q conventions/documentation.md). An override or elaboration made for project-specific reasons doesn't qualify. Don't disqualify one for adopting an alternative the target doc records as rejected — that is evidence against the rejection, and the candidate becomes revisiting it. Check each destination's PR history too — search open and closed PRs per candidate (`gh pr list --repo <owner>/<repo> --state all --search "<topic>"`), reading a hit's diff when its description doesn't settle the overlap: a candidate an open PR already covers is recommended defer, and one already proposed and closed without merging qualifies only with evidence the earlier PR lacked. Non-qualifiers are dropped without discussion and surface only in the report.
|
|
23
|
+
Qualify the candidates against q's documentation policy (see: @lab43/q conventions/documentation.md). An override or elaboration made for project-specific reasons doesn't qualify. Don't disqualify one for adopting an alternative the target doc records as rejected — that is evidence against the rejection, and the candidate becomes revisiting it. Check each destination's PR history too — search open and closed PRs per candidate (`gh pr list --repo <owner>/<repo> --state all --search "<topic>"`), reading a hit's diff when its description doesn't settle the overlap: a candidate an open PR already covers is recommended defer, and one already proposed and closed without merging qualifies only with evidence the earlier PR lacked. Non-qualifiers are dropped without discussion and surface only in the report.
|
|
24
24
|
|
|
25
|
-
Present the qualifiers grouped by destination — for each, the proposed change and the evidence behind it — and collect a ruling on each (AskUserQuestion),
|
|
25
|
+
Present the qualifiers grouped by destination — for each, the proposed change and the evidence behind it — and collect a ruling on each (AskUserQuestion), in conversational mode (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, Collaboration modes). In the same batch, ask which review mode — local or ship — the deliveries run under, every destination PR and the project-side deletions alike (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, Review modes). Discuss a candidate only where its ruling calls for it: the user pushes back, asks, or raises an alternative. The rulings:
|
|
26
26
|
|
|
27
27
|
- **Ship** — joins the change set the remaining steps carry to its destination's PR.
|
|
28
28
|
- **Defer** — stays recorded in the project, a candidate for a later run.
|
|
@@ -38,14 +38,14 @@ Then, for each destination with shipped candidates:
|
|
|
38
38
|
|
|
39
39
|
1. **Clone and branch**: clone fresh into a temporary directory outside the project (`gh repo clone <owner>/<repo>`) and branch.
|
|
40
40
|
2. **Read the checkout's briefing** — `CLAUDE.md` — first and follow it. It governs how the change is made.
|
|
41
|
-
3. **Apply the change set** for the destination. In the q repo, run `
|
|
41
|
+
3. **Apply the change set** for the destination. In the q repo, run `npm run check`. Leave every `version` untouched, in whichever manifests carry one — releasing is the maintainer's act, not the PR's.
|
|
42
42
|
4. **Commit**, in ship mode — the review history stays inspectable in git.
|
|
43
|
-
5. **Adversarial review**: validate the change set (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, Validation) with the **correctness** and **conventions** lenses, run against the checkout. Name q's own rules as the substitute grounding surface in both lenses' launches — every destination shares them, extensions being authored in projects that use q (source: q conventions/extensions.md). The checkout's own recorded deviations win where they speak. This project's project-tier rulings never apply.
|
|
44
|
-
6. **
|
|
43
|
+
5. **Adversarial review**: validate the change set (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, Validation) with the **correctness** and **conventions** lenses, run against the checkout. Name q's own rules as the substitute grounding surface in both lenses' launches — every destination shares them, extensions being authored in projects that use q (source: @lab43/q conventions/extensions.md). The checkout's own recorded deviations win where they speak. This project's project-tier rulings never apply.
|
|
44
|
+
6. **The local gate**: in local mode, run it over the checkout's diff, committing onto its branch (see: ${CLAUDE_PLUGIN_ROOT}/references/run-contract.md, The local gate). The gate's own reviewer pass takes the same substitute grounding as item 5's.
|
|
45
45
|
|
|
46
46
|
## Step 4: Open the PRs
|
|
47
47
|
|
|
48
|
-
Push each destination branch and open its PR with `gh pr create` against the default branch — the Step 2 agreement covers these PRs. Author the body per the PR-authoring rules (see: q conventions/pull-requests.md); the destination's own PR conventions and template win where they speak (source: q conventions/pull-requests.md). Carry the provoking context from this session in the body — the PR is the paper trail, so provenance belongs there, never in the conventions prose (source: q conventions/documentation.md, Conventions docs). If pushing to a repo is denied, fall back to a fork and say so.
|
|
48
|
+
Push each destination branch and open its PR with `gh pr create` against the default branch — the Step 2 agreement covers these PRs. Author the body per the PR-authoring rules (see: @lab43/q conventions/pull-requests.md); the destination's own PR conventions and template win where they speak (source: @lab43/q conventions/pull-requests.md). Carry the provoking context from this session in the body — the PR is the paper trail, so provenance belongs there, never in the conventions prose (source: @lab43/q conventions/documentation.md, Conventions docs). If pushing to a repo is denied, fall back to a fork and say so.
|
|
49
49
|
|
|
50
50
|
## Step 5: Report
|
|
51
51
|
|