@donghyeonlee/jjamppong-harness 0.1.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.
Files changed (62) hide show
  1. package/AGENTS.md +77 -0
  2. package/CONTEXT.md +51 -0
  3. package/README.md +85 -0
  4. package/bin/jjamppong.js +123 -0
  5. package/handoff.md +13 -0
  6. package/harness/contracts/capability-catalog.yaml +128 -0
  7. package/harness/contracts/gate-contract-matrix.yaml +188 -0
  8. package/harness/contracts/installer-contract.yaml +79 -0
  9. package/harness/contracts/ledger-event.schema.yaml +79 -0
  10. package/harness/contracts/path-policy.schema.yaml +51 -0
  11. package/harness/contracts/permission-decision.schema.yaml +95 -0
  12. package/harness/contracts/task.schema.yaml +88 -0
  13. package/harness/docs/adr/.gitkeep +1 -0
  14. package/harness/docs/adr/2026-06-02-jjamppong-planning-gate.md +33 -0
  15. package/harness/docs/agents/domain.md +14 -0
  16. package/harness/docs/agents/issue-tracker.md +9 -0
  17. package/harness/docs/agents/matt-pocock-skills.md +60 -0
  18. package/harness/docs/agents/triage-labels.md +11 -0
  19. package/harness/docs/solutions/.gitkeep +1 -0
  20. package/harness/docs/tasks/active/.gitkeep +1 -0
  21. package/harness/docs/tasks/archive/.gitkeep +1 -0
  22. package/harness/docs/tasks/archive/index.md +5 -0
  23. package/harness/docs/tasks/index.md +13 -0
  24. package/harness/doctor/doctor.js +114 -0
  25. package/harness/installer/install.js +235 -0
  26. package/harness/lifecycle/lifecycle.js +133 -0
  27. package/harness/permission/permission-decision.js +377 -0
  28. package/harness/release/CHECKSUMS.sha256 +84 -0
  29. package/harness/release/RELEASE-NOTES.md +33 -0
  30. package/harness/release/SOURCE-MANIFEST.md +31 -0
  31. package/harness/rules/module-types.md +98 -0
  32. package/harness/rules/rules.md +220 -0
  33. package/harness/rules/workflow.md +252 -0
  34. package/harness/state/compound.md +7 -0
  35. package/harness/state/intake.md +11 -0
  36. package/harness/state/module-structure.md +13 -0
  37. package/harness/state/planning.md +21 -0
  38. package/harness/templates/module/module-state.md +13 -0
  39. package/harness/templates/task/archive-summary.md +19 -0
  40. package/harness/templates/task/events.jsonl.template +1 -0
  41. package/harness/templates/task/gate-ledger.md +21 -0
  42. package/harness/templates/task/implementation-approval.md +11 -0
  43. package/harness/templates/task/planning/00-current-planning-context.md +3 -0
  44. package/harness/templates/task/planning/01-grill-summary.md +3 -0
  45. package/harness/templates/task/planning/02-research-summary.md +3 -0
  46. package/harness/templates/task/planning/02b-compound-lookup.md +3 -0
  47. package/harness/templates/task/planning/02c-architecture-orientation.md +3 -0
  48. package/harness/templates/task/planning/03-prd.md +3 -0
  49. package/harness/templates/task/planning/04-issues.md +3 -0
  50. package/harness/templates/task/planning/05-module-structure.md +3 -0
  51. package/harness/templates/task/planning/06-writing-plan.md +3 -0
  52. package/harness/templates/task/planning/07-plan-review.md +3 -0
  53. package/harness/templates/task/planning-pack.md +23 -0
  54. package/harness/templates/task/task.yaml +10 -0
  55. package/harness/templates/task/verification.md +12 -0
  56. package/harness/verify/verify.js +271 -0
  57. package/module-template/MODULE.md +25 -0
  58. package/module-template/README.md +9 -0
  59. package/modules/.gitkeep +0 -0
  60. package/package.json +40 -0
  61. package/proposals/README.md +16 -0
  62. package/scripts/install-jjamppong-harness.ps1 +62 -0
@@ -0,0 +1,377 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const CAPABILITY_EFFECT_ALIASES = {
8
+ 'file.write.module': ['file.write.module', 'code', 'tests', 'fixtures'],
9
+ 'file.write.folder_skeleton': ['file.write.folder_skeleton', 'folder_skeleton'],
10
+ 'file.write.outside_modules': ['file.write.outside_modules', 'outside_modules_write'],
11
+ 'file.write.harness_core': ['file.write.harness_core', 'harness_core_change'],
12
+ 'network.live_target': ['network.live_target', 'live_access'],
13
+ 'network.web_research': ['network.web_research'],
14
+ 'package.install': ['package.install', 'package_install'],
15
+ 'git.commit': ['git.commit', 'git_commit'],
16
+ 'git.push': ['git.push', 'git_push'],
17
+ 'installer.install': ['installer.install'],
18
+ 'parallel.write': ['parallel.write'],
19
+ };
20
+
21
+ const SECRET_PATTERNS = [
22
+ /(^|[\\/])\.env(\.|$|[\\/])/i,
23
+ /(^|[\\/])credentials\.json$/i,
24
+ /(^|[\\/])service-account\.json$/i,
25
+ /(^|[\\/])cookies\.txt$/i,
26
+ /(^|[\\/])token\.txt$/i,
27
+ /private[-_ ]?key/i,
28
+ ];
29
+
30
+ const EXECUTABLE_SKELETON_EXTENSIONS = new Set([
31
+ '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
32
+ '.py', '.rb', '.go', '.rs', '.java', '.cs',
33
+ '.ps1', '.sh', '.bat', '.cmd',
34
+ '.json', '.toml', '.yaml', '.yml',
35
+ ]);
36
+
37
+ function parseArgs(argv) {
38
+ const args = {};
39
+ for (let i = 0; i < argv.length; i += 1) {
40
+ const token = argv[i];
41
+ if (!token.startsWith('--')) continue;
42
+ const key = token.slice(2);
43
+ const next = argv[i + 1];
44
+ if (!next || next.startsWith('--')) {
45
+ args[key] = true;
46
+ } else {
47
+ args[key] = next;
48
+ i += 1;
49
+ }
50
+ }
51
+ return args;
52
+ }
53
+
54
+ function normalizeWindowsDriveShorthand(rawPath) {
55
+ if (/^[A-Za-z]:[^\\/]/.test(rawPath)) {
56
+ return `${rawPath.slice(0, 2)}${path.sep}${rawPath.slice(2)}`;
57
+ }
58
+ return rawPath;
59
+ }
60
+
61
+ function normalizeForCompare(value) {
62
+ return value.replace(/[\\/]+/g, path.sep).toLowerCase();
63
+ }
64
+
65
+ function readTextIfExists(filePath) {
66
+ if (!filePath || !fs.existsSync(filePath)) return '';
67
+ return fs.readFileSync(filePath, 'utf8');
68
+ }
69
+
70
+ function parseTaskYaml(text) {
71
+ const task = {};
72
+ for (const line of text.split(/\r?\n/)) {
73
+ const match = line.match(/^\s*([A-Za-z0-9_.-]+):\s*(.+?)\s*$/);
74
+ if (!match) continue;
75
+ task[match[1]] = match[2].replace(/^["']|["']$/g, '');
76
+ }
77
+ return task;
78
+ }
79
+
80
+ function readJsonl(filePath) {
81
+ if (!filePath || !fs.existsSync(filePath)) return [];
82
+ const text = fs.readFileSync(filePath, 'utf8');
83
+ return text
84
+ .split(/\r?\n/)
85
+ .map((line) => line.trim())
86
+ .filter(Boolean)
87
+ .map((line, index) => {
88
+ try {
89
+ return JSON.parse(line);
90
+ } catch (error) {
91
+ const err = new Error(`Invalid JSONL at ${filePath}:${index + 1}: ${error.message}`);
92
+ err.code = 'INVALID_JSONL';
93
+ throw err;
94
+ }
95
+ });
96
+ }
97
+
98
+ function classifyPath(rawPath, repoRoot) {
99
+ if (!rawPath) return null;
100
+
101
+ const normalizedInput = normalizeWindowsDriveShorthand(rawPath);
102
+ const absolutePath = path.resolve(repoRoot, normalizedInput);
103
+ const projectRoot = path.resolve(repoRoot);
104
+ const compareAbsolute = normalizeForCompare(absolutePath);
105
+ const compareRoot = normalizeForCompare(projectRoot);
106
+ const isWithinProjectRoot = compareAbsolute === compareRoot || compareAbsolute.startsWith(`${compareRoot}${path.sep}`);
107
+ const relative = path.relative(projectRoot, absolutePath);
108
+ const normalizedRelative = normalizeForCompare(relative);
109
+ const isWithinModules = isWithinProjectRoot && (normalizedRelative === 'modules' || normalizedRelative.startsWith(`modules${path.sep}`));
110
+
111
+ let realpath = absolutePath;
112
+ let escapeDetected = false;
113
+ let hasSymlinkOrJunction = false;
114
+ try {
115
+ realpath = fs.existsSync(absolutePath) ? fs.realpathSync.native(absolutePath) : absolutePath;
116
+ const compareRealpath = normalizeForCompare(realpath);
117
+ escapeDetected = !(compareRealpath === compareRoot || compareRealpath.startsWith(`${compareRoot}${path.sep}`));
118
+ } catch {
119
+ realpath = absolutePath;
120
+ }
121
+
122
+ const parts = absolutePath.split(/[\\/]+/);
123
+ let probe = path.parse(absolutePath).root;
124
+ for (const part of parts.slice(probe ? 1 : 0)) {
125
+ if (!part) continue;
126
+ probe = path.join(probe, part);
127
+ try {
128
+ if (fs.existsSync(probe) && fs.lstatSync(probe).isSymbolicLink()) {
129
+ hasSymlinkOrJunction = true;
130
+ }
131
+ } catch {
132
+ // Missing path segments are normal for planned writes.
133
+ }
134
+ }
135
+
136
+ const windowsAds = /^[A-Za-z]:/.test(rawPath)
137
+ ? /(^|[\\/])[^\\/]+:[^\\/]+$/.test(rawPath.slice(2))
138
+ : /(^|[\\/])[^\\/]+:[^\\/]+$/.test(rawPath);
139
+
140
+ return {
141
+ raw_path: rawPath,
142
+ normalized_path: normalizedInput,
143
+ absolute_path: absolutePath,
144
+ realpath,
145
+ project_root: projectRoot,
146
+ relative_path: relative,
147
+ is_within_project_root: isWithinProjectRoot,
148
+ is_within_modules: isWithinModules,
149
+ has_symlink_or_junction: hasSymlinkOrJunction,
150
+ escape_detected: escapeDetected,
151
+ has_alternate_data_stream: windowsAds,
152
+ };
153
+ }
154
+
155
+ function isSecretPath(rawPath) {
156
+ if (!rawPath) return false;
157
+ return SECRET_PATTERNS.some((pattern) => pattern.test(rawPath));
158
+ }
159
+
160
+ function eventPayload(event) {
161
+ return event && typeof event.payload === 'object' && event.payload !== null ? event.payload : {};
162
+ }
163
+
164
+ function gateStatus(events, gateId) {
165
+ for (let i = events.length - 1; i >= 0; i -= 1) {
166
+ const event = events[i];
167
+ const payload = eventPayload(event);
168
+ if (event.event_type === 'gate_status_change' && payload.gate_id === gateId) {
169
+ return payload.status || null;
170
+ }
171
+ if (event.event_type === 'approval_decision' && payload.gate_id === gateId) {
172
+ return payload.status || null;
173
+ }
174
+ }
175
+ return null;
176
+ }
177
+
178
+ function capabilityAliases(capability) {
179
+ return CAPABILITY_EFFECT_ALIASES[capability] || [capability];
180
+ }
181
+
182
+ function hasCapabilityInApproval(approval, capability) {
183
+ const payload = eventPayload(approval);
184
+ const aliases = capabilityAliases(capability);
185
+ const effects = payload.effects || {};
186
+ const capabilities = Array.isArray(payload.capabilities) ? payload.capabilities : [];
187
+ return aliases.some((alias) => effects[alias] === true || capabilities.includes(alias));
188
+ }
189
+
190
+ function targetPathCovered(approval, pathInfo) {
191
+ if (!pathInfo) return true;
192
+ const payload = eventPayload(approval);
193
+ const targetPaths = payload.target_paths || {};
194
+ const allowed = Array.isArray(targetPaths.allowed) ? targetPaths.allowed : [];
195
+ if (allowed.length === 0) return false;
196
+
197
+ const relative = normalizeForCompare(pathInfo.relative_path);
198
+ return allowed.some((pattern) => {
199
+ const normalizedPattern = normalizeForCompare(pattern);
200
+ if (normalizedPattern.endsWith(`${path.sep}**`)) {
201
+ const prefix = normalizedPattern.slice(0, -3);
202
+ return relative === prefix || relative.startsWith(`${prefix}${path.sep}`);
203
+ }
204
+ if (normalizedPattern.endsWith('/**')) {
205
+ const prefix = normalizedPattern.slice(0, -3).replace(/\//g, path.sep);
206
+ return relative === prefix || relative.startsWith(`${prefix}${path.sep}`);
207
+ }
208
+ return relative === normalizedPattern;
209
+ });
210
+ }
211
+
212
+ function invalidatedApprovalIds(events) {
213
+ const ids = new Set();
214
+ for (const event of events) {
215
+ if (event.event_type !== 'invalidation') continue;
216
+ const payload = eventPayload(event);
217
+ if (payload.approval_event_id) ids.add(payload.approval_event_id);
218
+ if (payload.approval_id) ids.add(payload.approval_id);
219
+ }
220
+ return ids;
221
+ }
222
+
223
+ function findApproval(events, capability, pathInfo) {
224
+ const invalidated = invalidatedApprovalIds(events);
225
+ return events.find((event) => {
226
+ if (event.event_type !== 'approval_decision') return false;
227
+ const payload = eventPayload(event);
228
+ if (payload.status !== 'approved') return false;
229
+ if (invalidated.has(event.event_id) || invalidated.has(payload.approval_id)) return false;
230
+ return hasCapabilityInApproval(event, capability) && targetPathCovered(event, pathInfo);
231
+ });
232
+ }
233
+
234
+ function decision(decisionValue, reason, extras = {}) {
235
+ return {
236
+ decision_id: `decision-${Date.now()}`,
237
+ schema_version: '0.1.0',
238
+ decision: decisionValue,
239
+ reason,
240
+ required_next_action: extras.required_next_action || { type: decisionValue === 'allow' ? 'run_verify' : 'ask_user' },
241
+ matched_events: extras.matched_events || [],
242
+ path_policy_result: extras.path_policy_result || null,
243
+ capabilities: extras.capabilities || {},
244
+ };
245
+ }
246
+
247
+ function decide(input) {
248
+ const repoRoot = path.resolve(input.repoRoot || process.cwd());
249
+ const taskDir = input.taskDir ? path.resolve(repoRoot, input.taskDir) : null;
250
+ const eventsPath = input.eventsPath || (taskDir ? path.join(taskDir, 'events.jsonl') : null);
251
+ const taskYamlPath = input.taskYamlPath || (taskDir ? path.join(taskDir, 'task.yaml') : null);
252
+ const capabilityCatalogPath = input.capabilityCatalogPath || path.join(repoRoot, 'harness', 'contracts', 'capability-catalog.yaml');
253
+ const capabilityCatalog = readTextIfExists(capabilityCatalogPath);
254
+
255
+ const task = parseTaskYaml(readTextIfExists(taskYamlPath));
256
+ const events = readJsonl(eventsPath);
257
+ const capability = input.capability;
258
+ const pathInfo = classifyPath(input.path, repoRoot);
259
+
260
+ const base = {
261
+ requested_action: input.action || null,
262
+ capability,
263
+ task_type: task.task_type || task.type || null,
264
+ path_policy_result: pathInfo,
265
+ };
266
+
267
+ if (!capability) {
268
+ return { ...base, ...decision('block', 'Missing capability.') };
269
+ }
270
+
271
+ if (!capabilityCatalog.includes(`${capability}:`) && !capabilityCatalog.includes(capability)) {
272
+ return { ...base, ...decision('deny', `Unknown capability: ${capability}`) };
273
+ }
274
+
275
+ if (capability === 'file.read.secret' || isSecretPath(input.path)) {
276
+ return { ...base, ...decision('deny', 'Secrets are deny-by-default.', { path_policy_result: pathInfo }) };
277
+ }
278
+
279
+ if (pathInfo) {
280
+ if (!pathInfo.is_within_project_root || pathInfo.escape_detected) {
281
+ return { ...base, ...decision('deny', 'Path escapes project root.', { path_policy_result: pathInfo }) };
282
+ }
283
+ if (pathInfo.has_alternate_data_stream) {
284
+ return { ...base, ...decision('deny', 'Windows alternate data stream paths are denied.', { path_policy_result: pathInfo }) };
285
+ }
286
+ if (pathInfo.has_symlink_or_junction) {
287
+ return { ...base, ...decision('deny', 'Symlink or junction path requires explicit safe handling.', { path_policy_result: pathInfo }) };
288
+ }
289
+ }
290
+
291
+ if (capability === 'file.read.project') {
292
+ const researchStatus = gateStatus(events, 'research');
293
+ if (researchStatus === 'open' || researchStatus === 'completed' || researchStatus === 'approved') {
294
+ return { ...base, ...decision('allow', 'Project read is allowed during research after grill.', { path_policy_result: pathInfo }) };
295
+ }
296
+ return { ...base, ...decision('deny', 'Project read before grill/research is denied.', { path_policy_result: pathInfo }) };
297
+ }
298
+
299
+ if (capability === 'network.web_research') {
300
+ const researchStatus = gateStatus(events, 'research');
301
+ if (researchStatus === 'open' || researchStatus === 'completed' || researchStatus === 'approved') {
302
+ return { ...base, ...decision('allow', 'General web research is allowed during research.', { path_policy_result: pathInfo }) };
303
+ }
304
+ return { ...base, ...decision('deny', 'Web research requires the research gate.', { path_policy_result: pathInfo }) };
305
+ }
306
+
307
+ if (capability === 'file.write.folder_skeleton') {
308
+ if (pathInfo && !pathInfo.is_within_modules) {
309
+ return { ...base, ...decision('deny', 'Folder skeleton writes must stay under modules/.', { path_policy_result: pathInfo }) };
310
+ }
311
+ const extension = path.extname(input.path || '').toLowerCase();
312
+ if (EXECUTABLE_SKELETON_EXTENSIONS.has(extension)) {
313
+ return { ...base, ...decision('deny', 'folder_skeleton cannot create executable source, runtime config, tests, or fixtures.', { path_policy_result: pathInfo }) };
314
+ }
315
+ }
316
+
317
+ if (capability === 'file.write.module' && pathInfo && !pathInfo.is_within_modules) {
318
+ return { ...base, ...decision('deny', 'Product module writes must stay under modules/ unless outside_modules_write is approved.', { path_policy_result: pathInfo }) };
319
+ }
320
+
321
+ if (capability === 'file.write.harness_core') {
322
+ const taskType = task.task_type || task.type;
323
+ if (taskType !== 'template_maintenance' && taskType !== 'harness_update') {
324
+ return { ...base, ...decision('proposal_required', 'Harness-core writes require template_maintenance or harness_update; product tasks must create proposals.', { path_policy_result: pathInfo }) };
325
+ }
326
+ }
327
+
328
+ const approval = findApproval(events, capability, pathInfo);
329
+ if (!approval) {
330
+ return {
331
+ ...base,
332
+ ...decision('deny', `No exact-scope approval found for ${capability}.`, {
333
+ path_policy_result: pathInfo,
334
+ required_next_action: { type: 'ask_user', gate_id: 'implementation', missing_capabilities: [capability] },
335
+ }),
336
+ };
337
+ }
338
+
339
+ return {
340
+ ...base,
341
+ ...decision('allow', `Allowed by approval event ${approval.event_id}.`, {
342
+ matched_events: [approval.event_id],
343
+ path_policy_result: pathInfo,
344
+ capabilities: { [capability]: 'approved' },
345
+ }),
346
+ };
347
+ }
348
+
349
+ function main() {
350
+ const args = parseArgs(process.argv.slice(2));
351
+ if (args.help) {
352
+ process.stdout.write('Usage: node permission-decision.js --repo-root <path> --task <task-dir> --capability <capability> [--path <path>] [--action <action>]\n');
353
+ return;
354
+ }
355
+ const result = decide({
356
+ repoRoot: args['repo-root'] || process.cwd(),
357
+ taskDir: args.task,
358
+ eventsPath: args.events,
359
+ taskYamlPath: args['task-yaml'],
360
+ capability: args.capability,
361
+ path: args.path,
362
+ action: args.action,
363
+ });
364
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
365
+ process.exitCode = result.decision === 'allow' ? 0 : 2;
366
+ }
367
+
368
+ if (require.main === module) {
369
+ main();
370
+ }
371
+
372
+ module.exports = {
373
+ decide,
374
+ classifyPath,
375
+ parseTaskYaml,
376
+ readJsonl,
377
+ };
@@ -0,0 +1,84 @@
1
+ b75c124a428acc599b3df9199ff9a7561fd3e8f4cfa798ac2e2abbe52deab91b .gitignore
2
+ ecb31bd77b238338a71f127d5c61debdd945cc3183531f82e17e3c1c06eb98b0 AGENTS.md
3
+ 0c4e9616d43c4c22700e342ac257cbf56206c9a4128f8804a92e527c0ff6d293 bin/jjamppong.js
4
+ dd42041866d0a2dbe3b04855460869b3933eda8dab6195fa14847854198a66bd CONTEXT.md
5
+ d72f8bf2dd6a12b2fbd770717de9a8b47f688d4b62c64b6226fea5eb736ad6c3 handoff.md
6
+ 9581d97900a5f8632b696132f11c2e65009c74eca5e76923350d076aaf714de9 harness/contracts/capability-catalog.yaml
7
+ 039108a852bbb2655491cc3db301939fc8e76a90b455a4b052940f2469fb7ea5 harness/contracts/gate-contract-matrix.yaml
8
+ ca6de7e2ca1d64180098423a93abca089e5375632af200dccf8aff6be120e6ae harness/contracts/installer-contract.yaml
9
+ 0c6fb5a1ba1c23605cac383f71bd875d3f9c40508d454fd38f700fb0e5331dd7 harness/contracts/ledger-event.schema.yaml
10
+ 94601bc3d6ab2f401bc2b457719f03cad6af7f3199ed205ff2f39a0484ca8d36 harness/contracts/path-policy.schema.yaml
11
+ 2a51483b3c30ea81635b08941c9bb48732b0243bb17968f8b7ad1d26ee4b4ed6 harness/contracts/permission-decision.schema.yaml
12
+ fa7cab6e4eda0e68a3319dea5a8c8c20067f1ee644ec95705239ab495a24e799 harness/contracts/task.schema.yaml
13
+ 1f1aeeea426e55fde36e7df1bdaef4230d4a7a77275f626e999a7a940c55b5bd harness/docs/adr/.gitkeep
14
+ ef1241c93309ad939963abc2f18907dae500747eb694efbc23b5d849f9ba1f0b harness/docs/adr/2026-06-02-jjamppong-planning-gate.md
15
+ 42d7fe25380a09024478c164aa1ff80b331f24d71a993f156f3687a26729f506 harness/docs/agents/domain.md
16
+ 07ce308c98c497ca8e2f5f03daac033762234dca20174e1aa976ba088defe2e1 harness/docs/agents/issue-tracker.md
17
+ c1eb34b679d817f7dbf4e6a9a799c2c76667e8eebf4decf2e07515e6a1ae34cf harness/docs/agents/matt-pocock-skills.md
18
+ 2d46304ad140ea4b688c157c274f44d40680b2131e583c92b3aea9964a043ab0 harness/docs/agents/triage-labels.md
19
+ 1f1aeeea426e55fde36e7df1bdaef4230d4a7a77275f626e999a7a940c55b5bd harness/docs/solutions/.gitkeep
20
+ 1f1aeeea426e55fde36e7df1bdaef4230d4a7a77275f626e999a7a940c55b5bd harness/docs/tasks/archive/.gitkeep
21
+ f9f3d984f71af625e6797569201fde1b6457842e1273084558cc437289d6d05a harness/docs/tasks/archive/index.md
22
+ 1f50310ee56eb4013a134da9553f2fe7abd1963bec94189d9415d7d899a094e8 harness/docs/tasks/index.md
23
+ c256f559b57cf3b16fcff2e3052f400e57c5a40cd2b2ed435e4960f1b6dc2d81 harness/doctor/doctor.js
24
+ a79fc4899f0a8411089b9a0d53ee01ff9bcc344f334c671eff1e9caa690af49b harness/installer/install.js
25
+ df387cf8e7dc5434c42bf3e24cb63367f552a7c0d30298fea0ef5962401fd34c harness/lifecycle/lifecycle.js
26
+ 8c78570ebe4fff482dde6f579e42218514fd4491cd599be57a6e12d2b69a0d80 harness/permission/permission-decision.js
27
+ 5da9b3f5c900f1ef364acdf02f7e05d666aeae5a362b01161f3c1fcca916552c harness/release/RELEASE-NOTES.md
28
+ 1060a7637db11382e5d93802b68a0cf7b801a6c66de2fc9325e876cc20cf669a harness/release/SOURCE-MANIFEST.md
29
+ 78c889ea476f2990a8c11e80e4e13c32807d8bc32866552b796846f41e08899f harness/rules/module-types.md
30
+ 5fff69c7bf8a351da7b92110dd3f4551611e67d23842d92edd2ef2df79b6b2d5 harness/rules/rules.md
31
+ a88f8bc9f9cd5e7bbe5f22da06183536e589e55d0498c5dd407b62b757b7ad27 harness/rules/workflow.md
32
+ 918e058ceab9f5541901696b9cf42ba3a42e6c73634eb4805933f49e859a8b0f harness/state/compound.md
33
+ 7b640f33fbc098d6b53d5e5cb46e11de8ecffc9058e47c1bb28e21b3ecaaf422 harness/state/intake.md
34
+ 057e196e889d59ef2d9da2a4aab58ab4db7835027b35e0f9365be95172e714d5 harness/state/module-structure.md
35
+ 9c3396e20c7f357a21221855426c655f2a3fa1243e9efd079106c549b5b2eef1 harness/state/planning.md
36
+ e8cf6147a5c1a9356a5e951c9fcb560b2bbd3f1e8b94dea8f197ca1849c12fd5 harness/templates/module/module-state.md
37
+ 206655c7bcfbe478a99f540b3a2f0296abbbc6d92bbd6b2bf30d93b1139ab984 harness/templates/task/archive-summary.md
38
+ 5249985ede496c7aa57c4d7a84f9982a279cbf18ed2b12bf6738a8cba3e55d66 harness/templates/task/events.jsonl.template
39
+ 0fcfef0b70e77153bfd88703df66c549841eae85018bd3c40c85444fbfa62f05 harness/templates/task/gate-ledger.md
40
+ 2425b79a3bc0f6c4737b1fc5ea1d86740b39d66dfd44232b75f7f6ddb1f0a7f4 harness/templates/task/implementation-approval.md
41
+ ce9f74f4ea7134c596217cb45c8a080cedde5112e7605c059575526682490d5b harness/templates/task/planning-pack.md
42
+ 72f61e4160171f8b9c555cb3a93b5dbb5bb16acb50a898a45810cc9a2c7c2a6c harness/templates/task/planning/00-current-planning-context.md
43
+ deaae829ebe6512a4fc26a258fa3e0c91d943d599bf3c5b7cfb3d02edd382841 harness/templates/task/planning/01-grill-summary.md
44
+ 17dd7a3dabcfc8b5a60cf63547ac80c871d8b49260724c0d7b2bc2dd6ed1ffb1 harness/templates/task/planning/02-research-summary.md
45
+ b901c62d779f99d9fddaf9eb6d9a20ebe9e0c2b6442a4d2585b4def34684c7a9 harness/templates/task/planning/02b-compound-lookup.md
46
+ 8ad2250f678a9c6bc446a28b91c29847839ed37fd40c73b0584152af1fed34f6 harness/templates/task/planning/02c-architecture-orientation.md
47
+ 1d055cd7e742091175811e2dbaf92db665001a47f53d522f20caa55ac11dfb0d harness/templates/task/planning/03-prd.md
48
+ 3141444e80168e0b4ab994da8d9d7cf20dcda14e3054eeb828d6bd19a7b6de75 harness/templates/task/planning/04-issues.md
49
+ 53ed4bc8d63213b040d4149c675fc0f4c74853f298da1194d0d2b6bd9b97942a harness/templates/task/planning/05-module-structure.md
50
+ 836019cf4fd420f0b55ff8ce9ea45af7ae94ea60f77a6a429c0227c8daa1475c harness/templates/task/planning/06-writing-plan.md
51
+ 402a08e86223ec7763b7a6dc7a98fe26ca302c9b51252e5942bf7ebfa9e78939 harness/templates/task/planning/07-plan-review.md
52
+ f098147d0d1bb082398aa71f41b1f9abed0065e84ab8c246cc9e16e5b1241484 harness/templates/task/task.yaml
53
+ b63627c8f9ff29f9156eab08a4e301ddc63593dd520973e3b39423457406de42 harness/templates/task/verification.md
54
+ aaf542c6904d52a428a7f57330ac623ec742baf6dfc098ea27ae4d8bb0386eb6 harness/verify/verify.js
55
+ 8c73f3983c33100122c46f77bd2d1c578efa0d8daa4e8afde7a1a145320223d8 module-template/MODULE.md
56
+ 93dc8a396d0c0235225fc576d8f11efdb6be8d21b0496f5cbfb951a82d3fefbc module-template/README.md
57
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 modules/.gitkeep
58
+ 4b10dbbf1569206d9c5d8163558fde1053fd8d272c4d92aea16920be1a66f82d package.json
59
+ 993d437fa79d30975560b816d5e9cca1a335374ff2dd1ea2d639d38e34cd0bbb plugins/agents-md-management/.codex-plugin/plugin.json
60
+ 29591d56d518ecc5523bc39b0785590e5dc46b766c6b53575706d8f9483dea50 plugins/agents-md-management/scripts/discover_agents_chain.py
61
+ 74745a5c8e8c4ed32eb13dc0d8023f0f43a7dd09aa40cc823018d4958381843f plugins/agents-md-management/scripts/score_agents_md.py
62
+ 23a95a18e7517a748fc2b181ec49829ad4125ca6a3b89ad242e39e3225e5e30d plugins/agents-md-management/skills/agents-md-chain-audit/references/codex-instruction-chain.md
63
+ 5109b6b26a9767046fc38b7dfacff7474c956f1a004d8f20f83ef787f721c48e plugins/agents-md-management/skills/agents-md-chain-audit/SKILL.md
64
+ 72557216d0e3f68f012309aa872f87470785b8f08eae7ddb46c8d0224b386023 plugins/agents-md-management/skills/agents-md-improver/references/quality-criteria.md
65
+ 8f9bf0ddeb95287e5de51cccf8372c99786c9a3887ea90797a9dbe82438fb3a3 plugins/agents-md-management/skills/agents-md-improver/SKILL.md
66
+ 10c0e7a0f94cf745f2e1e664ff7dde6cd9c47240c33885737bfe69aee9dbe13c plugins/agents-md-management/skills/revise-agents-md/references/routing-rules.md
67
+ 1dc59fb7578e0d8a224bd6a07840bbb3d1399b0a243df449ed5b4a5cebccc073 plugins/agents-md-management/skills/revise-agents-md/SKILL.md
68
+ 0b8771cf56a4cff9bd1e014886c8b26706272efc812fe1ff3dd2b8ab3c81b2e9 plugins/agents-md-management/tests/test_discover_agents_chain.py
69
+ 802a5a5808aa976da3a531b33db354afb65885c452bed7797b391a4b01745c72 plugins/agents-md-management/tests/test_score_agents_md.py
70
+ 3c3a99a59412ac89fb08e272c8a922a3bbf139db7309cce03e53017718a07ca5 proposals/README.md
71
+ 24eb211a753d08817e9e4fbc7587e787009e73c21cbea89fb5d27608ab62eaee README.md
72
+ d393d4be478abe78ee792efa25dcdbb42ddb114d4e95048aa394e39a9c7a07b8 scripts/install-jjamppong-harness.ps1
73
+ 68ec4952f3989fe8d5e0c44a77a701de1e4d1cc7d6ad4ab27480a26b11c2effc tests/contracts/regression-catalog.yaml
74
+ fdd103d46ca579c41b98c8f551ae012ac5ce2f72a6881d2f3d0e03a28163bf26 tests/contracts/run-all.ps1
75
+ c885ac843bc23134c6d2abb4672bdeb03628f8d65be76189bfe64eaa274f1575 tests/contracts/run-contract-regression.ps1
76
+ 98d9a4151052a3b570d0cf37dbb632ccab4eaeb736622a108f7a81a5b90a10f9 tests/contracts/test-agents-readme.ps1
77
+ a4d992330cae87f6491473fb2c1791e76850b7fc82592713e22bbcd1e55cfe6c tests/contracts/test-installer-package.ps1
78
+ b54f09f6eeca88f7d9f62b9132742c956bda8acd8420edab908d2588a9e09b46 tests/contracts/test-lifecycle-templates.ps1
79
+ b3e4d8587ddf83e63244694c9a2920f67a91281e6d3c1b18416c68ef6d7a1cde tests/contracts/test-permission-decision.ps1
80
+ 68d8b391a0c4b2931f1c583a3d72d43bb578ca142c75f0bd5ac72ad9dbed3a7f tests/contracts/test-release-candidate.ps1
81
+ a7c26a1c8fd6c6cedcb0c0eb881f3d163f488796b62671db45045bf3a5ce07ec tests/contracts/test-verify-doctor.ps1
82
+ a8bb7e23ccafd8d9aa6e9204dc07671c507e56228c17db3667bf6dd6770ce23e tests/contracts/test-workflow-rules.ps1
83
+ d94fbcb64f6c8e1067c4735161d04b9e2c54ab8e0c5e517498ac56abbd1fedf0 tests/contracts/verify-coverage-map.yaml
84
+ e8e1dab1fc27e76a303cc65291188a61f9d6ffb75381f2aefa2ec51038c70294 tests/fixtures/contracts/required-p0-regressions.txt
@@ -0,0 +1,33 @@
1
+ # Release Notes
2
+
3
+ ## 0.1.0 Release Candidate
4
+
5
+ This release candidate rebuilds the harness around executable contracts instead of prose-only rules.
6
+
7
+ Included:
8
+
9
+ - Contract schemas and regression catalog.
10
+ - PermissionDecision MVP for capability and path decisions.
11
+ - Verify/doctor commands for installed projects.
12
+ - npm/npx installer with install-only stop behavior and `harness.lock.yaml`.
13
+ - Task templates based on `events.jsonl` as canonical state.
14
+ - Short `AGENTS.md` and Korean README.
15
+ - Empty `harness/docs/tasks/active/` template state.
16
+
17
+ Important behavior:
18
+
19
+ - Installation stops after install and verify.
20
+ - `grill` happens before project reads for product planning.
21
+ - `research` is canonical; `evidence_check` is only a legacy alias.
22
+ - `plan_review completed` does not approve implementation.
23
+ - `module_structure` does not create folders.
24
+ - `folder_skeleton` does not create code, tests, fixtures, runtime config, package files, live access, commits, or pushes.
25
+ - Package install, live target access, commit, and push each require separate explicit approval.
26
+
27
+ Not included:
28
+
29
+ - Active product tasks.
30
+ - Product code.
31
+ - GitHub repository creation.
32
+ - Automatic commit or push.
33
+ - OuroSuper as an active workflow.
@@ -0,0 +1,31 @@
1
+ # Source Manifest
2
+
3
+ This manifest describes the release candidate source surfaces for Jjamppong Harness.
4
+
5
+ ## Canonical Surfaces
6
+
7
+ - `harness/contracts/`: machine-readable gate, permission, path, installer, and task contracts.
8
+ - `tests/contracts/`: regression tests that prove the contracts stay enforced.
9
+ - `harness/permission/`: PermissionDecision MVP.
10
+ - `harness/verify/`: installed-project verifier.
11
+ - `harness/doctor/`: read-only diagnosis and proposal generator.
12
+ - `harness/installer/` and `bin/`: npm/npx installer and CLI entry points.
13
+ - `harness/templates/`: task and module artifact templates.
14
+ - `harness/rules/`: human-readable projections of the contracts.
15
+ - `AGENTS.md`: short runtime instruction entry point.
16
+ - `README.md`: Korean human guide.
17
+
18
+ ## Non-Canonical Or Isolated Surfaces
19
+
20
+ - `harness/docs/tasks/active/` must be empty except `.gitkeep` in the template release candidate.
21
+ - `harness/docs/tasks/archive/` is cold storage and should be read through summaries/indexes first.
22
+ - `source-history/` is audit trail only if present. It must not outrank `FINAL-PLAN.md`, contracts, or tests.
23
+ - Historical references to OuroSuper may appear only as rationale or invalid nested-install examples, never as an active route.
24
+
25
+ ## Alias Rule
26
+
27
+ `research` is the canonical gate id. `evidence_check` is a legacy alias only.
28
+
29
+ ## Release Surface
30
+
31
+ Release checksums are recorded in `harness/release/CHECKSUMS.sha256`.
@@ -0,0 +1,98 @@
1
+ # Module Types
2
+
3
+ ## Principle
4
+
5
+ This harness does not ship fixed module type names.
6
+
7
+ Each project defines its own module types and folder standards through the Full Workflow.
8
+
9
+ Module structure design is a substantive planning decision inside the Full Workflow.
10
+
11
+ It is handled by the Module Structure Gate, not by an ad hoc side flow.
12
+
13
+ When a project has no approved module structure, the first product request uses the Module Structure Gate before product PRD, issue decomposition, writing plans, module folders, or product code.
14
+
15
+ ## When To Use This Rule
16
+
17
+ Read this file when:
18
+
19
+ - Starting module structure design
20
+ - Creating a new module
21
+ - Changing module folders
22
+ - Changing module type names
23
+ - Deciding whether a folder is standard or project-specific
24
+
25
+ ## Required Process
26
+
27
+ Module type and folder standards are substantive work.
28
+
29
+ They must follow:
30
+
31
+ ```text
32
+ Request Intake
33
+ -> setup-matt-pocock-skills Readiness Check
34
+ -> Grill Routing And Completion Gate
35
+ -> Grill Result Record
36
+ -> Module Structure Gate
37
+ -> to-prd
38
+ -> User PRD Approval
39
+ -> to-issues
40
+ -> User Issue Approval
41
+ -> Task Brief
42
+ -> Superpowers Writing Plans
43
+ -> Mandatory Plan Review Question
44
+ -> Implementation / Apply
45
+ -> Verification
46
+ -> ce-compound
47
+ -> Archive Task Artifacts
48
+ -> Learning Update Question
49
+ ```
50
+
51
+ ## Output Location
52
+
53
+ Record approved module type and folder standards in:
54
+
55
+ ```text
56
+ harness/state/module-structure.md
57
+ ```
58
+
59
+ If the request cannot create or change product module folders or product code, record `Module Structure Gate: not applicable` in `harness/docs/tasks/active/<slug>/grill.md` and do not ask module-structure questions.
60
+
61
+ ## Ledger Requirement
62
+
63
+ Module structure approval must be traceable to the active task gate ledger.
64
+
65
+ For the task that creates or changes module structure, the task gate ledger must contain `Gate id: module_structure` with `Status: approved` before `harness/state/module-structure.md` is treated as approved.
66
+
67
+ Future tasks may rely on `harness/state/module-structure.md` as the long-lived module structure record, but they must record one of these task-local entries before product PRD drafting:
68
+
69
+ - `Gate id: module_structure` with `Status: existing-approved`: existing approved structure applies.
70
+ - `Gate id: module_structure` with `Status: approved`: this task approved a new or changed structure.
71
+ - `Gate id: module_structure` with `Status: not-applicable`: this task cannot create or change product module folders or product code.
72
+
73
+ The state file records the approved structure. The task ledger records the user response that opened the gate.
74
+
75
+ ## Required Content
76
+
77
+ `harness/state/module-structure.md` must record:
78
+
79
+ - Project module types
80
+ - Folder set for each module type
81
+ - Active modules
82
+ - Deferred modules
83
+ - Any extra folder and the reason it exists
84
+
85
+ ## Creation Rule
86
+
87
+ Do not create a module under `modules/` unless its module type and folder set are recorded in `harness/state/module-structure.md`.
88
+
89
+ ## Template Rule
90
+
91
+ Every module starts from:
92
+
93
+ ```text
94
+ module-template/MODULE.md
95
+ module-template/README.md
96
+ ```
97
+
98
+ Then the approved folder set from `harness/state/module-structure.md` is added.