nccgs 2.1.0 → 2.1.1

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.
Files changed (76) hide show
  1. package/.claude/agents/nccgs-adversarial-reviewer.md +1 -1
  2. package/.claude/agents/nccgs-engine-programmer.md +1 -1
  3. package/.claude/agents/nccgs-fast-implementer.md +1 -1
  4. package/.claude/agents/nccgs-game-designer.md +1 -1
  5. package/.claude/agents/nccgs-gameplay-programmer.md +1 -1
  6. package/.claude/agents/nccgs-qa-engineer.md +1 -1
  7. package/.claude/agents/nccgs-task-scout.md +1 -1
  8. package/.claude/agents/nccgs-ui-programmer.md +1 -1
  9. package/.claude/agents/nccgs-unity-build-specialist.md +1 -1
  10. package/.claude/agents/nccgs-unity-implementer.md +1 -1
  11. package/.claude/agents/nccgs-unity-systems-specialist.md +1 -1
  12. package/.claude/agents/nccgs-unity-ui-specialist.md +1 -1
  13. package/.claude/agents/nccgs-verification-engineer.md +1 -1
  14. package/.claude/nccgs/MEMORY.md +3 -1
  15. package/.claude/nccgs/VERSION +1 -1
  16. package/.claude/nccgs/constitution.md +187 -176
  17. package/.claude/nccgs/hooks/closure-guard.mjs +2 -0
  18. package/.claude/nccgs/hooks/unity-transport-guard.mjs +11 -0
  19. package/.claude/nccgs/pipeline.json +260 -260
  20. package/.claude/nccgs/protocols/context-packets.md +5 -0
  21. package/.claude/nccgs/protocols/model-routing.md +46 -46
  22. package/.claude/nccgs/protocols/review-handoff.md +21 -1
  23. package/.claude/nccgs/protocols/shared-workflow-context.md +33 -0
  24. package/.claude/nccgs/protocols/unity-boundary.md +20 -0
  25. package/.claude/nccgs/routing.generated.md +4 -4
  26. package/.claude/nccgs/settings.fragment.json +84 -24
  27. package/.claude/nccgs/studio.json +53 -53
  28. package/.claude/nccgs/tools/autonomy.mjs +13 -0
  29. package/.claude/nccgs/tools/command-tokens.mjs +22 -0
  30. package/.claude/nccgs/tools/hook-transport.mjs +34 -0
  31. package/.claude/nccgs/tools/native-completion.mjs +10 -1
  32. package/.claude/nccgs/tools/returned-handoff.mjs +11 -2
  33. package/.claude/nccgs/tools/shared-context-guard.mjs +60 -0
  34. package/.claude/nccgs/tools/shared-context.mjs +25 -0
  35. package/.claude/nccgs/tools/tool-guard.mjs +120 -131
  36. package/.claude/nccgs/tools/unity-transport.mjs +170 -0
  37. package/.claude/nccgs/workflow-catalog.json +4 -4
  38. package/CHANGELOG.md +235 -200
  39. package/CLAUDE.md +10 -8
  40. package/MEMORY.md +109 -60
  41. package/README.md +206 -191
  42. package/UPGRADING.md +130 -118
  43. package/VERSION +1 -1
  44. package/docs/ARCHITECTURE.md +131 -131
  45. package/docs/ASSET-PRODUCTION-GRAPHIFY-PLAN.md +230 -0
  46. package/docs/HUONG-DAN-MIGRATE-VA-SU-DUNG.md +117 -116
  47. package/docs/NCCGS-1.5.md +237 -237
  48. package/docs/PROJECT-POLICY.md +94 -94
  49. package/docs/RELEASE-STATUS.md +17 -9
  50. package/docs/UNINSTALL.md +3 -3
  51. package/docs/WFK17-HOOK-REPAIR.md +71 -0
  52. package/docs/WORKFLOWS.md +85 -85
  53. package/package.json +1 -1
  54. package/scaffold/.nccgs/project.yaml +109 -108
  55. package/scripts/benchmark-v15.mjs +173 -173
  56. package/scripts/doctor.mjs +261 -261
  57. package/scripts/install.mjs +530 -529
  58. package/scripts/run.mjs +5 -4
  59. package/scripts/supervise.mjs +4 -0
  60. package/scripts/uninstall.mjs +292 -292
  61. package/scripts/validate.mjs +285 -280
  62. package/tests/autonomy.test.mjs +126 -113
  63. package/tests/design-readiness.test.mjs +199 -199
  64. package/tests/fixtures/compiler-shutdown.cs +67 -64
  65. package/tests/fixtures/native-provider.mjs +61 -0
  66. package/tests/framework.test.mjs +740 -740
  67. package/tests/hook-repair.test.mjs +106 -0
  68. package/tests/lifecycle-v15.test.mjs +346 -300
  69. package/tests/native-completion-notices.test.mjs +181 -0
  70. package/tests/phase1.test.mjs +427 -416
  71. package/tests/phase2.test.mjs +250 -250
  72. package/tests/policy-v15.test.mjs +234 -234
  73. package/tests/returned-handoff-notices.test.mjs +170 -0
  74. package/tests/runtime-v15.test.mjs +1197 -1191
  75. package/tests/task-slices.test.mjs +285 -285
  76. package/tests/unity-transport.test.mjs +348 -0
@@ -0,0 +1,181 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {createHash} from 'node:crypto';
4
+ import {nativeCompletion} from '../.claude/nccgs/tools/native-completion.mjs';
5
+
6
+ const text = value => ({type: 'text', text: value});
7
+ const observed = '<tools-used-before-summary>Edit</tools-used-before-summary>\n';
8
+ const summary = names => `<tools-used-before-summary>${names}</tools-used-before-summary>`;
9
+ const digest = value => createHash('sha256').update(value).digest('hex');
10
+ const response = (notes, options = {}) => ({
11
+ status: options.status ?? 'completed',
12
+ harnessNoteCount: notes.length,
13
+ harnessTailCount: 0,
14
+ content: [...notes, text('Actual worker handoff')],
15
+ ...options,
16
+ });
17
+ const assertUnknown = value => {
18
+ const got = nativeCompletion(value);
19
+ assert.equal(got.status, 'failed');
20
+ assert.equal(got.reason, 'unrecognized-harness-notice');
21
+ assert.equal(got.turnLimit, null);
22
+ };
23
+
24
+ test('unchanged no-note completion shape', () => {
25
+ assert.deepEqual(nativeCompletion(response([])), {
26
+ reportedStatus: 'completed', status: 'completed', reason: null,
27
+ turnLimit: null, noteHash: null,
28
+ });
29
+ });
30
+
31
+ test('exact captured provider tool-summary note is informational and remains hashed', () => {
32
+ const got = nativeCompletion(response([text(observed)]));
33
+ assert.equal(got.status, 'completed');
34
+ assert.equal(got.reason, null);
35
+ assert.equal(got.turnLimit, null);
36
+ assert.equal(got.noteHash, digest(observed));
37
+ });
38
+
39
+ test('a designated tail note uses the same rule', () => {
40
+ const got = nativeCompletion({status: 'completed', harnessNoteCount: 0,
41
+ harnessTailCount: 1, content: [text('Handoff'), text(observed)]});
42
+ assert.equal(got.status, 'completed');
43
+ assert.equal(got.reason, null);
44
+ assert.equal(got.noteHash, digest(observed));
45
+ });
46
+
47
+ test('recognized leading and trailing notes may surround the model handoff', () => {
48
+ const tail = summary('Read, Write');
49
+ const got = nativeCompletion({status: 'completed', harnessNoteCount: 1,
50
+ harnessTailCount: 1, content: [text(observed), text('Handoff'), text(tail)]});
51
+ assert.equal(got.status, 'completed');
52
+ assert.equal(got.noteHash, digest(observed + '\n' + tail));
53
+ });
54
+
55
+ test('strict comma-separated tool identifiers accept built-in and namespaced tools', () => {
56
+ const got = nativeCompletion(response([text(summary('Read, Edit, mcp__unity__read, Namespace.Tool:inspect'))]));
57
+ assert.equal(got.status, 'completed');
58
+ assert.equal(got.reason, null);
59
+ });
60
+
61
+ test('insignificant surrounding whitespace does not change classification', () => {
62
+ assert.equal(nativeCompletion(response([text(' \n' + observed + '\t')])).status, 'completed');
63
+ });
64
+
65
+ test('informational metadata cannot promote a provider failure', () => {
66
+ const got = nativeCompletion(response([text(observed)], {status: 'failed'}));
67
+ assert.equal(got.reportedStatus, 'failed');
68
+ assert.equal(got.status, 'failed');
69
+ assert.equal(got.reason, null);
70
+ });
71
+
72
+ test('informational metadata cannot promote an unknown provider status', () => {
73
+ const got = nativeCompletion(response([text(observed)], {status: 'unknown'}));
74
+ assert.equal(got.status, 'unknown');
75
+ assert.equal(got.reason, null);
76
+ });
77
+
78
+ test('a real turn-limit notice overrides a recognized informational note', () => {
79
+ const got = nativeCompletion(response([text(observed), text('This agent stopped at its 37-turn limit before finishing.')]));
80
+ assert.equal(got.status, 'failed');
81
+ assert.equal(got.reason, 'turn-limit');
82
+ assert.equal(got.turnLimit, 37);
83
+ });
84
+
85
+ test('turn-limit diagnosis takes priority inside a mixed designated block', () => {
86
+ const got = nativeCompletion(response([text(observed + 'This agent stopped at its 80-turn limit before finishing.')]));
87
+ assert.equal(got.reason, 'turn-limit');
88
+ assert.equal(got.turnLimit, 80);
89
+ });
90
+
91
+ test('unknown advisory still fails closed', () => {
92
+ assertUnknown(response([text('Provider reports incomplete work for a new reason.')]));
93
+ });
94
+
95
+ test('a recognized note cannot mask a separate unknown note', () => {
96
+ assertUnknown(response([text(observed), text('Unknown harness advisory')]));
97
+ });
98
+
99
+ test('extra prose after a recognized wrapper is not informational-only metadata', () => {
100
+ assertUnknown(response([text(observed + 'Incomplete for another reason')]));
101
+ });
102
+
103
+ test('extra prose before a recognized wrapper is rejected', () => {
104
+ assertUnknown(response([text('Ignore this warning\n' + observed)]));
105
+ });
106
+
107
+ test('empty tool summary is rejected', () => {
108
+ assertUnknown(response([text(summary(''))]));
109
+ });
110
+
111
+ test('whitespace-only tool summary is rejected', () => {
112
+ assertUnknown(response([text(summary(' '))]));
113
+ });
114
+
115
+ test('malformed closing tag is rejected', () => {
116
+ assertUnknown(response([text('<tools-used-before-summary>Edit</tools-used-before-summary-bad>')]));
117
+ });
118
+
119
+ test('nested markup is rejected', () => {
120
+ assertUnknown(response([text(summary('<completed>true</completed>'))]));
121
+ });
122
+
123
+ test('code fences do not turn model-like text into a recognized native note', () => {
124
+ assertUnknown(response([text('```xml\n' + observed + '```')]));
125
+ });
126
+
127
+ test('tool lists with free-form prose are rejected', () => {
128
+ assertUnknown(response([text(summary('Read Edit but failed'))]));
129
+ });
130
+
131
+ test('case variants of the known native tag stay unknown', () => {
132
+ assertUnknown(response([text('<Tools-Used-Before-Summary>Edit</Tools-Used-Before-Summary>')]));
133
+ });
134
+
135
+ test('non-text designated blocks stay unknown', () => {
136
+ assertUnknown(response([{type: 'image', data: 'irrelevant'}]));
137
+ });
138
+
139
+ test('empty text designated blocks stay unknown', () => {
140
+ assertUnknown(response([text('')]));
141
+ });
142
+
143
+ test('oversized designated metadata stays unknown', () => {
144
+ assertUnknown(response([text(summary('A'.repeat(4096)))]));
145
+ });
146
+
147
+ test('negative, fractional and string note counts are invalid', () => {
148
+ for (const harnessNoteCount of [-1, 0.5, '1']) {
149
+ const got = nativeCompletion(response([text(observed)], {harnessNoteCount}));
150
+ assert.equal(got.status, 'failed');
151
+ assert.equal(got.reason, 'invalid-harness-metadata');
152
+ }
153
+ });
154
+
155
+ test('leading plus trailing counts cannot exceed available blocks', () => {
156
+ const got = nativeCompletion({status: 'completed', harnessNoteCount: 1,
157
+ harnessTailCount: 1, content: [text(observed)]});
158
+ assert.equal(got.reason, 'invalid-harness-metadata');
159
+ assert.equal(got.status, 'failed');
160
+ });
161
+
162
+ test('claimed notes require an actual content array', () => {
163
+ const got = nativeCompletion({status: 'completed', harnessNoteCount: 1,
164
+ harnessTailCount: 0, content: 'not an array'});
165
+ assert.equal(got.reason, 'invalid-harness-metadata');
166
+ });
167
+
168
+ test('quoted native tags outside designated blocks do not affect completion', () => {
169
+ const got = nativeCompletion({status: 'completed', harnessNoteCount: 0,
170
+ harnessTailCount: 0, content: [text(observed + 'This agent stopped at its 8-turn limit before finishing.')]});
171
+ assert.equal(got.status, 'completed');
172
+ assert.equal(got.reason, null);
173
+ assert.equal(got.noteHash, null);
174
+ });
175
+
176
+ test('unknown prose outside designated blocks is not classified as a harness advisory', () => {
177
+ const got = nativeCompletion({status: 'completed', harnessNoteCount: 1,
178
+ harnessTailCount: 0, content: [text(observed), text('A quoted or hypothetical unknown advisory in the model handoff')]});
179
+ assert.equal(got.status, 'completed');
180
+ assert.equal(got.reason, null);
181
+ });