@jenga-ai/agent 1.2.4 → 1.3.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/README.md +1 -0
- package/agents/developer.md +18 -0
- package/agents/scrum-master.md +1 -0
- package/agents/tester.md +18 -0
- package/hooks/on_session_end.sh +27 -0
- package/package.json +18 -17
- package/skills/commit/SKILL.md +11 -1
- package/skills/dev-done/SKILL.md +46 -0
- package/skills/dev-done/scripts/classify-commit-outcome.sh +114 -0
- package/skills/init/SKILL.md +7 -6
- package/skills/init/assets/scope-thresholds_template.json +7 -0
- package/skills/init/scripts/init.sh +6 -0
- package/skills/publish/SKILL.md +66 -0
- package/skills/publish/adapters/npm-ci.md +34 -0
- package/skills/publish/adapters/npm.md +18 -0
- package/skills/publish/assets/ci-contract.md +27 -0
- package/skills/publish/assets/publish.example.json +27 -0
- package/skills/publish/schemas/publish.schema.json +20 -0
- package/skills/publish/scripts/npm_ci_pipeline.sh +29 -0
- package/skills/publish/scripts/npm_stage_inspect.sh +829 -0
- package/skills/publish/scripts/npm_stage_pipeline.sh +427 -0
- package/skills/publish/scripts/publish_common.sh +16 -0
- package/skills/publish/scripts/show_history.sh +12 -5
- package/skills/publish/scripts/validate_npm_stage_env.sh +184 -0
- package/skills/publish/scripts/write_ledger_entry.sh +92 -2
- package/skills/reconcile/SKILL.md +121 -11
- package/skills/reconcile/assets/report_format.md +17 -0
- package/skills/reconcile/scripts/resolve-reconcile-scope.sh +489 -0
- package/skills/uncharted/SKILL.md +200 -21
- package/skills/uncharted/scripts/directory-triage.sh +342 -0
- package/skills/uncharted/scripts/elicitation-state.sh +457 -0
- package/templates/SCRUM_BOARD_SCHEMA.md +57 -0
- package/templates/agent-context.md.tpl +23 -11
- package/templates/copilot-instructions.md.tpl +21 -11
- package/mcp/router/README.md +0 -19
- package/mcp/router/embedder.js +0 -23
- package/mcp/router/index.js +0 -204
- package/mcp/router/matcher.js +0 -87
- package/mcp/router/package-lock.json +0 -1048
- package/mcp/router/package.json +0 -11
- package/mcp/router/skill-index.js +0 -104
- package/skills/route/SKILL.md +0 -180
package/README.md
CHANGED
|
@@ -228,6 +228,7 @@ Skills live in `.agents/skills/<name>/SKILL.md`. Invoke with `/<name>` in your A
|
|
|
228
228
|
| `/do` | Execute tasks from the scrum board, drives the Developer agent through the full loop |
|
|
229
229
|
| `/dooo` | Parallel execution orchestrator — runs multiple tasks simultaneously via sub-agents |
|
|
230
230
|
| `/redo` | Rework a previous implementation by commit SHA or Epic/Story number |
|
|
231
|
+
| `/publish` | Configure, validate, and orchestrate scaffolded release workflows — `setup`, `deploy`, `stage` (npm/npm-ci pre-approval staged publishing), `history`, `release-notes` |
|
|
231
232
|
| `/error` | Guided troubleshooting — gathers context, investigates, and drives a fix |
|
|
232
233
|
| `/train` | Scaffold and run ML training jobs (new job from template or run existing) |
|
|
233
234
|
|
package/agents/developer.md
CHANGED
|
@@ -287,6 +287,24 @@ See `templates/PROBLEM_RAPPORT_TEMPLATE.md` for the required format. Commit the
|
|
|
287
287
|
|
|
288
288
|
---
|
|
289
289
|
|
|
290
|
+
## Investigative Mode
|
|
291
|
+
|
|
292
|
+
**Trigger.** You are sometimes dispatched not to implement a task, but purely to build understanding of existing code — e.g. by the scrum-master during `/uncharted`'s conversational architecture elicitation, when it needs to know what a named flow or target actually does before proposing graph nodes or asking the user to confirm/correct an understanding. This is a distinct dispatch mode from the standard Task Intake flow above, and it is recognized by the request itself (you are asked to *trace* or *investigate*, not to *implement*), not by any board field.
|
|
293
|
+
|
|
294
|
+
**Hard constraints.** Investigative Mode is strictly read-only:
|
|
295
|
+
- No worktree is created for write purposes, no application code is written or modified, no dependency installs or generated artifacts.
|
|
296
|
+
- No commits of any kind.
|
|
297
|
+
- No board status writes — task/story/epic status is the tester's exclusive responsibility, and Investigative Mode doesn't touch the board at all, not even a status you'd normally be permitted to leave alone.
|
|
298
|
+
- No edits to `PROJECT_SUMMARY.md`, board files, or any other project artifact. The only output is the trace itself, returned to whoever dispatched you.
|
|
299
|
+
|
|
300
|
+
**Sandbox — reuse the existing worktree hooks, read-only.** Per the story decision behind this mode (see `project/documentation/plans/uncharted-interactive-elicitation.md` and its solution assessment, Problem 7 / Solution A), do not invent a new isolation mechanism. Mount a throwaway worktree via the same `WorktreeCreate` hook used for normal tasks (see Worktree Management above) purely to get a disposable, isolated checkout to read from, and tear it down via `WorktreeRemove` once the investigation ends. This is convention-enforced, not filesystem-enforced: the hook mounts an ordinary writable worktree, and it is Investigative Mode's contract — not a permission bit — that keeps it read-only. Treat any temptation to write into that worktree (a scratch file, a quick local test run) as a violation of the mode, not a harmless side effect.
|
|
301
|
+
|
|
302
|
+
**What you trace.** For the named flow or target, follow what the code actually does: call paths, data flow, key decision points, error handling, and any conditions or configuration that change the behavior. Report this in plain language back to the dispatcher — this is not a new artifact type and is not written to disk as part of Investigative Mode itself; if the dispatching flow later decides the finding is worth persisting, that happens through its own normal mechanism (e.g. a graph write or a summary doc), not through you.
|
|
303
|
+
|
|
304
|
+
**Human-oracle-availability limitation.** For genuinely undocumented code, there is often no reliable code-level way to confirm what was *intended* — only what currently happens. Naming conventions can mislead, a branch that looks dead may be load-bearing for a caller you haven't found, and "this must be for X" is a guess dressed as a finding. When you hit this wall, say so explicitly — report the uncertainty, name what you did and didn't check, and stop short of presenting a guess as a settled fact. This is an accepted, standing limitation of the mode, not something to engineer around by fabricating confidence.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
290
308
|
## Hooks
|
|
291
309
|
|
|
292
310
|
Defined in agent frontmatter:
|
package/agents/scrum-master.md
CHANGED
|
@@ -76,6 +76,7 @@ This is a self-contained procedure, not a session-start-only step. It may be inv
|
|
|
76
76
|
6. **This is the only path** by which a mid-task agent request results in a `crucial_level` board write. Developer and tester never write `crucial_level`, `crucial_set_by`, or `crucial_note` directly to a board file themselves under any circumstance — they may only *request* the change via a `crucial_escalation` rapport, and the actual frontmatter write happens here, exclusively by scrum-master, closing the loop described in E39's Purpose section ("the actual frontmatter write still goes through scrum-master, never the subagent itself").
|
|
77
77
|
- `status_review`: Review the scrum board for any tasks or stories whose status should be updated based on recent activity.
|
|
78
78
|
- `story_rollup`: Check all tasks under the referenced story; if all are `Passed` or `Passed with remarks`, update the story status to `Passed` (or `Passed with remarks` if any remark exists). Then check epic rollup (see Rollup Logic).
|
|
79
|
+
- `elicitation_resume`: A `/uncharted` conversational architecture elicitation session (`onboard`'s default flow, or `segment --mode investigate` — E20_S08_T03) ended mid-run without converging. Read `state_file` (`project/queue/elicitation-state/<elicitation_id>.json`, written by `skills/uncharted/scripts/elicitation-state.sh`) to see exactly where it left off — which nodes already converged, which are still pending or flagged, and any directory-triage/checkpoint data already confirmed — then resume the conversational flow documented in `skills/uncharted/SKILL.md`'s Multi-Session Persistence subsection from that point rather than restarting the elicitation from scratch. If the state file is missing or unreadable, report that to the user rather than silently starting a fresh elicitation under the same id.
|
|
79
80
|
- After processing all triggers, **clear the file** by writing an empty file — do not leave processed triggers.
|
|
80
81
|
|
|
81
82
|
2. **Check `project/queue/project_summary_updates.jsonl`** — If non-empty, review each proposed update and apply, revise, or reject it with a short note. Clear the file after processing.
|
package/agents/tester.md
CHANGED
|
@@ -407,6 +407,24 @@ There is no default analytics run. Analytics only happen when explicitly scoped
|
|
|
407
407
|
|
|
408
408
|
---
|
|
409
409
|
|
|
410
|
+
## Investigative Mode
|
|
411
|
+
|
|
412
|
+
**Trigger.** You are sometimes dispatched not to validate a task's implementation, but purely to build understanding of what the existing test suite actually covers for a named flow or target — e.g. by the scrum-master during `/uncharted`'s conversational architecture elicitation, alongside the developer's Investigative Mode pass over the same flow. This is a distinct dispatch mode from the standard Sender Object / Task Intake / Status Management flow above, recognized by the request itself (you are asked to *trace coverage*, not to *validate a task*), not by any board field.
|
|
413
|
+
|
|
414
|
+
**Hard constraints.** Investigative Mode is strictly read-only:
|
|
415
|
+
- No worktree is created for write purposes, no test files are written or modified, no test runs that mutate state, no dependency installs.
|
|
416
|
+
- No commits of any kind.
|
|
417
|
+
- No board status writes — this mode doesn't touch task/story/epic status at all, even though status writes are ordinarily your exclusive responsibility.
|
|
418
|
+
- No edits to `PROJECT_SUMMARY.md`, board files, or any other project artifact. The only output is the trace itself, returned to whoever dispatched you.
|
|
419
|
+
|
|
420
|
+
**Sandbox — reuse the existing worktree hooks, read-only.** Per the story decision behind this mode (see `project/documentation/plans/uncharted-interactive-elicitation.md` and its solution assessment, Problem 7 / Solution A), do not invent a new isolation mechanism. Mount a throwaway worktree via the same `WorktreeCreate` hook the developer agent uses for normal tasks, purely to get a disposable, isolated checkout to read from, and tear it down via `WorktreeRemove` once the investigation ends. This is convention-enforced, not filesystem-enforced: the hook mounts an ordinary writable worktree, and it is Investigative Mode's contract — not a permission bit — that keeps it read-only. Do not execute the test suite in a way that writes fixtures, snapshots, or coverage artifacts back into that worktree; reading existing test files and existing coverage output (if already present) is the mode's ceiling.
|
|
421
|
+
|
|
422
|
+
**What you trace — the distinct vantage point.** Where the developer's Investigative Mode pass traces what the code *does*, yours traces what the test suite *actually exercises and verifies* for that same flow: which tests touch it, what they assert (and what they merely execute without asserting), and where the coverage gap is — untested branches, unasserted side effects, error paths with no test at all, or a flow that "passes" only because nothing checks the part that matters. These are two distinct vantage points on the same flow, not two names for the same read; do not simply restate the developer's trace with "and there's a test for it" appended.
|
|
423
|
+
|
|
424
|
+
**Human-oracle-availability limitation.** The same limitation the developer faces applies to you, with an added facet: a passing test suite doesn't clarify intent either — a test can be green because it correctly verifies the right behavior, or green because it asserts nothing meaningful, and the test's own docstring or name can be as misleading as the code's. When you cannot determine from the tests (or their absence) what the intended behavior actually is, say so explicitly — report the uncertainty and the specific gap you couldn't close, rather than presenting a guess as a settled coverage verdict. This is an accepted, standing limitation of the mode, not something to engineer around by fabricating confidence.
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
410
428
|
## Hooks
|
|
411
429
|
|
|
412
430
|
Defined in agent frontmatter:
|
package/hooks/on_session_end.sh
CHANGED
|
@@ -308,6 +308,33 @@ for HANDOFF_FILE in "$HANDOFF_DIR"/*.json; do
|
|
|
308
308
|
echo "$TRIGGER" >> "$DEV_QUEUE"
|
|
309
309
|
echo "[on_session_end] scrum-master → developer queue: implementation_assignment"
|
|
310
310
|
fi
|
|
311
|
+
|
|
312
|
+
# A conversational architecture elicitation session (/uncharted
|
|
313
|
+
# onboard's default flow, or segment --mode investigate — E20_S08_T03)
|
|
314
|
+
# ended mid-run without converging. The session driving it is
|
|
315
|
+
# responsible for calling skills/uncharted/scripts/elicitation-state.sh
|
|
316
|
+
# pause and then writing this handoff with status "elicitation_paused"
|
|
317
|
+
# as its last action (see skills/uncharted/SKILL.md's Multi-Session
|
|
318
|
+
# Persistence subsection). This routes that pause into a resume
|
|
319
|
+
# signal for the next scrum-master session, per the existing
|
|
320
|
+
# SessionEnd/queue pattern rather than a new persistence mechanism
|
|
321
|
+
# (solution-assessment-uncharted-interactive-elicitation.md, Problem 11).
|
|
322
|
+
if [ "$HANDOFF_STATUS" = "elicitation_paused" ]; then
|
|
323
|
+
TRIGGER=$(jq -n \
|
|
324
|
+
--slurpfile h "$HANDOFF_FILE" \
|
|
325
|
+
--arg type "elicitation_resume" \
|
|
326
|
+
--arg date "$TIMESTAMP" \
|
|
327
|
+
'{
|
|
328
|
+
type: $type,
|
|
329
|
+
date: $date,
|
|
330
|
+
sender: { agent: "scrum-master", session_id: $h[0].session_id, date: $date },
|
|
331
|
+
elicitation_id: ($h[0].elicitation_id // ""),
|
|
332
|
+
state_file: ($h[0].state_file // ""),
|
|
333
|
+
message: "A conversational architecture elicitation session paused mid-run. Resume it from the persisted state file."
|
|
334
|
+
}')
|
|
335
|
+
echo "$TRIGGER" >> "$QUEUE_FILE"
|
|
336
|
+
echo "[on_session_end] scrum-master (elicitation_paused) → scrum-master queue: elicitation_resume"
|
|
337
|
+
fi
|
|
311
338
|
;;
|
|
312
339
|
|
|
313
340
|
developer)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jenga-ai/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Structured multi-agent development workflow for AI coding agents — scrum board, role-bounded scrum master / developer / tester agents, and 30+ slash-command skills. Works with Claude Code, GitHub Copilot, and Codex.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
"postinstall": "node scripts/postinstall.js",
|
|
17
17
|
"test": "bats tests/*.bats",
|
|
18
18
|
"validate:npm-metadata": "bash scripts/validate_npm_metadata.sh",
|
|
19
|
-
"ui:dev": "npm run ui:dev --prefix project/app",
|
|
20
|
-
"ui:build": "npm run ui:build --prefix project/app",
|
|
21
|
-
"api:start": "npm run api:start --prefix project/app",
|
|
22
|
-
"api:dev": "npm run api:dev --prefix project/app",
|
|
23
|
-
"dashboard:start": "npm run dashboard:start --prefix project/app",
|
|
24
|
-
"dashboard:open": "npm run dashboard:open --prefix project/app"
|
|
19
|
+
"ui:dev": "npm run ui:dev --prefix project/app --",
|
|
20
|
+
"ui:build": "npm run ui:build --prefix project/app --",
|
|
21
|
+
"api:start": "npm run api:start --prefix project/app --",
|
|
22
|
+
"api:dev": "npm run api:dev --prefix project/app --",
|
|
23
|
+
"dashboard:start": "npm run dashboard:start --prefix project/app --",
|
|
24
|
+
"dashboard:open": "npm run dashboard:open --prefix project/app --"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"skills/",
|
|
@@ -31,7 +31,17 @@
|
|
|
31
31
|
"templates/",
|
|
32
32
|
"bin/",
|
|
33
33
|
"lib/",
|
|
34
|
-
"mcp/",
|
|
34
|
+
"mcp/execute-ticket/*.js",
|
|
35
|
+
"mcp/execute-ticket/package.json",
|
|
36
|
+
"mcp/help/*.js",
|
|
37
|
+
"mcp/help/package.json",
|
|
38
|
+
"mcp/router/*.js",
|
|
39
|
+
"mcp/router/package.json",
|
|
40
|
+
"mcp/router/package-lock.json",
|
|
41
|
+
"mcp/router/README.md",
|
|
42
|
+
"mcp/training_runner/*.js",
|
|
43
|
+
"mcp/training_runner/package.json",
|
|
44
|
+
"mcp/training_runner/package-lock.json",
|
|
35
45
|
"README.md",
|
|
36
46
|
"LICENSE"
|
|
37
47
|
],
|
|
@@ -67,15 +77,6 @@
|
|
|
67
77
|
"url": "https://knappkod.se/jenga-ai"
|
|
68
78
|
},
|
|
69
79
|
"license": "MIT",
|
|
70
|
-
"dependencies": {
|
|
71
|
-
"@huggingface/transformers": "^4.2.0"
|
|
72
|
-
},
|
|
73
|
-
"overrides": {
|
|
74
|
-
"sharp": "^0.34.5"
|
|
75
|
-
},
|
|
76
|
-
"comments": {
|
|
77
|
-
"audit": "sharp <0.35.0 and adm-zip <0.6.0 are transitive deps of @huggingface/transformers with no upstream fix available. Not exploitable in this context: only text feature-extraction pipeline is used — no image processing or ZIP handling at application boundary. Review when @huggingface/transformers ships a patched release."
|
|
78
|
-
},
|
|
79
80
|
"devDependencies": {
|
|
80
81
|
"bats": "^1.13.0"
|
|
81
82
|
}
|
package/skills/commit/SKILL.md
CHANGED
|
@@ -36,7 +36,17 @@ If `--inline` is absent **and** `JENGA_COMMIT_INLINE` is not set (or is not `1`)
|
|
|
36
36
|
|
|
37
37
|
If no epic, task, or story has been implemented, exit with the message: "No implementation to commit."
|
|
38
38
|
|
|
39
|
-
1. **Reconcile first** — Invoke the `/reconcile` skill before any other action, so the board is never committed in a drifted state.
|
|
39
|
+
1. **Reconcile first, scoped to this commit** — Invoke the `/reconcile` skill before any other action, so the board is never committed in a drifted state.
|
|
40
|
+
- **Determine the scope to pass** (in order):
|
|
41
|
+
1. If `/commit` was invoked with an explicit epic/story/task id argument (e.g. `/commit E46`, `/commit E17_S04_T02`), use that id.
|
|
42
|
+
2. Otherwise, if the calling context already identifies a single task/story/epic just completed (e.g. a developer or tester agent's sender object naming `task_id`/`story_id`/`epic_id`, or a `/do` invocation for one task), use that id.
|
|
43
|
+
3. Otherwise, derive it from what's staged: run `git diff --cached --name-only` and filter to paths under `project/board/epics/`, `project/board/stories/`, and `project/board/tasks/`. Extract the id each matched filename encodes.
|
|
44
|
+
- If every extracted id shares a single common story-or-narrower ancestor (all belong to one story's own file plus any of its own tasks' files, or are all exactly one task's own file, or are all exactly one epic's own file with nothing narrower staged), use that single most-specific id (story/task id if present, else the epic id) as the scope.
|
|
45
|
+
- If matched ids span more than one distinct story, or more than one distinct epic, this case does not resolve — do not guess between them; fall through to case 4.
|
|
46
|
+
- **If no board files at all are staged, skip `/reconcile` entirely — do not fall through to case 4.** A commit that touches no board file cannot itself commit board drift, so there is nothing for a pre-commit reconcile to protect against; running a full unscoped scan here would be pure waste, not a safety net. This is distinct from the multi-item case immediately above, which still falls through to case 4 since board files genuinely are changing there.
|
|
47
|
+
4. Otherwise — no argument, no sender context, and staged board files span more than one distinct story or epic — fall back to unscoped `/reconcile` (full board), unchanged from prior behavior.
|
|
48
|
+
- **Invoke** `/reconcile <scope>` when a scope was determined in cases 1-3, unscoped `/reconcile` on the case-4 fallback, or skip the reconcile step entirely per case 3's no-board-files-staged outcome. Do not re-derive or duplicate `/reconcile`'s own default-scope-to-epic expansion here — passing a bare story or task id through is sufficient; `/reconcile` itself resolves that to the containing epic.
|
|
49
|
+
- **If `/reconcile` was skipped** (case 3's no-board-files-staged outcome) — continue silently to the next step, exactly as the no-drift outcome below.
|
|
40
50
|
- **If reconcile detects and corrects drift** — inform the user what changed (e.g. demoted/promoted statuses, merged orphaned worktrees, cleaned `todo.md` entries) before proceeding.
|
|
41
51
|
- **If reconcile finds no drift** — continue silently to the next step.
|
|
42
52
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-done
|
|
3
|
+
description: Commit the current work and immediately sync it into the .claude/ and .agents/ mirrors. Shortcut that chains /commit followed by /self-sync.
|
|
4
|
+
keywords:
|
|
5
|
+
- dev done
|
|
6
|
+
- commit and sync
|
|
7
|
+
- commit and mirror
|
|
8
|
+
- done syncing
|
|
9
|
+
examples:
|
|
10
|
+
- "dev-done E42_S04_T01"
|
|
11
|
+
- "commit this and sync the mirrors"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Dev-Done — Commit, then Sync the Mirrors
|
|
15
|
+
|
|
16
|
+
Chains `/commit <scope-id>` and `/self-sync`, the same "convenience shortcut" pattern `skills/lgtm/SKILL.md`
|
|
17
|
+
uses for `/commit` + `/continue` — applied here to the commit -> mirror-sync sequence instead of the
|
|
18
|
+
commit -> next-task sequence. Useful right after implementing a root-level framework change
|
|
19
|
+
(`skills/`, `agents/`, `hooks/`, `scripts/`, `templates/`, `settings.json`), so the `.claude/` and
|
|
20
|
+
`.agents/` mirrors never sit stale waiting on a manual `/self-sync` call.
|
|
21
|
+
|
|
22
|
+
The deterministic decision of whether `/commit` halted early (nothing to commit) or completed
|
|
23
|
+
normally lives in `skills/dev-done/scripts/classify-commit-outcome.sh`, not inline here — see that
|
|
24
|
+
script's header for the exact contract.
|
|
25
|
+
|
|
26
|
+
## Instructions
|
|
27
|
+
|
|
28
|
+
1. Invoke the `/commit` skill with whatever scope-id argument `/dev-done` itself was given (e.g.
|
|
29
|
+
`/dev-done E42_S04_T01` invokes `/commit E42_S04_T01`) — the same EST scope-id argument contract
|
|
30
|
+
`/commit` already accepts (epic, story, or task id; see `skills/commit/SKILL.md`). Capture its full
|
|
31
|
+
output text.
|
|
32
|
+
|
|
33
|
+
2. Pass the captured output to the classifier script:
|
|
34
|
+
```
|
|
35
|
+
skills/dev-done/scripts/classify-commit-outcome.sh <<< "$COMMIT_OUTPUT"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
3. If the script exits `1` (HALT): print its stdout — the exact message `No implementation to
|
|
39
|
+
commit.` — to the user, and stop. Do **not** invoke `/self-sync`.
|
|
40
|
+
|
|
41
|
+
4. If the script exits `0` (PROCEED): invoke the `/self-sync` skill and wait for it to finish. This
|
|
42
|
+
happens regardless of whether `/commit` reported drift or doc-sync findings along the way — those
|
|
43
|
+
are informational, not blocking (see `skills/commit/SKILL.md`).
|
|
44
|
+
|
|
45
|
+
5. Report both steps' output to the user in sequence — the commit result first, then the self-sync
|
|
46
|
+
summary.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ---------------------------------------------------------------------------
|
|
3
|
+
# skills/dev-done/scripts/classify-commit-outcome.sh
|
|
4
|
+
#
|
|
5
|
+
# Deterministic classifier backing `/dev-done` (story E42_S04, task
|
|
6
|
+
# E42_S04_T01). `/dev-done` chains `/commit <scope-id>` then `/self-sync`,
|
|
7
|
+
# but must NOT proceed to `/self-sync` when `/commit` halted early because
|
|
8
|
+
# there was nothing to commit. Per CLAUDE.md's "Skill Implementation
|
|
9
|
+
# Principle — Scripts Over Inline Logic", that halt-vs-proceed check is a
|
|
10
|
+
# deterministic, repeatable text match — it does not belong as inline
|
|
11
|
+
# conditional logic in `skills/dev-done/SKILL.md`, so it lives here instead,
|
|
12
|
+
# the same way `skills/self-sync/SKILL.md` delegates its own filesystem work
|
|
13
|
+
# to `skills/self-sync/scripts/run.js` rather than inlining it.
|
|
14
|
+
#
|
|
15
|
+
# `/commit` and `/self-sync` are themselves agent-executed skills, not plain
|
|
16
|
+
# executables — this script never shells out to invoke either one. It only
|
|
17
|
+
# classifies text that the calling agent already captured from `/commit`'s
|
|
18
|
+
# output. Invoking `/commit` and `/self-sync` remains `SKILL.md`'s job.
|
|
19
|
+
#
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# WHAT IT MATCHES
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# `skills/commit/SKILL.md`'s Instructions section states, verbatim:
|
|
24
|
+
#
|
|
25
|
+
# "If no epic, task, or story has been implemented, exit with the message:
|
|
26
|
+
# "No implementation to commit.""
|
|
27
|
+
#
|
|
28
|
+
# That exact sentence — "No implementation to commit." — is the ONLY halt
|
|
29
|
+
# signal this script recognizes. If a future edit to `skills/commit/SKILL.md`
|
|
30
|
+
# changes that wording, this script's match must be updated to match, or it
|
|
31
|
+
# will silently stop recognizing the halt case (see Dependencies & Risks in
|
|
32
|
+
# the task's execution plan).
|
|
33
|
+
#
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
# INVOCATION
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
# classify-commit-outcome.sh [<captured-commit-output>]
|
|
38
|
+
#
|
|
39
|
+
# The text `/commit` produced is passed either as the single argument, or
|
|
40
|
+
# (when no argument is given) read from stdin in full, e.g.:
|
|
41
|
+
#
|
|
42
|
+
# skills/dev-done/scripts/classify-commit-outcome.sh <<< "$COMMIT_OUTPUT"
|
|
43
|
+
# printf '%s' "$COMMIT_OUTPUT" | skills/dev-done/scripts/classify-commit-outcome.sh
|
|
44
|
+
#
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# OUTPUT CONTRACT (stdout, single line, nothing else)
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# HALT case stdout is the exact literal message "No implementation to
|
|
49
|
+
# commit." — the calling agent relays this stdout verbatim
|
|
50
|
+
# to the user and does NOT invoke `/self-sync`.
|
|
51
|
+
# PROCEED case stdout is the literal token "PROCEED" — the calling
|
|
52
|
+
# agent invokes `/self-sync` next.
|
|
53
|
+
#
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# EXIT CODES
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
# 0 PROCEED — `/commit` completed normally (regardless of whether it
|
|
58
|
+
# reported drift or doc-sync findings along the way); stdout is
|
|
59
|
+
# "PROCEED".
|
|
60
|
+
# 1 HALT — `/commit` halted early with the exact "No implementation to
|
|
61
|
+
# commit." message; stdout is that exact message.
|
|
62
|
+
# 2 Usage/environment error (e.g. no input at all — neither an argument
|
|
63
|
+
# nor anything on stdin); nothing meaningful on stdout, an error on
|
|
64
|
+
# stderr.
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
set -euo pipefail
|
|
68
|
+
|
|
69
|
+
HALT_MESSAGE="No implementation to commit."
|
|
70
|
+
|
|
71
|
+
if [ "$#" -gt 1 ]; then
|
|
72
|
+
echo "Usage: $(basename "$0") [<captured-commit-output>]" >&2
|
|
73
|
+
echo " (or pipe the captured output on stdin with no argument)" >&2
|
|
74
|
+
exit 2
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
if [ "$#" -eq 1 ]; then
|
|
78
|
+
INPUT="$1"
|
|
79
|
+
else
|
|
80
|
+
# No argument — read the full captured output from stdin.
|
|
81
|
+
if [ -t 0 ]; then
|
|
82
|
+
echo "ERROR: no argument given and stdin is a terminal — nothing to classify" >&2
|
|
83
|
+
exit 2
|
|
84
|
+
fi
|
|
85
|
+
INPUT="$(cat)"
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
if [ -z "$INPUT" ]; then
|
|
89
|
+
echo "ERROR: no commit output provided to classify" >&2
|
|
90
|
+
exit 2
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Trim leading/trailing whitespace from the whole input for the exact-match case.
|
|
94
|
+
TRIMMED="$(printf '%s' "$INPUT" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
|
95
|
+
|
|
96
|
+
if [ "$TRIMMED" = "$HALT_MESSAGE" ]; then
|
|
97
|
+
printf '%s\n' "$HALT_MESSAGE"
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Also match the halt message appearing as its own standalone line anywhere
|
|
102
|
+
# in a longer captured response (e.g. wrapped in surrounding agent prose),
|
|
103
|
+
# since the calling agent's captured "output text" is free-form, not a
|
|
104
|
+
# guaranteed exact-equals string.
|
|
105
|
+
while IFS= read -r line; do
|
|
106
|
+
LINE_TRIMMED="$(printf '%s' "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
|
107
|
+
if [ "$LINE_TRIMMED" = "$HALT_MESSAGE" ]; then
|
|
108
|
+
printf '%s\n' "$HALT_MESSAGE"
|
|
109
|
+
exit 1
|
|
110
|
+
fi
|
|
111
|
+
done <<< "$INPUT"
|
|
112
|
+
|
|
113
|
+
printf 'PROCEED\n'
|
|
114
|
+
exit 0
|
package/skills/init/SKILL.md
CHANGED
|
@@ -128,12 +128,13 @@ This script handles all scaffolding in one step:
|
|
|
128
128
|
4. Creates `project/PROJECT_SUMMARY.md` with placeholder content
|
|
129
129
|
5. Creates `project/configs/workflow.json` with shared constants
|
|
130
130
|
6. Creates `project/configs/test-config.json` stub
|
|
131
|
-
7. Creates `project/
|
|
132
|
-
8. Creates `project/
|
|
133
|
-
9. Creates `
|
|
134
|
-
10. Creates `
|
|
135
|
-
11.
|
|
136
|
-
12.
|
|
131
|
+
7. Creates `project/configs/scope-thresholds.json` with default execution-scope thresholds (consumed by `/jenga` and `/do`, which halt if it's missing)
|
|
132
|
+
8. Creates `project/data/baselines.json`
|
|
133
|
+
9. Creates `project/logs/events.json`
|
|
134
|
+
10. Creates `docs/STRATEGY.md` — a strategic brief stub intended for investors, partners, and the product team
|
|
135
|
+
11. Creates `CHANGELOG.md` from the shared template — a running log of notable changes, seeded with an `[Unreleased]` section
|
|
136
|
+
12. Applies the chosen visibility mode via `scripts/apply-project-visibility.sh`, which records it as `project_files_visibility` in `jenga.config.json` and performs any `.gitignore` change
|
|
137
|
+
13. Stages and commits all files with the message `init: scaffold project structure and workflow config`
|
|
137
138
|
|
|
138
139
|
The visibility mode is validated before any scaffolding happens, so an invalid
|
|
139
140
|
value fails fast and leaves nothing behind. It is applied before the commit, so
|
|
@@ -67,6 +67,12 @@ cp "$ASSETS_DIR/workflow_template.json" project/configs/workflow.json
|
|
|
67
67
|
echo "→ Copying test-config.json from template..."
|
|
68
68
|
cp "$ASSETS_DIR/test-config_template.json" project/configs/test-config.json
|
|
69
69
|
|
|
70
|
+
# ─── 6.5. Create project/configs/scope-thresholds.json ───────────────────────
|
|
71
|
+
# Consumed by skills/jenga (Phase 0) and skills/do (Step 0); both halt if it's
|
|
72
|
+
# missing, so it must exist immediately after scaffold.
|
|
73
|
+
echo "→ Copying scope-thresholds.json from template..."
|
|
74
|
+
cp "$ASSETS_DIR/scope-thresholds_template.json" project/configs/scope-thresholds.json
|
|
75
|
+
|
|
70
76
|
# ─── 7. Create project/data/baselines.json ───────────────────────────────────
|
|
71
77
|
echo "→ Creating baselines.json..."
|
|
72
78
|
echo '{}' > project/data/baselines.json
|
package/skills/publish/SKILL.md
CHANGED
|
@@ -9,6 +9,8 @@ keywords:
|
|
|
9
9
|
- npm
|
|
10
10
|
- registry
|
|
11
11
|
- release notes
|
|
12
|
+
- staged publishing
|
|
13
|
+
- stage
|
|
12
14
|
examples:
|
|
13
15
|
- "publish setup --target staging-appstore"
|
|
14
16
|
- "publish setup --type mobile-ios"
|
|
@@ -21,6 +23,9 @@ examples:
|
|
|
21
23
|
- "publish deploy --target my-droplet --dry-run"
|
|
22
24
|
- "publish history --limit 5"
|
|
23
25
|
- "publish release-notes --target staging-appstore"
|
|
26
|
+
- "test a release before publishing"
|
|
27
|
+
- "publish stage --target npm-registry --dry-run"
|
|
28
|
+
- "approve a staged npm release"
|
|
24
29
|
metadata:
|
|
25
30
|
scope: multi-target-v2
|
|
26
31
|
primary_target: multi
|
|
@@ -76,6 +81,7 @@ If config or env validation fails, the skill exits with code `4` and does not co
|
|
|
76
81
|
|---|---|---|---|
|
|
77
82
|
| `/publish setup` | Prepare or refresh target configuration | `skills/publish/scripts/setup_wizard.sh` | Supported types: `mobile-ios`, `npm`, `npm-ci`, `droplet` |
|
|
78
83
|
| `/publish deploy` | Run the full 11-step deploy orchestration | `skills/publish/scripts/publish_deploy.sh` | Dispatches to the adapter for the target's `type` (`mobile-ios`, `npm`, `npm-ci`, or `droplet`); `--dry-run` is honoured end-to-end |
|
|
84
|
+
| `/publish stage` | Stage an npm release into npm's staged-publishing area, smoke-test it in isolation, then approve or reject it | `skills/publish/scripts/npm_stage_pipeline.sh` (the `publish` sub-command) and `skills/publish/scripts/npm_stage_inspect.sh` (`list`, `view`, `download`, `test`, `approve`, `reject`) | Supported for `npm` and `npm-ci` target types only |
|
|
79
85
|
| `/publish history` | Read the canonical publish ledger | `skills/publish/scripts/show_history.sh` | Target-agnostic; filter by `--target <name>` |
|
|
80
86
|
| `/publish release-notes` | Merge new release notes into the standing `CHANGELOG.md` (or a standalone draft via `--output`) without publishing | `skills/publish/scripts/generate_release_notes.sh` | Target-agnostic |
|
|
81
87
|
|
|
@@ -170,6 +176,66 @@ Example invocations:
|
|
|
170
176
|
|
|
171
177
|
Use `--dry-run` to rehearse a release, validate that gates pass, and confirm the generated release notes without publishing.
|
|
172
178
|
|
|
179
|
+
### `/publish stage`
|
|
180
|
+
|
|
181
|
+
Staged publishing for `npm`/`npm-ci` targets only. Not available for
|
|
182
|
+
`mobile-ios` or `droplet` — those adapters have no staging area to dispatch
|
|
183
|
+
to. The lifecycle is **stage → test → approve**, with `reject` as the
|
|
184
|
+
discard path at any point after staging:
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
staged ──▶ stage_tested ──▶ approved (approve requires 2FA; refuses without
|
|
188
|
+
│ │ a passing test unless --force <reason>)
|
|
189
|
+
│ │
|
|
190
|
+
└─────────────┴──▶ rejected (discard path — available from any staged state)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Two registry constraints trip people up and are worth stating plainly:
|
|
194
|
+
|
|
195
|
+
- **The package must already exist on the registry.** npm's staged-publishing
|
|
196
|
+
workflow only applies to a package that has had at least one normal,
|
|
197
|
+
non-staged publish already — `validate_npm_stage_env.sh` checks this
|
|
198
|
+
up front and exits `4` if it doesn't hold.
|
|
199
|
+
- **Approval requires 2FA; staging does not.** `npm stage publish` (the
|
|
200
|
+
`stage` sub-command below) can run non-interactively in CI. `npm stage
|
|
201
|
+
approve` cannot — npmjs.com requires an interactive one-time-password
|
|
202
|
+
step for approval, so it is always a human action even when staging was
|
|
203
|
+
automated (see the npm-ci adapter's OIDC staging section for the CI-stage,
|
|
204
|
+
human-approve split).
|
|
205
|
+
|
|
206
|
+
All seven sub-commands:
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
/publish stage publish <target> <path-to-publish.json> [--dry-run] [--non-interactive] [--otp <otp>]
|
|
210
|
+
/publish stage test <stage-id> [--keep] [--config <path>] [--dry-run] [--json]
|
|
211
|
+
/publish stage list [<package-spec>] [--config <path>] [--dry-run] [--json]
|
|
212
|
+
/publish stage view <stage-id> [--config <path>] [--dry-run] [--json]
|
|
213
|
+
/publish stage download <stage-id> [--out <dir>] [--config <path>] [--dry-run] [--json]
|
|
214
|
+
/publish stage approve <stage-id> [--otp <otp>] [--force <reason>] [--config <path>] [--dry-run] [--json]
|
|
215
|
+
/publish stage reject <stage-id> [--config <path>] [--dry-run] [--json]
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Implementation:
|
|
219
|
+
|
|
220
|
+
- `publish` → `bash skills/publish/scripts/npm_stage_pipeline.sh <target> <path-to-publish.json> [--dry-run] [--non-interactive] [--otp <otp>]` — six ordered phases (validate, gates, pack, stage, capture, ledger); writes a `staged` ledger entry on success.
|
|
221
|
+
- `test`, `list`, `view`, `download`, `approve`, `reject` → `bash skills/publish/scripts/npm_stage_inspect.sh <sub> [args] [--config <path>] [--dry-run] [--json]`
|
|
222
|
+
- `test` downloads the exact staged tarball into an isolated scratch directory outside the repo, installs it, runs the target's `npm.stage.smoke_cmd` (or the documented default check), and writes a `stage_tested` ledger entry (`pass`/`fail`).
|
|
223
|
+
- `approve` refuses to run unless a passing `test` is on record for that exact stage id, unless `--force <reason>` is given; writes an `approved` ledger entry.
|
|
224
|
+
- `reject` is the discard path omitted from npm's own staged-publishing docs page — documented here so it stays discoverable; writes a `rejected` ledger entry.
|
|
225
|
+
|
|
226
|
+
Example invocations:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
/publish stage publish npm-registry ./publish.json --dry-run
|
|
230
|
+
/publish stage test <stage-id>
|
|
231
|
+
/publish stage approve <stage-id>
|
|
232
|
+
/publish stage reject <stage-id>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Use `/publish stage` to rehearse and smoke-test a release before it goes
|
|
236
|
+
live — the point of staging is that a bad tarball is caught in isolation
|
|
237
|
+
instead of being caught by users after `npm publish`.
|
|
238
|
+
|
|
173
239
|
### `/publish history`
|
|
174
240
|
|
|
175
241
|
```text
|
|
@@ -190,6 +190,40 @@ A successful non-dry-run produces:
|
|
|
190
190
|
- A history entry in `project/logs/publish-history.json` written by
|
|
191
191
|
`publish_deploy.sh` with `platform_state: "triggered"`
|
|
192
192
|
|
|
193
|
+
## Staged Publishing
|
|
194
|
+
|
|
195
|
+
This target type also supports **staged publishing** via `/publish stage` —
|
|
196
|
+
npm's pre-publish staging area — with an OIDC-specific split between the
|
|
197
|
+
automated and human halves of the flow:
|
|
198
|
+
|
|
199
|
+
- **The actual `npm stage publish --provenance` call always runs inside
|
|
200
|
+
GitHub Actions, never on a local machine.** For `npm-ci` targets, phase 4
|
|
201
|
+
of `npm_stage_pipeline.sh` (run locally via `/publish stage publish`)
|
|
202
|
+
dispatches the `stage` job of the target's generated workflow (the same
|
|
203
|
+
`<workflow_path>` / OIDC Trusted Publisher link `npm_ci_pipeline.sh` uses
|
|
204
|
+
for `/publish deploy`, generated with a `mode: publish | stage`
|
|
205
|
+
`workflow_dispatch` input) via
|
|
206
|
+
`gh workflow run <workflow_filename> --repo <github_repo> -f mode=stage`,
|
|
207
|
+
then blocks on `gh run watch` until that run finishes. The `stage` job
|
|
208
|
+
itself runs a plain `npm stage publish --provenance --access <access>
|
|
209
|
+
--tag <dist_tag>` step, authorised through the same Trusted Publisher OIDC
|
|
210
|
+
link described above — no `NPM_TOKEN` is needed to stage, exactly as none
|
|
211
|
+
is needed to publish. Local `--dry-run` only prints the `gh workflow run`
|
|
212
|
+
command that would be dispatched; it never triggers a workflow run.
|
|
213
|
+
- **Approval is always a human, out-of-CI step.** `npm stage approve`
|
|
214
|
+
requires an interactive npm 2FA one-time password — there is no OIDC
|
|
215
|
+
equivalent for approval. A human runs
|
|
216
|
+
`bash skills/publish/scripts/npm_stage_inspect.sh test <stage-id>` (or
|
|
217
|
+
relies on the automated CI-staged test result) and then
|
|
218
|
+
`bash skills/publish/scripts/npm_stage_inspect.sh approve <stage-id> --otp <otp>`
|
|
219
|
+
from their own machine. `reject` (same script) is available to either
|
|
220
|
+
side to discard a staged candidate.
|
|
221
|
+
|
|
222
|
+
Same registry-existence precondition as a normal `npm-ci` deploy: staged
|
|
223
|
+
publishing only applies to a package that has already had at least one
|
|
224
|
+
non-staged publish (`validate_npm_stage_env.sh` checks this up front, exit
|
|
225
|
+
`4` if not).
|
|
226
|
+
|
|
193
227
|
## Post-deploy manual steps
|
|
194
228
|
|
|
195
229
|
After a successful deploy trigger, the adapter prints the workflow run URL.
|
|
@@ -109,6 +109,24 @@ A successful run produces:
|
|
|
109
109
|
- A local (and optionally pushed) git tag `v<version>` matching the published `version`
|
|
110
110
|
- A history row appended to `project/logs/publish-history.json`
|
|
111
111
|
|
|
112
|
+
## Staged Publishing
|
|
113
|
+
|
|
114
|
+
Before a live `deploy`, this target type also supports **staged publishing**
|
|
115
|
+
via `/publish stage` — npm's own pre-publish staging area, which lets a
|
|
116
|
+
release be smoke-tested from the exact tarball that would ship before it
|
|
117
|
+
becomes visible on the registry. See `skills/publish/SKILL.md`'s
|
|
118
|
+
`### /publish stage` section for the full command reference; summary here:
|
|
119
|
+
|
|
120
|
+
- `bash skills/publish/scripts/npm_stage_pipeline.sh <target> <path-to-publish.json> [--dry-run] [--non-interactive] [--otp <otp>]` runs validate → gates → pack → stage → capture → ledger and writes a `staged` ledger entry.
|
|
121
|
+
- `bash skills/publish/scripts/npm_stage_inspect.sh test <stage-id>` installs the staged tarball into an isolated scratch directory and smoke-tests it, writing a `stage_tested` ledger entry.
|
|
122
|
+
- `bash skills/publish/scripts/npm_stage_inspect.sh approve <stage-id>` requires an npm 2FA one-time password and refuses without a passing `test` on record unless `--force <reason>` is given.
|
|
123
|
+
- `bash skills/publish/scripts/npm_stage_inspect.sh reject <stage-id>` discards the staged version.
|
|
124
|
+
|
|
125
|
+
Same registry-existence precondition as a normal `npm` publish: staged
|
|
126
|
+
publishing only applies to a package that has already had at least one
|
|
127
|
+
non-staged publish (`validate_npm_stage_env.sh` checks this up front, exit
|
|
128
|
+
`4` if not).
|
|
129
|
+
|
|
112
130
|
## Post-deploy manual steps
|
|
113
131
|
|
|
114
132
|
After a successful publish, the deploy flow must print:
|
|
@@ -71,6 +71,33 @@ These inputs remain optional in non-interactive mode:
|
|
|
71
71
|
| `3` | Deploy failure | Used by the iOS adapter pipeline for build/export/upload failures |
|
|
72
72
|
| `4` | Config or environment invalid | Implemented by validation scripts |
|
|
73
73
|
|
|
74
|
+
## Staged publishing (`/publish stage`) — exit codes and ledger states
|
|
75
|
+
|
|
76
|
+
`/publish stage` (npm/npm-ci only) has its own exit-code contract, separate
|
|
77
|
+
from the deploy contract above:
|
|
78
|
+
|
|
79
|
+
| Code | Meaning |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `0` | Success (staged, or `--dry-run`/`--help` completed cleanly) |
|
|
82
|
+
| `1` | User declined the pack confirmation prompt (or no tty was available) — `npm_stage_pipeline.sh` only |
|
|
83
|
+
| `2` | A mandatory pre-deploy gate failed; nothing was staged — `npm_stage_pipeline.sh` only |
|
|
84
|
+
| `3` | The underlying `npm stage <sub>` command failed, the tarball install failed, or the smoke test failed (`npm_stage_inspect.sh`); or `npm stage publish`/stage-id capture failed (`npm_stage_pipeline.sh`) |
|
|
85
|
+
| `4` | Config/environment validation failure (`validate_npm_stage_env.sh`, or bad args), or (`npm_stage_inspect.sh` only) a usage error, an unknown sub-command, or the `approve` test interlock refusing without a passing test on record and no `--force <reason>` |
|
|
86
|
+
|
|
87
|
+
`test` (via `npm_stage_inspect.sh`) surfaces a smoke-test failure as a
|
|
88
|
+
non-zero exit — code `3` — distinct from a usage error (`4`).
|
|
89
|
+
|
|
90
|
+
The publish ledger (`project/logs/publish-history.json`) gains four new
|
|
91
|
+
`platform_state` values alongside the existing `uploaded`/`partial`/
|
|
92
|
+
`dry-run`/`failed`:
|
|
93
|
+
|
|
94
|
+
| `platform_state` | Written by | Meaning |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `staged` | `npm_stage_pipeline.sh` | Version staged to npm's staged-publishing area; not yet visible on the registry |
|
|
97
|
+
| `stage_tested` | `npm_stage_inspect.sh test` | The staged tarball was installed in isolation and smoke-tested; ledger row records `pass`/`fail` |
|
|
98
|
+
| `approved` | `npm_stage_inspect.sh approve` | The staged version was approved (2FA-gated) and is now published |
|
|
99
|
+
| `rejected` | `npm_stage_inspect.sh reject` | The staged version was discarded and will never be published |
|
|
100
|
+
|
|
74
101
|
## Precedence rules
|
|
75
102
|
|
|
76
103
|
1. Command-line flags win over environment variables.
|
|
@@ -80,6 +80,33 @@
|
|
|
80
80
|
"provider_short_name": "exampleco"
|
|
81
81
|
},
|
|
82
82
|
"notes": "Production upload example using App Store export mode."
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "npm-ci-example",
|
|
86
|
+
"type": "npm-ci",
|
|
87
|
+
"platform": "npm-ci-oidc",
|
|
88
|
+
"github_repo": "exampleco/example-package",
|
|
89
|
+
"workflow_path": ".github/workflows/npm-publish.yml",
|
|
90
|
+
"checks": {
|
|
91
|
+
"pre": [
|
|
92
|
+
"lint",
|
|
93
|
+
"type-check"
|
|
94
|
+
],
|
|
95
|
+
"post": [
|
|
96
|
+
"smoke-test"
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"npm": {
|
|
100
|
+
"package_name": "@exampleco/example-package",
|
|
101
|
+
"access": "public",
|
|
102
|
+
"registry": "https://registry.npmjs.org",
|
|
103
|
+
"dist_tag": "latest",
|
|
104
|
+
"stage": {
|
|
105
|
+
"smoke_cmd": "npm run smoke-test",
|
|
106
|
+
"require_test_before_approve": true
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"notes": "Scaffold example only. Demonstrates the optional npm.stage block used by '/publish stage'. Replace package_name and github_repo with your real values."
|
|
83
110
|
}
|
|
84
111
|
]
|
|
85
112
|
}
|