@rse/ase 0.9.36 → 0.9.38

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/dst/ase-hook.js CHANGED
@@ -250,6 +250,11 @@ export default class HookCommand {
250
250
  const val = cfg.get("agent.persona");
251
251
  if (typeof val === "string")
252
252
  persona = val;
253
+ /* determine project boxing transparency */
254
+ let boxing = process.env.ASE_PROJECT_BOXING ?? "white";
255
+ const valBoxing = cfg.get("project.boxing");
256
+ if (typeof valBoxing === "string")
257
+ boxing = valBoxing;
253
258
  /* determine headless mode */
254
259
  const headless = (process.env.ASE_HEADLESS ?? "false") === "true" ? "true" : "false";
255
260
  /* provide ASE information to Anthropic Claude Code CLI shell commands
@@ -260,6 +265,7 @@ export default class HookCommand {
260
265
  `export ASE_PLUGIN_ROOT=${quote([pluginRoot])}\n` +
261
266
  `export ASE_USER_ID=${quote([userId])}\n` +
262
267
  `export ASE_PROJECT_ID=${quote([projectId])}\n` +
268
+ `export ASE_PROJECT_BOXING=${quote([boxing])}\n` +
263
269
  `export ASE_TASK_ID=${quote([taskId])}\n` +
264
270
  `export ASE_SESSION_ID=${quote([sessionId])}\n` +
265
271
  `export ASE_HEADLESS=${quote([headless])}\n` +
@@ -274,6 +280,7 @@ export default class HookCommand {
274
280
  `<ase-persona-style>${persona}</ase-persona-style>\n` +
275
281
  `<ase-user-id>${userId}</ase-user-id>\n` +
276
282
  `<ase-project-id>${projectId}</ase-project-id>\n` +
283
+ `<ase-project-boxing>${boxing}</ase-project-boxing>\n` +
277
284
  `<ase-task-id>${taskId}</ase-task-id>\n` +
278
285
  `<ase-session-id>${sessionId}</ase-session-id>\n` +
279
286
  `<ase-headless>${headless}</ase-headless>\n` +
@@ -289,7 +296,7 @@ export default class HookCommand {
289
296
  const banner = `\n⧉ ASE: ⎈ version: ${versionCurrentPlugin}${versionHint !== "" ? " " + versionHint.replaceAll(/\*/g, "") : ""}` +
290
297
  `\n⧉ ASE: ※ user: ${userId}, ⚑ project: ${projectId}` +
291
298
  `\n⧉ ASE: ◉ task: ${taskId}, ⏻ session: ${sessionId}` +
292
- `\n⧉ ASE: ☯ persona: ${persona}`;
299
+ `\n⧉ ASE: ☯ persona: ${persona}, ▢ boxing: ${boxing}`;
293
300
  /* inject markdown into session context.
294
301
  Anthropic Claude Code CLI and OpenAI Codex CLI expect the context nested in
295
302
  "hookSpecificOutput"; GitHub Copilot CLI expects a flat top-level
package/dst/ase-setup.js CHANGED
@@ -4,6 +4,7 @@
4
4
  ** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
5
5
  */
6
6
  import fs from "node:fs/promises";
7
+ import os from "node:os";
7
8
  import path from "node:path";
8
9
  import { fileURLToPath } from "node:url";
9
10
  import { execa } from "execa";
@@ -11,6 +12,9 @@ import which from "which";
11
12
  import * as dotenvx from "@dotenvx/dotenvx";
12
13
  import Table from "cli-table3";
13
14
  import chalk from "chalk";
15
+ import writeFileAtomic from "write-file-atomic";
16
+ import { mkdirp } from "mkdirp";
17
+ import JsonAsty from "json-asty";
14
18
  import Version from "./ase-version.js";
15
19
  const toolSpecs = {
16
20
  "claude": { cli: "claude", label: "Anthropic Claude Code CLI", pInstall: "install", pRemove: "uninstall", pUpdate: "update" },
@@ -567,6 +571,226 @@ export default class SetupCommand {
567
571
  }
568
572
  }
569
573
  ];
574
+ /* the default "ase statusline" format lines used when the user does
575
+ not override them with positional arguments to "activate" */
576
+ statuslineFormatDflt = [
577
+ "<blue>%u</blue> <red>%p</red> <black>%T</black> %s",
578
+ "%m %e %t",
579
+ "%P %c"
580
+ ];
581
+ /* resolve the tool settings file for a given installation scope */
582
+ statuslineSettingsFile(tool, scope) {
583
+ if (tool === "copilot") {
584
+ const home = process.env.COPILOT_HOME ?? "";
585
+ const base = home !== "" ? home : path.join(os.homedir(), ".copilot");
586
+ return path.join(base, "settings.json");
587
+ }
588
+ else if (scope === "project")
589
+ return path.join(process.cwd(), ".claude", "settings.json");
590
+ else if (scope === "local")
591
+ return path.join(process.cwd(), ".claude", "settings.local.json");
592
+ else
593
+ return path.join(os.homedir(), ".claude", "settings.json");
594
+ }
595
+ /* reject the statusline operation for tools without a scriptable
596
+ statusline mechanism (only the OpenAI Codex CLI lacks one) */
597
+ requireStatuslineTool(tool) {
598
+ if (tool === "codex")
599
+ throw new Error("statusline configuration is not supported for --tool codex " +
600
+ `(the ${toolSpecs[tool].label} has no scriptable statusline mechanism)`);
601
+ }
602
+ /* reject a non-default --scope for the GitHub Copilot CLI, which has a
603
+ single per-user settings file and thus no scope concept */
604
+ requireStatuslineScope(tool, scope) {
605
+ if (tool === "copilot" && scope !== "user")
606
+ throw new Error("--scope is only supported for --tool claude " +
607
+ `(the ${toolSpecs[tool].label} has a single per-user settings file)`);
608
+ }
609
+ /* build the "ase statusline ..." command string from the activate
610
+ options and the effective format lines */
611
+ statuslineCommand(opts, format) {
612
+ const parts = ["ase", "statusline", "-w", String(opts.width), "-m", String(opts.margin)];
613
+ if (!opts.icons)
614
+ parts.push("--no-icons");
615
+ if (!opts.labels)
616
+ parts.push("--no-labels");
617
+ const lines = format.length > 0 ? format : this.statuslineFormatDflt;
618
+ for (const line of lines)
619
+ parts.push(`'${line.replace(/'/g, "'\\''")}'`);
620
+ return parts.join(" ");
621
+ }
622
+ /* determine whether an existing "statusLine" value object is owned by us */
623
+ statuslineIsOwned(member) {
624
+ const obj = member.query("/ * [ pos() == 2 ]")[0];
625
+ if (obj === undefined)
626
+ return false;
627
+ for (const m of obj.query("/ member")) {
628
+ const key = m.query("/ string [ pos() == 1 ]")[0];
629
+ if (key !== undefined && key.get("value") === "command") {
630
+ const val = m.query("/ string [ pos() == 2 ]")[0];
631
+ const cmd = val !== undefined ? String(val.get("value") ?? "") : "";
632
+ return cmd.startsWith("ase statusline");
633
+ }
634
+ }
635
+ return false;
636
+ }
637
+ /* locate the top-level "statusLine" member node within the root object */
638
+ statuslineFindMember(root) {
639
+ for (const m of root.query("/ member")) {
640
+ const key = m.query("/ string [ pos() == 1 ]")[0];
641
+ if (key !== undefined && key.get("value") === "statusLine")
642
+ return m;
643
+ }
644
+ return undefined;
645
+ }
646
+ /* build the "statusLine" member subtree natively inside the target
647
+ AST, reproducing the exact whitespace tokens json-asty emits for a
648
+ canonically 4-space-indented settings.json */
649
+ statuslineBuildMember(root, command) {
650
+ const strMember = (key, val, epilog) => {
651
+ const k = root.create("string").set({ body: JSON.stringify(key), value: key, epilog: ": " });
652
+ const v = root.create("string").set({ body: JSON.stringify(val), value: val });
653
+ const m = root.create("member");
654
+ if (epilog !== undefined)
655
+ m.set({ epilog });
656
+ return m.add(k, v);
657
+ };
658
+ const numMember = (key, val, epilog) => {
659
+ const k = root.create("string").set({ body: JSON.stringify(key), value: key, epilog: ": " });
660
+ const v = root.create("number").set({ body: String(val), value: val });
661
+ const m = root.create("member");
662
+ if (epilog !== undefined)
663
+ m.set({ epilog });
664
+ return m.add(k, v);
665
+ };
666
+ const obj = root.create("object").set({ prolog: "{\n ", epilog: "\n }\n" });
667
+ obj.add(strMember("type", "command", ",\n "), strMember("command", command, ",\n "), numMember("padding", 0));
668
+ const key = root.create("string").set({
669
+ body: JSON.stringify("statusLine"),
670
+ value: "statusLine",
671
+ epilog: ": "
672
+ });
673
+ return root.create("member").add(key, obj);
674
+ }
675
+ /* normalize the previous-last member so a following ",\n " comma reads cleanly */
676
+ statuslineMakeNonLast(member) {
677
+ const val = member.query("/ * [ pos() == 2 ]")[0];
678
+ if (val !== undefined) {
679
+ const ep = val.get("epilog");
680
+ if (typeof ep === "string" && ep.endsWith("\n"))
681
+ val.set({ epilog: ep.replace(/\n$/, "") });
682
+ }
683
+ member.set({ epilog: ",\n " });
684
+ }
685
+ /* read a settings.json file into a JSON-ASTy AST */
686
+ async statuslineReadAst(file) {
687
+ let text = "";
688
+ try {
689
+ text = await fs.readFile(file, "utf8");
690
+ }
691
+ catch {
692
+ /* missing file: start from an empty object */
693
+ }
694
+ if (text.trim() === "")
695
+ text = "{}";
696
+ return JsonAsty.parse(text);
697
+ }
698
+ /* write a JSON-ASTy AST back to a settings.json file */
699
+ async statuslineWriteAst(file, root) {
700
+ await mkdirp(path.dirname(file));
701
+ const text = JsonAsty.unparse(root);
702
+ writeFileAtomic.sync(file, text, { encoding: "utf8" });
703
+ }
704
+ /* handler for "ase setup statusline activate" */
705
+ async doStatuslineActivate(tool, scope, opts, format) {
706
+ this.requireStatuslineTool(tool);
707
+ this.requireStatuslineScope(tool, scope);
708
+ const file = this.statuslineSettingsFile(tool, scope);
709
+ const command = this.statuslineCommand(opts, format);
710
+ const root = await this.statuslineReadAst(file);
711
+ const existing = this.statuslineFindMember(root);
712
+ if (existing !== undefined) {
713
+ /* preserve a foreign, hand-crafted statusLine: skip and warn */
714
+ if (!this.statuslineIsOwned(existing)) {
715
+ this.log.write("warning", "setup: statusline: activate: a non-ASE \"statusLine\" " +
716
+ `is already present in ${file}: preserving it (skipped)`);
717
+ return 0;
718
+ }
719
+ /* replace the value object in place, preserving its epilog */
720
+ const valNew = this.statuslineBuildMember(root, command).query("/ object")[0];
721
+ const valOld = existing.query("/ * [ pos() == 2 ]")[0];
722
+ valNew.set({ epilog: valOld.get("epilog") });
723
+ existing.del(valOld).add(valNew);
724
+ this.log.write("info", `setup: statusline: activate: updating ASE "statusLine" in ${file}`);
725
+ }
726
+ else {
727
+ /* insert a fresh statusLine member */
728
+ const members = root.query("/ member");
729
+ const member = this.statuslineBuildMember(root, command);
730
+ if (members.length === 0) {
731
+ root.set({ prolog: "{\n ", epilog: "}\n" });
732
+ root.add(member);
733
+ }
734
+ else {
735
+ root.set({ epilog: "}\n" });
736
+ this.statuslineMakeNonLast(members[members.length - 1]);
737
+ root.add(member);
738
+ }
739
+ this.log.write("info", `setup: statusline: activate: adding ASE "statusLine" to ${file}`);
740
+ }
741
+ await this.statuslineWriteAst(file, root);
742
+ return 0;
743
+ }
744
+ /* handler for "ase setup statusline deactivate" */
745
+ async doStatuslineDeactivate(tool, scope) {
746
+ this.requireStatuslineTool(tool);
747
+ this.requireStatuslineScope(tool, scope);
748
+ const file = this.statuslineSettingsFile(tool, scope);
749
+ /* a missing settings file means nothing to remove */
750
+ try {
751
+ await fs.access(file);
752
+ }
753
+ catch {
754
+ this.log.write("info", `setup: statusline: deactivate: no settings file ${file} (skipped)`);
755
+ return 0;
756
+ }
757
+ /* read file */
758
+ const root = await this.statuslineReadAst(file);
759
+ const target = this.statuslineFindMember(root);
760
+ if (target === undefined) {
761
+ this.log.write("info", `setup: statusline: deactivate: no "statusLine" in ${file} (skipped)`);
762
+ return 0;
763
+ }
764
+ /* preserve a foreign, hand-crafted statusLine: skip and warn */
765
+ if (!this.statuslineIsOwned(target)) {
766
+ this.log.write("warning", "setup: statusline: deactivate: a non-ASE \"statusLine\" " +
767
+ `is present in ${file}: preserving it (skipped)`);
768
+ return 0;
769
+ }
770
+ /* remove the member and repair the whitespace at the seam */
771
+ const members = root.query("/ member");
772
+ const wasLast = members[members.length - 1] === target;
773
+ root.del(target);
774
+ if (wasLast) {
775
+ if (members.length > 1) {
776
+ /* promote the new last member: strip its comma epilog and
777
+ restore the pre-"}" newline into the root epilog when the
778
+ new-last value is a scalar (a container value carries the
779
+ newline in its own epilog already) */
780
+ const newLast = members[members.length - 2];
781
+ newLast.set({ epilog: undefined });
782
+ const val = newLast.query("/ * [ pos() == 2 ]")[0];
783
+ const ep = val !== undefined ? val.get("epilog") : undefined;
784
+ if (!(typeof ep === "string" && ep.endsWith("\n")))
785
+ root.set({ epilog: "\n}\n" });
786
+ }
787
+ else
788
+ root.set({ epilog: "}\n" });
789
+ }
790
+ await this.statuslineWriteAst(file, root);
791
+ this.log.write("info", `setup: statusline: deactivate: removing ASE "statusLine" from ${file}`);
792
+ return 0;
793
+ }
570
794
  /* parse and validate the --tool option */
571
795
  parseTool(value) {
572
796
  if (value !== "claude" && value !== "copilot" && value !== "codex")
@@ -683,5 +907,42 @@ export default class SetupCommand {
683
907
  .action(async (servers, opts) => {
684
908
  process.exit(await this.doMcp("deactivate", this.parseTool(opts.tool), servers ?? "all", this.parseScope(opts.scope)));
685
909
  });
910
+ /* parser for the non-negative integer "ase setup statusline" options */
911
+ const parseNonNeg = (name) => (value) => {
912
+ const n = Number.parseInt(value, 10);
913
+ if (!Number.isFinite(n) || n < 0)
914
+ throw new Error(`invalid --${name} value: "${value}" (expected a non-negative integer)`);
915
+ return n;
916
+ };
917
+ /* register CLI sub-command "ase setup statusline" */
918
+ const statuslineCmd = setupCmd
919
+ .command("statusline")
920
+ .description("activate or deactivate the ASE statusline for a tool")
921
+ .action(() => {
922
+ statuslineCmd.outputHelp();
923
+ process.exit(1);
924
+ });
925
+ /* register CLI sub-command "ase setup statusline activate" */
926
+ statuslineCmd
927
+ .command("activate [format...]")
928
+ .description("activate the ASE statusline (optionally with custom format lines)")
929
+ .option("-t, --tool <tool>", "target tool (\"claude\" or \"copilot\"; \"codex\" is unsupported)", toolDflt)
930
+ .option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
931
+ .option("-w, --width <n>", "force terminal width to <n> characters (0 = auto-detect via /dev/tty)", parseNonNeg("width"), 0)
932
+ .option("-m, --margin <n>", "reduce maximum used terminal width by <n> characters on each side", parseNonNeg("margin"), 2)
933
+ .option("--no-icons", "disable icons in placeholder rendering")
934
+ .option("--no-labels", "disable labels in front of bold values")
935
+ .action(async (format, opts) => {
936
+ process.exit(await this.doStatuslineActivate(this.parseTool(opts.tool), this.parseScope(opts.scope), { width: opts.width, margin: opts.margin, icons: opts.icons, labels: opts.labels }, format ?? []));
937
+ });
938
+ /* register CLI sub-command "ase setup statusline deactivate" */
939
+ statuslineCmd
940
+ .command("deactivate")
941
+ .description("deactivate the ASE statusline")
942
+ .option("-t, --tool <tool>", "target tool (\"claude\" or \"copilot\"; \"codex\" is unsupported)", toolDflt)
943
+ .option("-s, --scope <scope>", "target scope (\"user\", \"project\", or \"local\")", "user")
944
+ .action(async (opts) => {
945
+ process.exit(await this.doStatuslineDeactivate(this.parseTool(opts.tool), this.parseScope(opts.scope)));
946
+ });
686
947
  }
687
948
  }
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "homepage": "http://github.com/rse/ase",
7
7
  "repository": { "url": "git+https://github.com/rse/ase.git", "type": "git" },
8
8
  "bugs": { "url": "http://github.com/rse/ase/issues" },
9
- "version": "0.9.36",
9
+ "version": "0.9.38",
10
10
  "license": "Apache-2.0",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -54,6 +54,7 @@
54
54
  "pretty-ms": "9.3.0",
55
55
  "luxon": "3.7.2",
56
56
  "@modelcontextprotocol/sdk": "1.29.0",
57
+ "json-asty": "1.3.2",
57
58
  "zod": "4.4.3",
58
59
  "which": "7.0.0",
59
60
  "update-notifier": "7.3.1",
@@ -65,6 +66,9 @@
65
66
  "ofetch": "1.5.1",
66
67
  "picomatch": "4.0.5"
67
68
  },
69
+ "allowScripts": {
70
+ "es5-ext": true
71
+ },
68
72
  "engines": {
69
73
  "npm": ">=10.0.0",
70
74
  "node": ">=22.0.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.36",
3
+ "version": "0.9.38",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.36",
3
+ "version": "0.9.38",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ase",
3
- "version": "0.9.36",
3
+ "version": "0.9.38",
4
4
  "description": "Agentic Software Engineering (ASE)",
5
5
  "keywords": [ "agentic", "software", "engineering" ],
6
6
  "homepage": "https://ase.tools",
@@ -16,7 +16,7 @@ any skill flow:
16
16
  ⧉ **ASE**: ⎈ version: **<ase-version/>** <ase-version-hint/>
17
17
  ⧉ **ASE**: ※ user: **<ase-user-id/>**, ⚑ project: **<ase-project-id/>**
18
18
  ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ⏻ session: **<ase-session-id/>**
19
- ⧉ **ASE**: ☯ persona: **<ase-persona-style/>**
19
+ ⧉ **ASE**: ☯ persona: **<ase-persona-style/>**, ▢ boxing: **<ase-project-boxing/>**
20
20
  </template>
21
21
 
22
22
  Prohibitions
@@ -56,12 +56,14 @@ following procedure:
56
56
  If this line exists, parse it according to the format `<label/>: <description/>`.
57
57
  Set <n/> to <n/> + 1 (increment entry count).
58
58
  Set <label-key/> to `<ase-tpl-key digit="<n/>"/>`.
59
- Set <label-text/> to `<ase-tpl-pad width="<width/>" text="<label/>:"/>`.
59
+ Set <label-text/> to `**<label/>:**`.
60
+ Set <label-pad/> to `<ase-tpl-pad width="<width/>" text="<label/>:"/>` with
61
+ the leading `<label/>:` text removed, i.e. only the trailing padding spaces.
60
62
  Append an entry to <text/>:
61
63
 
62
64
  <text>
63
65
  <text/>
64
- <ase-tpl-boxline><label-key/> ▶ **<label-text/>** <description/></ase-tpl-boxline>
66
+ <ase-tpl-boxline><label-key/> ▶ <label-text/><label-pad/> <description/></ase-tpl-boxline>
65
67
  </text>
66
68
 
67
69
  <if condition="<keys/> is empty">
@@ -243,9 +243,58 @@ Skill Identification
243
243
  steps at the end of a skill beside the explicit outputs via any
244
244
  <template/>.
245
245
 
246
+ Artifact Boxing Transparency
247
+ ----------------------------
248
+
249
+ - *IMPORTANT*: The *boxing* of a project (indicated by
250
+ <ase-project-boxing/> configuration) classifies how *transparent*
251
+ its *source artifacts* (specification, architecture, source code,
252
+ documentation, infrastructure, task plan, and other artifacts) are,
253
+ and thus how *deeply* you *MUST* inspect or produce them and how
254
+ *much* of their internals you *MUST* surface.
255
+
256
+ - *IMPORTANT*: Boxing modulates only the *work depth* (inspection and
257
+ production thoroughness) and the *output visibility* (surfaced
258
+ content and findings) of *artifact-touching* skills, per the three
259
+ lists below. It *MUST* *NOT* alter the *communication style*
260
+ (governed exclusively by the persona), and it *MUST* *NOT* apply to
261
+ skills that neither read, critique, nor write any project source
262
+ artifact.
263
+
264
+ - If <ase-project-boxing/> is `white`:
265
+ - Transparency: *fully*
266
+ - Work depth: inspect *fully*, produce *thoroughly*
267
+ - Findings surfaced: *all*
268
+ - Internals exposed: *full* (complete diffs, exhaustive rationale)
269
+
270
+ - If <ase-project-boxing/> is `grey`:
271
+ - Transparency: *partially*
272
+ - Work depth: inspect *significant* parts, produce *reasonably*
273
+ - Findings surfaced: *essentials*, summarize rest
274
+ - Internals exposed: *key* only (essential hunks, condensed rationale), summarize rest
275
+
276
+ - If <ase-project-boxing/> is `black`:
277
+ - Transparency: *none*
278
+ - Work depth: *minimum* work to satisfy the request
279
+ - Findings surfaced: *none* (report only the *outcome*)
280
+ - Internals exposed: *none* (no diffs, no code, no per-artifact explanation)
281
+
282
+ - *IMPORTANT*: Precedence rule: skill's *explicit* `black` branches
283
+ deterministically skip or suppress and *win* over the implicit decisions
284
+ above. Where no such branches exist, the decisions above apply.
285
+
246
286
  Template Patterns
247
287
  -----------------
248
288
 
289
+ - *IMPORTANT*: In every `<template/>` below you *MUST* reproduce all
290
+ box-drawing and decorative glyphs *verbatim*, character-for-character,
291
+ exactly as written. This especially includes the corner and edge
292
+ characters `╭`, `╰`, and `│`, the shoulder character `━`, the bar
293
+ character `─`, and the trailing dotted character `┈`. You *MUST* *NOT*
294
+ substitute visually similar ASCII fallbacks (e.g. `+`, `|`, `-`) or
295
+ other Unicode look-alikes, and you *MUST* *NOT* drop, add, or reorder
296
+ any of these glyphs.
297
+
249
298
  - When `<ase-tpl-head/>` (no title attribute) should be expanded, use:
250
299
 
251
300
  <template>
@@ -360,3 +409,4 @@ Template Patterns
360
409
  clamped to zero so an over-long text never yields a negative count):
361
410
 
362
411
  <template><text/><ws/></template>
412
+
@@ -6,7 +6,7 @@
6
6
  "homepage": "http://github.com/rse/ase",
7
7
  "repository": { "url": "git+https://github.com/rse/ase.git", "type": "git" },
8
8
  "bugs": { "url": "http://github.com/rse/ase/issues" },
9
- "version": "0.9.36",
9
+ "version": "0.9.38",
10
10
  "license": "Apache-2.0",
11
11
  "author": {
12
12
  "name": "Dr. Ralf S. Engelschall",
@@ -121,6 +121,22 @@ interface quality, quality attributes, and architecture governance.
121
121
 
122
122
  <flow>
123
123
  1. <step id="STEP 1: Investigate Code Base">
124
+
125
+ <if condition="<ase-project-boxing/> is equal `black`">
126
+
127
+ The project source artifacts are classified as a *black box*, so
128
+ the user does *not* want the architecture inspected or its findings
129
+ surfaced. *Skip* the entire investigation and reporting: do *not*
130
+ invoke any `Agent` tool and do *not* read any source, only output
131
+ the following <template/> and then *SKIP* the remaining steps STEP 2
132
+ through STEP 4:
133
+
134
+ <template>
135
+ <ase-tpl-bullet-normal/> **ARCHITECTURE ANALYSIS**: *suppressed* (`project.boxing` is `black`)
136
+ </template>
137
+
138
+ </if>
139
+
124
140
  Investigate the code from an *architectural* perspective. If the
125
141
  code base is large, you *MUST* use the `Agent` tool (not inline
126
142
  work) to create multiple sub-agents to split the investigation
@@ -54,6 +54,20 @@ problems in *performance* and *efficiency*, or problems in *security*.
54
54
 
55
55
  2. <step id="STEP 2: Investigate Code Base">
56
56
 
57
+ <if condition="<ase-project-boxing/> is equal `black`">
58
+
59
+ The project source artifacts are classified as a *black box*, so
60
+ the user does *not* want them inspected or their problems surfaced.
61
+ *Skip* the entire investigation and analysis: do *not* invoke any
62
+ `Glob` or `Agent` tool and do *not* read any source, only output
63
+ the following <template/> and then *SKIP* the remaining step STEP 3:
64
+
65
+ <template>
66
+ <ase-tpl-bullet-normal/> **CODE ANALYSIS**: *suppressed* (`project.boxing` is `black`)
67
+ </template>
68
+
69
+ </if>
70
+
57
71
  First, use the following <template/> to give a hint on this step:
58
72
 
59
73
  <template>
@@ -109,14 +123,19 @@ problems in *performance* and *efficiency*, or problems in *security*.
109
123
 
110
124
  3. <step id="STEP 3: Show Results">
111
125
 
112
- Before reporting, *apply the severity floor* selected via
113
- <getopt-option-severity/> (default `LOW`): define the ordinal rank
114
- `LOW`=1, `MEDIUM`=2, `HIGH`=3. *Keep* a detected problem if and only
115
- if its `severity` field is `ACCEPTED` *or* `rank(severity)` is greater
116
- than or equal to `rank(<getopt-option-severity/>)`; *silently drop*
117
- all other problems (they are neither reported nor persisted). With
118
- the default floor `LOW`, all problems are kept. `ACCEPTED` problems
119
- are *never* dropped.
126
+ Before reporting, determine the *effective severity floor* <floor/>:
127
+ define the ordinal rank `LOW`=1, `MEDIUM`=2, `HIGH`=3, start from
128
+ <floor><getopt-option-severity/></floor> (default `LOW`), and - if
129
+ <ase-project-boxing/> is equal `grey` - raise <floor/> to `MEDIUM`
130
+ whenever its current rank is below `rank(MEDIUM)` (grey boxing
131
+ surfaces only *material* findings of severity `MEDIUM` and above).
132
+
133
+ Then *apply the effective severity floor* <floor/>: *Keep* a detected
134
+ problem if and only if its `severity` field is `ACCEPTED` *or*
135
+ `rank(severity)` is greater than or equal to `rank(<floor/>)`;
136
+ *silently drop* all other problems (they are neither reported nor
137
+ persisted). With the default floor `LOW`, all problems are kept.
138
+ `ACCEPTED` problems are *never* dropped.
120
139
 
121
140
  Then renumber the surviving problems contiguously as `P<n/>` with
122
141
  <n/> = 1, 2, ... in the original ordering. If *all* problems are
@@ -140,8 +140,11 @@ permitted way to persist artifacts is via `ase_task_save(...)`.
140
140
  You *MUST* perform the following sub-steps *internally* and *without
141
141
  any output* until and including the recommendation decision. Only
142
142
  sub-steps 4-6 below are allowed to produce output, and only if
143
- <getopt-option-auto/> is equal `false`. If <getopt-option-auto/> is
144
- equal `true`, *skip* the reporting sub-steps 4-6 entirely (perform
143
+ <getopt-option-auto/> is equal `false` *and* <ase-project-boxing/>
144
+ is *not* equal `black`.
145
+
146
+ If <getopt-option-auto/> is equal `true` or <ase-project-boxing/> is
147
+ equal `black`, *skip* the reporting sub-steps 4-6 entirely (perform
145
148
  no output at all) to speed up processing.
146
149
 
147
150
  1. *Propose* corresponding *feature approach*, including optionally,
@@ -187,7 +190,7 @@ permitted way to persist artifacts is via `ase_task_save(...)`.
187
190
  <ase-tpl-foot title="APPROACHES"/>
188
191
  </template>
189
192
 
190
- 7. <if condition="<getopt-option-auto/> is not equal `true`">
193
+ 7. <if condition="<getopt-option-auto/> is not equal `true` and <ase-project-boxing/> is not equal `black`">
191
194
 
192
195
  In the following, you *MUST* *NOT* use your built-in
193
196
  <user-dialog-tool/> tool! Instead, you *MUST* just show a
@@ -43,6 +43,23 @@ code and *explain* it in a *brief*, *standardized*, and *concise* way.
43
43
  Second, explain *WHY* the code does it (*rationale*).
44
44
 
45
45
  Keep your explanations *brief* and *concise*.
46
+
47
+ <if condition="<ase-project-boxing/> is equal `black`">
48
+
49
+ The project source artifacts are classified as a *black box*, so the
50
+ user wants only the *minimal* surface explanation. Output *only* the
51
+ *WHAT* block (omit the *WHY* block) with the following <template/>:
52
+
53
+ <template>
54
+ <ase-tpl-bullet-normal/> **WHAT** (You should know what):
55
+ - [...]
56
+ - [...]
57
+ - [...]
58
+ </template>
59
+
60
+ </if>
61
+ <else>
62
+
46
63
  Output the result with the following <template/>:
47
64
 
48
65
  <template>
@@ -56,9 +73,21 @@ code and *explain* it in a *brief*, *standardized*, and *concise* way.
56
73
  - [...]
57
74
  - [...]
58
75
  </template>
76
+
77
+ </else>
59
78
  </step>
60
79
 
61
80
  3. <step id="STEP 3: ANALOGY and DIAGRAM">
81
+
82
+ <if condition="<ase-project-boxing/> is equal `black`">
83
+
84
+ The project source artifacts are classified as a *black box*, so
85
+ deeper elaboration is *not* wanted. *Skip* this STEP 3 and the
86
+ following STEP 4 entirely: do *not* output any ANALOGY, DIAGRAM,
87
+ CRUXES, or GOTCHAS, and do *not* dispatch any diagram rendering.
88
+
89
+ </if>
90
+
62
91
  **Give insights with ANALOGY and DIAGRAM**.
63
92
 
64
93
  First, give an analogy by comparing the code to something from
@@ -34,6 +34,21 @@ Give *insights* into the project through the source code of <getopt-arguments/>.
34
34
 
35
35
  <flow>
36
36
  1. <step id="STEP 1: PROJECT ABSTRACT">
37
+
38
+ <if condition="<ase-project-boxing/> is equal `black`">
39
+
40
+ The project source artifacts are classified as a *black box*, so the
41
+ user does *not* want them inspected or their internals surfaced.
42
+ *Skip* the entire insight gathering: do *not* read any source, only
43
+ output the following <template/> and then *SKIP* the remaining steps
44
+ STEP 2 through STEP 4:
45
+
46
+ <template>
47
+ <ase-tpl-bullet-normal/> **PROJECT INSIGHT**: *suppressed* (`project.boxing` is `black`)
48
+ </template>
49
+
50
+ </if>
51
+
37
52
  Determine an <abstract/> summary of this project.
38
53
  For this, check a potentially existing `README.*` file
39
54
  or scan the source files and figure it out indirectly.
@@ -33,6 +33,21 @@ related to a set of code quality aspects.
33
33
 
34
34
  1. <step id="STEP 1: Investigation">
35
35
 
36
+ <if condition="<ase-project-boxing/> is equal `black`">
37
+
38
+ The project source artifacts are classified as a *black box*, so
39
+ the user does *not* want them inspected or their problems surfaced.
40
+ *Skip* the entire investigation and reporting: do *not* invoke any
41
+ `Glob` or `Agent` tool and do *not* read any source, only output
42
+ the following <template/> and then *SKIP* the remaining steps STEP 2
43
+ and STEP 3:
44
+
45
+ <template>
46
+ <ase-tpl-bullet-normal/> **LINT**: *suppressed* (`project.boxing` is `black`)
47
+ </template>
48
+
49
+ </if>
50
+
36
51
  First, use the following <template/> to give a hint on this step:
37
52
 
38
53
  <template>
@@ -76,13 +91,18 @@ related to a set of code quality aspects.
76
91
  sort the list by `file` and then numerically by `line`, and set
77
92
  <problems/> to that list.
78
93
 
79
- Then *apply the severity floor* selected via <getopt-option-severity/>
80
- (default `LOW`): define the ordinal rank `LOW`=1, `MEDIUM`=2,
81
- `HIGH`=3. *Keep* a problem in <problems/> if and only if its
82
- `severity` field is `ACCEPTED` *or* `rank(severity)` is greater than
83
- or equal to `rank(<getopt-option-severity/>)`; *silently drop* all
84
- other problems. With the default floor `LOW`, all problems are kept.
85
- `ACCEPTED` problems are *never* dropped.
94
+ Then determine the *effective severity floor* <floor/>: define the
95
+ ordinal rank `LOW`=1, `MEDIUM`=2, `HIGH`=3, start from
96
+ <floor><getopt-option-severity/></floor> (default `LOW`), and - if
97
+ <ase-project-boxing/> is equal `grey` - raise <floor/> to `MEDIUM`
98
+ whenever its current rank is below `rank(MEDIUM)` (grey boxing
99
+ surfaces only *material* findings of severity `MEDIUM` and above).
100
+
101
+ Then *apply the effective severity floor* <floor/>: *Keep* a problem
102
+ in <problems/> if and only if its `severity` field is `ACCEPTED` *or*
103
+ `rank(severity)` is greater than or equal to `rank(<floor/>)`;
104
+ *silently drop* all other problems. With the default floor `LOW`, all
105
+ problems are kept. `ACCEPTED` problems are *never* dropped.
86
106
 
87
107
  You *MUST* *NOT* output anything else in this step 1.
88
108
 
@@ -145,6 +165,7 @@ related to a set of code quality aspects.
145
165
 
146
166
  2. Set <context></context> (set to empty).
147
167
  Set <diff></diff> (set to empty).
168
+ Set <diff-condensed></diff-condensed> (set to empty).
148
169
 
149
170
  3. Iterate over the change set:
150
171
 
@@ -153,7 +174,9 @@ related to a set of code quality aspects.
153
174
  1. Set <file/> to the `file` field of <item/>.
154
175
  Set <change-hunks/> to the `change-hunks` field of <item/>.
155
176
 
156
- Set <diff-file/> to the following <template/>:
177
+ Unless <ase-project-boxing/> is equal `grey` (where the
178
+ full unified diff is suppressed and this per-file header is
179
+ unused), set <diff-file/> to the following <template/>:
157
180
 
158
181
  <template>
159
182
  --- <file/> (original)
@@ -170,11 +193,18 @@ related to a set of code quality aspects.
170
193
  Set <new-text/> to the `new_text` field of <item/>.
171
194
  Set <context-after/> to the `context_after` field of <item/>.
172
195
 
173
- 2. Determine the hunk *body* as an ordered list of lines,
174
- each carrying a one-character prefix (` ` for context,
175
- `-` for old-side, `+` for new-side). Build it by
176
- concatenating, in order and *skipping any part that is
177
- empty*:
196
+ 2. *Skip* this substep entirely when <ase-project-boxing/>
197
+ is equal `grey` - the full unified diff is suppressed
198
+ there, so none of the <hunk-body/>, <old-count/>,
199
+ <new-count/>, <old-start/>, or <new-start/> values
200
+ below are consumed (substep 4 builds only the condensed
201
+ one-liner instead).
202
+
203
+ Otherwise, determine the hunk *body* as an ordered list
204
+ of lines, each carrying a one-character prefix (` ` for
205
+ context, `-` for old-side, `+` for new-side). Build it
206
+ by concatenating, in order and *skipping any part that
207
+ is empty*:
178
208
 
179
209
  - one ` `-prefixed line for *each* line of
180
210
  <context-before/> (if non-empty),
@@ -217,7 +247,27 @@ related to a set of code quality aspects.
217
247
 
218
248
  <template>`<file/>`:<line/></template>
219
249
 
220
- 4. Append the following <template/> to <diff-file/>,
250
+ 4. <if condition="<ase-project-boxing/> is equal `grey`">
251
+
252
+ The full unified diff is *suppressed* under grey boxing
253
+ (see substep 5 below), so *skip* the full-diff append
254
+ and instead build only a *condensed* one-line
255
+ representation of this hunk: determine <old-snippet/>
256
+ as the *single-line* collapse of <old-text/> (join its
257
+ lines with ` ⏎ `, or `∅` when <old-text/> is empty for
258
+ a pure insertion) and <new-snippet/> as the same
259
+ collapse of <new-text/> (or `∅` when empty for a pure
260
+ deletion). Then append the following <template/> to
261
+ <diff-condensed/> as a new line:
262
+
263
+ <template>
264
+ <ase-tpl-bullet-signal/> `<file/>`:<line/>: `<old-snippet/>` → `<new-snippet/>`
265
+ </template>
266
+
267
+ </if>
268
+ <else>
269
+
270
+ Append the following <template/> to <diff-file/>,
221
271
  emitting <hunk-body/> verbatim (one already-prefixed
222
272
  line per line, with no extra blank or space-only lines):
223
273
 
@@ -226,9 +276,12 @@ related to a set of code quality aspects.
226
276
  <hunk-body/>
227
277
  </template>
228
278
 
279
+ </else>
280
+
229
281
  </for>
230
282
 
231
- 3. Append <diff-file/> to <diff/>.
283
+ 3. Unless <ase-project-boxing/> is equal `grey` (where
284
+ <diff-file/> is unused), append <diff-file/> to <diff/>.
232
285
 
233
286
  </for>
234
287
 
@@ -241,7 +294,25 @@ related to a set of code quality aspects.
241
294
 
242
295
  </template>
243
296
 
244
- 5. <if condition="<getopt-option-auto/> is not 'true'">
297
+ 5. <if condition="<getopt-option-auto/> is not 'true' and <ase-project-boxing/> is not equal `black`">
298
+
299
+ <if condition="<ase-project-boxing/> is equal `grey`">
300
+
301
+ The project source artifacts are classified as a *grey box*, so
302
+ the user does *not* want the full artifact internals surfaced:
303
+ *suppress* the full unified diff and instead show only the
304
+ *condensed* one-line-per-hunk representation. Report the
305
+ solution with the following <template/>:
306
+
307
+ <template>
308
+ <ase-tpl-bullet-normal/> **<aspect/> SOLUTION**:
309
+
310
+ <diff-condensed/>
311
+
312
+ </template>
313
+
314
+ </if>
315
+ <else>
245
316
 
246
317
  Report the solution with the following <template/>:
247
318
 
@@ -254,6 +325,8 @@ related to a set of code quality aspects.
254
325
 
255
326
  </template>
256
327
 
328
+ </else>
329
+
257
330
  </if>
258
331
  <else>
259
332
 
@@ -140,8 +140,11 @@ permitted way to persist artifacts is via `ase_task_save(...)`.
140
140
  You *MUST* perform the following sub-steps *internally* and *without
141
141
  any output* until and including the recommendation decision. Only
142
142
  sub-steps 4-6 below are allowed to produce output, and only if
143
- <getopt-option-auto/> is equal `false`. If <getopt-option-auto/> is
144
- equal `true`, *skip* the reporting sub-steps 4-6 entirely (perform
143
+ <getopt-option-auto/> is equal `false` *and* <ase-project-boxing/>
144
+ is *not* equal `black`.
145
+
146
+ If <getopt-option-auto/> is equal `true` or <ase-project-boxing/> is
147
+ equal `black`, *skip* the reporting sub-steps 4-6 entirely (perform
145
148
  no output at all) to speed up processing.
146
149
 
147
150
  1. *Propose* corresponding *refactoring approach*, including
@@ -187,7 +190,7 @@ permitted way to persist artifacts is via `ase_task_save(...)`.
187
190
  <ase-tpl-foot title="APPROACHES"/>
188
191
  </template>
189
192
 
190
- 7. <if condition="<getopt-option-auto/> is not `true`">
193
+ 7. <if condition="<getopt-option-auto/> is not equal `true` and <ase-project-boxing/> is not equal `black`">
191
194
 
192
195
  In the following, you *MUST* *NOT* use your built-in
193
196
  <user-dialog-tool/> tool! Instead, you *MUST* just show a
@@ -194,8 +194,11 @@ permitted way to persist artifacts is via `ase_task_save(...)`.
194
194
  You *MUST* perform the following sub-steps *internally* and *without
195
195
  any output* until and including the recommendation decision. Only
196
196
  sub-steps 4-6 below are allowed to produce output, and only if
197
- <getopt-option-auto/> is equal `false`. If <getopt-option-auto/> is
198
- equal `true`, *skip* the reporting sub-steps 4-6 entirely (perform
197
+ <getopt-option-auto/> is equal `false` *and* <ase-project-boxing/>
198
+ is *not* equal `black`.
199
+
200
+ If <getopt-option-auto/> is equal `true` or <ase-project-boxing/> is
201
+ equal `black`, *skip* the reporting sub-steps 4-6 entirely (perform
199
202
  no output at all) to speed up processing.
200
203
 
201
204
  1. *Propose* corresponding *resolution approach*, including optionally,
@@ -241,7 +244,7 @@ permitted way to persist artifacts is via `ase_task_save(...)`.
241
244
  <ase-tpl-foot title="APPROACHES"/>
242
245
  </template>
243
246
 
244
- 7. <if condition="<getopt-option-auto/> is not `true`">
247
+ 7. <if condition="<getopt-option-auto/> is not equal `true` and <ase-project-boxing/> is not equal `black`">
245
248
 
246
249
  In the following, you *MUST* *NOT* use your built-in
247
250
  <user-dialog-tool/> tool! Instead, you *MUST* just show a
@@ -33,6 +33,21 @@ Analyze documents for spelling, punctuation, or grammar errors
33
33
 
34
34
  1. <step id="STEP 1: Investigation">
35
35
 
36
+ <if condition="<ase-project-boxing/> is equal `black`">
37
+
38
+ The project source artifacts are classified as a *black box*, so
39
+ the user does *not* want them inspected or their problems surfaced.
40
+ *Skip* the entire investigation and reporting: do *not* invoke any
41
+ `Glob` or `Agent` tool and do *not* read any document, only output
42
+ the following <template/> and then *SKIP* the remaining steps STEP 2
43
+ and STEP 3:
44
+
45
+ <template>
46
+ <ase-tpl-bullet-normal/> **PROOFREAD**: *suppressed* (`project.boxing` is `black`)
47
+ </template>
48
+
49
+ </if>
50
+
36
51
  First, use the following <template/> to give a hint on this step:
37
52
 
38
53
  <template>
@@ -141,7 +156,26 @@ Analyze documents for spelling, punctuation, or grammar errors
141
156
  <description/>
142
157
  </template>
143
158
 
144
- 3. <if condition="<getopt-option-auto/> is not 'true'">
159
+ 3. <if condition="<getopt-option-auto/> is not 'true' and <ase-project-boxing/> is equal `grey`">
160
+
161
+ The project source artifacts are classified as a *grey box*, so
162
+ the user does *not* want the full artifact internals surfaced:
163
+ *suppress* the full unified diff and instead show only a
164
+ *condensed* one-line representation. Determine <old-snippet/>
165
+ as the *single-line* collapse of <old-text/> (join its lines
166
+ with ` ⏎ `, or `∅` when <old-text/> is empty for a pure
167
+ insertion) and <new-snippet/> as the same collapse of
168
+ <new-text/> (or `∅` when empty for a pure deletion). Then
169
+ report the correction with the following <template/>:
170
+
171
+ <template>
172
+
173
+ <ase-tpl-bullet-normal/> **<type/> CORRECTION**: `<old-snippet/>` → `<new-snippet/>`
174
+
175
+ </template>
176
+
177
+ </if>
178
+ <elseif condition="<getopt-option-auto/> is not 'true'">
145
179
 
146
180
  Determine the hunk *body* as an ordered list of lines, each
147
181
  carrying a one-character prefix (` ` for context, `-` for
@@ -195,7 +229,7 @@ Analyze documents for spelling, punctuation, or grammar errors
195
229
 
196
230
  </template>
197
231
 
198
- </if>
232
+ </elseif>
199
233
 
200
234
  4. <if condition="<getopt-option-auto/> is not 'true'">
201
235
 
@@ -38,9 +38,12 @@ Format
38
38
  The *ChangeLog* file is a Markdown formatted file named `CHANGELOG.md`,
39
39
  and contains sections with headers in the style `N.M.K (YYYY-MM-DD)`.
40
40
 
41
- Each *ChangeLog* entry is always formatted as `<prefix/>: <summary/>`
42
- where the <prefix/> is one of the following tags and their usual related
43
- changes:
41
+ Each *ChangeLog* entry is always formatted as...
42
+
43
+ `<change-type/> [<artifact-kind/>]: <summary/>`
44
+
45
+ ...where the <change-type/> is one of the following tags and their usual
46
+ related changes:
44
47
 
45
48
  - `FEATURE`: new functionality or configuration
46
49
  - `IMPROVEMENT`: improved functionality or configuration
@@ -49,9 +52,21 @@ changes:
49
52
  - `CLEANUP`: cleaned up functionality or configuration
50
53
  - `REFACTOR`: refactored functionality or configuration
51
54
 
55
+ The <artifact-kind/> is one or more of the following *artifact* tags,
56
+ classifying which kind of artifact the change primarily touches. If
57
+ multiple artifact kinds apply, comma-separate them
58
+ (e.g. `[arch, code]`):
59
+
60
+ - `spec`: requirement/specification artifacts
61
+ - `arch`: architecture/design artifacts
62
+ - `code`: source code artifacts
63
+ - `docs`: documentation artifacts
64
+ - `infr`: infrastructure/build/tooling artifacts
65
+ - `othr`: any other artifacts
66
+
52
67
  The <summary/> is not longer than about 60-80 characters. The
53
68
  *ChangeLog* entries for a single product release version are also always
54
- grouped and sorted according to the above <prefix/> list.
69
+ grouped and sorted according to the above <change-type/> list.
55
70
 
56
71
  Processing
57
72
  ----------
@@ -123,6 +138,16 @@ Processing
123
138
  For each Git commit, reduce the Git commit messages to a single
124
139
  short <summary/> sentence, not longer than 60-80 characters.
125
140
 
141
+ For each entry, also determine the <artifact-kind/> *artifact kind*
142
+ tag(s) from the paths of the changed files. To classify a changed
143
+ file to its artifact class, call the `ase_artifact_list(kind: [
144
+ ... ])` tool of the `ase` MCP server *once*, passing the `kind`
145
+ tokens (`spec`, `arch`, `code`, `docs`, `infr`, `othr`), and read
146
+ the returned `artifacts` array of `{ kind, files }` objects to match
147
+ each changed file to its kind. Map the matched lower-cased kind back
148
+ to its upper-cased <artifact-kind/> tag, and comma-separate multiple
149
+ tags when more than one artifact class applies.
150
+
126
151
  If a <summary/> is too short or especially is not comprehensible
127
152
  enough because of too little context information, add some essential
128
153
  context, especially references to the class/module/package, etc.
@@ -144,11 +169,11 @@ Processing
144
169
  Without immediately modifying the `CHANGELOG.md` file, *consolidate*
145
170
  the entries in the first (most recent) section only, by summarizing
146
171
  and merging closely related entries. Perform the entry consolidation
147
- per <prefix/> group only.
172
+ per <change-type/> group only.
148
173
 
149
174
  Without immediately modifying the `CHANGELOG.md` file, *sort* the
150
175
  entries in the first (most recent) section only. Instead of the
151
- chronological commit order, group the entries by the <prefix/>es.
176
+ chronological commit order, group the entries by the <change-type/>s.
152
177
 
153
178
  </step>
154
179
 
@@ -15,10 +15,13 @@ The `ase-meta-changelog` skill helps to *complete*, *consolidate*, and
15
15
  file, based on the underlying *Git* commits and the currently staged
16
16
  changes in the Git *index*.
17
17
 
18
- Each entry is formatted as `<prefix>: <summary>` where *prefix* is
19
- one of `FEATURE`, `IMPROVEMENT`, `BUGFIX`, `UPDATE`, `CLEANUP`, or
20
- `REFACTOR`. Entries are grouped and sorted by this prefix. The
21
- date in the section header is also updated to the current date.
18
+ Each entry is formatted as `<change-type> [<artifact-kind>]: <summary>`
19
+ where *change-type* is one of `FEATURE`, `IMPROVEMENT`, `BUGFIX`,
20
+ `UPDATE`, `CLEANUP`, or `REFACTOR`, and *artifact-kind* is one or more
21
+ comma-separated *artifact class* tags out of `spec`, `arch`, `code`,
22
+ `docs`, `infr`, or `othr`. Entries are grouped and sorted by this
23
+ prefix. The date in the section header is also updated to the current
24
+ date.
22
25
 
23
26
  ## EXAMPLES
24
27
 
@@ -123,10 +123,28 @@ Procedure
123
123
  *not* output any further explanation.
124
124
  </if>
125
125
 
126
+ 4. <if condition="
127
+ <ase-project-boxing/> is equal `black` and
128
+ (<getopt-option-coherence/> is equal `true` or
129
+ <getopt-option-risk/> is equal `true` or
130
+ <getopt-option-blast/> is equal `true`)
131
+ ">
132
+ The project source artifacts are classified as a *black box*, so
133
+ the user does *not* want the staged changes scrutinized or their
134
+ coherence, risk, and blast-radius findings surfaced. The
135
+ requested scrutiny of STEP 3 through STEP 5 is therefore
136
+ suppressed: only output the following <template/> and then *SKIP*
137
+ the remaining steps STEP 3 through STEP 5:
138
+
139
+ <template>
140
+ <ase-tpl-bullet-normal/> **DIFF SCRUTINY**: *suppressed* (`project.boxing` is `black`)
141
+ </template>
142
+ </if>
143
+
126
144
  </step>
127
145
 
128
146
  3. <step id="STEP 3: Assess Intent Coherence"
129
- condition="<getopt-option-coherence/> is equal `true` and <diff/> is NOT empty">
147
+ condition="<getopt-option-coherence/> is equal `true` and <diff/> is NOT empty and <ase-project-boxing/> is not equal `black`">
130
148
 
131
149
  1. From the *same* captured <diff/> and <stat/>, *reconstruct the
132
150
  single intended change* as a thesis - the one logical, coherent purpose
@@ -203,7 +221,7 @@ Procedure
203
221
  </step>
204
222
 
205
223
  4. <step id="STEP 4: Score Against Risk Rubric"
206
- condition="<getopt-option-risk/> is equal `true` and <diff/> is NOT empty">
224
+ condition="<getopt-option-risk/> is equal `true` and <diff/> is NOT empty and <ase-project-boxing/> is not equal `black`">
207
225
 
208
226
  1. Score the *same* captured <diff/> and <stat/> information
209
227
  against the four-axis rubric below. Each axis is scored on an
@@ -275,7 +293,7 @@ Procedure
275
293
  </step>
276
294
 
277
295
  5. <step id="STEP 5: Render Blast-Radius Map"
278
- condition="<getopt-option-blast/> is equal `true` and <diff/> is NOT empty">
296
+ condition="<getopt-option-blast/> is equal `true` and <diff/> is NOT empty and <ase-project-boxing/> is not equal `black`">
279
297
 
280
298
  1. From the *same* captured <diff/> and <stat/>, *extract the
281
299
  touched modules* - the distinct changed source files (or their
@@ -67,6 +67,19 @@ Procedure
67
67
 
68
68
  2. <step id="STEP 2: Review Investigation">
69
69
 
70
+ <if condition="<ase-project-boxing/> is equal `black`">
71
+
72
+ The project source artifacts are classified as a *black box*, so
73
+ the user does *not* want the staged changes scrutinized or their
74
+ findings surfaced. *Skip* the entire review investigation: do
75
+ *not* invoke the `Agent` tool and do *not* read any change, set
76
+ <findings/> to the *empty* list, set <verdict/> to `SKIPPED (boxing:
77
+ black)`, set <summary/> to a *one-line* neutral restatement of the
78
+ change intent derived solely from STEP 1, and proceed *directly* to
79
+ STEP 3.
80
+
81
+ </if>
82
+
70
83
  First, use the following <template/> to give a hint on this step:
71
84
 
72
85
  <template>
@@ -102,13 +115,20 @@ Procedure
102
115
  severity floor below, so the floor only affects which findings are
103
116
  *rendered*, never the verdict.
104
117
 
105
- Then *apply the severity floor* selected via <getopt-option-severity/>
106
- (default `LOW`): define the ordinal rank `LOW`=1, `MEDIUM`=2,
107
- `HIGH`=3. *Keep* a finding in <findings/> if and only if its
108
- `severity` field is `ACCEPTED` *or* `rank(severity)` is greater than
109
- or equal to `rank(<getopt-option-severity/>)`; *silently drop* all
110
- other findings. With the default floor `LOW`, all findings are kept.
111
- `ACCEPTED` findings are *never* dropped.
118
+ Then determine the *effective severity floor* <floor/>: define the
119
+ ordinal rank `LOW`=1, `MEDIUM`=2, `HIGH`=3, start from
120
+ <floor><getopt-option-severity/></floor> (default `LOW`), and - if
121
+ <ase-project-boxing/> is equal `grey` - raise <floor/> to `MEDIUM`
122
+ whenever its current rank is below `rank(MEDIUM)` (grey boxing
123
+ surfaces only *material* findings of severity `MEDIUM` and above).
124
+ The floor affects only which findings are *rendered*, never the
125
+ <verdict/> derived above.
126
+
127
+ Then *apply the effective severity floor* <floor/>: *Keep* a finding
128
+ in <findings/> if and only if its `severity` field is `ACCEPTED` *or*
129
+ `rank(severity)` is greater than or equal to `rank(<floor/>)`;
130
+ *silently drop* all other findings. With the default floor `LOW`, all
131
+ findings are kept. `ACCEPTED` findings are *never* dropped.
112
132
 
113
133
  You *MUST* *NOT* output anything else in this STEP 2.
114
134